From acc2a546cfb7c94b280578e735419032713845e0 Mon Sep 17 00:00:00 2001 From: Esiur Project Date: Sat, 20 Aug 2022 01:57:18 +0300 Subject: [PATCH] GetHierarchy --- Esiur/Esiur.csproj | 2 +- Esiur/Resource/Template/AttributeTemplate.cs | 7 + Esiur/Resource/Template/ConstantTemplate.cs | 4 +- Esiur/Resource/Template/MemberData.cs | 45 +++ Esiur/Resource/Template/TypeTemplate.cs | 335 ++++++++++--------- Test/MyChildRecord.cs | 1 + Test/MyChildResource.cs | 6 +- Test/MyResource.cs | 6 + Test/Program.cs | 27 ++ 9 files changed, 280 insertions(+), 153 deletions(-) create mode 100644 Esiur/Resource/Template/MemberData.cs diff --git a/Esiur/Esiur.csproj b/Esiur/Esiur.csproj index e094037..4519c97 100644 --- a/Esiur/Esiur.csproj +++ b/Esiur/Esiur.csproj @@ -6,7 +6,7 @@ Ahmed Kh. Zamil http://www.esiur.com true - 2.2.8 + 2.2.9 https://github.com/esiur/esiur-dotnet Ahmed Kh. Zamil diff --git a/Esiur/Resource/Template/AttributeTemplate.cs b/Esiur/Resource/Template/AttributeTemplate.cs index d4d8ac5..8bdc473 100644 --- a/Esiur/Resource/Template/AttributeTemplate.cs +++ b/Esiur/Resource/Template/AttributeTemplate.cs @@ -21,4 +21,11 @@ public class AttributeTemplate : MemberTemplate { } + + public static AttributeTemplate MakeAttributeTemplate(Type type, PropertyInfo pi, byte index = 0, string customName = null, TypeTemplate typeTemplate = null) + { + var at = new AttributeTemplate(typeTemplate, index, customName, pi.DeclaringType != type); + at.PropertyInfo = pi; + return at; + } } diff --git a/Esiur/Resource/Template/ConstantTemplate.cs b/Esiur/Resource/Template/ConstantTemplate.cs index 3844573..8395277 100644 --- a/Esiur/Resource/Template/ConstantTemplate.cs +++ b/Esiur/Resource/Template/ConstantTemplate.cs @@ -69,7 +69,7 @@ public class ConstantTemplate : MemberTemplate } - public static ConstantTemplate MakeConstantTemplate(Type type, FieldInfo ci, PublicAttribute publicAttr, byte index = 0, TypeTemplate typeTemplate = null) + public static ConstantTemplate MakeConstantTemplate(Type type, FieldInfo ci, byte index = 0, string customName = null, TypeTemplate typeTemplate = null) { var annotationAttr = ci.GetCustomAttribute(true); var nullableAttr = ci.GetCustomAttribute(true); @@ -84,7 +84,7 @@ public class ConstantTemplate : MemberTemplate if (typeTemplate?.Type == TemplateType.Enum) value = Convert.ChangeType(value, ci.FieldType.GetEnumUnderlyingType()); - var ct = new ConstantTemplate(typeTemplate, index, publicAttr?.Name ?? ci.Name, ci.DeclaringType != type, valueType, value, annotationAttr?.Annotation); + var ct = new ConstantTemplate(typeTemplate, index, customName ?? ci.Name, ci.DeclaringType != type, valueType, value, annotationAttr?.Annotation); ct.FieldInfo = ci; return ct; diff --git a/Esiur/Resource/Template/MemberData.cs b/Esiur/Resource/Template/MemberData.cs new file mode 100644 index 0000000..a2f4107 --- /dev/null +++ b/Esiur/Resource/Template/MemberData.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace Esiur.Resource.Template; + +public class MemberData +{ + public MemberInfo Info; + public string Name; + public int Order; + //public bool Inherited; + public MemberData? Parent; + public MemberData? Child; + public byte Index; + + public MemberInfo GetMemberInfo() + { + var rt = Info; + var md = Child; + while (md != null) + { + rt = Info; + md = md.Child; + } + return rt; + } + + public string? GetAnnotation() + { + string rt = null; + var md = this; + while (md != null) + { + var annotationAttr = md.Info.GetCustomAttribute(); + if (annotationAttr != null) + rt = annotationAttr.Annotation; + md = md.Child; + } + + return rt; + } +} + diff --git a/Esiur/Resource/Template/TypeTemplate.cs b/Esiur/Resource/Template/TypeTemplate.cs index c96b3e0..7ab6dec 100644 --- a/Esiur/Resource/Template/TypeTemplate.cs +++ b/Esiur/Resource/Template/TypeTemplate.cs @@ -369,7 +369,7 @@ public class TypeTemplate } - + public static ConstantTemplate MakeConstantTemplate(Type type, FieldInfo ci, PublicAttribute publicAttr, byte index = 0, TypeTemplate typeTemplate = null) { @@ -392,7 +392,7 @@ public class TypeTemplate } - + public TypeTemplate(Type type, bool addToWarehouse = false) { if (Codec.InheritsClass(type, typeof(DistributedResource))) @@ -426,158 +426,71 @@ public class TypeTemplate - PropertyInfo[] propsInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); - EventInfo[] eventsInfo = type.GetEvents(BindingFlags.Public | BindingFlags.Instance); - MethodInfo[] methodsInfo = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); - FieldInfo[] constantsInfo = type.GetFields(BindingFlags.Public | BindingFlags.Static); + //PropertyInfo[] propsInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); + //EventInfo[] eventsInfo = type.GetEvents(BindingFlags.Public | BindingFlags.Instance); + //MethodInfo[] methodsInfo = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); + //FieldInfo[] constantsInfo = type.GetFields(BindingFlags.Public | BindingFlags.Static); - bool classIsPublic = type.IsEnum || (type.GetCustomAttribute() != null); + //bool classIsPublic = type.IsEnum || (type.GetCustomAttribute() != null); - - - - var addAttribute = (PropertyInfo pi, AttributeAttribute attributeAttr) => + var hierarchy = GetHierarchy(type); + + if (hierarchy.ContainsKey(MemberTypes.Field)) { - var an = attributeAttr.Name ?? pi.Name; - var at = new AttributeTemplate(this, 0, an, pi.DeclaringType != type); - at.PropertyInfo = pi; - attributes.Add(at); - }; - - - - - - if (classIsPublic) - { - - foreach (var ci in constantsInfo) + foreach (var cd in hierarchy[MemberTypes.Field]) { - var privateAttr = ci.GetCustomAttribute(true); - - if (privateAttr != null) - continue; - - var publicAttr = ci.GetCustomAttribute(true); - - var ct = MakeConstantTemplate(type, ci, publicAttr, (byte)constants.Count, this); - constants.Add(ct); - } - - - foreach (var pi in propsInfo) - { - var privateAttr = pi.GetCustomAttribute(true); - - if (privateAttr == null) - { - var publicAttr = pi.GetCustomAttribute(true); - var pt = PropertyTemplate.MakePropertyTemplate(type, pi, (byte)properties.Count, publicAttr?.Name, this); - properties.Add(pt); - } - else - { - var attributeAttr = pi.GetCustomAttribute(true); - if (attributeAttr != null) - { - addAttribute(pi, attributeAttr); - } - } - } - - if (templateType == TemplateType.Resource - || templateType == TemplateType.Wrapper) - { - - foreach (var ei in eventsInfo) - { - var privateAttr = ei.GetCustomAttribute(true); - if (privateAttr != null) - continue; - - var publicAttr = ei.GetCustomAttribute(true); - var et = EventTemplate.MakeEventTemplate(type, ei, (byte)events.Count, publicAttr?.Name, this); - events.Add(et); - } - - foreach (MethodInfo mi in methodsInfo) - { - var privateAttr = mi.GetCustomAttribute(true); - if (privateAttr != null) - continue; - - var publicAttr = mi.GetCustomAttribute(true); - var ft = FunctionTemplate.MakeFunctionTemplate(type, mi, (byte)functions.Count, publicAttr?.Name, this); - functions.Add(ft); - //addFunction(mi, publicAttr); - } - - } - } - else - { - foreach (var ci in constantsInfo) - { - var publicAttr = ci.GetCustomAttribute(true); - - if (publicAttr == null) - continue; - - var ct = MakeConstantTemplate(type, ci, publicAttr, (byte)constants.Count, this); - constants.Add(ct); - } - - - foreach (var pi in propsInfo) - { - var publicAttr = pi.GetCustomAttribute(true); - - if (publicAttr != null) - { - var pt = PropertyTemplate.MakePropertyTemplate(type, pi, (byte)properties.Count, publicAttr?.Name, this); - properties.Add(pt); - } - else - { - var attributeAttr = pi.GetCustomAttribute(true); - if (attributeAttr != null) - { - addAttribute(pi, attributeAttr); - } - } - } - - if (templateType == TemplateType.Resource - || templateType == TemplateType.Wrapper) - { - - foreach (var ei in eventsInfo) - { - var publicAttr = ei.GetCustomAttribute(true); - - if (publicAttr == null) - continue; - - var et = EventTemplate.MakeEventTemplate(type, ei, (byte)events.Count, publicAttr?.Name, this); - events.Add(et); - } - - foreach (MethodInfo mi in methodsInfo) - { - var publicAttr = mi.GetCustomAttribute(true); - if (publicAttr == null) - continue; - - var ft = FunctionTemplate.MakeFunctionTemplate(type, mi, (byte)functions.Count, publicAttr?.Name, this); - functions.Add(ft); - //addFunction(mi, publicAttr); - } + constants.Add(ConstantTemplate.MakeConstantTemplate + (type, (FieldInfo)cd.GetMemberInfo(), cd.Index, cd.Name, this)); } } - // append signals + if (hierarchy.ContainsKey(MemberTypes.Property)) + { + foreach (var pd in hierarchy[MemberTypes.Property]) + { + properties.Add(PropertyTemplate.MakePropertyTemplate + (type, (PropertyInfo)pd.GetMemberInfo(), pd.Index, pd.Name, this)); + } + } + + if (templateType == TemplateType.Resource + || templateType == TemplateType.Wrapper) + { + if (hierarchy.ContainsKey(MemberTypes.Method)) + { + foreach (var fd in hierarchy[MemberTypes.Method]) + { + functions.Add(FunctionTemplate.MakeFunctionTemplate + (type, (MethodInfo)fd.GetMemberInfo(), fd.Index, fd.Name, this)); + } + } + + if (hierarchy.ContainsKey(MemberTypes.Event)) + { + foreach (var ed in hierarchy[MemberTypes.Event]) + { + events.Add(EventTemplate.MakeEventTemplate + (type, (EventInfo)ed.GetMemberInfo(), ed.Index, ed.Name, this)); + } + } + + } + + // add attributes + var attrs = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(x => x.GetCustomAttribute() != null); + + foreach (var attr in attrs) + { + var attrAttr = attr.GetCustomAttribute(); + + attributes.Add(AttributeTemplate + .MakeAttributeTemplate(type, attr, 0, attrAttr?.Name ?? attr.Name, this)); + } + + // append signals) for (var i = 0; i < events.Count; i++) members.Add(events[i]); // append slots @@ -645,22 +558,146 @@ public class TypeTemplate { var parent = type.BaseType; - if (parent == typeof(Resource) - || parent == typeof(Record) - || parent == typeof(EntryPoint)) - return false; while (parent != null) { + if (parent == typeof(Resource) + || parent == typeof(Record) + || parent == typeof(EntryPoint)) + return false; + if (parent.GetInterfaces().Contains(typeof(IResource)) || parent.GetInterfaces().Contains(typeof(IRecord))) return true; + parent = parent.BaseType; } return false; } + + + public static Dictionary> GetHierarchy(Type type) + { + var members = new List(); + + var order = 0; + + while (type != null) + { + var classIsPublic = type.IsEnum || (type.GetCustomAttribute() != null); + + if (classIsPublic) + { + var mis = type.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly) + .Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field + || x.MemberType == MemberTypes.Event || x.MemberType == MemberTypes.Method) + .Where(x => !(x is FieldInfo c && !c.IsStatic)) + .Where(x => x.GetCustomAttribute() == null) + .Where(x => !(x is MethodInfo m && m.IsSpecialName)) + .Select(x => new MemberData() + { + Name = x.GetCustomAttribute()?.Name ?? x.Name, + Info = x, + Order = order + }) + .OrderBy(x => x.Name); + + members.AddRange(mis.ToArray()); + + } + else + { + var mis = type.GetMembers(BindingFlags.Public | BindingFlags.Instance + | BindingFlags.DeclaredOnly | BindingFlags.Static) + .Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field + || x.MemberType == MemberTypes.Event || x.MemberType == MemberTypes.Method) + .Where(x => !(x is FieldInfo c && !c.IsStatic)) + .Where(x => x.GetCustomAttribute() != null) + .Where(x => !(x is MethodInfo m && m.IsSpecialName)) + .Select(x => new MemberData + { + Name = x.GetCustomAttribute()?.Name ?? x.Name, + Info = x, + Order = order + }) + .OrderBy(x => x.Name); + + members.AddRange(mis.ToArray()); + + } + + type = type.BaseType; + + if (type == typeof(Resource) + || type == typeof(Record) + || type == typeof(EntryPoint)) + break; + + if (type.GetInterfaces().Contains(typeof(IResource)) + || type.GetInterfaces().Contains(typeof(IRecord))) + { + order++; + continue; + } + + break; + } + + // round 2: check for duplicates + for (var i = 0; i < members.Count; i++) + { + var mi = members[i]; + for (var j = i + 1; j < members.Count; j++) + { + var pi = members[j]; + if (pi.Info.MemberType != mi.Info.MemberType) + continue; + + //if (ci.Info.Name == mi.Info.Name && ci.Order == mi.Order) + // throw new Exception($"Method overload is not supported. Method '{ci.Info.Name}'."); + + if (pi.Name == mi.Name) + { + if (pi.Order == mi.Order) + throw new Exception($"Duplicate definitions for members public name '{mi.Info.DeclaringType.Name}:{mi.Info.Name}' and '{pi.Info.DeclaringType.Name}:{pi.Info.Name}'."); + else + { + // @TODO: check for return type and parameters they must match + if (pi.Info.Name != mi.Info.Name) + throw new Exception($"Duplicate definitions for members public name '{mi.Info.DeclaringType.Name}:{mi.Info.Name}' and '{pi.Info.DeclaringType.Name}:{pi.Info.Name}'."); + } + + mi.Parent = pi; + pi.Child = mi; + } + + } + } + + + // assign indexies + var groups = members.Where(x => x.Parent == null) + .OrderBy(x => x.Name).OrderByDescending(x => x.Order) + .GroupBy(x => x.Info.MemberType); + + foreach (var group in groups) + { + byte index = 0; + foreach (var mi in group) + { + //if (mi.Parent == null) + mi.Index = index++; + } + } + + var rt = groups.ToDictionary(g => g.Key, g => g.ToList()); + + return rt; + } + + public static TypeTemplate Parse(byte[] data) { return Parse(data, 0, (uint)data.Length); diff --git a/Test/MyChildRecord.cs b/Test/MyChildRecord.cs index 484174f..124ce53 100644 --- a/Test/MyChildRecord.cs +++ b/Test/MyChildRecord.cs @@ -11,5 +11,6 @@ namespace Test public class MyChildRecord : MyRecord { public string ChildName { get; set; } + } } diff --git a/Test/MyChildResource.cs b/Test/MyChildResource.cs index 2a5c122..a32349d 100644 --- a/Test/MyChildResource.cs +++ b/Test/MyChildResource.cs @@ -11,6 +11,10 @@ namespace Test public partial class MyChildResource : MyResource { [Public] string childName; - [Public] public int ChildMethod(string childName) => 111; + [Public("Hell2o")] public int ChildMethod(string childName) => 111; + [Public] public new string Hello() => "Hi from Child"; + + [Public] public string HelloChild() => "Hi from Child"; + } } diff --git a/Test/MyResource.cs b/Test/MyResource.cs index 66a3423..237cb77 100644 --- a/Test/MyResource.cs +++ b/Test/MyResource.cs @@ -13,5 +13,11 @@ namespace Test { [Public][Annotation("Comment")] string description; [Public] int categoryId; + + + [Public] public string Hello() => "Hi"; + + [Public] public string HelloParent() => "Hi from Parent"; + } } diff --git a/Test/Program.cs b/Test/Program.cs index 6f38399..ef616e1 100644 --- a/Test/Program.cs +++ b/Test/Program.cs @@ -42,6 +42,7 @@ using System.Runtime.CompilerServices; using Esiur.Proxy; using System.Text.RegularExpressions; using System.Collections.Generic; +using System.Reflection; namespace Test { @@ -54,6 +55,10 @@ namespace Test static async Task Main(string[] args) { + var ppp = GetOrderedProperties(typeof(MyChildResource)).ToArray(); + var childMethods = typeof(MyChildResource).GetMembers( BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance); + var parentMethods = typeof(MyResource).GetMethods(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance); + // Create stores to keep objects. var system = await Warehouse.Put("sys", new MemoryStore()); var server = await Warehouse.Put("sys/server", new DistributedServer()); @@ -222,6 +227,28 @@ namespace Test return value.ToString(); } + + + + + public static IEnumerable GetOrderedProperties(Type type) + { + Dictionary lookup = new Dictionary(); + + int count = 0; + lookup[type] = count++; + Type parent = type.BaseType; + while (parent != null) + { + lookup[parent] = count; + count++; + parent = parent.BaseType; + } + + return type.GetProperties() + .OrderByDescending(prop => lookup[prop.DeclaringType]); + } + }