using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using TNodeCore.Editor.Blackboard;
using TNodeCore.Editor.EditorPersistence;
using TNodeCore.Editor.NodeGraphView;
using TNodeCore.Runtime.Attributes;
using TNodeCore.Runtime.Models;
using TNodeGraphViewImpl.Editor.GraphBlackboard;
using TNodeGraphViewImpl.Editor.NodeGraphView;
using TNodeGraphViewImpl.Editor.NodeViews;
using UnityEditor;
using UnityEngine;
namespace TNodeGraphViewImpl.Editor.Cache{
///
/// Internal singleton class for caching TNodeCore reflection Data.
///
internal class NodeEditorTypeDictionary:Dictionary{
private class NodeEditorTypeDictionaryComparer : IEqualityComparer{
public bool Equals(Type x, Type y){
return x?.ToString() == y?.ToString();
}
public int GetHashCode(Type obj){
return obj.ToString().GetHashCode();
}
}
public NodeEditorTypeDictionary():base(new NodeEditorTypeDictionaryComparer()){
}
}
internal class NodeEditorSingleton{
private static NodeEditorSingleton _instance;
public readonly Dictionary FromGenericToSpecific = new NodeEditorTypeDictionary();
public readonly Dictionary> GraphDataUsage = new Dictionary>();
public Dictionary GraphBlackboard = new Dictionary ();
public static NodeEditorSingleton Instance{
get{ return _instance ??= new NodeEditorSingleton(); }
}
private static readonly string[] ExcludedAssemblies = new[]{
"Microsoft", "UnityEngine","UnityEditor","mscorlib",
"System","Mono","PlasticPipe","unityplastic","ExCSS",
"Unity","PlayerBuildProgramLibrary","netstandard","log4net","Newtonsoft","Bee","nunit","PsdPlugin"
};
public static T CreateViewComponentFromBaseType(){
var implementedType = NodeEditorSingleton.Instance.FromGenericToSpecific[typeof(T)];
var instance = (T)Activator.CreateInstance(implementedType);
return instance;
}
public static object CreateViewComponentFromBaseType(Type t){
if (NodeEditorSingleton.Instance.FromGenericToSpecific.ContainsKey(t)){
var implementedType = NodeEditorSingleton.Instance.FromGenericToSpecific[t];
var instance = Activator.CreateInstance(implementedType);
return instance;
}
//check if t is a generic type node view
if (t is{IsGenericType: true} && t.GetGenericTypeDefinition() == typeof(BaseNodeView<>)){
var instance = Activator.CreateInstance(typeof(BaseNodeView));
return instance;
}
return null;
}
private NodeEditorSingleton(){
//exclude unity ,system ,and microsoft types
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
assemblies = assemblies.Where(x => !ExcludedAssemblies.Contains(x.FullName.Split('.',',',' ')[0])).ToArray();
foreach (var ass in assemblies){
}
foreach(var assembly in assemblies){
foreach(var type in assembly.GetTypes()){
if(type.IsClass && !type.IsAbstract){
//Register Node View And Graph View via its parent class
SetViewComponentAttribute(type);
//Register Node Data by GraphUsageAttribute.
SetGraphUsageAttribute(type);
}
}
}
GraphEditorData.GraphViewImplCreator+=GraphViewImplCreator;
}
private IBaseDataGraphView GraphViewImplCreator(Type arg){
var genericType = typeof(BaseDataGraphView<>).MakeGenericType(arg);
var instance = CreateViewComponentFromBaseType(genericType) as IBaseDataGraphView;
if (instance == null){
//fallback to default graph view
instance = (IBaseDataGraphView) Activator.CreateInstance(genericType);
}
return instance;
}
private void SetGraphUsageAttribute(Type type){
foreach (var attribute in type.GetCustomAttributes(typeof(GraphUsageAttribute), true)){
var parent = type.BaseType;
if (typeof(Model).IsAssignableFrom(type.BaseType)){
//Check if GraphDataUsage dictionary has GraphDataType of attribute
if (typeof(NodeData).IsAssignableFrom(type)){
if (attribute is GraphUsageAttribute attributeCasted){
if (GraphDataUsage.ContainsKey(attributeCasted.GraphDataType)){
GraphDataUsage[attributeCasted.GraphDataType].Add(type);
}
else{
GraphDataUsage.Add(attributeCasted.GraphDataType, new List{type});
}
}
}
if (typeof(BlackboardData).IsAssignableFrom(type)){
if (attribute is GraphUsageAttribute attributeCasted){
if (GraphBlackboard.ContainsKey(attributeCasted.GraphDataType)){
GraphBlackboard[attributeCasted.GraphDataType] = type;
}
else{
GraphBlackboard.Add(attributeCasted.GraphDataType, type);
}
}
}
}
}
}
private readonly Type[] _acceptedTypesForGenericToSpecific = new Type[]{typeof(BaseNodeView<>),typeof(BaseDataGraphView<>),typeof(GraphBlackboardView<>)};
private readonly Type[] _defaultTypes = new []{typeof(DefaultBaseNodeView),typeof(DefaultGraphBlackboardView)};
private void SetViewComponentAttribute(Type type){
foreach (var attribute in type.GetCustomAttributes(typeof(ViewComponentAttribute), false)){
//fetch this type 's parent class
var parent = type.BaseType;
//Check if this type is a generic type and is a generic type of BaseNodeView or BaseDataGraphView,
//Two level generic definition is now supported by TNodeCore
//Deeper nested generic definition is not supported by TNodeCore
if (parent is{IsGenericType: true} &&
(_acceptedTypesForGenericToSpecific.Contains(parent.GetGenericTypeDefinition()) ||
(parent.GetGenericTypeDefinition().IsGenericType && _acceptedTypesForGenericToSpecific.Contains(parent.GetGenericTypeDefinition().GetGenericTypeDefinition()))
)
){
//Get the generic type of this type
//Add this type to the dictionary
FromGenericToSpecific.Add(parent, type);
}
//TODO Note that a node component only applied to a specific type of editor,so ,same GraphView could behave differently in different editor.it's a todo feature.
}
}
public void Initialize(){
//Do nothing indeed
}
}
//Outer wrapper for the singleton class
public static class NodeEditorExtensions{
///
/// by given a generic type T,return the implementation instance of the generic type
///
///
///
public static string GetTypeCategory(Type type){
var category = type.GetCustomAttribute();
return category?.Category ?? "";
}
///
/// by given a generic type t,return the implementation instance of the generic type
///
///
public static IBlackboardView CreateBlackboardDataFromBlackboardDataType(Type t){
var type = typeof(GraphBlackboardView<>).MakeGenericType(t);
var res = NodeEditorSingleton.CreateViewComponentFromBaseType(type) as IBlackboardView;
return res ?? new DefaultGraphBlackboardView();
}
public static IBlackboardView CreateBlackboardWithGraphData(GraphData graphData){
var graphType = graphData.GetType();
if (NodeEditorSingleton.Instance.GraphBlackboard.ContainsKey(graphType)){
var type = NodeEditorSingleton.Instance.GraphBlackboard[graphType];
return CreateBlackboardDataFromBlackboardDataType(type);
}
return null;
}
public static IBlackboardView CreateBlackboardWithGraphData(Type graphType){
if (NodeEditorSingleton.Instance.GraphBlackboard.ContainsKey(graphType)){
var type = NodeEditorSingleton.Instance.GraphBlackboard[graphType];
return CreateBlackboardDataFromBlackboardDataType(type);
}
return null;
}
public static bool HasSpecificTypeComponent() where T : class{
return NodeEditorSingleton.Instance.FromGenericToSpecific.ContainsKey(typeof(T));
}
public static bool HasSpecificTypeComponent(Type t) {
return NodeEditorSingleton.Instance.FromGenericToSpecific.ContainsKey(t);
}
public static List GetGraphDataUsage(Type t){
if (NodeEditorSingleton.Instance.GraphDataUsage.ContainsKey(t)){
return NodeEditorSingleton.Instance.GraphDataUsage[t];
}
return new List();
}
public static List GetGraphCategories(Type t){
if(!NodeEditorSingleton.Instance.GraphDataUsage.ContainsKey(t)){
return new List();
}
var list = NodeEditorSingleton.Instance.GraphDataUsage[t];
//Merge same category
var res = list.Select(x=>x.GetCustomAttribute().Category).Distinct().ToList();
return res;
}
//TODO Move this method to runtime place
public static BlackboardData GetAppropriateBlackboardData(Type t){
if (NodeEditorSingleton.Instance.GraphBlackboard.ContainsKey(t)){
return (BlackboardData)Activator.CreateInstance(NodeEditorSingleton.Instance.GraphBlackboard[t]);
}
return null;
}
public static object CreateNodeViewFromNodeType(Type t){
//Check the generic type of BaseNodeView by t
if (t.IsGenericType){
//AKA if BlackboardDragNode is pulled
//Get BlackboardDragNode as generic type
var genericTypeDefinition = t.GetGenericTypeDefinition();
//What you want is a BaseNodeView> to be created
var genericViewType = typeof(BaseNodeView<>).MakeGenericType(genericTypeDefinition);
//search for the specific type of genericViewType in the dictionary
if (NodeEditorSingleton.Instance.FromGenericToSpecific.ContainsKey(genericViewType)){
var implementedType = NodeEditorSingleton.Instance.FromGenericToSpecific[genericViewType];
//The implementedType is still a generic type ,so we make it a specific type by using MakeGenericType
//Get argument type of t
var argumentType = t.GetGenericArguments()[0];
var instance = Activator.CreateInstance(implementedType.MakeGenericType(argumentType));
return instance;
}
else{
return new DefaultBaseNodeView();
}
}
var type = typeof(BaseNodeView<>).MakeGenericType(t);
if (NodeEditorSingleton.Instance.FromGenericToSpecific.ContainsKey(type)){
var implementedType = NodeEditorSingleton.Instance.FromGenericToSpecific[type];
var instance = Activator.CreateInstance(implementedType);
return instance;
}
else{
return new DefaultBaseNodeView();
}
}
}
[InitializeOnLoad]
public class Launcher{
static Launcher(){
//Get version of the package
Debug.Log("TNodeCore v0.01 is launched");
NodeEditorSingleton.Instance.Initialize();
}
}
}