2
0
mirror of https://github.com/esiur/esiur-dart.git synced 2025-06-27 14:53:11 +00:00
This commit is contained in:
2019-08-07 17:12:20 +03:00
parent b796bf9436
commit 1bf0bc32ae
80 changed files with 394 additions and 13 deletions

View File

@ -0,0 +1,46 @@
/*
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.
*/
import '../Core/IDestructible.dart';
import 'ResourceTrigger.dart';
import '../Core/AsyncReply.dart';
import 'Instance.dart';
abstract class IResource extends IDestructible
{
AsyncReply<bool> trigger(ResourceTrigger trigger);
/*
{
// do nothing
return new AsyncReply.ready(true);
}
destroy()
{
// Destroyed
}
*/
Instance instance;
}

View File

@ -0,0 +1,46 @@
/*
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.
*/
import './IResource.dart';
import '../Core/AsyncReply.dart';
import '../Data/KeyList.dart';
import './Template/PropertyTemplate.dart';
import '../Data/PropertyValue.dart';
// old
// abstract class IStore extends IResource
// new
abstract class IStore implements IResource
{
AsyncReply<IResource> get(String path);
AsyncReply<IResource> retrieve(int iid);
bool put(IResource resource);
String link(IResource resource);
bool record(IResource resource, String propertyName, dynamic value, int age, DateTime dateTime);
bool modify(IResource resource, String propertyName, dynamic value, int age, DateTime dateTime);
bool remove(IResource resource);
AsyncReply<KeyList<PropertyTemplate, List<PropertyValue>>> getRecord(IResource resource, DateTime fromDate, DateTime toDate);
}

View File

