diff --git a/Esiur/Data/DataDeserializer.cs b/Esiur/Data/DataDeserializer.cs index 5db0625..ddc98e9 100644 --- a/Esiur/Data/DataDeserializer.cs +++ b/Esiur/Data/DataDeserializer.cs @@ -1586,7 +1586,7 @@ public static class DataDeserializer var pvs = new List(); for (var i = 0; i < ar.Length; i += 3) - pvs.Add(new PropertyValue(ar[2], Convert.ToUInt64(ar[0]), (DateTime?)ar[1])); + pvs.Add(new PropertyValue(ar[i + 2], Convert.ToUInt64(ar[i]), (DateTime?)ar[i+1])); rt.Trigger(pvs.ToArray()); diff --git a/Esiur/Data/IDynamicResource.cs b/Esiur/Data/IDynamicResource.cs new file mode 100644 index 0000000..ba48227 --- /dev/null +++ b/Esiur/Data/IDynamicResource.cs @@ -0,0 +1,20 @@ +using Esiur.Core; +using Esiur.Resource.Template; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data +{ + public interface IDynamicResource + { + public PropertyValue[] SerializeResource(); + public Map SerializeResourceAfter(ulong age); + + public object GetResourceProperty(byte index); + public AsyncReply SetResourcePropertyAsync(byte index, object value); + public void SetResourceProperty(byte index, object value); + + public TypeTemplate ResourceTemplate { get; } + } +} diff --git a/Esiur/Net/IIP/DistributedConnectionProtocol.cs b/Esiur/Net/IIP/DistributedConnectionProtocol.cs index f48f67f..451b910 100644 --- a/Esiur/Net/IIP/DistributedConnectionProtocol.cs +++ b/Esiur/Net/IIP/DistributedConnectionProtocol.cs @@ -68,7 +68,7 @@ partial class DistributedConnection object subscriptionsLock = new object(); - AsyncQueue queue = new (); + AsyncQueue queue = new(); @@ -450,7 +450,7 @@ partial class DistributedConnection var (valueOffset, valueSize, args) = DataDeserializer.LimitedCountListParser(dataType.Data, dataType.Offset, dataType.ContentLength, Instance.Warehouse, 2); - var rid =Convert.ToUInt32(args[0]); + var rid = Convert.ToUInt32(args[0]); var index = (byte)args[1]; Fetch(rid, null).Then(r => @@ -559,26 +559,13 @@ partial class DistributedConnection // unsubscribe Unsubscribe(r); - if (r is DistributedResource dr) - { - // reply ok - SendReply(IIPPacketReply.Completed, callback, - r.Instance.Template.ClassId, - r.Instance.Age, - r.Instance.Link, - r.Instance.Hops, - dr._Serialize()); - } - else - { - // reply ok - SendReply(IIPPacketReply.Completed, callback, - r.Instance.Template.ClassId, - r.Instance.Age, - r.Instance.Link, - r.Instance.Hops, - r.Instance.Serialize()); - } + // reply ok + SendReply(IIPPacketReply.Completed, callback, + r.Instance.Template.ClassId, + r.Instance.Age, + r.Instance.Link, + r.Instance.Hops, + r.Instance.Serialize()); // subscribe Subscribe(r); @@ -618,26 +605,15 @@ partial class DistributedConnection // unsubscribe Unsubscribe(r); - if (r is DistributedResource dr) - { - // reply ok - SendReply(IIPPacketReply.Completed, callback, - r.Instance.Template.ClassId, - r.Instance.Age, - r.Instance.Link, - r.Instance.Hops, - dr._SerializeAfter(age)); - } - else - { - // reply ok - SendReply(IIPPacketReply.Completed, callback, - r.Instance.Template.ClassId, - r.Instance.Age, - r.Instance.Link, - r.Instance.Hops, - r.Instance.SerializeAfter(age)); - } + + // reply ok + SendReply(IIPPacketReply.Completed, callback, + r.Instance.Template.ClassId, + r.Instance.Age, + r.Instance.Link, + r.Instance.Hops, + r.Instance.SerializeAfter(age)); + // subscribe Subscribe(r); @@ -1612,7 +1588,7 @@ partial class DistributedConnection } - if (r is DistributedResource) + if (r is IDynamicResource) { var (_, parsed) = Codec.ParseAsync(data, offset, this, null); if (parsed is AsyncReply) @@ -1620,7 +1596,7 @@ partial class DistributedConnection (parsed as AsyncReply).Then((value) => { // propagation - (r as DistributedResource)._Set(index, value).Then((x) => + (r as IDynamicResource).SetResourcePropertyAsync(index, value).Then((x) => { SendReply(IIPPacketReply.Completed, callback); }).Error(x => @@ -1912,7 +1888,7 @@ partial class DistributedConnection { template = Instance.Warehouse.GetTemplateByClassId(classId, TemplateType.Resource); if (template?.DefinedType != null && template.IsWrapper) - dr = Activator.CreateInstance(template.DefinedType, this, id,Convert.ToUInt64( args[1]), (string)args[2]) as DistributedResource; + dr = Activator.CreateInstance(template.DefinedType, this, id, Convert.ToUInt64(args[1]), (string)args[2]) as DistributedResource; else dr = new DistributedResource(this, id, Convert.ToUInt64(args[1]), (string)args[2]); } @@ -1955,7 +1931,9 @@ partial class DistributedConnection // ClassId, ResourceAge, ResourceLink, Content if (resource == null) { - Instance.Warehouse.Put(this.Instance.Link + "/" + id.ToString(), dr, tmp) + dr.ResourceTemplate = tmp; + + Instance.Warehouse.Put(this.Instance.Link + "/" + id.ToString(), dr) .Then(initResource) .Error(ex => reply.TriggerError(ex)); } @@ -1973,7 +1951,7 @@ partial class DistributedConnection { if (resource == null) { - Instance.Warehouse.Put(this.Instance.Link + "/" + id.ToString(), dr, template) + Instance.Warehouse.Put(this.Instance.Link + "/" + id.ToString(), dr) .Then(initResource).Error((ex) => reply.TriggerError(ex)); } else diff --git a/Esiur/Net/IIP/DistributedResource.cs b/Esiur/Net/IIP/DistributedResource.cs index 4836d6b..9f9725f 100644 --- a/Esiur/Net/IIP/DistributedResource.cs +++ b/Esiur/Net/IIP/DistributedResource.cs @@ -46,7 +46,7 @@ using Esiur.Net.Packets; namespace Esiur.Net.IIP; //[System.Runtime.InteropServices.ComVisible(true)] -public class DistributedResource : DynamicObject, IResource, INotifyPropertyChanged +public class DistributedResource : DynamicObject, IResource, INotifyPropertyChanged, IDynamicResource { /// @@ -57,6 +57,7 @@ public class DistributedResource : DynamicObject, IResource, INotifyPropertyChan public event PropertyChangedEventHandler PropertyChanged; uint instanceId; + TypeTemplate template; DistributedConnection connection; @@ -67,7 +68,7 @@ public class DistributedResource : DynamicObject, IResource, INotifyPropertyChan //Structure properties = new Structure(); string link; - //ulong age; + ulong age; protected object[] properties; internal List parents = new List(); @@ -77,17 +78,6 @@ public class DistributedResource : DynamicObject, IResource, INotifyPropertyChan - /// - /// Resource template for the remotely located resource. - /// - //public ResourceTemplate Template - //{ - // get { return template; } - //} - - - - /// /// Connection responsible for the distributed resource. /// @@ -160,45 +150,9 @@ public class DistributedResource : DynamicObject, IResource, INotifyPropertyChan this.link = link; this.connection = connection; this.instanceId = instanceId; - - //this.Instance.Template = template; - //this.Instance.Age = age; - //this.template = template; - //this.age = age; - + this.age = age; } - /// - /// Export properties as byte array. - /// - /// - internal PropertyValue[] _Serialize() - { - var props = new PropertyValue[properties.Length]; - - for (byte i = 0; i < properties.Length; i++) - props[i] = new PropertyValue(properties[i], - Instance.GetAge(i), - Instance.GetModificationDate(i)); - - return props; - } - - internal Map _SerializeAfter(ulong age = 0) - { - var rt = new Map(); - - for (byte i = 0; i < properties.Length; i++) - if (Instance.GetAge(i) > age) - rt.Add(i, new PropertyValue(properties[i], - Instance.GetAge(i), - Instance.GetModificationDate(i))); - - - return rt; - } - - internal bool _Attach(PropertyValue[] properties) { if (attached) @@ -357,17 +311,14 @@ public class DistributedResource : DynamicObject, IResource, INotifyPropertyChan } } - /// - /// Get a property value. - /// - /// Zero-based property index. - /// Value - protected internal object _Get(byte index) - { - if (index >= properties.Length) - return null; - return properties[index]; - } + ///// + ///// Get a property value. + ///// + ///// Zero-based property index. + ///// Value + //protected internal object _Get(byte index) + //{ + //} public bool TryGetPropertyValue(byte index, out object value) { @@ -427,65 +378,19 @@ public class DistributedResource : DynamicObject, IResource, INotifyPropertyChan /// Zero-based property index. /// Value /// Indicator when the property is set. - protected object _SetSync(byte index, object value) - { - //Console.WriteLine("Setting..." + index + " " + value); + //protected object _SetSync(byte index, object value) + //{ + //} - if (destroyed) - throw new Exception("Trying to access a destroyed object."); - - if (suspended) - throw new Exception("Trying to access a suspended object."); - - if (!attached) - return null; - - if (index >= properties.Length) - return null; - - // Don't set the same current value - if (properties[index] == value) - return value; - - var rt = _Set(index, value).Wait(); - - //Console.WriteLine("Done Setting"); - return rt; - } - - /// - /// Set property value. - /// - /// Zero-based property index. - /// Value - /// Indicator when the property is set. - protected internal AsyncReply _Set(byte index, object value) - { - if (destroyed) - throw new Exception("Trying to access a destroyed object."); - - if (suspended) - throw new Exception("Trying to access a suspended object."); - - if (!attached) - return null; - - if (index >= properties.Length) - return null; - - var reply = new AsyncReply(); - - connection.SendSetProperty(instanceId, index, value) - .Then((res) => - { - // not really needed, server will always send property modified, - // this only happens if the programmer forgot to emit in property setter - properties[index] = value; - reply.Trigger(null); - }); - - return reply; - } + ///// + ///// Set property value. + ///// + ///// Zero-based property index. + ///// Value + ///// Indicator when the property is set. + //protected internal AsyncReply _Set(byte index, object value) + //{ + //} public override bool TrySetMember(SetMemberBinder binder, object value) { @@ -502,7 +407,7 @@ public class DistributedResource : DynamicObject, IResource, INotifyPropertyChan if (pt != null) { - _Set(pt.Index, value); + SetResourceProperty(pt.Index, value); return true; } else @@ -518,33 +423,6 @@ public class DistributedResource : DynamicObject, IResource, INotifyPropertyChan } - /* - public async void InvokeMethod(byte index, object[] arguments, DistributedConnection sender) - { - // get function parameters - Type t = this.GetType(); - - MethodInfo mi = t.GetMethod(GetFunctionName(index), BindingFlags.DeclaredOnly | - BindingFlags.Public | - BindingFlags.Instance | BindingFlags.InvokeMethod); - if (mi != null) - { - try - { - var res = await invokeMethod(mi, arguments, sender); - object rt = Codec.Compose(res); - sender.SendParams((byte)0x80, instanceId, index, rt); - } - catch(Exception ex) - { - var msg = ex.InnerException != null ? ex.InnerException.Message : ex.Message; - sender.SendParams((byte)0x8E, instanceId, index, Codec.Compose(msg)); - } - } - } - */ - - /// /// Resource interface. @@ -555,6 +433,18 @@ public class DistributedResource : DynamicObject, IResource, INotifyPropertyChan set; } + public TypeTemplate ResourceTemplate + { + get + { + return template; + } + internal set + { + template = value; + } + } + /// /// Create a new instance of distributed resource. /// @@ -587,6 +477,82 @@ public class DistributedResource : DynamicObject, IResource, INotifyPropertyChan PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } + public PropertyValue[] SerializeResource() + { + var props = new PropertyValue[properties.Length]; + + for (byte i = 0; i < properties.Length; i++) + props[i] = new PropertyValue(properties[i], + Instance.GetAge(i), + Instance.GetModificationDate(i)); + + return props; + } + + public Map SerializeResourceAfter(ulong age = 0) + { + var rt = new Map(); + + for (byte i = 0; i < properties.Length; i++) + if (Instance.GetAge(i) > age) + rt.Add(i, new PropertyValue(properties[i], + Instance.GetAge(i), + Instance.GetModificationDate(i))); + + + return rt; + } + + + + + public object GetResourceProperty(byte index) + { + if (index >= properties.Length) + return null; + return properties[index]; + } + + public AsyncReply SetResourcePropertyAsync(byte index, object value) + { + if (destroyed) + throw new Exception("Trying to access a destroyed object."); + + if (suspended) + throw new Exception("Trying to access a suspended object."); + + if (!attached) + throw new Exception("Resource is not attached."); + + if (index >= properties.Length) + throw new Exception("Property index not found."); ; + + var reply = new AsyncReply(); + + connection.SendSetProperty(instanceId, index, value) + .Then((res) => + { + // not really needed, server will always send property modified, + // this only happens if the programmer forgot to emit in property setter + properties[index] = value; + reply.Trigger(null); + }); + + return reply; + + } + + public void SetResourceProperty(byte index, object value) + { + // Don't set the same current value + if (properties[index] == value) + return; + + SetResourcePropertyAsync(index, value).Wait(); + + return; + } + ~DistributedResource() { Destroy(); diff --git a/Esiur/Proxy/TemplateGenerator.cs b/Esiur/Proxy/TemplateGenerator.cs index 3985ae8..a684a78 100644 --- a/Esiur/Proxy/TemplateGenerator.cs +++ b/Esiur/Proxy/TemplateGenerator.cs @@ -74,9 +74,13 @@ public static class TemplateGenerator rt.AppendLine("using System;\r\nusing Esiur.Resource;\r\nusing Esiur.Core;\r\nusing Esiur.Data;\r\nusing Esiur.Net.IIP;"); rt.AppendLine($"namespace {nameSpace} {{"); - if (template.Annotation != null) - rt.AppendLine($"[Annotation({ToLiteral(template.Annotation)})]"); - + if (template.Annotations != null) + { + foreach (var ann in template.Annotations) + { + rt.AppendLine($"[Annotation({ToLiteral(ann.Key)}, {ToLiteral(ann.Value)})]"); + } + } rt.AppendLine($"[ClassId(\"{template.ClassId.Data.ToHex(0, 16, null)}\")]"); rt.AppendLine($"[Export] public class {className} : IRecord {{"); @@ -85,8 +89,17 @@ public static class TemplateGenerator foreach (var p in template.Properties) { var ptTypeName = GetTypeName(p.ValueType, templates); - if (p.ReadAnnotation != null) - rt.AppendLine($"[Annotation({ToLiteral(p.ReadAnnotation)})]"); + + + if (p.Annotations != null) + { + foreach (var ann in p.Annotations) + { + rt.AppendLine($"[Annotation({ToLiteral(ann.Key)}, {ToLiteral(ann.Value)})]"); + } + } + + rt.AppendLine($"public {ptTypeName} {p.Name} {{ get; set; }}"); rt.AppendLine(); } @@ -108,8 +121,13 @@ public static class TemplateGenerator rt.AppendLine("using System;\r\nusing Esiur.Resource;\r\nusing Esiur.Core;\r\nusing Esiur.Data;\r\nusing Esiur.Net.IIP;"); rt.AppendLine($"namespace {nameSpace} {{"); - if (template.Annotation != null) - rt.AppendLine($"[Annotation({ToLiteral(template.Annotation)})]"); + if (template.Annotations != null) + { + foreach (var ann in template.Annotations) + { + rt.AppendLine($"[Annotation({ToLiteral(ann.Key)}, {ToLiteral(ann.Value)})]"); + } + } rt.AppendLine($"[ClassId(\"{template.ClassId.Data.ToHex(0, 16, null)}\")]"); rt.AppendLine($"[Export] public enum {className} {{"); @@ -275,8 +293,13 @@ public static class TemplateGenerator rt.AppendLine($"namespace {nameSpace} {{"); - if (template.Annotation != null) - rt.AppendLine($"[Annotation({ToLiteral(template.Annotation)})]"); + if (template.Annotations != null) + { + foreach (var ann in template.Annotations) + { + rt.AppendLine($"[Annotation({ToLiteral(ann.Key)}, {ToLiteral(ann.Value)})]"); + } + } rt.AppendLine($"[ClassId(\"{template.ClassId.Data.ToHex(0, 16, null)}\")]"); @@ -371,8 +394,13 @@ public static class TemplateGenerator if (p.Inherited) continue; - if (p.ReadAnnotation != null) - rt.AppendLine($"[Annotation({ToLiteral(p.ReadAnnotation)})]"); + if (p.Annotations != null) + { + foreach (var ann in p.Annotations) + { + rt.AppendLine($"[Annotation({ToLiteral(ann.Key)}, {ToLiteral(ann.Value)})]"); + } + } var ptTypeName = GetTypeName(p.ValueType, templates); rt.AppendLine($"[Export] public {ptTypeName} {p.Name} {{"); @@ -389,8 +417,11 @@ public static class TemplateGenerator if (c.Inherited) continue; - if (c.Annotation != null) - rt.AppendLine($"[Annotation({ToLiteral(c.Annotation)})]"); + if (c.Annotations != null) + { + foreach (var ann in c.Annotations) + rt.AppendLine($"[Annotation({ToLiteral(ann.Key)}, {ToLiteral(ann.Value)})]"); + } var ctTypeName = GetTypeName(c.ValueType, templates); rt.AppendLine($"[Export] public const {ctTypeName} {c.Name} = {c.Value};"); @@ -410,8 +441,13 @@ public static class TemplateGenerator var etTypeName = GetTypeName(e.ArgumentType, templates); rt.AppendLine($"case {e.Index}: {e.Name}?.Invoke(({etTypeName})args); break;"); - if (e.Annotation != null) - eventsList.AppendLine($"[Annotation({ToLiteral(e.Annotation)})]"); + + if (e.Annotations != null) + { + foreach (var ann in e.Annotations) + rt.AppendLine($"[Annotation({ToLiteral(ann.Key)}, {ToLiteral(ann.Value)})]"); + } + eventsList.AppendLine($"[Export] public event ResourceEventHandler<{etTypeName}> {e.Name};"); } diff --git a/Esiur/Resource/AnnotationAttribute.cs b/Esiur/Resource/AnnotationAttribute.cs index 8a21b08..9fee077 100644 --- a/Esiur/Resource/AnnotationAttribute.cs +++ b/Esiur/Resource/AnnotationAttribute.cs @@ -4,7 +4,7 @@ using System.Text; namespace Esiur.Resource; -[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Event, AllowMultiple = true)] +[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Event | AttributeTargets.Parameter, AllowMultiple = true)] public class AnnotationAttribute : Attribute { @@ -13,7 +13,7 @@ public class AnnotationAttribute : Attribute public AnnotationAttribute(string annotation) { - Key = null; + Key = ""; Value = annotation; } public AnnotationAttribute(string key, string value) diff --git a/Esiur/Resource/Instance.cs b/Esiur/Resource/Instance.cs index 056e28a..f6edf74 100644 --- a/Esiur/Resource/Instance.cs +++ b/Esiur/Resource/Instance.cs @@ -116,58 +116,6 @@ public class Instance } return rt; - - - - /* - var st = new Structure(); - - if (attributes == null) - { - var clone = this.attributes.Keys.ToList(); - clone.Add("managers"); - attributes = clone.ToArray();// this.attributes.Keys.ToList().Add("managers"); - } - - foreach (var attr in attributes) - { - if (attr == "name") - st["name"] = this.name; - else if (attr == "managers") - { - var mngrs = new List(); - - foreach (var manager in this.managers) - mngrs.Add(new Structure() - { - ["type"] = manager.GetType().FullName + "," + manager.GetType().GetTypeInfo().Assembly.GetName().Name, - ["settings"] = manager.Settings - }); - - st["managers"] = mngrs.ToArray(); - } - else if (attr == "parents") - { - //st["parents"] = parents.ToArray(); - } - else if (attr == "children") - { - //st["children"] = children.ToArray(); - } - else if (attr == "childrenCount") - { - //st["childrenCount"] = children.Count; - } - else if (attr == "type") - { - st["type"] = resource.GetType().FullName; - } - else - st[attr] = this.attributes[attr]; - } - - return st; - */ } public bool SetAttributes(Map attributes, bool clearAttributes = false) @@ -454,17 +402,21 @@ public class Instance /// public PropertyValue[] Serialize() { + IResource res; + + if (!resource.TryGetTarget(out res)) + throw new Exception("Resource no longer available."); + + + if (res is IDynamicResource dynamicResource) + return dynamicResource.SerializeResource(); + var props = new List(); foreach (var pt in template.Properties) { - IResource res; - - if (resource.TryGetTarget(out res)) - { - var rt = pt.PropertyInfo.GetValue(res, null); - props.Add(new PropertyValue(rt, ages[pt.Index], modificationDates[pt.Index])); - } + var rt = pt.PropertyInfo.GetValue(res, null); + props.Add(new PropertyValue(rt, ages[pt.Index], modificationDates[pt.Index])); } return props.ToArray(); @@ -476,93 +428,31 @@ public class Instance /// public Map SerializeAfter(ulong age = 0) { + IResource res; + + if (!resource.TryGetTarget(out res)) + throw new Exception("Resource no longer available."); + + if (res is IDynamicResource dynamicResource) + return dynamicResource.SerializeResourceAfter(age); + var props = new Map(); foreach (var pt in template.Properties) { - IResource res; - if (resource.TryGetTarget(out res)) + if (res.Instance.GetAge(pt.Index) > age) { - if (res.Instance.GetAge(pt.Index) > age) - { - var rt = pt.PropertyInfo.GetValue(res, null); - props.Add(pt.Index, - new PropertyValue(rt, - ages[pt.Index], - modificationDates[pt.Index])); - } + var rt = pt.PropertyInfo.GetValue(res, null); + props.Add(pt.Index, + new PropertyValue(rt, + ages[pt.Index], + modificationDates[pt.Index])); } } return props; } - /* - public bool Deserialize(byte[] data, uint offset, uint length) - { - - var props = Codec.ParseValues(data, offset, length); - Deserialize(props); - return true; - } - */ - /* - public byte[] Serialize(bool includeLength = false, DistributedConnection sender = null) - { - - //var bl = new BinaryList(); - List props = new List(); - - foreach (var pt in template.Properties) - { - - var pi = resource.GetType().GetProperty(pt.Name); - - var rt = pi.GetValue(resource, null); - - // this is a cool hack to let the property know the sender - if (rt is Func) - rt = (rt as Func)(sender); - - props.Add(rt); - - } - - if (includeLength) - { - return Codec.Compose(props.ToArray(), false); - } - else - { - var rt = Codec.Compose(props.ToArray(), false); - return DC.Clip(rt, 4, (uint)(rt.Length - 4)); - } - } - - public byte[] StorageSerialize() - { - - var props = new List(); - - foreach(var pt in template.Properties) - { - if (!pt.Storable) - continue; - - var pi = resource.GetType().GetProperty(pt.Name); - - if (!pi.CanWrite) - continue; - - var rt = pi.GetValue(resource, null); - - props.Add(rt); - - } - - return Codec.Compose(props.ToArray(), false); - } - */ /// /// If True, the instance can be stored to disk. @@ -619,7 +509,7 @@ public class Instance return; object value; - if (GetPropertyValue(propertyName, out value)) + if (TryGetPropertyValue(propertyName, out value)) { var pt = template.GetPropertyTemplateByName(propertyName); EmitModification(pt, value); @@ -675,66 +565,37 @@ public class Instance /// Property name /// Output value /// True, if the resource has the property. - public bool GetPropertyValue(string name, out object value) + public bool TryGetPropertyValue(string name, out object value) { - /* -#if NETSTANDARD - PropertyInfo pi = resource.GetType().GetTypeInfo().GetProperty(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); - -#else - PropertyInfo pi = resource.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); -#endif -*/ - var pt = template.GetPropertyTemplateByName(name); - if (pt != null && pt.PropertyInfo != null) + IResource res; + if (resource.TryGetTarget(out res)) { - /* -#if NETSTANDARD - object[] ca = pi.GetCustomAttributes(typeof(ResourceProperty), false).ToArray(); - -#else - object[] ca = pi.GetCustomAttributes(typeof(ResourceProperty), false); -#endif - - if (ca.Length > 0) + if (res is IDynamicResource dynamicResource) { - value = pi.GetValue(resource, null); - //if (value is Func) - // value = (value as Func)(sender); + value = dynamicResource.GetResourceProperty(pt.Index); return true; } - */ - - IResource res; - if (resource.TryGetTarget(out res)) - value = pt.PropertyInfo.GetValue(res, null); - else + else if (pt != null && pt.PropertyInfo != null) { - value = null; - return false; + value = pt.PropertyInfo.GetValue(res, null); + return true; } - - return true; - } value = null; return false; } - - /* - public bool Inherit + public object GetPropertyValueOrDefault(string name, object defaultValue = null) { - get { return inherit; } - }*/ - - /// - /// List of parents. - /// - //public AutoList Parents => parents; + object value; + if (TryGetPropertyValue(name, out value)) + return value; + else + return defaultValue; + } /// /// Store responsible for creating and keeping the resource. @@ -746,11 +607,6 @@ public class Instance public bool IsDestroyed { get; private set; } - /// - /// List of children. - /// - // public AutoList Children => children; - /// /// The unique and permanent link to the resource. /// @@ -793,35 +649,6 @@ public class Instance return new AsyncBag(default(T[])); } - /* - { - get - { - if (this.store != null) - return this.store.Link(this.resource); - else - { - var l = new List(); - //l.Add(name); - - var p = this.resource; // parents.First(); - - while (true) - { - l.Insert(0, p.Instance.name); - - if (p.Instance.parents.Count == 0) - break; - - p = p.Instance.parents.First(); - } - - return String.Join("/", l.ToArray()); - } - } - } - * - */ /// /// Instance name. @@ -913,7 +740,7 @@ public class Instance /// Name of the instance. /// Resource to manage. /// Store responsible for the resource. - public Instance(Warehouse warehouse, uint id, string name, IResource resource, IStore store, TypeTemplate customTemplate = null, ulong age = 0) + public Instance(Warehouse warehouse, uint id, string name, IResource resource, IStore store, ulong age = 0) { this.Warehouse = warehouse; this.store = store; @@ -933,10 +760,14 @@ public class Instance resource.OnDestroy += Resource_OnDestroy; - if (customTemplate != null) - this.template = customTemplate; + if (resource is IDynamicResource dynamicResource) + { + this.template = dynamicResource.ResourceTemplate; + } else + { this.template = Warehouse.GetTemplateByType(resource.GetType()); + } // set ages for (byte i = 0; i < template.Properties.Length; i++) @@ -1020,34 +851,6 @@ public class Instance } } - - //IQueryable Children => store.GetChildren(this); - - - /* - * private void Children_OnRemoved(Instance parent, IResource value) - { - value.Instance.parents.Remove(resource); - } - - private void Children_OnAdd(Instance parent, IResource value) - { - if (!value.Instance.parents.Contains(resource)) - value.Instance.parents.Add(resource); - } - - private void Parents_OnRemoved(Instance parent, IResource value) - { - value.Instance.children.Remove(resource); - } - - private void Parents_OnAdd(Instance parent, IResource value) - { - if (!value.Instance.children.Contains(resource)) - value.Instance.children.Add(resource); - } - */ - private void Resource_OnDestroy(object sender) { IsDestroyed = true; diff --git a/Esiur/Resource/Template/ArgumentTemplate.cs b/Esiur/Resource/Template/ArgumentTemplate.cs index 62d3b27..955e93e 100644 --- a/Esiur/Resource/Template/ArgumentTemplate.cs +++ b/Esiur/Resource/Template/ArgumentTemplate.cs @@ -30,20 +30,30 @@ public class ArgumentTemplate offset += cs; var (size, type) = TRU.Parse(data, offset); + offset += size; Map annotations = null; if (hasAnnotations) { - var acs = data.GetUInt32(offset, Endian.Little); - offset += 2; + //var acs = data.GetUInt32(offset, Endian.Little); + //offset += 2; var (l, a) = Codec.ParseSync(data, offset, null); // for saftey, Map might change in the future if (a is Map ann) annotations = ann; + + cs += l; } - return (cs + 2 + size, new ArgumentTemplate(name, index, type, optional, annotations)); + return (cs + 2 + size, new ArgumentTemplate() + { + Name = name, + Index = index, + Type = type, + Optional = optional, + Annotations = annotations + }); } public ArgumentTemplate() @@ -51,15 +61,7 @@ public class ArgumentTemplate } - public ArgumentTemplate(string name, int index, TRU type, bool optional, Map annotations) - { - Name = name; - Index = index; - Type = type; - Optional = optional; - Annotations = annotations; - } - + public override string ToString() { if (Optional) @@ -90,10 +92,8 @@ public class ArgumentTemplate .AddUInt8((byte)name.Length) .AddUInt8Array(name) .AddUInt8Array(Type.Compose()) - .AddUInt32((ushort)exp.Length) .AddUInt8Array(exp) .ToArray(); - } } } diff --git a/Esiur/Resource/Template/AttributeTemplate.cs b/Esiur/Resource/Template/AttributeTemplate.cs index 214d96a..c67614a 100644 --- a/Esiur/Resource/Template/AttributeTemplate.cs +++ b/Esiur/Resource/Template/AttributeTemplate.cs @@ -10,6 +10,7 @@ namespace Esiur.Resource.Template; public class AttributeTemplate : MemberTemplate { + public PropertyInfo PropertyInfo { get; @@ -17,16 +18,14 @@ public class AttributeTemplate : MemberTemplate } - public AttributeTemplate(TypeTemplate template, byte index, string name, bool inherited) - : base(template, index, name, inherited) - { - - } - 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; + return new AttributeTemplate() + { + Index = index, + Inherited = pi.DeclaringType != type, + Name = customName, + PropertyInfo = pi + }; } } diff --git a/Esiur/Resource/Template/ConstantTemplate.cs b/Esiur/Resource/Template/ConstantTemplate.cs index f5eea7d..100b3d1 100644 --- a/Esiur/Resource/Template/ConstantTemplate.cs +++ b/Esiur/Resource/Template/ConstantTemplate.cs @@ -9,33 +9,59 @@ namespace Esiur.Resource.Template; public class ConstantTemplate : MemberTemplate { - public readonly object Value; + public object Value { get; set; } - public Map Annotations; - public readonly TRU ValueType; + public Map Annotations { get; set; } + public TRU ValueType { get; set; } - public FieldInfo FieldInfo { get; set; } + public FieldInfo FieldInfo { get; set; } - public ConstantTemplate(TypeTemplate template, byte index, string name, bool inherited, TRU valueType, object value, Map annotations) - : base(template, index, name, inherited) + + public static (uint, ConstantTemplate) Parse(byte[] data, uint offset, byte index, bool inherited) { - Annotations = annotations; - ValueType = valueType; - Value = value; - //try - //{ - // Codec.Compose(value, null); - // Value = value; - //} - //catch - //{ - // throw new Exception($"Constant `{template.ClassName}.{name}` can't be serialized."); - //} + var oOffset = offset; + + var hasAnnotation = ((data[offset++] & 0x10) == 0x10); + + var name = data.GetString(offset + 1, data[offset]); + offset += (uint)data[offset] + 1; + + var (dts, valueType) = TRU.Parse(data, offset); + + offset += dts; + + (dts, var value) = Codec.ParseSync(data, offset, Warehouse.Default); + + offset += dts; + + Map annotations = null; + + // arguments + if (hasAnnotation) // Annotation ? + { + var (len, anns) = Codec.ParseSync(data, offset, null); + + if (anns is Map map) + annotations = map; + + offset += len; + } + + return (offset - oOffset, new ConstantTemplate() + { + Index = index, + Name = name, + Inherited = inherited, + ValueType = valueType, + Value = value, + Annotations = annotations + }); + } - public override byte[] Compose() + public byte[] Compose() { - var name = base.Compose(); + var name = DC.ToBytes(Name); var hdr = Inherited ? (byte)0x80 : (byte)0; @@ -93,10 +119,17 @@ public class ConstantTemplate : MemberTemplate } - var ct = new ConstantTemplate(typeTemplate, index, customName ?? ci.Name, ci.DeclaringType != type, valueType, value, annotations); - ct.FieldInfo = ci; - return ct; + return new ConstantTemplate() + { + Name = customName, + Index = index, + Inherited = ci.DeclaringType != type, + ValueType = valueType, + Value = value, + FieldInfo = ci, + Annotations = annotations, + }; } diff --git a/Esiur/Resource/Template/EventTemplate.cs b/Esiur/Resource/Template/EventTemplate.cs index 3713ab2..9f29451 100644 --- a/Esiur/Resource/Template/EventTemplate.cs +++ b/Esiur/Resource/Template/EventTemplate.cs @@ -12,6 +12,7 @@ namespace Esiur.Resource.Template; public class EventTemplate : MemberTemplate { + public Map Annotations { get; @@ -29,9 +30,48 @@ public class EventTemplate : MemberTemplate public TRU ArgumentType { get; set; } - public override byte[] Compose() + + public static (uint, EventTemplate) Parse(byte[] data, uint offset, byte index, bool inherited) { - var name = base.Compose(); + var oOffset = offset; + + var hasAnnotation = ((data[offset] & 0x10) == 0x10); + var subscribable = ((data[offset++] & 0x8) == 0x8); + + var name = data.GetString(offset + 1, data[offset]); + offset += (uint)data[offset] + 1; + + var (dts, argType) = TRU.Parse(data, offset); + + offset += dts; + + // Annotation ? + Map annotations = null; + + if (hasAnnotation) + { + var (len, anns) = Codec.ParseSync(data, offset, null); + + if (anns is Map map) + annotations = map; + + offset += len; + } + + return (offset - oOffset, new EventTemplate() + { + Index = index, + Name = name, + Inherited = inherited, + ArgumentType = argType, + Subscribable = subscribable, + Annotations = annotations + }); + } + + public byte[] Compose() + { + var name = Name.ToBytes(); var hdr = Inherited ? (byte)0x80 : (byte)0; @@ -62,13 +102,13 @@ public class EventTemplate : MemberTemplate .ToArray(); } - public EventTemplate(TypeTemplate template, byte index, string name, bool inherited, TRU argumentType, Map annotations = null, bool subscribable = false) - : base(template, index, name, inherited) - { - this.Annotations = annotations; - this.Subscribable = subscribable; - this.ArgumentType = argumentType; - } + //public EventTemplate(TypeTemplate template, byte index, string name, bool inherited, TRU argumentType, Map annotations = null, bool subscribable = false) + // : base(template, index, name, inherited) + //{ + // this.Annotations = annotations; + // this.Subscribable = subscribable; + // this.ArgumentType = argumentType; + //} public static EventTemplate MakeEventTemplate(Type type, EventInfo ei, byte index = 0, string customName = null, TypeTemplate typeTemplate = null) { @@ -121,21 +161,26 @@ public class EventTemplate : MemberTemplate evtType.SetNull(nullableAttrFlags); } - var et = new EventTemplate(typeTemplate, index, customName ?? ei.Name, ei.DeclaringType != type, evtType); - et.EventInfo = ei; - + Map annotations = null; if (annotationAttrs != null && annotationAttrs.Count() > 0) { - et.Annotations = new Map(); + annotations = new Map(); foreach (var attr in annotationAttrs) - et.Annotations.Add(attr.Key, attr.Value); + annotations.Add(attr.Key, attr.Value); } - if (subscribableAttr != null) - et.Subscribable = true; - return et; + return new EventTemplate() + { + Name = customName ?? ei.Name, + ArgumentType = evtType, + Index = index, + Inherited = ei.DeclaringType != type, + Annotations = annotations, + EventInfo = ei, + Subscribable = subscribableAttr != null + }; } } diff --git a/Esiur/Resource/Template/FunctionTemplate.cs b/Esiur/Resource/Template/FunctionTemplate.cs index b5635e5..5c714a1 100644 --- a/Esiur/Resource/Template/FunctionTemplate.cs +++ b/Esiur/Resource/Template/FunctionTemplate.cs @@ -10,6 +10,7 @@ using System.Text; using System.Threading.Tasks; namespace Esiur.Resource.Template; + public class FunctionTemplate : MemberTemplate { @@ -38,10 +39,61 @@ public class FunctionTemplate : MemberTemplate } - public override byte[] Compose() + public static (uint, FunctionTemplate) Parse(byte[] data, uint offset, byte index, bool inherited) { - var name = base.Compose(); + var oOffset = offset; + + var isStatic = ((data[offset] & 0x4) == 0x4); + var hasAnnotation = ((data[offset++] & 0x10) == 0x10); + + var name = data.GetString(offset + 1, data[offset]); + offset += (uint)data[offset] + 1; + + // return type + var (rts, returnType) = TRU.Parse(data, offset); + offset += rts; + + // arguments count + var argsCount = data[offset++]; + List arguments = new(); + + for (var a = 0; a < argsCount; a++) + { + var (cs, argType) = ArgumentTemplate.Parse(data, offset, a); + arguments.Add(argType); + offset += cs; + } + + Map annotations = null; + + // arguments + if (hasAnnotation) // Annotation ? + { + var (len, anns) = Codec.ParseSync(data, offset, null); + + if (anns is Map map) + annotations = map; + + offset += len; + } + + return (offset - oOffset, new FunctionTemplate() + { + Index = index, + Name = name, + Arguments = arguments.ToArray(), + IsStatic = isStatic, + Inherited = inherited, + Annotations = annotations, + ReturnType = returnType, + }); + } + + public byte[] Compose() + { + + var name = DC.ToBytes(Name); var bl = new BinaryList() .AddUInt8((byte)name.Length) @@ -56,8 +108,7 @@ public class FunctionTemplate : MemberTemplate if (Annotations != null) { var exp = Codec.Compose(Annotations, null, null);// DC.ToBytes(Annotation); - bl.AddInt32(exp.Length) - .AddUInt8Array(exp); + bl.AddUInt8Array(exp); bl.InsertUInt8(0, (byte)((Inherited ? (byte)0x90 : (byte)0x10) | (IsStatic ? 0x4 : 0))); } else @@ -66,14 +117,14 @@ public class FunctionTemplate : MemberTemplate return bl.ToArray(); } - public FunctionTemplate(TypeTemplate template, byte index, string name, bool inherited, bool isStatic, ArgumentTemplate[] arguments, TRU returnType, Map annotations = null) - : base(template, index, name, inherited) - { - this.Arguments = arguments; - this.ReturnType = returnType; - this.Annotations = annotations; - this.IsStatic = isStatic; - } + //public FunctionTemplate(TypeTemplate template, byte index, string name, bool inherited, bool isStatic, ArgumentTemplate[] arguments, TRU returnType, Map annotations = null) + // : base(template, index, name, inherited) + //{ + // this.Arguments = arguments; + // this.ReturnType = returnType; + // this.Annotations = annotations; + // this.IsStatic = isStatic; + //} @@ -99,7 +150,10 @@ public class FunctionTemplate : MemberTemplate } else { - rtType = TRU.FromType(mi.ReturnType); + if (mi.ReturnType == typeof(Task)) + rtType = TRU.FromType(null); + else + rtType = TRU.FromType(mi.ReturnType); } if (rtType == null) @@ -162,7 +216,7 @@ public class FunctionTemplate : MemberTemplate if (args.Length > 0) { - if (args.Last().ParameterType == typeof(DistributedConnection) + if (args.Last().ParameterType == typeof(DistributedConnection) || args.Last().ParameterType == typeof(InvocationContext)) args = args.Take(args.Count() - 1).ToArray(); } @@ -219,32 +273,35 @@ public class FunctionTemplate : MemberTemplate }) .ToArray(); - var fn = customName ?? mi.Name; - - var ft = new FunctionTemplate(typeTemplate, index, fn, mi.DeclaringType != type, - mi.IsStatic, - arguments, rtType); - + Map annotations = null; if (annotationAttrs != null && annotationAttrs.Count() > 0) { - ft.Annotations = new Map(); + annotations = new Map(); foreach (var attr in annotationAttrs) - ft.Annotations.Add(attr.Key, attr.Value); + annotations.Add(attr.Key, attr.Value); } else { - ft.Annotations = new Map(); - ft.Annotations.Add(null, "(" + String.Join(",", + annotations = new Map(); + annotations.Add("", "(" + String.Join(",", mi.GetParameters().Where(x => x.ParameterType != typeof(DistributedConnection)) .Select(x => "[" + x.ParameterType.Name + "] " + x.Name)) + ") -> " + mi.ReturnType.Name); } - ft.MethodInfo = mi; - // functions.Add(ft); + return new FunctionTemplate() + { + Name = customName ?? mi.Name, + Index = index, + Inherited = mi.DeclaringType != type, + IsStatic = mi.IsStatic, + ReturnType = rtType, + Arguments = arguments, + MethodInfo = mi, + Annotations = annotations + }; - return ft; } public override string ToString() diff --git a/Esiur/Resource/Template/MemberData.cs b/Esiur/Resource/Template/MemberData.cs index a715393..139ac3d 100644 --- a/Esiur/Resource/Template/MemberData.cs +++ b/Esiur/Resource/Template/MemberData.cs @@ -40,19 +40,19 @@ public class MemberData 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; - } + //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; - } + // return rt; + //} } diff --git a/Esiur/Resource/Template/MemberTemplate.cs b/Esiur/Resource/Template/MemberTemplate.cs index 9c43b31..8ff8408 100644 --- a/Esiur/Resource/Template/MemberTemplate.cs +++ b/Esiur/Resource/Template/MemberTemplate.cs @@ -9,24 +9,24 @@ namespace Esiur.Resource.Template; public class MemberTemplate { - public readonly byte Index; - public readonly string Name; - public readonly bool Inherited; - public readonly TypeTemplate Template; + public byte Index { get; set; } + public string Name { get; set; } + public bool Inherited { get; set; } + public TypeTemplate Template { get; set; } - public MemberTemplate(TypeTemplate template, byte index, string name, bool inherited) - { - Template = template; - Index = index; - Name = name; - Inherited = inherited; - } + //public MemberTemplate() + //{ + // Template = template; + // Index = index; + // Name = name; + // Inherited = inherited; + //} public string Fullname => Template.ClassName + "." + Name; - public virtual byte[] Compose() - { - return DC.ToBytes(Name); - } + //public virtual byte[] Compose() + //{ + // return DC.ToBytes(Name); + //} } diff --git a/Esiur/Resource/Template/PropertyTemplate.cs b/Esiur/Resource/Template/PropertyTemplate.cs index ddcfae9..d4388f1 100644 --- a/Esiur/Resource/Template/PropertyTemplate.cs +++ b/Esiur/Resource/Template/PropertyTemplate.cs @@ -9,8 +9,11 @@ using System.Text; using System.Threading.Tasks; namespace Esiur.Resource.Template; + public class PropertyTemplate : MemberTemplate { + public Map Annotations { get; set; } + public enum PropertyPermission : byte { Read = 1, @@ -42,7 +45,7 @@ public class PropertyTemplate : MemberTemplate set; } - public bool IsNullable { get; set; } + //public bool IsNullable { get; set; } public bool Recordable { @@ -57,17 +60,17 @@ public class PropertyTemplate : MemberTemplate set; }*/ - public string ReadAnnotation - { - get; - set; - } + //public string ReadAnnotation + //{ + // get; + // set; + //} - public string WriteAnnotation - { - get; - set; - } + //public string WriteAnnotation + //{ + // get; + // set; + //} /* public bool Storable @@ -81,50 +84,105 @@ public class PropertyTemplate : MemberTemplate return $"{Name}: {ValueType}"; } - public override byte[] Compose() + public static (uint, PropertyTemplate) Parse(byte[] data, uint offset, byte index, bool inherited) { - var name = base.Compose(); + var oOffset = offset; + + + + var hasAnnotation = ((data[offset] & 0x8) == 0x8); + var recordable = ((data[offset] & 1) == 1); + var permission = (PropertyTemplate.PropertyPermission)((data[offset++] >> 1) & 0x3); + var name = data.GetString(offset + 1, data[offset]); + + offset += (uint)data[offset] + 1; + + var (dts, valueType) = TRU.Parse(data, offset); + + offset += dts; + + Map annotations = null; + + // arguments + if (hasAnnotation) // Annotation ? + { + var (len, anns) = Codec.ParseSync(data, offset, null); + + if (anns is Map map) + annotations = map; + + offset += len; + } + + return (offset - oOffset, new PropertyTemplate() + { + Index = index, + Name = name, + Inherited = inherited, + Permission = permission, + Recordable = recordable, + ValueType = valueType, + Annotations = annotations + }); + + } + + public byte[] Compose() + { + var name = DC.ToBytes(Name); + var pv = ((byte)(Permission) << 1) | (Recordable ? 1 : 0); if (Inherited) pv |= 0x80; - if (WriteAnnotation != null && ReadAnnotation != null) + //if (WriteAnnotation != null && ReadAnnotation != null) + //{ + // var rexp = DC.ToBytes(ReadAnnotation); + // var wexp = DC.ToBytes(WriteAnnotation); + // return new BinaryList() + // .AddUInt8((byte)(0x38 | pv)) + // .AddUInt8((byte)name.Length) + // .AddUInt8Array(name) + // .AddUInt8Array(ValueType.Compose()) + // .AddInt32(wexp.Length) + // .AddUInt8Array(wexp) + // .AddInt32(rexp.Length) + // .AddUInt8Array(rexp) + // .ToArray(); + //} + //else if (WriteAnnotation != null) + //{ + // var wexp = DC.ToBytes(WriteAnnotation); + // return new BinaryList() + // .AddUInt8((byte)(0x30 | pv)) + // .AddUInt8((byte)name.Length) + // .AddUInt8Array(name) + // .AddUInt8Array(ValueType.Compose()) + // .AddInt32(wexp.Length) + // .AddUInt8Array(wexp) + // .ToArray(); + //} + //else if (ReadAnnotation != null) + //{ + // var rexp = DC.ToBytes(ReadAnnotation); + // return new BinaryList() + // .AddUInt8((byte)(0x28 | pv)) + // .AddUInt8((byte)name.Length) + // .AddUInt8Array(name) + // .AddUInt8Array(ValueType.Compose()) + // .AddInt32(rexp.Length) + // .AddUInt8Array(rexp) + // .ToArray(); + //} + if (Annotations != null) { - var rexp = DC.ToBytes(ReadAnnotation); - var wexp = DC.ToBytes(WriteAnnotation); - return new BinaryList() - .AddUInt8((byte)(0x38 | pv)) - .AddUInt8((byte)name.Length) - .AddUInt8Array(name) - .AddUInt8Array(ValueType.Compose()) - .AddInt32(wexp.Length) - .AddUInt8Array(wexp) - .AddInt32(rexp.Length) - .AddUInt8Array(rexp) - .ToArray(); - } - else if (WriteAnnotation != null) - { - var wexp = DC.ToBytes(WriteAnnotation); - return new BinaryList() - .AddUInt8((byte)(0x30 | pv)) - .AddUInt8((byte)name.Length) - .AddUInt8Array(name) - .AddUInt8Array(ValueType.Compose()) - .AddInt32(wexp.Length) - .AddUInt8Array(wexp) - .ToArray(); - } - else if (ReadAnnotation != null) - { - var rexp = DC.ToBytes(ReadAnnotation); + var rexp = Codec.Compose(Annotations, null, null); return new BinaryList() .AddUInt8((byte)(0x28 | pv)) .AddUInt8((byte)name.Length) .AddUInt8Array(name) .AddUInt8Array(ValueType.Compose()) - .AddInt32(rexp.Length) .AddUInt8Array(rexp) .ToArray(); } @@ -139,17 +197,17 @@ public class PropertyTemplate : MemberTemplate } } - public PropertyTemplate(TypeTemplate template, byte index, string name, bool inherited, - TRU valueType, string readAnnotation = null, string writeAnnotation = null, bool recordable = false) - : base(template, index, name, inherited) - { - this.Recordable = recordable; - //this.Storage = storage; - if (readAnnotation != null) - this.ReadAnnotation = readAnnotation; - this.WriteAnnotation = writeAnnotation; - this.ValueType = valueType; - } + //public PropertyTemplate(TypeTemplate template, byte index, string name, bool inherited, + // TRU valueType, string readAnnotation = null, string writeAnnotation = null, bool recordable = false) + // : base(template, index, name, inherited) + //{ + // this.Recordable = recordable; + // //this.Storage = storage; + // if (readAnnotation != null) + // this.ReadAnnotation = readAnnotation; + // this.WriteAnnotation = writeAnnotation; + // this.ValueType = valueType; + //} public static PropertyTemplate MakePropertyTemplate(Type type, PropertyInfo pi, byte index = 0, string customName = null, TypeTemplate typeTemplate = null) { @@ -162,7 +220,7 @@ public class PropertyTemplate : MemberTemplate if (propType == null) throw new Exception($"Unsupported type `{pi.PropertyType}` in property `{type.Name}.{pi.Name}`"); - var annotationAttr = pi.GetCustomAttribute(true); + var annotationAttrs = pi.GetCustomAttributes(true); var storageAttr = pi.GetCustomAttribute(true); //var nullabilityContext = new NullabilityInfoContext(); @@ -196,19 +254,46 @@ public class PropertyTemplate : MemberTemplate propType.SetNull(nullableAttrFlags); } - var pt = new PropertyTemplate(typeTemplate, index, customName ?? pi.Name, pi.DeclaringType != type, propType); - if (storageAttr != null) - pt.Recordable = storageAttr.Mode == StorageMode.Recordable; + Map annotations = null; - if (annotationAttr != null) - pt.ReadAnnotation = annotationAttr.Annotation; + if (annotationAttrs != null && annotationAttrs.Count() > 0) + { + annotations = new Map(); + foreach (var attr in annotationAttrs) + annotations.Add(attr.Key, attr.Value); + } else - pt.ReadAnnotation = GetTypeAnnotationName(pi.PropertyType); + { + annotations = new Map(); + annotations.Add("", GetTypeAnnotationName(pi.PropertyType)); + } - pt.PropertyInfo = pi; + return new PropertyTemplate() + { + Name = customName ?? pi.Name, + Index = index, + Inherited = pi.DeclaringType != type, + ValueType = propType, + PropertyInfo = pi, + Recordable = storageAttr == null ? false : storageAttr.Mode == StorageMode.Recordable, + Permission = (pi.CanWrite && pi.CanRead) ? PropertyPermission.ReadWrite : (pi.CanWrite ? PropertyPermission.Write : PropertyPermission.Read), + Annotations = annotations, + }; - return pt; + //var pt = new PropertyTemplate(typeTemplate, index, customName ?? pi.Name, pi.DeclaringType != type, propType); + + //if (storageAttr != null) + // pt.Recordable = storageAttr.Mode == StorageMode.Recordable; + + //if (annotationAttr != null) + // pt.ReadAnnotation = annotationAttr.Annotation; + //else + // pt.ReadAnnotation = GetTypeAnnotationName(pi.PropertyType); + + //pt.PropertyInfo = pi; + + //return pt; } diff --git a/Esiur/Resource/Template/TemplateType.cs b/Esiur/Resource/Template/TemplateType.cs index c66c292..b0fc577 100644 --- a/Esiur/Resource/Template/TemplateType.cs +++ b/Esiur/Resource/Template/TemplateType.cs @@ -7,5 +7,6 @@ public enum TemplateType : byte { Resource, Record, - Enum + Enum, + Function } diff --git a/Esiur/Resource/Template/TypeTemplate.cs b/Esiur/Resource/Template/TypeTemplate.cs index 58ee646..2e354f1 100644 --- a/Esiur/Resource/Template/TypeTemplate.cs +++ b/Esiur/Resource/Template/TypeTemplate.cs @@ -25,10 +25,9 @@ public class TypeTemplate protected UUID classId; protected UUID? parentId; - public string Annotation { get; set; } + public Map Annotations { get; set; } string className; - List members = new List(); List functions = new List(); List events = new List(); List properties = new List(); @@ -137,10 +136,10 @@ public class TypeTemplate get { return className; } } - public MemberTemplate[] Methods - { - get { return members.ToArray(); } - } + //public MemberTemplate[] Methods + //{ + // get { return members.ToArray(); } + //} public FunctionTemplate[] Functions { @@ -383,34 +382,12 @@ public class TypeTemplate - public static ConstantTemplate MakeConstantTemplate(Type type, FieldInfo ci, ExportAttribute exportAttr, byte index = 0, TypeTemplate typeTemplate = null) - { - var annotationAttr = ci.GetCustomAttribute(true); - - var valueType = TRU.FromType(ci.FieldType); - - if (valueType == null) - throw new Exception($"Unsupported type `{ci.FieldType}` in constant `{type.Name}.{ci.Name}`"); - - var value = ci.GetValue(null); - - if (typeTemplate.Type == TemplateType.Enum) - value = Convert.ChangeType(value, ci.FieldType.GetEnumUnderlyingType()); - - var ct = new ConstantTemplate(typeTemplate, index, exportAttr?.Name ?? ci.Name, ci.DeclaringType != type, valueType, value, annotationAttr?.Annotation); - - return ct; - - } + public bool IsWrapper { get; private set; } public TypeTemplate(Type type, Warehouse warehouse = null) { - - //if (!type.IsPublic) - // throw new Exception("Not public"); - if (Codec.ImplementsInterface(type, typeof(IResource))) templateType = TemplateType.Resource; else if (Codec.ImplementsInterface(type, typeof(IRecord))) @@ -422,12 +399,6 @@ public class TypeTemplate IsWrapper = Codec.InheritsClass(type, typeof(DistributedResource)); - //if (isRecord && isResource) - // throw new Exception("Type can't have both IResource and IRecord interfaces"); - - //if (!(isResource || isRecord)) - // throw new Exception("Type is neither a resource nor a record."); - type = ResourceProxy.GetBaseType(type); DefinedType = type; @@ -440,17 +411,6 @@ public class TypeTemplate if (warehouse != null) warehouse.PutTemplate(this); - - - //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); - - var hierarchy = GetHierarchy(type); if (hierarchy.ContainsKey(MemberTypes.Field)) @@ -505,19 +465,6 @@ public class TypeTemplate .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 - for (var i = 0; i < functions.Count; i++) - members.Add(functions[i]); - // append properties - for (var i = 0; i < properties.Count; i++) - members.Add(properties[i]); - - // append constants - for (var i = 0; i < constants.Count; i++) - members.Add(constants[i]); // bake it binarily var b = new BinaryList(); @@ -526,8 +473,10 @@ public class TypeTemplate var hasParent = HasParent(type); - var classAnnotation = type.GetCustomAttribute(false); - var hasClassAnnotation = classAnnotation != null && classAnnotation.Annotation != null; + var classAnnotations = type.GetCustomAttributes(false); + + + var hasClassAnnotation = (classAnnotations != null) && (classAnnotations.Count() > 0); var classNameBytes = DC.ToBytes(className); @@ -546,15 +495,19 @@ public class TypeTemplate if (hasClassAnnotation) { - var classAnnotationBytes = DC.ToBytes(classAnnotation.Annotation); - b.AddUInt16((ushort)classAnnotationBytes.Length) - .AddUInt8Array(classAnnotationBytes); + Annotations = new Map(); + + foreach (var ann in classAnnotations) + Annotations.Add(ann.Key, ann.Value); + + var classAnnotationBytes = Codec.Compose (Annotations, null, null); + + b.AddUInt8Array(classAnnotationBytes); - Annotation = classAnnotation.Annotation; } b.AddInt32(version) - .AddUInt16((ushort)members.Count); + .AddUInt16((ushort)(functions.Count + properties.Count + events.Count + constants.Count)); foreach (var ft in functions) b.AddUInt8Array(ft.Compose()); @@ -758,9 +711,11 @@ public class TypeTemplate if (hasClassAnnotation) { - var len = data.GetUInt16(offset, Endian.Little); - offset += 2; - od.Annotation = data.GetString(offset, len); + var (len, anns) = Codec.ParseSync(data, offset, null); + + if (anns is Map annotations) + od.Annotations = annotations; + offset += len; } @@ -781,151 +736,32 @@ public class TypeTemplate if (type == 0) // function { - string annotation = null; - var isStatic = ((data[offset] & 0x4) == 0x4); - - - var hasAnnotation = ((data[offset++] & 0x10) == 0x10); - - var name = data.GetString(offset + 1, data[offset]); - offset += (uint)data[offset] + 1; - - // return type - var (rts, returnType) = TRU.Parse(data, offset); - offset += rts; - - // arguments count - var argsCount = data[offset++]; - List arguments = new(); - - for (var a = 0; a < argsCount; a++) - { - var (cs, argType) = ArgumentTemplate.Parse(data, offset, a); - arguments.Add(argType); - offset += cs; - } - - // arguments - if (hasAnnotation) // Annotation ? - { - var cs = data.GetUInt32(offset, Endian.Little); - offset += 4; - annotation = data.GetString(offset, cs); - offset += cs; - } - - var ft = new FunctionTemplate(od, functionIndex++, name, inherited, isStatic, arguments.ToArray(), returnType, annotation); - + var (len, ft) = FunctionTemplate.Parse(data, offset, functionIndex++, inherited); + offset += len; od.functions.Add(ft); } else if (type == 1) // property { - - string readAnnotation = null, writeAnnotation = null; - - var hasReadAnnotation = ((data[offset] & 0x8) == 0x8); - var hasWriteAnnotation = ((data[offset] & 0x10) == 0x10); - var recordable = ((data[offset] & 1) == 1); - var permission = (PropertyTemplate.PropertyPermission)((data[offset++] >> 1) & 0x3); - var name = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]); - - offset += (uint)data[offset] + 1; - - var (dts, valueType) = TRU.Parse(data, offset); - - offset += dts; - - if (hasReadAnnotation) // annotation ? - { - var cs = data.GetUInt32(offset, Endian.Little); - offset += 4; - readAnnotation = data.GetString(offset, cs); - offset += cs; - } - - if (hasWriteAnnotation) // annotation ? - { - var cs = data.GetUInt32(offset, Endian.Little); - offset += 4; - writeAnnotation = data.GetString(offset, cs); - offset += cs; - } - - var pt = new PropertyTemplate(od, propertyIndex++, name, inherited, valueType, readAnnotation, writeAnnotation, recordable); - + var (len, pt) = PropertyTemplate.Parse(data, offset, propertyIndex++, inherited); + offset += len; od.properties.Add(pt); + } else if (type == 2) // Event { - - string annotation = null; - var hasAnnotation = ((data[offset] & 0x10) == 0x10); - var listenable = ((data[offset++] & 0x8) == 0x8); - - var name = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, (int)data[offset]); - offset += (uint)data[offset] + 1; - - var (dts, argType) = TRU.Parse(data, offset); - - offset += dts; - - if (hasAnnotation) // annotation ? - { - var cs = data.GetUInt32(offset, Endian.Little); - offset += 4; - annotation = data.GetString(offset, cs); - offset += cs; - } - - var et = new EventTemplate(od, eventIndex++, name, inherited, argType, annotation, listenable); - + var (len, et) = EventTemplate.Parse(data, offset, propertyIndex++, inherited); + offset += len; od.events.Add(et); - } // constant else if (type == 3) { - string annotation = null; - var hasAnnotation = ((data[offset++] & 0x10) == 0x10); - - var name = data.GetString(offset + 1, data[offset]); - offset += (uint)data[offset] + 1; - - var (dts, valueType) = TRU.Parse(data, offset); - - offset += dts; - - (dts, var value) = Codec.ParseSync(data, offset, Warehouse.Default); - - offset += dts; - - if (hasAnnotation) // annotation ? - { - var cs = data.GetUInt32(offset, Endian.Little); - offset += 4; - annotation = data.GetString(offset, cs); - offset += cs; - } - - var ct = new ConstantTemplate(od, eventIndex++, name, inherited, valueType, value, annotation); - + var (len, ct) = ConstantTemplate.Parse(data, offset, propertyIndex++, inherited); + offset += len; od.constants.Add(ct); } } - // append signals - for (int i = 0; i < od.events.Count; i++) - od.members.Add(od.events[i]); - // append slots - for (int i = 0; i < od.functions.Count; i++) - od.members.Add(od.functions[i]); - // append properties - for (int i = 0; i < od.properties.Count; i++) - od.members.Add(od.properties[i]); - // append constants - for (int i = 0; i < od.constants.Count; i++) - od.members.Add(od.constants[i]); - return od; } diff --git a/Esiur/Resource/Warehouse.cs b/Esiur/Resource/Warehouse.cs index 40d1c73..38e6c88 100644 --- a/Esiur/Resource/Warehouse.cs +++ b/Esiur/Resource/Warehouse.cs @@ -425,7 +425,7 @@ public class Warehouse /// Resource instance. /// IStore that manages the resource. Can be null if the resource is a store. /// Parent resource. if not presented the store becomes the parent for the resource. - public async AsyncReply Put(string path, T resource, TypeTemplate customTemplate = null, ulong age = 0, IPermissionsManager manager = null, object attributes = null) where T : IResource + public async AsyncReply Put(string path, T resource, ulong age = 0, IPermissionsManager manager = null, object attributes = null) where T : IResource { if (resource.Instance != null) throw new Exception("Resource already initialized."); @@ -464,7 +464,7 @@ public class Warehouse var resourceReference = new WeakReference(resource); - resource.Instance = new Instance(this, resourceCounter++, instanceName, resource, store, customTemplate, age); + resource.Instance = new Instance(this, resourceCounter++, instanceName, resource, store, age); if (attributes != null) if (attributes is Map attrs) @@ -574,7 +574,7 @@ public class Warehouse public async AsyncReply New(Type type, string path, IPermissionsManager manager = null, object attributes = null, object properties = null) { var res = Create(type, properties); - return await Put(path, res, null, 0, manager, attributes); + return await Put(path, res, 0, manager, attributes); } public async AsyncReply New(string path, IPermissionsManager manager = null, object attributes = null, object properties = null)