mirror of
https://github.com/esiur/esiur-dart.git
synced 2025-06-27 14:53:11 +00:00
1.0
This commit is contained in:
46
bin/Resource/IResource.dart
Normal file
46
bin/Resource/IResource.dart
Normal 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;
|
||||
}
|
43
bin/Resource/IStore.dart
Normal file
43
bin/Resource/IStore.dart
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.
|
||||
|
||||
*/
|
||||
|
||||
import './IResource.dart';
|
||||
import '../Core/AsyncReply.dart';
|
||||
import '../Data/KeyList.dart';
|
||||
import './Template/PropertyTemplate.dart';
|
||||
import '../Data/PropertyValue.dart';
|
||||
|
||||
abstract class IStore extends 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);
|
||||
}
|
680
bin/Resource/Instance.dart
Normal file
680
bin/Resource/Instance.dart
Normal 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]);
|
||||
}
|
||||
}
|
34
bin/Resource/ResourceTrigger.dart
Normal file
34
bin/Resource/ResourceTrigger.dart
Normal 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,
|
||||
}
|
6
bin/Resource/StorageMode.dart
Normal file
6
bin/Resource/StorageMode.dart
Normal file
@ -0,0 +1,6 @@
|
||||
class StorageMode
|
||||
{
|
||||
static const NonVolatile = 0;
|
||||
static const Volatile = 1;
|
||||
static const Recordable = 2;
|
||||
}
|
45
bin/Resource/Template/EventTemplate.dart
Normal file
45
bin/Resource/Template/EventTemplate.dart
Normal 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;
|
||||
}
|
||||
}
|
42
bin/Resource/Template/FunctionTemplate.dart
Normal file
42
bin/Resource/Template/FunctionTemplate.dart
Normal 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;
|
||||
}
|
||||
}
|
34
bin/Resource/Template/MemberTemplate.dart
Normal file
34
bin/Resource/Template/MemberTemplate.dart
Normal 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);
|
||||
}
|
||||
}
|
6
bin/Resource/Template/MemberType.dart
Normal file
6
bin/Resource/Template/MemberType.dart
Normal file
@ -0,0 +1,6 @@
|
||||
enum MemberType
|
||||
{
|
||||
Function,// = 0,
|
||||
Property,// = 1,
|
||||
Event// = 2,
|
||||
}
|
6
bin/Resource/Template/PropertyPermission.dart
Normal file
6
bin/Resource/Template/PropertyPermission.dart
Normal file
@ -0,0 +1,6 @@
|
||||
class PropertyPermission
|
||||
{
|
||||
static const int Read = 1;
|
||||
static const int Write = 2;
|
||||
static const int ReadWrite = 3;
|
||||
}
|
80
bin/Resource/Template/PropertyTemplate.dart
Normal file
80
bin/Resource/Template/PropertyTemplate.dart
Normal 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;
|
||||
}
|
||||
}
|
330
bin/Resource/Template/ResourceTemplate.dart
Normal file
330
bin/Resource/Template/ResourceTemplate.dart
Normal 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]);
|
||||
|
||||
}
|
||||
}
|
||||
|
458
bin/Resource/Warehouse.dart
Normal file
458
bin/Resource/Warehouse.dart
Normal 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;
|
||||
}
|
Reference in New Issue
Block a user