@ -0,0 +1,680 @@
import 'dart:core';
import '../Data/DC.dart';
import '../Data/Structure.dart';
import '../Data/AutoList.dart';
import './IStore.dart';
import './IResource.dart';
import '../Data/KeyList.dart';
import './StorageMode.dart';
import '../Data/ValueObject.dart';
import '../Core/IEventHandler.dart';
import '../Security/Permissions/Ruling.dart';
import '../Security/Permissions/IPermissionsManager.dart';
import '../Security/Permissions/ActionType.dart';
import './Template/ResourceTemplate.dart';
import './Template/PropertyTemplate.dart';
import './Template/FunctionTemplate.dart';
import './Template/EventTemplate.dart';
import '../Security/Authority/Session.dart';
import './Template/MemberTemplate.dart';
import '../Data/PropertyValue.dart';
import 'Warehouse.dart';
class Instance extends IEventHandler
{
String _name;
AutoList<IResource, Instance> _children;
IResource _resource;
IStore _store;
AutoList<IResource, Instance> _parents;
//bool inherit;
ResourceTemplate _template;
AutoList<IPermissionsManager, Instance> _managers;
KeyList<String, dynamic> _attributes;
List<int> _ages = new List<int>();
List<DateTime> _modificationDates = new List<DateTime>();
int _instanceAge;
DateTime _instanceModificationDate;
int _id;
/// <summary>
/// Instance attributes are custom properties associated with the instance, a place to store information by IStore.
/// </summary>
KeyList<String, dynamic> get attributes => _attributes;
@override
String toString() => _name + " (" + link + ")";
bool removeAttributes([List<String> attributes = null])
{
if (attributes == null)
this._attributes.clear();
else
{
for (var attr in attributes)
this.attributes.remove(attr);
}
return true;
}
Structure getAttributes([List<String> attributes = null])
{
var st = new Structure();
if (attributes == null)
{
var clone = this.attributes.keys.toList();
clone.add("managers");
attributes = clone.toList();
}
for(var attr in attributes)
{
if (attr == "name")
st["name"] = _name;
else if (attr == "managers")
{
var mngrs = new List<Structure>();
for (var i = 0; i < _managers.length; i++)
{
var mst = new Structure();
mst["type"] = _managers[i].runtimeType;
mst["settings"] = _managers[i].settings;
mngrs.add(mst);
}
st["managers"] = mngrs;
}
else if (attr == "parents")
{
st["parents"] = _parents.toList();
}
else if (attr == "children")
{
st["children"] = _children.toList();
}
else if (attr == "childrenCount")
{
st["childrenCount"] = _children.count;
}
else if (attr == "type")
{
st["type"] = resource.runtimeType;
}
else
st[attr] = _attributes[attr];
}
return st;
}
bool setAttributes(Structure attributes, [bool clearAttributes = false])
{
try
{
if (clearAttributes)
_attributes.clear();
for (var attrKey in attributes.keys)
if (attrKey == "name")
_name = attributes[attrKey];
else if (attrKey == "managers")
{
_managers.clear();
var mngrs = attributes[attrKey] as List;
// this is not implemented now, Flutter doesn't support mirrors, needs a workaround @ Warehouse.registerManager
/*
for (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;
manager.Initialize(settings, this.resource);
this.managers.Add(manager);
}
else
return false;
}
*/
}
else
{
_attributes[attrKey] = attributes[attrKey];
}
}
catch(ex)
{
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>
int getAge(int index)
{
if (index < _ages.length)
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>
void setAge(int index, int value)
{
if (index < _ages.length)
{
_ages[index] = value;
if (value > _instanceAge)
_instanceAge = value;
}
}
/// <summary>
/// Set the modification date of a property.
/// </summary>
/// <param name="index">Zero-based property index.</param>
/// <param name="value">Modification date.</param>
void setModificationDate(int index, DateTime value)
{
if (index < _modificationDates.length)
{
_modificationDates[index] = value;
if (_instanceModificationDate == null || value.millisecondsSinceEpoch > _instanceModificationDate.millisecondsSinceEpoch)
_instanceModificationDate = value;
}
}
/// <summary>
/// Get modification date of a specific property.
/// </summary>
/// <param name="index">Zero-based property index</param>
/// <returns>Modification date.</returns>
DateTime getModificationDate(int index)
{
if (index < _modificationDates.length)
return _modificationDates[index];
else
return new DateTime(0);
}
/// <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>
bool loadProperty(String name, int age, DateTime modificationDate, dynamic value)
{
/*
var pt = _template.getPropertyTemplate(name);
if (pt == null)
return false;
if (pt.info.propertyType == typeof(DistributedPropertyContext))
return false;
try
{
if (pt.into.canWrite)
pt.info.setValue(resource, DC.CastConvert(value, pt.Info.PropertyType));
}
catch(ex)
{
//
}
setAge(pt.index, age);
setModificationDate(pt.index, modificationDate);
*/
return true;
}
/// <summary>
/// Age of the instance, incremented by 1 in every modification.
/// </summary>
int get age => _instanceAge;
// this must be internal
set age (value) => _instanceAge = value;
/// <summary>
/// Last modification date.
/// </summary>
DateTime get modificationDate => _instanceModificationDate;
/// <summary>
/// Instance Id.
/// </summary>
int get id => _id;
/// <summary>
/// Import properties from bytes array.
/// </summary>
/// <param name="properties"></param>
/// <returns></returns>
bool deserialize(List<PropertyValue> properties)
{
for (var i = 0; i < properties.length; i++)
{
var pt = _template.getPropertyTemplateByIndex(i);
if (pt != null)
{
var pv = properties[i];
loadProperty(pt.name, pv.age, pv.date, pv.value);
}
}
return true;
}
/// <summary>
/// Export all properties with ResourceProperty attributed as bytes array.
/// </summary>
/// <returns></returns>
List<PropertyValue> serialize()
{
List<PropertyValue> props = new List<PropertyValue>();
for (var pt in _template.properties)
{
// var rt = pt.info.getValue(resource, null);
// props.add(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<object> props = new List<object>();
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<DistributedConnection, object>)
rt = (rt as Func<DistributedConnection, object>)(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<object>();
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);
}
*/
/// <summary>
/// If True, the instance can be stored to disk.
/// </summary>
/// <returns></returns>
bool isStorable()
{
return false;
}
void emitModification(PropertyTemplate pt, dynamic value)
{
_instanceAge++;
var now = DateTime.now().toUtc();
_ages[pt.index] = _instanceAge;
_modificationDates[pt.index] = now;
if (pt.storage == StorageMode.NonVolatile)
{
_store.modify(_resource, pt.name, value, _ages[pt.index], now);
}
else if (pt.storage == StorageMode.Recordable)
{
_store.record(_resource, pt.name, value, _ages[pt.index], now);
}
emitArgs("resourceModified", [_resource, pt.name, value]);
}
/// <summary>
/// Notify listeners that a property was modified.
/// </summary>
/// <param name="propertyName"></param>
/// <param name="newValue"></param>
/// <param name="oldValue"></param>
modified(String propertyName)
{
var valueObject = new ValueObject();
if (getPropertyValue(propertyName, valueObject))
{
var pt = _template.getPropertyTemplateByName(propertyName);
emitModification(pt, valueObject.value);
}
}
emitResourceEvent(issuer, List<Session> receivers, String name, List<dynamic> args)
{
emitArgs("resourceEventOccurred", [_resource, issuer, receivers, name, args]);
}
/// <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>
bool getPropertyValue(String name, ValueObject valueObject)
{
var pt = _template.getPropertyTemplateByName(name);
/*
if (pt != null && pt.info != null)
{
valueObject.value = pt.info.getValue(_resource, null);
return true;
}*/
valueObject.value = null;
return false;
}
/*
public bool Inherit
{
get { return inherit; }
}*/
/// <summary>
/// List of parents.
/// </summary>
AutoList<IResource, Instance> get parents => _parents;
/// <summary>
/// Store responsible for creating and keeping the resource.
/// </summary>
IStore get store => _store;
/// <summary>
/// List of children.
/// </summary>
AutoList<IResource, Instance> get children => _children;
/// <summary>
/// The unique and permanent link to the resource.
/// </summary>
String get link
{
if (_store != null)
return _store.link(_resource);
else
{
var l = new List<String>();
var p = _resource;
while (true)
{
l.insert(0, p.instance.name);
if (p.instance.parents.count == 0)
break;
p = p.instance.parents.first();
}
return l.join("/");
}
}
/// <summary>
/// Instance name.
/// </summary>
String get name => _name;
set name(value) => name = value;
/// <summary>
/// Resource managed by this instance.
/// </summary>
IResource get resource => _resource;
/// <summary>
/// Resource template describes the properties, functions and events of the resource.
/// </summary>
ResourceTemplate get template => _template;
/// <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>
Ruling applicable(Session session, ActionType action, MemberTemplate member, [dynamic inquirer = null])
{
for(var i = 0; i < _managers.length; i++)
{
var r = _managers[i].applicable(this.resource, session, action, member, inquirer);
if (r != Ruling.DontCare)
return r;
}
return Ruling.DontCare;
}
/// <summary>
/// Execution managers.
/// </summary>
AutoList<IPermissionsManager, Instance> get managers => _managers;
/// <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>
Instance(int id, String name, IResource resource, IStore store, [ResourceTemplate customTemplate = null, int age = 0])
{
_store = store;
_resource = resource;
_id = id;
_name = name;
_instanceAge = age;
_attributes = new KeyList<String, dynamic>(this);
_children = new AutoList<IResource, Instance>(this);
_parents = new AutoList<IResource, Instance>(this);
_managers = new AutoList<IPermissionsManager, Instance>(this);
_children.on("add", children_OnAdd);
_children.on("remove", children_OnRemoved);
_parents.on("add", parents_OnAdd);
_parents.on("remove", parents_OnRemoved);
resource.on("destroy", resource_OnDestroy);
if (customTemplate != null)
_template = customTemplate;
else
_template = Warehouse.getTemplateByType(resource.runtimeType);
// set ages
for (int i = 0; i < _template.properties.length; i++)
{
_ages.add(0);
_modificationDates.add(new DateTime(0));//DateTime.MinValue);
}
/*
// connect events
Type t = resource.runtimeType;
var events = t.GetTypeInfo().GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach (var evt in events)
{
//if (evt.EventHandlerType != typeof(ResourceEventHanlder))
// continue;
if (evt.EventHandlerType == typeof(ResourceEventHanlder))
{
var ca = (ResourceEvent[])evt.GetCustomAttributes(typeof(ResourceEvent), true);
if (ca.Length == 0)
continue;
ResourceEventHanlder proxyDelegate = (args) => EmitResourceEvent(null, null, evt.Name, args);
evt.AddEventHandler(resource, proxyDelegate);
}
else if (evt.EventHandlerType == typeof(CustomResourceEventHanlder))
{
var ca = (ResourceEvent[])evt.GetCustomAttributes(typeof(ResourceEvent), true);
if (ca.Length == 0)
continue;
CustomResourceEventHanlder proxyDelegate = (issuer, receivers, args) => EmitResourceEvent(issuer, receivers, evt.Name, args);
evt.AddEventHandler(resource, proxyDelegate);
}
}
*/
}
void children_OnRemoved(Instance parent, IResource value)
{
value.instance.parents.remove(_resource);
}
void children_OnAdd(Instance parent, IResource value)
{
if (!value.instance.parents.contains(_resource))
value.instance.parents.add(_resource);
}
void parents_OnRemoved(Instance parent, IResource value)
{
value.instance.children.remove(_resource);
}
void parents_OnAdd(Instance parent, IResource value)
{
if (!value.instance.children.contains(_resource))
value.instance.children.add(_resource);
}
void resource_OnDestroy(sender)
{
emitArgs("resourceDestroyed", [sender]);
}
}

View File

@ -0,0 +1,34 @@
/*
Copyright (c) 2019 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.
*/
enum ResourceTrigger
{
Open,
Initialize,
Terminate,
Configure,
SystemInitialized,
SystemTerminated,
SystemReload,
}

View File

@ -0,0 +1,6 @@
class StorageMode
{
static const NonVolatile = 0;
static const Volatile = 1;
static const Recordable = 2;
}

View File

@ -0,0 +1,45 @@
import 'MemberTemplate.dart';
import '../../Data/DC.dart';
import '../../Data/BinaryList.dart';
import 'ResourceTemplate.dart';
import 'MemberType.dart';
class EventTemplate extends MemberTemplate
{
String expansion;
DC compose()
{
var name = super.compose();
if (expansion != null)
{
var exp = DC.stringToBytes(expansion);
return new BinaryList()
.addUint8(0x50)
.addInt32(exp.length)
.addDC(exp)
.addUint8(name.length)
.addDC(name)
.toDC();
}
else
{
return new BinaryList()
.addUint8(0x40)
.addUint8(name.length)
.addDC(name)
.toDC();
}
}
EventTemplate(ResourceTemplate template, int index, String name, String expansion)
: super(template, MemberType.Property, index, name)
{
this.expansion = expansion;
}
}

View File

@ -0,0 +1,42 @@
import 'MemberTemplate.dart';
import '../../Data/DC.dart';
import '../../Data/BinaryList.dart';
import 'ResourceTemplate.dart';
import 'MemberType.dart';
class FunctionTemplate extends MemberTemplate
{
String expansion;
bool isVoid;
DC compose()
{
var name = super.compose();
if (expansion != null)
{
var exp = DC.stringToBytes(expansion);
return new BinaryList().addUint8((0x10 | (isVoid ? 0x8 : 0x0)))
.addUint8(name.length)
.addDC(name)
.addInt32(exp.length)
.addDC(exp)
.toDC();
}
else
return new BinaryList().addUint8((isVoid ? 0x8 : 0x0))
.addUint8(name.length)
.addDC(name)
.toDC();
}
FunctionTemplate(ResourceTemplate template, int index, String name, bool isVoid, String expansion)
:super(template, MemberType.Property, index, name)
{
this.isVoid = isVoid;
this.expansion = expansion;
}
}

View File

@ -0,0 +1,34 @@
import 'MemberType.dart';
import '../../Data/DC.dart';
import './ResourceTemplate.dart';
class MemberTemplate
{
int get index => _index;
String get name => _name;
MemberType get type => _type;
ResourceTemplate _template;
String _name;
MemberType _type;
int _index;
ResourceTemplate get template => _template;
MemberTemplate(ResourceTemplate template, MemberType type, int index, String name)
{
this._template = template;
this._type = type;
this._index = index;
this._name = name;
}
String get fullname => _template.className + "." + _name;
DC compose()
{
// return DC.ToBytes(Name);
}
}

View File

@ -0,0 +1,6 @@
enum MemberType
{
Function,// = 0,
Property,// = 1,
Event// = 2,
}

View File

@ -0,0 +1,6 @@
class PropertyPermission
{
static const int Read = 1;
static const int Write = 2;
static const int ReadWrite = 3;
}

View File

@ -0,0 +1,80 @@
import 'MemberTemplate.dart';
import '../../Data/DC.dart';
import '../../Data/BinaryList.dart';
import 'ResourceTemplate.dart';
import 'MemberType.dart';
import 'PropertyPermission.dart';
import '../StorageMode.dart';
class PropertyTemplate extends MemberTemplate
{
int permission;
int storage;
String readExpansion;
String writeExpansion;
DC compose()
{
var name = super.compose();
var pv = ((permission) << 1) | (storage == StorageMode.Recordable ? 1 : 0);
if (writeExpansion != null && readExpansion != null)
{
var rexp = DC.stringToBytes(readExpansion);
var wexp = DC.stringToBytes(writeExpansion);
return new BinaryList()
.addUint8(0x38 | pv)
.addUint8(name.length)
.addDC(name)
.addInt32(wexp.length)
.addDC(wexp)
.addInt32(rexp.length)
.addDC(rexp)
.toDC();
}
else if (writeExpansion != null)
{
var wexp = DC.stringToBytes(writeExpansion);
return new BinaryList()
.addUint8(0x30 | pv)
.addUint8(name.length)
.addDC(name)
.addInt32(wexp.length)
.addDC(wexp)
.toDC();
}
else if (readExpansion != null)
{
var rexp = DC.stringToBytes(readExpansion);
return new BinaryList()
.addUint8(0x28 | pv)
.addUint8(name.length)
.addDC(name)
.addInt32(rexp.length)
.addDC(rexp)
.toDC();
}
else
return new BinaryList()
.addUint8(0x20 | pv)
.addUint8(name.length)
.addDC(name)
.toDC();
}
PropertyTemplate(ResourceTemplate template, int index, String name, String read, String write, int storage)
:super(template, MemberType.Property, index, name)
{
//this.Recordable = recordable;
this.storage = storage;
this.readExpansion = read;
this.writeExpansion = write;
}
}

View File

@ -0,0 +1,330 @@
import './MemberTemplate.dart';
import '../../Data/Guid.dart';
import '../../Data/DC.dart';
import './EventTemplate.dart';
import './PropertyTemplate.dart';
import './FunctionTemplate.dart';
import '../StorageMode.dart';
class ResourceTemplate
{
Guid _classId;
String _className;
List<MemberTemplate> _members = new List<MemberTemplate>();
List<FunctionTemplate> _functions = new List<FunctionTemplate>();
List<EventTemplate> _events = new List<EventTemplate>();
List<PropertyTemplate> _properties = new List<PropertyTemplate>();
int _version;
//bool isReady;
DC _content;
DC get content => _content;
/*
MemberTemplate getMemberTemplate(MemberInfo member)
{
if (member is MethodInfo)
return getFunctionTemplate(member.Name);
else if (member is EventInfo)
return getEventTemplate(member.Name);
else if (member is PropertyInfo)
return getPropertyTemplate(member.Name);
else
return null;
}
*/
EventTemplate getEventTemplateByName(String eventName)
{
for (var i in _events)
if (i.name == eventName)
return i;
return null;
}
EventTemplate getEventTemplateByIndex(int index)
{
for (var i in _events)
if (i.index == index)
return i;
return null;
}
FunctionTemplate getFunctionTemplateByName(String functionName)
{
for (var i in _functions)
if (i.name == functionName)
return i;
return null;
}
FunctionTemplate getFunctionTemplateByIndex(int index)
{
for (var i in _functions)
if (i.index == index)
return i;
return null;
}
PropertyTemplate getPropertyTemplateByIndex(int index)
{
for (var i in _properties)
if (i.index == index)
return i;
return null;
}
PropertyTemplate getPropertyTemplateByName(String propertyName)
{
for (var i in _properties)
if (i.name == propertyName)
return i;
return null;
}
Guid get classId => _classId;
String get className => _className;
List<MemberTemplate> get methods => _members;
List<FunctionTemplate> get functions => _functions;
List<EventTemplate> get events => _events;
List<PropertyTemplate> get properties => _properties;
ResourceTemplate()
{
}
ResourceTemplate.fromType(Type type)
{
}
/*
ResourceTemplate(Type type)
{
type = ResourceProxy.GetBaseType(type);
// set guid
var typeName = Encoding.UTF8.GetBytes(type.FullName);
var hash = SHA256.Create().ComputeHash(typeName).Clip(0, 16);
classId = new Guid(hash);
className = type.FullName;
#if NETSTANDARD1_5
PropertyInfo[] propsInfo = type.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
EventInfo[] eventsInfo = type.GetTypeInfo().GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
MethodInfo[] methodsInfo = type.GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
#else
PropertyInfo[] propsInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
EventInfo[] eventsInfo = type.GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
MethodInfo[] methodsInfo = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
#endif
//byte currentIndex = 0;
byte i = 0;
foreach (var pi in propsInfo)
{
var ps = (ResourceProperty[])pi.GetCustomAttributes(typeof(ResourceProperty), true);
if (ps.Length > 0)
{
var pt = new PropertyTemplate(this, i++, pi.Name, ps[0].ReadExpansion, ps[0].WriteExpansion, ps[0].Storage);
pt.Info = pi;
properties.Add(pt);
}
}
i = 0;
foreach (var ei in eventsInfo)
{
var es = (ResourceEvent[])ei.GetCustomAttributes(typeof(ResourceEvent), true);
if (es.Length > 0)
{
var et = new EventTemplate(this, i++, ei.Name, es[0].Expansion);
events.Add(et);
}
}
i = 0;
foreach (MethodInfo mi in methodsInfo)
{
var fs = (ResourceFunction[])mi.GetCustomAttributes(typeof(ResourceFunction), true);
if (fs.Length > 0)
{
var ft = new FunctionTemplate(this, i++, mi.Name, mi.ReturnType == typeof(void), fs[0].Expansion);
functions.Add(ft);
}
}
// append signals
for (i = 0; i < events.Count; i++)
members.Add(events[i]);
// append slots
for (i = 0; i < functions.Count; i++)
members.Add(functions[i]);
// append properties
for (i = 0; i < properties.Count; i++)
members.Add(properties[i]);
// bake it binarily
var b = new BinaryList();
b.AddGuid(classId)
.AddUInt8((byte)className.Length)
.AddString(className)
.AddInt32(version)
.AddUInt16((ushort)members.Count);
foreach (var ft in functions)
b.AddUInt8Array(ft.Compose());
foreach (var pt in properties)
b.AddUInt8Array(pt.Compose());
foreach (var et in events)
b.AddUInt8Array(et.Compose());
content = b.ToArray();
}
*/
ResourceTemplate.parse(DC data, [int offset = 0, int contentLength])
{
// cool Dart feature
contentLength ??= data.length;
int ends = offset + contentLength;
int oOffset = offset;
// start parsing...
//var od = new ResourceTemplate();
_content = data.clip(offset, contentLength);
_classId = data.getGuid(offset);
offset += 16;
_className = data.getString(offset + 1, data[offset]);
offset += data[offset] + 1;
_version = data.getInt32(offset);
offset += 4;
var methodsCount = data.getUint16(offset);
offset += 2;
var functionIndex = 0;
var propertyIndex = 0;
var eventIndex = 0;
for (int i = 0; i < methodsCount; i++)
{
var type = data[offset] >> 5;
if (type == 0) // function
{
String expansion = null;
var hasExpansion = ((data[offset] & 0x10) == 0x10);
var isVoid = ((data[offset++] & 0x08) == 0x08);
var name = data.getString(offset + 1, data[offset]);
offset += data[offset] + 1;
if (hasExpansion) // expansion ?
{
var cs = data.getUint32(offset);
offset += 4;
expansion = data.getString(offset, cs);
offset += cs;
}
var ft = new FunctionTemplate(this, functionIndex++, name, isVoid, expansion);
_functions.add(ft);
}
else if (type == 1) // property
{
String readExpansion = null, writeExpansion = null;
var hasReadExpansion = ((data[offset] & 0x8) == 0x8);
var hasWriteExpansion = ((data[offset] & 0x10) == 0x10);
var recordable = ((data[offset] & 1) == 1);
var permission = (data[offset++] >> 1) & 0x3;
var name = data.getString(offset + 1, data[offset]);
offset += data[offset] + 1;
if (hasReadExpansion) // expansion ?
{
var cs = data.getUint32(offset);
offset += 4;
readExpansion = data.getString(offset, cs);
offset += cs;
}
if (hasWriteExpansion) // expansion ?
{
var cs = data.getUint32(offset);
offset += 4;
writeExpansion = data.getString(offset, cs);
offset += cs;
}
var pt = new PropertyTemplate(this, propertyIndex++, name, readExpansion, writeExpansion, recordable ? StorageMode.Recordable : StorageMode.Volatile);
_properties.add(pt);
}
else if (type == 2) // Event
{
String expansion = null;
var hasExpansion = ((data[offset++] & 0x10) == 0x10);
var name = data.getString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, (int)data[offset]);
offset += data[offset] + 1;
if (hasExpansion) // expansion ?
{
var cs = data.getUint32(offset);
offset += 4;
expansion = data.getString(offset, cs);
offset += cs;
}
var et = new EventTemplate(this, eventIndex++, name, expansion);
_events.add(et);
}
}
// append signals
for (int i = 0; i < _events.length; i++)
_members.add(_events[i]);
// append slots
for (int i = 0; i < _functions.length; i++)
_members.add(_functions[i]);
// append properties
for (int i = 0; i < _properties.length; i++)
_members.add(_properties[i]);
}
}

View File

@ -0,0 +1,458 @@
/*
Copyright (c) 2019 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.
*/
import '../Data/AutoList.dart';
import './Template/ResourceTemplate.dart';
import '../Data/Guid.dart';
import '../Data/KeyList.dart';
import '../Data/Structure.dart';
import '../Security/Permissions/IPermissionsManager.dart';
import 'IResource.dart';
import 'Instance.dart';
import 'IStore.dart';
import '../Core/AsyncReply.dart';
import '../Core/AsyncBag.dart';
import 'ResourceTrigger.dart';
import '../Net/IIP/DistributedConnection.dart';
// Centeral Resource Issuer
class Warehouse
{
static AutoList<IResource, Instance> _stores = new AutoList<IResource, Instance>(null);
static Map<int, IResource> _resources = new Map<int, IResource>();
static int resourceCounter = 0;
static KeyList<Guid, ResourceTemplate> _templates = new KeyList<Guid, ResourceTemplate>();
static bool storeIsOpen = false;
//public delegate void StoreConnectedEvent(IStore store, string name);
//public delegate void StoreDisconnectedEvent(IStore store);
//public static event StoreConnectedEvent StoreConnected;
///public static event StoreDisconnectedEvent StoreDisconnected;
static KeyList<String, IStore Function()> protocols = getProtocols();
/// <summary>
/// Get a store by its name.
/// </summary>
/// <param name="name">Store instance name</param>
/// <returns></returns>
static IStore getStore(String name)
{
for(var s in _stores)
if (s.instance.name == name)
return s;
return null;
}
/// <summary>
/// Get a resource by instance Id.
/// </summary>
/// <param name="id">Instance Id</param>
/// <returns></returns>
static AsyncReply<IResource> getById(int id)
{
if (_resources.containsKey(id))
return new AsyncReply<IResource>.ready(_resources[id]);
else
return new AsyncReply<IResource>.ready(null);
}
/// <summary>
/// Open the warehouse.
/// This function issues the initialize trigger to all stores and resources.
/// </summary>
/// <returns>True, if no problem occurred.</returns>
static AsyncReply<bool> open()
{
var bag = new AsyncBag<bool>();
for(var s in _stores)
bag.add(s.trigger(ResourceTrigger.Initialize));
bag.seal();
var rt = new AsyncReply<bool>();
bag.then((x)
{
for (var b in x)
if (!b)
{
rt.trigger(false);
return;
}
var rBag = new AsyncBag<bool>();
for (var rk in _resources.keys)
rBag.add(_resources[rk].trigger(ResourceTrigger.SystemInitialized));
rBag.seal();
rBag.then((y)
{
for (var b in y)
if (!b)
{
rt.trigger(false);
return;
}
rt.trigger(true);
storeIsOpen = true;
});
});
return rt;
}
/// <summary>
/// Close the warehouse.
/// This function issues terminate trigger to all resources and stores.
/// </summary>
/// <returns>True, if no problem occurred.</returns>
static AsyncReply<bool> close()
{
var bag = new AsyncBag<bool>();
for (var resource in _resources.values)
if (!(resource is IStore))
bag.add(resource.trigger(ResourceTrigger.Terminate));
for (var s in _stores)
bag.add(s.trigger(ResourceTrigger.Terminate));
for (var resource in _resources.values)
if (!(resource is IStore))
bag.add(resource.trigger(ResourceTrigger.SystemTerminated));
for (var store in _stores)
bag.add(store.trigger(ResourceTrigger.SystemTerminated));
bag.seal();
var rt = new AsyncReply<bool>();
bag.then((x)
{
for (var b in x)
if (!b)
{
rt.trigger(false);
return;
}
rt.trigger(true);
});
return rt;
}
static List<IResource> qureyIn(List<String> path, int index, AutoList<IResource, Instance> resources)
{
var rt = new List<IResource>();
if (index == path.length - 1)
{
if (path[index] == "")
for (var child in resources)
rt.add(child);
else
for (var child in resources)
if (child.instance.name == path[index])
rt.add(child);
}
else
for (var child in resources)
if (child.instance.name == path[index])
rt.addAll(qureyIn(path, index+1, child.instance.children));
return rt;
}
static AsyncReply<List<IResource>> query(String path)
{
if (path == null || path == "")
{
var roots = _stores.where((s) => s.instance.parents.length == 0).toList();
return new AsyncReply<List<IResource>>.ready(roots);
}
else
{
var rt = new AsyncReply<List<IResource>>();
get(path).then((x)
{
var p = path.split('/');
if (x == null)
{
rt.trigger(qureyIn(p, 0, _stores));
}
else
{
var ar = qureyIn(p, 0, _stores).where((r) => r != x).toList();
ar.insert(0, x);
rt.trigger(ar);
}
});
return rt;
}
}
/// <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>
static AsyncReply<dynamic> get(String path, [attributes = null, IResource parent = null, IPermissionsManager manager = null])
{
var p = path.split('/');
IResource res;
for(IStore d in _stores)
if (p[0] == d.instance.name)
{
var i = 1;
res = d;
while(p.length > i)
{
var si = i;
for (IResource r in res.instance.children)
if (r.instance.name == p[i])
{
i++;
res = r;
break;
}
if (si == i)
// not found, ask the store
return d.get(path.substring(p[0].length + 1));
}
return new AsyncReply<IResource>.ready(res);
}
// Should we create a new store ?
if (path.contains("://"))
{
var url = path.split("://");
var hostname = url[1].split('/')[0];
var pathname = url[1].split('/').skip(1).join("/");
var rt = new AsyncReply<IResource>();
if (protocols.containsKey(url[0]))
{
var handler = protocols[url[0]];
var store = handler();
put(store, url[0] + "://" + hostname, null, parent, null, 0, manager, attributes);
store.trigger(ResourceTrigger.Open).then((x){
if (pathname.length > 0 && pathname != "")
store.get(pathname).then((r) => rt.trigger(r)
).error((e) =>
rt.triggerError(e)
);
else
rt.trigger(store);
}).error((e) {
rt.triggerError(e);
Warehouse.remove(store);
});
}
return rt;
}
return new AsyncReply<IResource>.ready(null);
}
/// <summary>
/// Put a resource in the warehouse.
/// </summary>
/// <param name="resource">Resource instance.</param>
/// <param name="name">Resource name.</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>
static void put(IResource resource, String name, [IStore store = null, IResource parent = null, ResourceTemplate customTemplate = null, int age = 0, IPermissionsManager manager = null, attributes = null])
{
resource.instance = new Instance(resourceCounter++, name, resource, store, customTemplate, age);
if (attributes != null)
resource.instance.setAttributes(Structure.fromMap(attributes));
if (manager != null)
resource.instance.managers.add(manager);
if (store == parent)
parent = null;
if (parent == null)
{
if (!(resource is IStore))
store.instance.children.add(resource);
}
else
parent.instance.children.add(resource);
if (resource is IStore)
{
_stores.add(resource);
//StoreConnected?.Invoke(resource as IStore, name);
}
else
store.put(resource);
_resources[resource.instance.id] = resource;
//if (!storeIsOpen)
// resource.trigger(ResourceTrigger.Initialize);
}
static T New<T extends IResource>(String name, [IStore store = null, IResource parent = null, IPermissionsManager manager = null, Structure attributes = null])
{
/*
var type = ResourceProxy.GetProxy<T>();
var res = Activator.CreateInstance(type) as IResource;
put(res, name, store, parent, null, 0, manager, attributes);
return (T)res;
*/
}
/// <summary>
/// Put a resource template in the templates warehouse.
/// </summary>
/// <param name="template">Resource template.</param>
static void putTemplate(ResourceTemplate template)
{
if (!_templates.containsKey(template.classId))
_templates.add(template.classId, template);
}
/// <summary>
/// Get a template by type from the templates warehouse. If not in the warehouse, a new ResourceTemplate is created and added to the warehouse.
/// </summary>
/// <param name="type">.Net type.</param>
/// <returns>Resource template.</returns>
static ResourceTemplate getTemplateByType(Type type)
{
// loaded ?
for (var t in _templates.values)
if (t.className == type.toString())
return t;
var template = new ResourceTemplate.fromType(type);
_templates.add(template.classId, template);
return template;
}
/// <summary>
/// Get a template by class Id from the templates warehouse. If not in the warehouse, a new ResourceTemplate is created and added to the warehouse.
/// </summary>
/// <param name="classId">Class Id.</param>
/// <returns>Resource template.</returns>
static AsyncReply<ResourceTemplate> getTemplateByClassId(Guid classId)
{
if (_templates.containsKey(classId))
return new AsyncReply<ResourceTemplate>.ready(_templates[classId]);
return null;
}
/// <summary>
/// Get a template by class name from the templates warehouse. If not in the warehouse, a new ResourceTemplate is created and added to the warehouse.
/// </summary>
/// <param name="className">Class name.</param>
/// <returns>Resource template.</returns>
static AsyncReply<ResourceTemplate> getTemplateByClassName(String className)
{
for (var t in _templates.values)
if (t.className == className)
return new AsyncReply<ResourceTemplate>.ready(t);
return null;
}
static bool remove(IResource resource)
{
if (resource.instance == null)
return false;
if (_resources.containsKey(resource.instance.id))
_resources.remove(resource.instance.id);
else
return false;
if (resource is IStore)
{
_stores.remove(resource);
// remove all objects associated with the store
var toBeRemoved = _resources.values.where((x) => x.instance.store == resource);
for (var o in toBeRemoved)
remove(o);
// StoreDisconnected?.Invoke(resource as IStore);
}
if (resource.instance.store != null)
resource.instance.store.remove(resource);
resource.destroy();
return true;
}
}
KeyList<String, IStore Function()> getProtocols()
{
var rt = new KeyList<String, IStore Function()>();
rt.add("iip", () => new DistributedConnection());
return rt;
}