mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2026-04-04 12:28:21 +00:00
Layout
This commit is contained in:
29
Libraries/Esiur/Resource/AnnotationAttribute.cs
Normal file
29
Libraries/Esiur/Resource/AnnotationAttribute.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Event | AttributeTargets.Parameter, AllowMultiple = true)]
|
||||
public class AnnotationAttribute : Attribute
|
||||
{
|
||||
|
||||
public readonly string? Key;
|
||||
public readonly string Value;
|
||||
|
||||
public AnnotationAttribute(string annotation)
|
||||
{
|
||||
Key = "";
|
||||
Value = annotation;
|
||||
}
|
||||
public AnnotationAttribute(string key, string value)
|
||||
{
|
||||
Key = key;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
//public AnnotationAttribute(params string[] annotations)
|
||||
//{
|
||||
// this.Annotation = String.Join("\n", annotations);
|
||||
//}
|
||||
}
|
||||
40
Libraries/Esiur/Resource/AttributeAttribute.cs
Normal file
40
Libraries/Esiur/Resource/AttributeAttribute.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2020 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class AttributeAttribute : System.Attribute
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public AttributeAttribute(string name = null)
|
||||
{
|
||||
this.Name = name;
|
||||
}
|
||||
}
|
||||
7
Libraries/Esiur/Resource/CustomEventOccurredEvent.cs
Normal file
7
Libraries/Esiur/Resource/CustomEventOccurredEvent.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
|
||||
public delegate void CustomEventOccurredEvent(CustomEventOccurredInfo info);
|
||||
27
Libraries/Esiur/Resource/CustomEventOccurredInfo.cs
Normal file
27
Libraries/Esiur/Resource/CustomEventOccurredInfo.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Esiur.Data.Types;
|
||||
using Esiur.Security.Authority;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
|
||||
public class CustomEventOccurredInfo
|
||||
{
|
||||
public readonly EventDef EventDef;
|
||||
public readonly IResource Resource;
|
||||
public readonly object Value;
|
||||
public readonly object Issuer;
|
||||
public readonly Func<Session, bool> Receivers;
|
||||
|
||||
public string Name => EventDef.Name;
|
||||
|
||||
public CustomEventOccurredInfo(IResource resource, EventDef eventDef, Func<Session, bool> receivers, object issuer, object value)
|
||||
{
|
||||
Resource = resource;
|
||||
EventDef = eventDef;
|
||||
Receivers = receivers;
|
||||
Issuer = issuer;
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
8
Libraries/Esiur/Resource/EventOccurredEvent.cs
Normal file
8
Libraries/Esiur/Resource/EventOccurredEvent.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Resource
|
||||
{
|
||||
public delegate void EventOccurredEvent(EventOccurredInfo info);
|
||||
}
|
||||
26
Libraries/Esiur/Resource/EventOccurredInfo.cs
Normal file
26
Libraries/Esiur/Resource/EventOccurredInfo.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Esiur.Data.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Resource
|
||||
{
|
||||
|
||||
public class EventOccurredInfo
|
||||
{
|
||||
|
||||
public readonly EventDef Definition;
|
||||
|
||||
public string Name => Definition.Name;
|
||||
|
||||
public readonly IResource Resource;
|
||||
public readonly object Value;
|
||||
|
||||
public EventOccurredInfo(IResource resource, EventDef eventDef, object value)
|
||||
{
|
||||
Resource = resource;
|
||||
Value = value;
|
||||
Definition = eventDef;
|
||||
}
|
||||
}
|
||||
}
|
||||
38
Libraries/Esiur/Resource/ExportAttribute.cs
Normal file
38
Libraries/Esiur/Resource/ExportAttribute.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Event | AttributeTargets.Class | AttributeTargets.Enum)]
|
||||
|
||||
public class ExportAttribute : Attribute
|
||||
{
|
||||
public string Name { get; private set; } = null;
|
||||
public PropertyPermission? Permission { get; private set; }
|
||||
|
||||
public ExportAttribute()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ExportAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public ExportAttribute(PropertyPermission permission)
|
||||
{
|
||||
Permission = permission;
|
||||
}
|
||||
|
||||
public ExportAttribute(string name, PropertyPermission permission)
|
||||
{
|
||||
Name = name;
|
||||
Permission = permission;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
43
Libraries/Esiur/Resource/IResource.cs
Normal file
43
Libraries/Esiur/Resource/IResource.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using Esiur.Core;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Esiur.Resource;
|
||||
|
||||
public delegate bool QueryFilter<T>(T value);
|
||||
|
||||
|
||||
public interface IResource : IDestructible
|
||||
{
|
||||
AsyncReply<bool> Trigger(ResourceTrigger trigger);
|
||||
|
||||
[NotMapped]
|
||||
[JsonIgnore]
|
||||
Instance? Instance { get; set; }
|
||||
}
|
||||
77
Libraries/Esiur/Resource/IStore.cs
Normal file
77
Libraries/Esiur/Resource/IStore.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using Esiur.Data;
|
||||
using Esiur.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Esiur.Security.Permissions;
|
||||
using Esiur.Security.Authority;
|
||||
using Esiur.Data.Types;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
public interface IStore : IResource
|
||||
{
|
||||
AsyncReply<IResource> Get(string path);
|
||||
AsyncReply<bool> Put(IResource resource, string path);
|
||||
string Link(IResource resource);
|
||||
//bool Record(IResource resource, string propertyName, object value, ulong? age, DateTime? dateTime);
|
||||
bool Modify(IResource resource, PropertyDef propertyDef, object value, ulong? age, DateTime? dateTime);
|
||||
AsyncReply<bool> Remove(IResource resource);
|
||||
|
||||
AsyncReply<bool> Remove(string path);
|
||||
|
||||
AsyncReply<bool> Move(IResource resource, string newPath);
|
||||
|
||||
//bool RemoveAttributes(IResource resource, string[] attributes = null);
|
||||
|
||||
//Structure GetAttributes(IResource resource, string[] attributes = null);
|
||||
|
||||
//bool SetAttributes(IResource resource, Structure attributes, bool clearAttributes = false);
|
||||
|
||||
|
||||
|
||||
//AsyncReply<bool> AddChild(IResource parent, IResource child);
|
||||
//AsyncReply<bool> RemoveChild(IResource parent, IResource child);
|
||||
|
||||
//AsyncReply<bool> AddParent(IResource child, IResource parent);
|
||||
//AsyncReply<bool> RemoveParent(IResource child, IResource parent);
|
||||
|
||||
|
||||
AsyncBag<T> Children<T>(IResource resource, string name) where T : IResource;
|
||||
AsyncBag<T> Parents<T>(IResource resource, string name) where T : IResource;
|
||||
|
||||
|
||||
|
||||
//AsyncReply<PropertyValue[]> GetPropertyRecord(IResource resource, string propertyName, ulong fromAge, ulong toAge);
|
||||
//AsyncReply<PropertyValue[]> GetPropertyRecordByDate(IResource resource, string propertyName, DateTime fromDate, DateTime toDate);
|
||||
|
||||
//AsyncReply<KeyList<PropertyDef, PropertyValue[]>> GetRecord(IResource resource, ulong fromAge, ulong toAge);
|
||||
// AsyncReply<KeyList<PropertyDef, PropertyValue[]>> GetRecordByDate(IResource resource, DateTime fromDate, DateTime toDate);
|
||||
|
||||
//AsyncReply<KeyList<PropertyDef, PropertyValue[]>> GetRecord(IResource resource, DateTime fromDate, DateTime toDate);
|
||||
}
|
||||
9
Libraries/Esiur/Resource/IgnoreAttribute.cs
Normal file
9
Libraries/Esiur/Resource/IgnoreAttribute.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
public class IgnoreAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
14
Libraries/Esiur/Resource/ImportAttribute.cs
Normal file
14
Libraries/Esiur/Resource/ImportAttribute.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class ImportAttribute : Attribute
|
||||
{
|
||||
public ImportAttribute(params string[] urls)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
846
Libraries/Esiur/Resource/Instance.cs
Normal file
846
Libraries/Esiur/Resource/Instance.cs
Normal file
@@ -0,0 +1,846 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Esiur.Data;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Reflection;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Security.Permissions;
|
||||
using Esiur.Security.Authority;
|
||||
using Esiur.Proxy;
|
||||
using Esiur.Core;
|
||||
using System.Text.Json;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Reflection.Emit;
|
||||
using Esiur.Data.Types;
|
||||
using Esiur.Protocol;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
|
||||
[NotMapped]
|
||||
public class Instance
|
||||
{
|
||||
string name;
|
||||
|
||||
// public int IntVal { get; set; }
|
||||
|
||||
|
||||
WeakReference<IResource> resource;
|
||||
IStore store;
|
||||
TypeDef definition;
|
||||
AutoList<IPermissionsManager, Instance> managers;
|
||||
|
||||
|
||||
public event PropertyModifiedEvent PropertyModified;
|
||||
|
||||
|
||||
|
||||
public event EventOccurredEvent EventOccurred;
|
||||
public event CustomEventOccurredEvent CustomEventOccurred;
|
||||
public event ResourceDestroyedEvent Destroyed;
|
||||
|
||||
bool loading = false;
|
||||
|
||||
//KeyList<string, object> attributes;
|
||||
|
||||
List<ulong?> ages = new();
|
||||
List<DateTime?> modificationDates = new();
|
||||
private ulong instanceAge;
|
||||
private byte hops;
|
||||
private DateTime instanceModificationDate;
|
||||
|
||||
uint id;
|
||||
|
||||
public KeyList<string, object> Variables { get; } = new KeyList<string, object>();
|
||||
|
||||
/// <summary>
|
||||
/// Instance attributes are custom properties associated with the instance, a place to store information by IStore.
|
||||
/// </summary>
|
||||
//public KeyList<string, object> Attributes
|
||||
//{
|
||||
// get
|
||||
// {
|
||||
// return attributes;
|
||||
// }
|
||||
//}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return name + " (" + Link + ")";
|
||||
}
|
||||
|
||||
public bool RemoveAttributes(string[] attributes = null)
|
||||
{
|
||||
|
||||
return false;
|
||||
|
||||
/*
|
||||
IResource res;
|
||||
|
||||
if (!resource.TryGetTarget(out res))
|
||||
return false;
|
||||
|
||||
return store.RemoveAttributes(res, attributes);
|
||||
*/
|
||||
|
||||
/*
|
||||
if (attributes == null)
|
||||
this.attributes.Clear();
|
||||
else
|
||||
{
|
||||
foreach (var attr in attributes)
|
||||
this.attributes.Remove(attr);
|
||||
}
|
||||
|
||||
return true;
|
||||
*/
|
||||
}
|
||||
|
||||
public Map<string, object> GetAttributes(string[] attributes = null)
|
||||
{
|
||||
// @TODO
|
||||
var rt = new Map<string, object>();
|
||||
|
||||
if (attributes != null)
|
||||
{
|
||||
for (var i = 0; i < attributes.Length; i++)
|
||||
{
|
||||
var at = definition.GetAttributeDef(attributes[i]);
|
||||
if (at != null)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
public bool SetAttributes(Map<string, object> attributes, bool clearAttributes = false)
|
||||
{
|
||||
|
||||
// @ TODO
|
||||
IResource res;
|
||||
|
||||
if (resource.TryGetTarget(out res))
|
||||
{
|
||||
foreach (var kv in attributes)
|
||||
{
|
||||
var at = definition.GetAttributeDef(kv.Key);
|
||||
|
||||
if (at != null)
|
||||
if (at.PropertyInfo.CanWrite)
|
||||
at.PropertyInfo.SetValue(res, RuntimeCaster.Cast(kv.Value, at.PropertyInfo.PropertyType));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
|
||||
/*
|
||||
try
|
||||
{
|
||||
|
||||
if (clearAttributes)
|
||||
this.attributes.Clear();
|
||||
|
||||
foreach (var attr in attributes)
|
||||
if (attr.Key == "name")
|
||||
this.name = attr.Value as string;
|
||||
else if (attr.Key == "managers")
|
||||
{
|
||||
this.managers.Clear();
|
||||
|
||||
var mngrs = attr.Value as object[];
|
||||
|
||||
foreach (var mngr in mngrs)
|
||||
{
|
||||
var m = mngr as Structure;
|
||||
var type = Type.GetType(m["type"] as string);
|
||||
if (Codec.ImplementsInterface(type, typeof(IPermissionsManager)))
|
||||
{
|
||||
var settings = m["settings"] as Structure;
|
||||
var manager = Activator.CreateInstance(type) as IPermissionsManager;
|
||||
|
||||
IResource res;
|
||||
if (this.resource.TryGetTarget(out res))
|
||||
{
|
||||
manager.Initialize(settings, res);
|
||||
this.managers.Add(manager);
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.attributes[attr.Key] = attr.Value;
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
public Structure GetAttributes()
|
||||
{
|
||||
var st = new Structure();
|
||||
foreach (var a in attributes.Keys)
|
||||
st[a] = attributes[a];
|
||||
|
||||
st["name"] = name;
|
||||
|
||||
var mngrs = new List<Structure>();
|
||||
|
||||
foreach (var manager in managers)
|
||||
{
|
||||
var mngr = new Structure();
|
||||
mngr["settings"] = manager.Settings;
|
||||
mngr["type"] = manager.GetType().FullName;
|
||||
mngrs.Add(mngr);
|
||||
}
|
||||
|
||||
st["managers"] = mngrs;
|
||||
|
||||
return st;
|
||||
}*/
|
||||
|
||||
/// <summary>
|
||||
/// Get the age of a given property index.
|
||||
/// </summary>
|
||||
/// <param name="index">Zero-based property index.</param>
|
||||
/// <returns>Age.</returns>
|
||||
public ulong? GetAge(byte index)
|
||||
{
|
||||
if (index < ages.Count)
|
||||
return ages[index];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the age of a property.
|
||||
/// </summary>
|
||||
/// <param name="index">Zero-based property index.</param>
|
||||
/// <param name="value">Age.</param>
|
||||
public void SetAge(byte index, ulong? value)
|
||||
{
|
||||
if (index < ages.Count)
|
||||
{
|
||||
ages[index] = value;
|
||||
if (value > instanceAge)
|
||||
instanceAge = (ulong)value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the modification date of a property.
|
||||
/// </summary>
|
||||
/// <param name="index">Zero-based property index.</param>
|
||||
/// <param name="value">Modification date.</param>
|
||||
public void SetModificationDate(byte index, DateTime? value)
|
||||
{
|
||||
if (index < modificationDates.Count)
|
||||
{
|
||||
modificationDates[index] = value;
|
||||
if (value > instanceModificationDate)
|
||||
instanceModificationDate = (DateTime)value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get modification date of a specific property.
|
||||
/// </summary>
|
||||
/// <param name="index">Zero-based property index</param>
|
||||
/// <returns>Modification date.</returns>
|
||||
public DateTime? GetModificationDate(byte index)
|
||||
{
|
||||
if (index < modificationDates.Count)
|
||||
return modificationDates[index];
|
||||
else
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Load property value (used by stores)
|
||||
/// </summary>
|
||||
/// <param name="name">Property name</param>
|
||||
/// <param name="age">Property age</param>
|
||||
/// <param name="value">Property value</param>
|
||||
/// <returns></returns>
|
||||
public bool LoadProperty(string name, ulong? age, DateTime? modificationDate, object value)
|
||||
{
|
||||
|
||||
IResource res;
|
||||
|
||||
if (!resource.TryGetTarget(out res))
|
||||
return false;
|
||||
|
||||
var pt = definition.GetPropertyDefByName(name);
|
||||
|
||||
if (pt == null)
|
||||
return false;
|
||||
|
||||
/*
|
||||
#if NETSTANDARD
|
||||
var pi = resource.GetType().GetTypeInfo().GetProperty(name, new[] { resource.GetType() });
|
||||
#else
|
||||
var pi = resource.GetType().GetProperty(pt.Name);
|
||||
#endif
|
||||
*/
|
||||
|
||||
if (pt.PropertyInfo.PropertyType.IsGenericType
|
||||
&& pt.PropertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(PropertyContext<>))
|
||||
return false;
|
||||
|
||||
|
||||
if (pt.PropertyInfo.CanWrite)
|
||||
{
|
||||
try
|
||||
{
|
||||
loading = true;
|
||||
|
||||
pt.PropertyInfo.SetValue(res, RuntimeCaster.Cast(value, pt.PropertyInfo.PropertyType));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//Console.WriteLine(resource.ToString() + " " + name);
|
||||
Global.Log(ex);
|
||||
}
|
||||
|
||||
loading = false;
|
||||
}
|
||||
|
||||
|
||||
SetAge(pt.Index, age);
|
||||
SetModificationDate(pt.Index, modificationDate);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Age of the instance, incremented by 1 in every modification.
|
||||
/// </summary>
|
||||
public ulong Age
|
||||
{
|
||||
get { return instanceAge; }
|
||||
internal set { instanceAge = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of nodes to reach the original resource.
|
||||
/// </summary>
|
||||
public byte Hops
|
||||
{
|
||||
get { return hops; }
|
||||
internal set { hops = value; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Last modification date.
|
||||
/// </summary>
|
||||
public DateTime? ModificationDate
|
||||
{
|
||||
get
|
||||
{
|
||||
return instanceModificationDate;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instance Id.
|
||||
/// </summary>
|
||||
public uint Id
|
||||
{
|
||||
get { return id; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Import properties from bytes array.
|
||||
/// </summary>
|
||||
/// <param name="properties"></param>
|
||||
/// <returns></returns>
|
||||
public bool Deserialize(PropertyValue[] properties)
|
||||
{
|
||||
for (byte i = 0; i < properties.Length; i++)
|
||||
{
|
||||
var pt = this.definition.GetPropertyDefByIndex(i);
|
||||
if (pt != null)
|
||||
{
|
||||
var pv = properties[i];
|
||||
LoadProperty(pt.Name, pv.Age, pv.Date, pv.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public string ToJson()
|
||||
{
|
||||
IResource res;
|
||||
if (resource.TryGetTarget(out res))
|
||||
return JsonSerializer.Serialize(res, Global.SerializeOptions);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Export all properties with ResourceProperty attributed as bytes array.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
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<PropertyValue>();
|
||||
|
||||
foreach (var pt in definition.Properties)
|
||||
{
|
||||
var rt = pt.PropertyInfo.GetValue(res, null);
|
||||
props.Add(new PropertyValue(rt, ages[pt.Index], modificationDates[pt.Index]));
|
||||
}
|
||||
|
||||
return props.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Export all properties with ResourceProperty attributed as bytes array after a specific age.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Map<byte, PropertyValue> 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<byte, PropertyValue>();
|
||||
|
||||
foreach (var pt in definition.Properties)
|
||||
{
|
||||
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]));
|
||||
}
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// If True, the instance can be stored to disk.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsStorable()
|
||||
{
|
||||
#if NETSTANDARD
|
||||
var attrs = resource.GetType().GetTypeInfo().GetCustomAttributes(typeof(Storable), true).ToArray();
|
||||
#else
|
||||
var attrs = resource.GetType().GetCustomAttributes(typeof(Storable), true);
|
||||
#endif
|
||||
return attrs.Length > 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
internal void EmitModification(PropertyDef pt, object value)
|
||||
{
|
||||
|
||||
IResource res;
|
||||
if (this.resource.TryGetTarget(out res))
|
||||
{
|
||||
instanceAge++;
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
ages[pt.Index] = instanceAge;
|
||||
modificationDates[pt.Index] = now;
|
||||
|
||||
//if (pt.HasHistory)
|
||||
//{
|
||||
// store.Record(res, pt.Name, value, ages[pt.Index], now);
|
||||
//}
|
||||
//else //if (pt.Storage == StorageMode.Recordable)
|
||||
//{
|
||||
store.Modify(res, pt, value, ages[pt.Index], now);
|
||||
//}
|
||||
|
||||
//ResourceModified?.Invoke(res, pt.Name, value);
|
||||
|
||||
PropertyModified?.Invoke(new PropertyModificationInfo(res, pt, value, instanceAge));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notify listeners that a property was modified.
|
||||
/// </summary>
|
||||
/// <param name="propertyName"></param>
|
||||
/// <param name="newValue"></param>
|
||||
/// <param name="oldValue"></param>
|
||||
public void Modified([CallerMemberName] string propertyName = "")
|
||||
{
|
||||
if (loading)
|
||||
return;
|
||||
|
||||
object value;
|
||||
if (TryGetPropertyValue(propertyName, out value))
|
||||
{
|
||||
var pt = definition.GetPropertyDefByName(propertyName);
|
||||
EmitModification(pt, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// internal void EmitResourceEvent(string name, string[] users, EpConnection[] connections, object[] args)
|
||||
|
||||
internal void EmitCustomResourceEvent(object issuer, Func<Session, bool> receivers, EventDef eventDef, object value)
|
||||
{
|
||||
IResource res;
|
||||
if (this.resource.TryGetTarget(out res))
|
||||
{
|
||||
CustomEventOccurred?.Invoke(new CustomEventOccurredInfo(res, eventDef, receivers, issuer, value));
|
||||
}
|
||||
}
|
||||
|
||||
internal void EmitResourceEvent(EventDef eventDef, object value)
|
||||
{
|
||||
IResource res;
|
||||
if (this.resource.TryGetTarget(out res))
|
||||
{
|
||||
EventOccurred?.Invoke(new EventOccurredInfo(res, eventDef, value));
|
||||
}
|
||||
}
|
||||
|
||||
internal void EmitResourceEventByIndex(byte eventIndex, object value)
|
||||
{
|
||||
IResource res;
|
||||
if (this.resource.TryGetTarget(out res))
|
||||
{
|
||||
var eventDef = definition.GetEventDefByIndex(eventIndex);
|
||||
EventOccurred?.Invoke(new EventOccurredInfo(res, eventDef, value));
|
||||
}
|
||||
}
|
||||
|
||||
internal void EmitCustomResourceEventByIndex(object issuer, Func<Session, bool> receivers, byte eventIndex, object value)
|
||||
{
|
||||
IResource res;
|
||||
if (this.resource.TryGetTarget(out res))
|
||||
{
|
||||
var eventDef = definition.GetEventDefByIndex(eventIndex);
|
||||
CustomEventOccurred?.Invoke(new CustomEventOccurredInfo(res, eventDef, receivers, issuer, value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get the value of a given property by name.
|
||||
/// </summary>
|
||||
/// <param name="name">Property name</param>
|
||||
/// <param name="value">Output value</param>
|
||||
/// <returns>True, if the resource has the property.</returns>
|
||||
public bool TryGetPropertyValue(string name, out object value)
|
||||
{
|
||||
var pt = definition.GetPropertyDefByName(name);
|
||||
|
||||
IResource res;
|
||||
if (resource.TryGetTarget(out res))
|
||||
{
|
||||
if (res is IDynamicResource dynamicResource)
|
||||
{
|
||||
value = dynamicResource.GetResourceProperty(pt.Index);
|
||||
return true;
|
||||
}
|
||||
else if (pt != null && pt.PropertyInfo != null)
|
||||
{
|
||||
value = pt.PropertyInfo.GetValue(res, null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public object GetPropertyValueOrDefault(string name, object defaultValue = null)
|
||||
{
|
||||
object value;
|
||||
if (TryGetPropertyValue(name, out value))
|
||||
return value;
|
||||
else
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Store responsible for creating and keeping the resource.
|
||||
/// </summary>
|
||||
public IStore Store
|
||||
{
|
||||
get { return store; }
|
||||
}
|
||||
|
||||
public bool IsDestroyed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique and permanent link to the resource.
|
||||
/// </summary>
|
||||
public string Link
|
||||
{
|
||||
get
|
||||
{
|
||||
IResource res;
|
||||
if (this.resource.TryGetTarget(out res))
|
||||
{
|
||||
if (res == res.Instance.store)
|
||||
return name; // root store
|
||||
else
|
||||
return store.Instance.name + "/" + store.Link(res);
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public AsyncBag<T> Children<T>(string name = null) where T : IResource
|
||||
{
|
||||
IResource res;
|
||||
if (this.resource.TryGetTarget(out res))
|
||||
{
|
||||
return store.Children<T>(res, name);
|
||||
}
|
||||
else
|
||||
return new AsyncBag<T>(null);
|
||||
}
|
||||
|
||||
public AsyncBag<T> Parents<T>(string name = null) where T : IResource
|
||||
{
|
||||
IResource res;
|
||||
if (this.resource.TryGetTarget(out res))
|
||||
{
|
||||
return store.Parents<T>(res, name);
|
||||
}
|
||||
else
|
||||
return new AsyncBag<T>(default(T[]));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Instance name.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
set { name = value; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Resource managed by this instance.
|
||||
/// </summary>
|
||||
public IResource Resource
|
||||
{
|
||||
get
|
||||
{
|
||||
IResource res;
|
||||
if (this.resource.TryGetTarget(out res))
|
||||
{
|
||||
return res;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resource TypeDef describes the properties, functions and events of the resource.
|
||||
/// </summary>
|
||||
public TypeDef Definition
|
||||
{
|
||||
get { return definition; }
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for permission.
|
||||
/// </summary>
|
||||
/// <param name="session">Caller sessions.</param>
|
||||
/// <param name="action">Action type</param>
|
||||
/// <param name="member">Function, property or event to check for permission.</param>
|
||||
/// <param name="inquirer">Permission inquirer.</param>
|
||||
/// <returns>Ruling.</returns>
|
||||
public Ruling Applicable(Session session, ActionType action, MemberDef member, object inquirer = null)
|
||||
{
|
||||
IResource res;
|
||||
if (this.resource.TryGetTarget(out res))
|
||||
{
|
||||
//return store.Applicable(res, session, action, member, inquirer);
|
||||
|
||||
foreach (IPermissionsManager manager in managers)
|
||||
{
|
||||
var r = manager.Applicable(res, session, action, member, inquirer);
|
||||
if (r != Ruling.DontCare)
|
||||
return r;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return Ruling.DontCare;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execution managers.
|
||||
/// </summary>
|
||||
public AutoList<IPermissionsManager, Instance> Managers => managers;
|
||||
|
||||
public readonly Warehouse Warehouse;
|
||||
/// <summary>
|
||||
/// Create new instance.
|
||||
/// </summary>
|
||||
/// <param name="id">Instance Id.</param>
|
||||
/// <param name="name">Name of the instance.</param>
|
||||
/// <param name="resource">Resource to manage.</param>
|
||||
/// <param name="store">Store responsible for the resource.</param>
|
||||
public Instance(Warehouse warehouse, uint id, string name, IResource resource, IStore store, ulong age = 0)
|
||||
{
|
||||
this.Warehouse = warehouse;
|
||||
this.store = store;
|
||||
this.resource = new WeakReference<IResource>(resource);
|
||||
this.id = id;
|
||||
this.name = name ?? "";
|
||||
this.instanceAge = age;
|
||||
|
||||
//this.attributes = new KeyList<string, object>(this);
|
||||
//children = new AutoList<IResource, Instance>(this);
|
||||
//parents = new AutoList<IResource, Instance>(this);
|
||||
managers = new AutoList<IPermissionsManager, Instance>(this);
|
||||
//children.OnAdd += Children_OnAdd;
|
||||
//children.OnRemoved += Children_OnRemoved;
|
||||
//parents.OnAdd += Parents_OnAdd;
|
||||
//parents.OnRemoved += Parents_OnRemoved;
|
||||
|
||||
resource.OnDestroy += Resource_OnDestroy;
|
||||
|
||||
if (resource is IDynamicResource dynamicResource)
|
||||
{
|
||||
this.definition = dynamicResource.ResourceDefinition;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.definition = Warehouse.GetTypeDefByType(resource.GetType());
|
||||
}
|
||||
|
||||
// set ages
|
||||
for (byte i = 0; i < definition.Properties.Length; i++)
|
||||
{
|
||||
ages.Add(0);
|
||||
modificationDates.Add(DateTime.MinValue);
|
||||
}
|
||||
|
||||
|
||||
// connect events
|
||||
if (!(resource is EpResource))
|
||||
{
|
||||
|
||||
Type t = ResourceProxy.GetBaseType(resource);
|
||||
|
||||
var events = t.GetTypeInfo().GetEvents(BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
var emitEventByIndexMethod = GetType().GetMethod("EmitResourceEventByIndex", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
var emitCustomEventByIndexMethod = GetType().GetMethod("EmitCustomResourceEventByIndex", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
foreach (var evt in definition.Events)
|
||||
{
|
||||
|
||||
if (evt.EventInfo == null)
|
||||
continue;
|
||||
|
||||
var eventGenericType = evt.EventInfo.EventHandlerType.GetGenericTypeDefinition();
|
||||
|
||||
if (eventGenericType == typeof(ResourceEventHandler<>))
|
||||
{
|
||||
|
||||
var dm = new DynamicMethod("_", null,
|
||||
new Type[] { typeof(Instance), evt.EventInfo.EventHandlerType.GenericTypeArguments[0] },
|
||||
typeof(Instance).Module, true);
|
||||
|
||||
|
||||
var il = dm.GetILGenerator();
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldc_I4, (int)evt.Index);
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
il.Emit(OpCodes.Box, evt.EventInfo.EventHandlerType.GenericTypeArguments[0]);
|
||||
il.Emit(OpCodes.Callvirt, emitEventByIndexMethod);
|
||||
il.Emit(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Ret);
|
||||
|
||||
|
||||
var proxyDelegate = dm.CreateDelegate(evt.EventInfo.EventHandlerType, this);
|
||||
|
||||
//ResourceEventHandler<object> proxyDelegate = new ResourceEventHandler<object>((args) => EmitResourceEvent(evt, args));
|
||||
evt.EventInfo.AddEventHandler(resource, proxyDelegate);
|
||||
|
||||
|
||||
}
|
||||
else if (eventGenericType == typeof(CustomResourceEventHandler<>))
|
||||
{
|
||||
var dm = new DynamicMethod("_", null,
|
||||
new Type[] { typeof(Instance), typeof(object), typeof(Func<Session, bool>),
|
||||
evt.EventInfo.EventHandlerType.GenericTypeArguments[0] },
|
||||
typeof(Instance).Module, true);
|
||||
|
||||
|
||||
var il = dm.GetILGenerator();
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
il.Emit(OpCodes.Ldarg_2);
|
||||
il.Emit(OpCodes.Ldc_I4, (int)evt.Index);
|
||||
il.Emit(OpCodes.Ldarg_3);
|
||||
il.Emit(OpCodes.Box, evt.EventInfo.EventHandlerType.GenericTypeArguments[0]);
|
||||
il.Emit(OpCodes.Callvirt, emitCustomEventByIndexMethod);
|
||||
il.Emit(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Ret);
|
||||
|
||||
|
||||
var proxyDelegate = dm.CreateDelegate(evt.EventInfo.EventHandlerType, this);
|
||||
|
||||
evt.EventInfo.AddEventHandler(resource, proxyDelegate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void Resource_OnDestroy(object sender)
|
||||
{
|
||||
IsDestroyed = true;
|
||||
Destroyed?.Invoke((IResource)sender);
|
||||
}
|
||||
}
|
||||
26
Libraries/Esiur/Resource/PropertyModificationInfo.cs
Normal file
26
Libraries/Esiur/Resource/PropertyModificationInfo.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Esiur.Data.Types;
|
||||
using Esiur.Resource;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
|
||||
public struct PropertyModificationInfo
|
||||
{
|
||||
public readonly IResource Resource;
|
||||
public readonly PropertyDef PropertyDef;
|
||||
public string Name => PropertyDef.Name;
|
||||
public readonly ulong Age;
|
||||
public object Value;
|
||||
|
||||
public PropertyModificationInfo(IResource resource, PropertyDef propertyDef, object value, ulong age)
|
||||
{
|
||||
Resource = resource;
|
||||
PropertyDef = propertyDef;
|
||||
Age = age;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
7
Libraries/Esiur/Resource/PropertyModifiedEvent.cs
Normal file
7
Libraries/Esiur/Resource/PropertyModifiedEvent.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
|
||||
public delegate void PropertyModifiedEvent(PropertyModificationInfo data);
|
||||
13
Libraries/Esiur/Resource/PropertyPermission.cs
Normal file
13
Libraries/Esiur/Resource/PropertyPermission.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Resource
|
||||
{
|
||||
public enum PropertyPermission : byte
|
||||
{
|
||||
Read = 1,
|
||||
Write,
|
||||
ReadWrite,
|
||||
}
|
||||
}
|
||||
60
Libraries/Esiur/Resource/Resource.cs
Normal file
60
Libraries/Esiur/Resource/Resource.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Esiur.Core;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
public class Resource : IResource
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
public event DestroyedEvent OnDestroy;
|
||||
|
||||
public virtual void Destroy()
|
||||
{
|
||||
OnDestroy?.Invoke(this);
|
||||
}
|
||||
|
||||
public virtual AsyncReply<bool> Trigger(ResourceTrigger trigger)
|
||||
{
|
||||
if (trigger == ResourceTrigger.Initialize)
|
||||
return new AsyncReply<bool>(this.Create());
|
||||
else
|
||||
return new AsyncReply<bool>(true);
|
||||
}
|
||||
|
||||
|
||||
protected virtual bool Create()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
~Resource()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
14
Libraries/Esiur/Resource/ResourceAttribute.cs
Normal file
14
Libraries/Esiur/Resource/ResourceAttribute.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public class ResourceAttribute : Attribute
|
||||
{
|
||||
public ResourceAttribute()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
8
Libraries/Esiur/Resource/ResourceDestroyedEvent.cs
Normal file
8
Libraries/Esiur/Resource/ResourceDestroyedEvent.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Resource
|
||||
{
|
||||
public delegate void ResourceDestroyedEvent(IResource resource);
|
||||
}
|
||||
45
Libraries/Esiur/Resource/ResourceEventHandler.cs
Normal file
45
Libraries/Esiur/Resource/ResourceEventHandler.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
using Esiur.Data;
|
||||
using Esiur.Core;
|
||||
using Esiur.Security.Authority;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
|
||||
//public delegate R DCovariant<out R>();
|
||||
|
||||
public delegate void ResourceEventHandler<in T>(T argument);//where T : class;
|
||||
// public delegate void CustomUsersEventHanlder(string[] usernames, params object[] args);
|
||||
//public delegate void CustomReceiversEventHanlder(EpConnection[] connections, params object[] args);
|
||||
//public delegate void CustomInquirerEventHanlder(object inquirer, params object[] args);
|
||||
|
||||
public delegate void CustomResourceEventHandler<in T>(object issuer, Func<Session, bool> receivers, T argument);// object issuer, Session[] receivers, params object[] args);
|
||||
|
||||
// public delegate void CustomReceiversEventHanlder(string[] usernames, EpConnection[] connections, params object[] args);
|
||||
|
||||
27
Libraries/Esiur/Resource/ResourceQuery.cs
Normal file
27
Libraries/Esiur/Resource/ResourceQuery.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
|
||||
public class ResourceQuery : IQueryable<IResource>
|
||||
{
|
||||
public Type ElementType => throw new NotImplementedException();
|
||||
|
||||
public Expression Expression => throw new NotImplementedException();
|
||||
|
||||
public IQueryProvider Provider => throw new NotImplementedException();
|
||||
|
||||
public IEnumerator<IResource> GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
41
Libraries/Esiur/Resource/ResourceTrigger.cs
Normal file
41
Libraries/Esiur/Resource/ResourceTrigger.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
public enum ResourceTrigger : int
|
||||
{
|
||||
Open = 0,
|
||||
Initialize,
|
||||
Terminate,
|
||||
Configure,
|
||||
SystemInitialized,
|
||||
SystemTerminated,
|
||||
SystemReload,
|
||||
}
|
||||
71
Libraries/Esiur/Resource/Storable.cs
Normal file
71
Libraries/Esiur/Resource/Storable.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using Esiur.Data;
|
||||
using Esiur.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
|
||||
[AttributeUsage(AttributeTargets.All)]
|
||||
public class Storable : global::System.Attribute
|
||||
{
|
||||
public delegate object SerializerFunction(object value);
|
||||
public delegate object DeserializerFunction(object data);
|
||||
|
||||
SerializerFunction serializer;
|
||||
DeserializerFunction deserializer;
|
||||
Tru dataType;
|
||||
|
||||
public Storable()
|
||||
{
|
||||
//dataType = = DataType.Void;
|
||||
}
|
||||
|
||||
public DeserializerFunction Deserializer
|
||||
{
|
||||
get { return deserializer; }
|
||||
}
|
||||
|
||||
public SerializerFunction Serializer
|
||||
{
|
||||
get { return serializer; }
|
||||
}
|
||||
|
||||
public Storable(Tru type)
|
||||
{
|
||||
this.dataType = type;
|
||||
}
|
||||
|
||||
public Storable(Tru type, SerializerFunction serializer, DeserializerFunction deserializer)
|
||||
{
|
||||
this.dataType = type;
|
||||
this.serializer = serializer;
|
||||
this.deserializer = deserializer;
|
||||
}
|
||||
}
|
||||
15
Libraries/Esiur/Resource/StorageAttribute.cs
Normal file
15
Libraries/Esiur/Resource/StorageAttribute.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class StorageAttribute : Attribute
|
||||
{
|
||||
public StorageMode Mode { get; set; }
|
||||
public StorageAttribute(StorageMode mode)
|
||||
{
|
||||
Mode = mode;
|
||||
}
|
||||
}
|
||||
11
Libraries/Esiur/Resource/StorageMode.cs
Normal file
11
Libraries/Esiur/Resource/StorageMode.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
public enum StorageMode : byte
|
||||
{
|
||||
NonVolatile,
|
||||
Volatile,
|
||||
History
|
||||
}
|
||||
58
Libraries/Esiur/Resource/StoreGeneric.cs
Normal file
58
Libraries/Esiur/Resource/StoreGeneric.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Data.Types;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
public abstract class Store<T> : IStore where T : IResource
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
public event DestroyedEvent OnDestroy;
|
||||
|
||||
|
||||
|
||||
public abstract AsyncBag<T1> Children<T1>(IResource resource, string name) where T1 : IResource;
|
||||
|
||||
public virtual void Destroy()
|
||||
{
|
||||
OnDestroy?.Invoke(this);
|
||||
}
|
||||
|
||||
public abstract AsyncReply<IResource> Get(string path);
|
||||
|
||||
public abstract AsyncReply<KeyList<PropertyDef, PropertyValue[]>> GetRecord(IResource resource, DateTime fromDate, DateTime toDate);
|
||||
|
||||
|
||||
public abstract string Link(IResource resource);
|
||||
|
||||
public abstract bool Modify(IResource resource, PropertyDef propertyDef, object value, ulong? age, DateTime? dateTime);
|
||||
|
||||
|
||||
public abstract AsyncReply<bool> Put(IResource resource, string path);
|
||||
|
||||
public abstract bool Record(IResource resource, string propertyName, object value, ulong? age, DateTime? dateTime);
|
||||
|
||||
|
||||
|
||||
|
||||
public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger);
|
||||
|
||||
//public async AsyncReply<T> New(string name = null, object attributes = null, object properties = null)
|
||||
//{
|
||||
// var resource = await Warehouse.New<T>(name, this, null, null, attributes, properties);
|
||||
// resource.Instance.Managers.AddRange(this.Instance.Managers.ToArray());
|
||||
// return resource;
|
||||
//}
|
||||
|
||||
public abstract AsyncReply<bool> Remove(IResource resource);
|
||||
|
||||
public abstract AsyncReply<bool> Remove(string path);
|
||||
|
||||
public abstract AsyncReply<bool> Move(IResource resource, string newPath);
|
||||
|
||||
public abstract AsyncBag<T1> Parents<T1>(IResource resource, string name) where T1 : IResource;
|
||||
}
|
||||
|
||||
40
Libraries/Esiur/Resource/SubscribableAttribute.cs
Normal file
40
Libraries/Esiur/Resource/SubscribableAttribute.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2021 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Event)]
|
||||
public class SubscribableAttribute : System.Attribute
|
||||
{
|
||||
|
||||
public SubscribableAttribute()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
19
Libraries/Esiur/Resource/TypeIdAttribute.cs
Normal file
19
Libraries/Esiur/Resource/TypeIdAttribute.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Esiur.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Resource
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum)]
|
||||
public class TypeIdAttribute : Attribute
|
||||
{
|
||||
public Uuid Id { get; private set; }
|
||||
|
||||
public TypeIdAttribute(string id)
|
||||
{
|
||||
var data = DC.FromHex(id, null);
|
||||
Id = new Uuid(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
668
Libraries/Esiur/Resource/Warehouse.cs
Normal file
668
Libraries/Esiur/Resource/Warehouse.cs
Normal file
@@ -0,0 +1,668 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Data.Types;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Net.Packets;
|
||||
using Esiur.Protocol;
|
||||
using Esiur.Proxy;
|
||||
using Esiur.Security.Permissions;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Resource;
|
||||
|
||||
// Central Resources Manager
|
||||
public class Warehouse
|
||||
{
|
||||
|
||||
public static Warehouse Default = new Warehouse();
|
||||
|
||||
//static byte prefixCounter;
|
||||
|
||||
//static AutoList<IStore, Instance> stores = new AutoList<IStore, Instance>(null);
|
||||
ConcurrentDictionary<uint, WeakReference<IResource>> resources = new ConcurrentDictionary<uint, WeakReference<IResource>>();
|
||||
ConcurrentDictionary<IStore, List<WeakReference<IResource>>> stores = new ConcurrentDictionary<IStore, List<WeakReference<IResource>>>();
|
||||
|
||||
|
||||
uint resourceCounter = 0;
|
||||
|
||||
|
||||
KeyList<TypeDefKind, KeyList<Uuid, TypeDef>> typeDefs
|
||||
= new KeyList<TypeDefKind, KeyList<Uuid, TypeDef>>()
|
||||
{
|
||||
[TypeDefKind.Resource] = new KeyList<Uuid, TypeDef>(),
|
||||
[TypeDefKind.Record] = new KeyList<Uuid, TypeDef>(),
|
||||
[TypeDefKind.Enum] = new KeyList<Uuid, TypeDef>(),
|
||||
};
|
||||
|
||||
bool warehouseIsOpen = false;
|
||||
|
||||
public delegate void StoreEvent(IStore store);
|
||||
public event StoreEvent StoreConnected;
|
||||
public event StoreEvent StoreDisconnected;
|
||||
|
||||
public delegate AsyncReply<IStore> ProtocolInstance(string name, object properties);
|
||||
|
||||
public KeyList<string, ProtocolInstance> Protocols { get; } = new KeyList<string, ProtocolInstance>();
|
||||
|
||||
private Regex urlRegex = new Regex(@"^(?:([\S]*)://([^/]*)/?)");
|
||||
|
||||
|
||||
public Warehouse()
|
||||
{
|
||||
Protocols.Add("EP",
|
||||
async (name, attributes)
|
||||
=> await New<EpConnection>(name, null, attributes));
|
||||
|
||||
new TypeDef(typeof(EpAuthPacketIAuthHeader), this);
|
||||
|
||||
new TypeDef(typeof(EpAuthPacketIAuthDestination), this);
|
||||
new TypeDef(typeof(EpAuthPacketIAuthFormat), this);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get a store by its name.
|
||||
/// </summary>
|
||||
/// <param name="name">Store instance name</param>
|
||||
/// <returns></returns>
|
||||
public IStore GetStore(string name)
|
||||
{
|
||||
foreach (var s in stores)
|
||||
if (s.Key.Instance.Name == name)
|
||||
return s.Key;
|
||||
return null;
|
||||
}
|
||||
|
||||
public WeakReference<IResource>[] Resources => resources.Values.ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// Get a resource by instance Id.
|
||||
/// </summary>
|
||||
/// <param name="id">Instance Id</param>
|
||||
/// <returns></returns>
|
||||
public AsyncReply<IResource> GetById(uint id)
|
||||
{
|
||||
if (resources.ContainsKey(id))
|
||||
{
|
||||
IResource r;
|
||||
if (resources[id].TryGetTarget(out r))
|
||||
return new AsyncReply<IResource>(r);
|
||||
else
|
||||
return new AsyncReply<IResource>(null);
|
||||
}
|
||||
else
|
||||
return new AsyncReply<IResource>(null);
|
||||
}
|
||||
|
||||
void LoadGenerated()
|
||||
{
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
var generatedType = assembly.GetType("Esiur.Generated");
|
||||
if (generatedType != null)
|
||||
{
|
||||
var resourceTypes = (Type[])generatedType.GetProperty("Resources").GetValue(null);
|
||||
foreach (var t in resourceTypes)
|
||||
{
|
||||
RegisterTypeDef(new TypeDef(t));
|
||||
}
|
||||
|
||||
var recordTypes = (Type[])generatedType.GetProperty("Records").GetValue(null);
|
||||
foreach (var t in recordTypes)
|
||||
{
|
||||
RegisterTypeDef(new TypeDef(t));
|
||||
}
|
||||
|
||||
var enumsTypes = (Type[])generatedType.GetProperty("Enums").GetValue(null);
|
||||
foreach (var t in enumsTypes)
|
||||
{
|
||||
RegisterTypeDef(new TypeDef(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the warehouse.
|
||||
/// This function issues the initialize trigger to all stores and resources.
|
||||
/// </summary>
|
||||
/// <returns>True, if no problem occurred.</returns>
|
||||
public async AsyncReply<bool> Open()
|
||||
{
|
||||
if (warehouseIsOpen)
|
||||
return false;
|
||||
|
||||
// Load generated models
|
||||
LoadGenerated();
|
||||
|
||||
|
||||
warehouseIsOpen = true;
|
||||
|
||||
var resSnap = resources.Select(x =>
|
||||
{
|
||||
IResource r;
|
||||
if (x.Value.TryGetTarget(out r))
|
||||
return r;
|
||||
else
|
||||
return null;
|
||||
}).Where(r => r != null).ToArray();
|
||||
|
||||
foreach (var r in resSnap)
|
||||
{
|
||||
//IResource r;
|
||||
//if (rk.Value.TryGetTarget(out r))
|
||||
//{
|
||||
var rt = await r.Trigger(ResourceTrigger.Initialize);
|
||||
//if (!rt)
|
||||
// return false;
|
||||
|
||||
if (!rt)
|
||||
{
|
||||
Global.Log("Warehouse", LogType.Warning, $"Resource failed at Initialize {r.Instance.Name} [{r.Instance.Definition.Name}]");
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
foreach (var r in resSnap)
|
||||
{
|
||||
var rt = await r.Trigger(ResourceTrigger.SystemInitialized);
|
||||
if (!rt)
|
||||
{
|
||||
Global.Log("Warehouse", LogType.Warning, $"Resource failed at SystemInitialized {r.Instance.Name} [{r.Instance.Definition.Name}]");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close the warehouse.
|
||||
/// This function issues terminate trigger to all resources and stores.
|
||||
/// </summary>
|
||||
/// <returns>True, if no problem occurred.</returns>
|
||||
public AsyncReply<bool> Close()
|
||||
{
|
||||
|
||||
var bag = new AsyncBag<bool>();
|
||||
|
||||
foreach (var resource in resources.Values)
|
||||
{
|
||||
IResource r;
|
||||
if (resource.TryGetTarget(out r))
|
||||
{
|
||||
if (!(r is IStore))
|
||||
bag.Add(r.Trigger(ResourceTrigger.Terminate));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var store in stores)
|
||||
bag.Add(store.Key.Trigger(ResourceTrigger.Terminate));
|
||||
|
||||
|
||||
foreach (var resource in resources.Values)
|
||||
{
|
||||
IResource r;
|
||||
if (resource.TryGetTarget(out r))
|
||||
{
|
||||
if (!(r is IStore))
|
||||
bag.Add(r.Trigger(ResourceTrigger.SystemTerminated));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach (var store in stores)
|
||||
bag.Add(store.Key.Trigger(ResourceTrigger.SystemTerminated));
|
||||
|
||||
bag.Seal();
|
||||
|
||||
var rt = new AsyncReply<bool>();
|
||||
bag.Then((x) =>
|
||||
{
|
||||
foreach (var b in x)
|
||||
if (!b)
|
||||
{
|
||||
rt.Trigger(false);
|
||||
return;
|
||||
}
|
||||
|
||||
rt.Trigger(true);
|
||||
});
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
|
||||
public async AsyncReply<IResource> Query(string path)
|
||||
{
|
||||
var p = path.Trim().TrimStart('/').Split('/');
|
||||
|
||||
foreach (var store in stores.Keys)
|
||||
{
|
||||
if (p[0] == store.Instance.Name)
|
||||
{
|
||||
if (p.Length == 1)
|
||||
return store;
|
||||
|
||||
var res = await store.Get(String.Join("/", p.Skip(1).ToArray()));
|
||||
if (res != null)
|
||||
return res;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a resource by its path.
|
||||
/// Resource path is sperated by '/' character, e.g. "system/http".
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns>Resource instance.</returns>
|
||||
public async AsyncReply<T> Get<T>(string path, object attributes = null, IResource parent = null, IPermissionsManager manager = null)
|
||||
where T : IResource
|
||||
{
|
||||
|
||||
if (urlRegex.IsMatch(path))
|
||||
{
|
||||
|
||||
var url = urlRegex.Split(path);
|
||||
|
||||
if (Protocols.ContainsKey(url[1]))
|
||||
{
|
||||
if (!warehouseIsOpen)
|
||||
await Open();
|
||||
|
||||
var handler = Protocols[url[1]];
|
||||
var store = await handler(url[2], attributes);
|
||||
|
||||
try
|
||||
{
|
||||
if (url[3].Length > 0 && url[3] != "")
|
||||
return (T)await store.Get(url[3]);
|
||||
else
|
||||
return (T)store;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Remove(store);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var res = await Query(path);
|
||||
|
||||
if (res == null)
|
||||
return default(T);
|
||||
else
|
||||
return (T)res;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Put a resource in the warehouse.
|
||||
/// </summary>
|
||||
/// <param name="name">Resource name.</param>
|
||||
/// <param name="resource">Resource instance.</param>
|
||||
/// <param name="store">IStore that manages the resource. Can be null if the resource is a store.</param>
|
||||
/// <param name="parent">Parent resource. if not presented the store becomes the parent for the resource.</param>
|
||||
public async AsyncReply<T> Put<T>(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.");
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
throw new ArgumentNullException("Invalid path.");
|
||||
|
||||
var location = path.TrimStart('/').Split('/');
|
||||
|
||||
IStore store = null;
|
||||
//IResource parent = null;
|
||||
|
||||
var instanceName = location.Last();
|
||||
|
||||
if (location.Length == 1)
|
||||
{
|
||||
if (!(resource is IStore))
|
||||
throw new Exception("Resource is not a store, root level path is not allowed.");
|
||||
|
||||
store = (IStore)resource;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// get parent
|
||||
var parent = await Get<IResource>(string.Join("/", location.Take(location.Length - 1)));
|
||||
|
||||
if (parent == null)
|
||||
throw new Exception("Can't find parent");
|
||||
|
||||
store = parent.Instance.Store;// GetStore(location[0]);
|
||||
|
||||
//if (store == null)
|
||||
// throw new Exception("Store not found.");
|
||||
}
|
||||
|
||||
var resourceReference = new WeakReference<IResource>(resource);
|
||||
|
||||
resource.Instance = new Instance(this, resourceCounter++, instanceName, resource, store, age);
|
||||
|
||||
if (attributes != null)
|
||||
if (attributes is Map<string, object> attrs)
|
||||
resource.Instance.SetAttributes(attrs);
|
||||
else
|
||||
resource.Instance.SetAttributes(Map<string, object>.FromObject(attributes));
|
||||
|
||||
try
|
||||
{
|
||||
if (resource is IStore)
|
||||
stores.TryAdd(resource as IStore, new List<WeakReference<IResource>>());
|
||||
else if ((IResource)resource != store)
|
||||
{
|
||||
if (!await store.Put(resource, string.Join("/", location.Skip(1).ToArray())))
|
||||
throw new Exception("Store failed to put the resource.");
|
||||
}
|
||||
|
||||
var t = resource.GetType();
|
||||
Global.Counters["T-" + t.Namespace + "." + t.Name]++;
|
||||
|
||||
resources.TryAdd(resource.Instance.Id, resourceReference);
|
||||
|
||||
if (warehouseIsOpen)
|
||||
{
|
||||
await resource.Trigger(ResourceTrigger.Initialize);
|
||||
if (resource is IStore)
|
||||
await resource.Trigger(ResourceTrigger.Open);
|
||||
}
|
||||
|
||||
if (resource is IStore)
|
||||
StoreConnected?.Invoke(resource as IStore);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Remove(resource);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
return resource;
|
||||
|
||||
}
|
||||
|
||||
public T Create<T>(object properties = null)
|
||||
{
|
||||
return (T)Create(typeof(T), properties);
|
||||
}
|
||||
|
||||
public IResource Create(Type type, object properties = null)
|
||||
{
|
||||
type = ResourceProxy.GetProxy(type);
|
||||
|
||||
var res = Activator.CreateInstance(type) as IResource;
|
||||
|
||||
|
||||
if (properties != null)
|
||||
{
|
||||
if (properties is Map<byte, object> map)
|
||||
{
|
||||
var typeDef = GetTypeDefByType(type);
|
||||
foreach (var kvp in map)
|
||||
typeDef.GetPropertyDefByIndex(kvp.Key).PropertyInfo.SetValue(res, kvp.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
var ps = Map<string, object>.FromObject(properties);
|
||||
|
||||
foreach (var p in ps)
|
||||
{
|
||||
var pi = type.GetProperty(p.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
|
||||
if (pi != null)
|
||||
{
|
||||
if (pi.CanWrite)
|
||||
{
|
||||
try
|
||||
{
|
||||
pi.SetValue(res, p.Value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Global.Log(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var fi = type.GetField(p.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
|
||||
if (fi != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
fi.SetValue(res, p.Value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Global.Log(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public async AsyncReply<IResource> New(Type type, string path, IPermissionsManager manager = null, object attributes = null, object properties = null)
|
||||
{
|
||||
var res = Create(type, properties);
|
||||
return await Put(path, res, 0, manager, attributes);
|
||||
}
|
||||
|
||||
public async AsyncReply<T> New<T>(string path, IPermissionsManager manager = null, object attributes = null, object properties = null)
|
||||
where T : IResource
|
||||
{
|
||||
return (T)(await New(typeof(T), path, manager, attributes, properties));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Put a resource schema in the schemas warehouse.
|
||||
/// </summary>
|
||||
/// <param name="typeDef">Resource type definition.</param>
|
||||
public void RegisterTypeDef(TypeDef typeDef)
|
||||
{
|
||||
if (typeDefs[typeDef.Kind].ContainsKey(typeDef.Id))
|
||||
throw new Exception($"TypeDef with same class Id already exists. {typeDefs[typeDef.Kind][typeDef.Id].Name} -> {typeDef.Name}");
|
||||
|
||||
typeDefs[typeDef.Kind][typeDef.Id] = typeDef;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get a TypeDef by type from the warehouse. If not in the warehouse, a new TypeDef is created and added to the warehouse.
|
||||
/// </summary>
|
||||
/// <param name="type">.Net type.</param>
|
||||
/// <returns>Resource TypeDef.</returns>
|
||||
public TypeDef GetTypeDefByType(Type type)
|
||||
{
|
||||
if (!(type.IsClass || type.IsEnum))
|
||||
return null;
|
||||
|
||||
var baseType = ResourceProxy.GetBaseType(type);
|
||||
|
||||
if (baseType == typeof(IResource)
|
||||
|| baseType == typeof(IRecord))
|
||||
return null;
|
||||
|
||||
TypeDefKind typeDefKind;
|
||||
if (Codec.ImplementsInterface(type, typeof(IResource)))
|
||||
typeDefKind = TypeDefKind.Resource;
|
||||
else if (Codec.ImplementsInterface(type, typeof(IRecord)))
|
||||
typeDefKind = TypeDefKind.Record;
|
||||
else if (type.IsEnum)
|
||||
typeDefKind = TypeDefKind.Enum;
|
||||
else
|
||||
return null;
|
||||
|
||||
var typeDef = typeDefs[typeDefKind].Values.FirstOrDefault(x => x.DefinedType == baseType);
|
||||
if (typeDef != null)
|
||||
return typeDef;
|
||||
|
||||
// create new TypeDef for type
|
||||
typeDef = new TypeDef(baseType, this);
|
||||
TypeDef.GetDependencies(typeDef, this);
|
||||
|
||||
return typeDef;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a TypeDef by TypeId from the warehouse. If not in the warehouse, a new TypeDef is created and added to the warehouse.
|
||||
/// </summary>
|
||||
/// <param name="typeId">typeId.</param>
|
||||
/// <returns>TypeDef.</returns>
|
||||
public TypeDef GetTypeDefById(Uuid typeId, TypeDefKind? typeDefKind = null)
|
||||
{
|
||||
if (typeDefKind == null)
|
||||
{
|
||||
// look into resources
|
||||
var typeDef = typeDefs[TypeDefKind.Resource][typeId];
|
||||
if (typeDef != null)
|
||||
return typeDef;
|
||||
|
||||
// look into records
|
||||
typeDef = typeDefs[TypeDefKind.Record][typeId];
|
||||
if (typeDef != null)
|
||||
return typeDef;
|
||||
|
||||
// look into enums
|
||||
typeDef = typeDefs[TypeDefKind.Enum][typeId];
|
||||
return typeDef;
|
||||
|
||||
}
|
||||
else
|
||||
return typeDefs[typeDefKind.Value][typeId];
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a TypeDef by type name . If not in the warehouse, a new TypeDef is created and added to the warehouse.
|
||||
/// </summary>
|
||||
/// <param name="typeName">Class full name.</param>
|
||||
/// <returns>TypeDef.</returns>
|
||||
public TypeDef GetTypeDefByName(string typeName, TypeDefKind? typeDefKind = null)
|
||||
{
|
||||
if (typeDefKind == null)
|
||||
{
|
||||
// look into resources
|
||||
var typeDef = typeDefs[TypeDefKind.Resource].Values.FirstOrDefault(x => x.Name == typeName);
|
||||
if (typeDef != null)
|
||||
return typeDef;
|
||||
|
||||
// look into records
|
||||
typeDef = typeDefs[TypeDefKind.Record].Values.FirstOrDefault(x => x.Name == typeName);
|
||||
if (typeDef != null)
|
||||
return typeDef;
|
||||
|
||||
// look into enums
|
||||
typeDef = typeDefs[TypeDefKind.Enum].Values.FirstOrDefault(x => x.Name == typeName);
|
||||
return typeDef;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return typeDefs[typeDefKind.Value].Values.FirstOrDefault(x => x.Name == typeName);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(IResource resource)
|
||||
{
|
||||
|
||||
if (resource.Instance == null)
|
||||
return false;
|
||||
|
||||
|
||||
WeakReference<IResource> resourceReference;
|
||||
|
||||
if (resources.ContainsKey(resource.Instance.Id))
|
||||
resources.TryRemove(resource.Instance.Id, out resourceReference);
|
||||
else
|
||||
return false;
|
||||
|
||||
|
||||
if (resource != resource.Instance.Store)
|
||||
{
|
||||
List<WeakReference<IResource>> list;
|
||||
if (stores.TryGetValue(resource.Instance.Store, out list))
|
||||
{
|
||||
|
||||
lock (((ICollection)list).SyncRoot)
|
||||
list.Remove(resourceReference);
|
||||
|
||||
}
|
||||
}
|
||||
if (resource is IStore)
|
||||
{
|
||||
var store = resource as IStore;
|
||||
|
||||
List<WeakReference<IResource>> toBeRemoved;
|
||||
|
||||
stores.TryRemove(store, out toBeRemoved);
|
||||
|
||||
|
||||
foreach (var o in toBeRemoved)
|
||||
{
|
||||
IResource r;
|
||||
if (o.TryGetTarget(out r))
|
||||
Remove(r);
|
||||
}
|
||||
|
||||
StoreDisconnected?.Invoke(resource as IStore);
|
||||
}
|
||||
|
||||
if (resource.Instance.Store != null)
|
||||
resource.Instance.Store.Remove(resource);
|
||||
|
||||
resource.Destroy();
|
||||
|
||||
resource.Instance = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user