using Flee.InternalTypes; using System; using System.Collections.Generic; using System.ComponentModel; using System.Reflection; namespace Flee.PublicTypes { /// /// /// public sealed class VariableCollection : IDictionary { private IDictionary _myVariables; private readonly ExpressionContext _myContext; public event EventHandler ResolveVariableType; public event EventHandler ResolveVariableValue; public event EventHandler ResolveFunction; public event EventHandler InvokeFunction; internal VariableCollection(ExpressionContext context) { _myContext = context; this.CreateDictionary(); this.HookOptions(); } #region "Methods - Non Public" private void HookOptions() { _myContext.Options.CaseSensitiveChanged += OnOptionsCaseSensitiveChanged; } private void CreateDictionary() { _myVariables = new Dictionary(_myContext.Options.StringComparer); } private void OnOptionsCaseSensitiveChanged(object sender, EventArgs e) { this.CreateDictionary(); } internal void Copy(VariableCollection dest) { dest.CreateDictionary(); dest.HookOptions(); foreach (KeyValuePair pair in _myVariables) { IVariable copyVariable = pair.Value.Clone(); dest._myVariables.Add(pair.Key, copyVariable); } } internal void DefineVariableInternal(string name, Type variableType, object variableValue) { Utility.AssertNotNull(variableType, "variableType"); if (_myVariables.ContainsKey(name) == true) { string msg = Utility.GetGeneralErrorMessage("VariableWithNameAlreadyDefined", name); throw new ArgumentException(msg); } IVariable v = this.CreateVariable(variableType, variableValue); _myVariables.Add(name, v); } internal Type GetVariableTypeInternal(string name) { IVariable value = null; bool success = _myVariables.TryGetValue(name, out value); if (success == true) { return value.VariableType; } ResolveVariableTypeEventArgs args = new ResolveVariableTypeEventArgs(name); ResolveVariableType?.Invoke(this, args); return args.VariableType; } private IVariable GetVariable(string name, bool throwOnNotFound) { IVariable value = null; bool success = _myVariables.TryGetValue(name, out value); if (success == false & throwOnNotFound == true) { string msg = Utility.GetGeneralErrorMessage("UndefinedVariable", name); throw new ArgumentException(msg); } else { return value; } } private IVariable CreateVariable(Type variableValueType, object variableValue) { Type variableType = default(Type); // Is the variable value an expression? IExpression expression = variableValue as IExpression; ExpressionOptions options = null; if (expression != null) { options = expression.Context.Options; // Get its result type variableValueType = options.ResultType; // Create a variable that wraps the expression if (options.IsGeneric == false) { variableType = typeof(DynamicExpressionVariable<>); } else { variableType = typeof(GenericExpressionVariable<>); } } else { // Create a variable for a regular value _myContext.AssertTypeIsAccessible(variableValueType); variableType = typeof(GenericVariable<>); } // Create the generic variable instance variableType = variableType.MakeGenericType(variableValueType); IVariable v = (IVariable)Activator.CreateInstance(variableType); return v; } internal Type ResolveOnDemandFunction(string name, Type[] argumentTypes) { ResolveFunctionEventArgs args = new ResolveFunctionEventArgs(name, argumentTypes); ResolveFunction?.Invoke(this, args); return args.ReturnType; } private static T ReturnGenericValue(object value) { if (value == null) { return default(T); } else { return (T)value; } } private static void ValidateSetValueType(Type requiredType, object value) { if (value == null) { // Can always assign null value return; } Type valueType = value.GetType(); if (requiredType.IsAssignableFrom(valueType) == false) { string msg = Utility.GetGeneralErrorMessage("VariableValueNotAssignableToType", valueType.Name, requiredType.Name); throw new ArgumentException(msg); } } internal static MethodInfo GetVariableLoadMethod(Type variableType) { MethodInfo mi = typeof(VariableCollection).GetMethod("GetVariableValueInternal", BindingFlags.Public | BindingFlags.Instance); mi = mi.MakeGenericMethod(variableType); return mi; } internal static MethodInfo GetFunctionInvokeMethod(Type returnType) { MethodInfo mi = typeof(VariableCollection).GetMethod("GetFunctionResultInternal", BindingFlags.Public | BindingFlags.Instance); mi = mi.MakeGenericMethod(returnType); return mi; } internal static MethodInfo GetVirtualPropertyLoadMethod(Type returnType) { MethodInfo mi = typeof(VariableCollection).GetMethod("GetVirtualPropertyValueInternal", BindingFlags.Public | BindingFlags.Instance); mi = mi.MakeGenericMethod(returnType); return mi; } private Dictionary GetNameValueDictionary() { Dictionary dict = new Dictionary(); foreach (KeyValuePair pair in _myVariables) { dict.Add(pair.Key, pair.Value.ValueAsObject); } return dict; } #endregion "Methods - Non Public" #region "Methods - Public" public Type GetVariableType(string name) { IVariable v = this.GetVariable(name, true); return v.VariableType; } public void DefineVariable(string name, Type variableType) { this.DefineVariableInternal(name, variableType, null); } public T GetVariableValueInternal(string name) { if (_myVariables.TryGetValue(name, out IVariable variable)) { if (variable is IGenericVariable generic) { return (T)generic.GetValue(); } } GenericVariable result = new GenericVariable(); GenericVariable vTemp = new GenericVariable(); ResolveVariableValueEventArgs args = new ResolveVariableValueEventArgs(name, typeof(T)); ResolveVariableValue?.Invoke(this, args); ValidateSetValueType(typeof(T), args.VariableValue); vTemp.ValueAsObject = args.VariableValue; result = vTemp; return (T)result.GetValue(); } public T GetVirtualPropertyValueInternal(string name, object component) { PropertyDescriptorCollection coll = TypeDescriptor.GetProperties(component); PropertyDescriptor pd = coll.Find(name, true); object value = pd.GetValue(component); ValidateSetValueType(typeof(T), value); return ReturnGenericValue(value); } public T GetFunctionResultInternal(string name, object[] arguments) { InvokeFunctionEventArgs args = new InvokeFunctionEventArgs(name, arguments); if (InvokeFunction != null) { InvokeFunction(this, args); } object result = args.Result; ValidateSetValueType(typeof(T), result); return ReturnGenericValue(result); } #endregion "Methods - Public" #region "IDictionary Implementation" private void Add1(System.Collections.Generic.KeyValuePair item) { this.Add(item.Key, item.Value); } void System.Collections.Generic.ICollection>.Add(System.Collections.Generic.KeyValuePair item) { Add1(item); } public void Clear() { _myVariables.Clear(); } private bool Contains1(System.Collections.Generic.KeyValuePair item) { return this.ContainsKey(item.Key); } bool System.Collections.Generic.ICollection>.Contains(System.Collections.Generic.KeyValuePair item) { return Contains1(item); } private void CopyTo(System.Collections.Generic.KeyValuePair[] array, int arrayIndex) { Dictionary dict = this.GetNameValueDictionary(); ICollection> coll = dict; coll.CopyTo(array, arrayIndex); } private bool Remove1(System.Collections.Generic.KeyValuePair item) { return this.Remove(item.Key); } bool System.Collections.Generic.ICollection>.Remove(System.Collections.Generic.KeyValuePair item) { return Remove1(item); } public void Add(string name, object value) { Utility.AssertNotNull(value, "value"); this.DefineVariableInternal(name, value.GetType(), value); this[name] = value; } public bool ContainsKey(string name) { return _myVariables.ContainsKey(name); } public bool Remove(string name) { return _myVariables.Remove(name); } public bool TryGetValue(string key, out object value) { IVariable v = this.GetVariable(key, false); value = v?.ValueAsObject; return v != null; } public System.Collections.Generic.IEnumerator> GetEnumerator() { Dictionary dict = this.GetNameValueDictionary(); return dict.GetEnumerator(); } private System.Collections.IEnumerator GetEnumerator1() { return this.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator1(); } public int Count => _myVariables.Count; public bool IsReadOnly => false; public object this[string name] { get { IVariable v = this.GetVariable(name, true); return v.ValueAsObject; } set { IVariable v = null; if (_myVariables.TryGetValue(name, out v) == true) { v.ValueAsObject = value; } else { this.Add(name, value); } } } public System.Collections.Generic.ICollection Keys => _myVariables.Keys; public System.Collections.Generic.ICollection Values { get { Dictionary dict = this.GetNameValueDictionary(); return dict.Values; } } void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { CopyTo(array, arrayIndex); } #endregion "IDictionary Implementation" } }