/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*
* This code file is based on Kerosene ORM solution for parsing dynamic
* lambda expressions by Moisés Barba Cebeira
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Text;
namespace DynamORM.Helpers.Dynamics
{
///
/// Class able to parse dynamic lambda expressions. Allows to create dynamic logic.
///
public class DynamicParser : IExtendedDisposable
{
#region Node
///
/// Generic bindable operation where some of its operands is a dynamic argument, or a dynamic member or
/// a method of that argument.
///
[Serializable]
public class Node : IDynamicMetaObjectProvider, IFinalizerDisposable, ISerializable
{
private DynamicParser _parser = null;
#region MetaNode
///
/// Represents the dynamic binding and a binding logic of
/// an object participating in the dynamic binding.
///
internal class MetaNode : DynamicMetaObject
{
///
/// Initializes a new instance of the class.
///
/// The parameter.
/// The restrictions.
/// The value.
public MetaNode(Expression parameter, BindingRestrictions rest, object value)
: base(parameter, rest, value)
{
}
// Func was cool but caused memory leaks
private DynamicMetaObject GetBinder(Node node)
{
Node o = (Node)this.Value;
node.Parser = o.Parser;
o.Parser.Last = node;
ParameterExpression p = Expression.Variable(typeof(Node), "ret");
BlockExpression exp = Expression.Block(new ParameterExpression[] { p }, Expression.Assign(p, Expression.Constant(node)));
return new MetaNode(exp, this.Restrictions, node);
}
///
/// Performs the binding of the dynamic get member operation.
///
/// An instance of the that represents the details of the dynamic operation.
///
/// The new representing the result of the binding.
///
public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
{
return GetBinder(new GetMember((Node)this.Value, binder.Name));
}
///
/// Performs the binding of the dynamic set member operation.
///
/// An instance of the that represents the details of the dynamic operation.
/// The representing the value for the set member operation.
///
/// The new representing the result of the binding.
///
public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
{
return GetBinder(new SetMember((Node)this.Value, binder.Name, value.Value));
}
///
/// Performs the binding of the dynamic get index operation.
///
/// An instance of the that represents the details of the dynamic operation.
/// An array of instances - indexes for the get index operation.
///
/// The new representing the result of the binding.
///
public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes)
{
return GetBinder(new GetIndex((Node)this.Value, MetaList2List(indexes)));
}
///
/// Performs the binding of the dynamic set index operation.
///
/// An instance of the that represents the details of the dynamic operation.
/// An array of instances - indexes for the set index operation.
/// The representing the value for the set index operation.
///
/// The new representing the result of the binding.
///
public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value)
{
return GetBinder(new SetIndex((Node)this.Value, MetaList2List(indexes), value.Value));
}
///
/// Performs the binding of the dynamic invoke operation.
///
/// An instance of the that represents the details of the dynamic operation.
/// An array of instances - arguments to the invoke operation.
///
/// The new representing the result of the binding.
///
public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args)
{
return GetBinder(new Invoke((Node)this.Value, MetaList2List(args)));
}
///
/// Performs the binding of the dynamic invoke member operation.
///
/// An instance of the that represents the details of the dynamic operation.
/// An array of instances - arguments to the invoke member operation.
///
/// The new representing the result of the binding.
///
public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
{
return GetBinder(new Method((Node)this.Value, binder.Name, MetaList2List(args)));
}
///
/// Performs the binding of the dynamic binary operation.
///
/// An instance of the that represents the details of the dynamic operation.
/// An instance of the representing the right hand side of the binary operation.
///
/// The new representing the result of the binding.
///
public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg)
{
return GetBinder(new Binary((Node)this.Value, binder.Operation, arg.Value));
}
///
/// Performs the binding of the dynamic unary operation.
///
/// An instance of the that represents the details of the dynamic operation.
///
/// The new representing the result of the binding.
///
public override DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder)
{
Node o = (Node)this.Value;
Unary node = new Unary(o, binder.Operation) { Parser = o.Parser };
o.Parser.Last = node;
// If operation is 'IsTrue' or 'IsFalse', we will return false to keep the engine working...
object ret = node;
if (binder.Operation == ExpressionType.IsTrue) ret = (object)false;
if (binder.Operation == ExpressionType.IsFalse) ret = (object)false;
ParameterExpression p = Expression.Variable(ret.GetType(), "ret"); // the type is now obtained from "ret"
BlockExpression exp = Expression.Block(
new ParameterExpression[] { p },
Expression.Assign(p, Expression.Constant(ret))); // the expression is now obtained from "ret"
return new MetaNode(exp, this.Restrictions, node);
}
///
/// Performs the binding of the dynamic conversion operation.
///
/// An instance of the that represents the details of the dynamic operation.
///
/// The new representing the result of the binding.
///
public override DynamicMetaObject BindConvert(ConvertBinder binder)
{
Node o = (Node)this.Value;
Convert node = new Convert(o, binder.ReturnType) { Parser = o.Parser };
o.Parser.Last = node;
// Reducing the object to return if this is an assignment node...
object ret = o;
bool done = false;
while (!done)
{
if (ret is SetMember)
ret = ((SetMember)o).Value;
else if (ret is SetIndex)
ret = ((SetIndex)o).Value;
else
done = true;
}
// Creating an instance...
if (binder.ReturnType == typeof(string)) ret = ret.ToString();
else
{
try
{
if (binder.ReturnType.IsNullableType())
ret = null; // to avoid cast exceptions
else
ret = Activator.CreateInstance(binder.ReturnType, true); // true to allow non-public ctor as well
}
catch
{
// as the last resort scenario
ret = new object();
}
}
ParameterExpression p = Expression.Variable(binder.ReturnType, "ret");
BlockExpression exp = Expression.Block(
new ParameterExpression[] { p },
Expression.Assign(p, Expression.Constant(ret, binder.ReturnType))); // specifying binder.ReturnType
return new MetaNode(exp, this.Restrictions, node);
}
private static object[] MetaList2List(DynamicMetaObject[] metaObjects)
{
if (metaObjects == null) return null;
object[] list = new object[metaObjects.Length];
for (int i = 0; i < metaObjects.Length; i++)
list[i] = metaObjects[i].Value;
return list;
}
}
#endregion MetaNode
#region Argument
///
/// Describe a dynamic argument used in a dynamic lambda expression.
///
[Serializable]
public class Argument : Node, ISerializable
{
///
/// Initializes a new instance of the class.
///
/// The name.
public Argument(string name)
: base(name)
{
}
///
/// Initializes a new instance of the class.
///
/// The info.
/// The context.
protected Argument(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
/// Returns a that represents this instance.
/// A that represents this instance.
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Node::Argument::Disposed}";
return Name;
}
}
#endregion Argument
#region GetMember
///
/// Describe a 'get member' operation, as in 'x => x.Member'.
///
[Serializable]
public class GetMember : Node, ISerializable
{
///
/// Initializes a new instance of the class.
///
/// The host.
/// The name.
public GetMember(Node host, string name)
: base(host, name)
{
}
///
/// Initializes a new instance of the class.
///
/// The info.
/// The context.
protected GetMember(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
/// Returns a that represents this instance.
/// A that represents this instance.
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Node::GetMember::Disposed}";
return string.Format("{0}.{1}", Host.Sketch(), Name.Sketch());
}
}
#endregion GetMember
#region SetMember
///
/// Describe a 'set member' operation, as in 'x => x.Member = y'.
///
[Serializable]
public class SetMember : Node, ISerializable
{
///
/// Gets the value that has been (virtually) assigned to this member. It might be null if the null value has been
/// assigned to this instance, or if this instance is disposed.
///
public object Value { get; private set; }
///
/// Initializes a new instance of the class.
///
/// The host.
/// The name.
/// The value.
public SetMember(Node host, string name, object value)
: base(host, name)
{
Value = value;
}
///
/// Initializes a new instance of the class.
///
/// The info.
/// The context.
protected SetMember(SerializationInfo info, StreamingContext context)
: base(info, context)
{
string type = info.GetString("MemberType");
Value = type == "NULL" ? null : info.GetValue("MemberValue", Type.GetType(type));
}
///
/// Gets the object data.
///
/// The info.
/// The context.
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("MemberType", Value == null ? "NULL" : Value.GetType().AssemblyQualifiedName);
if (Value != null)
info.AddValue("MemberValue", Value);
base.GetObjectData(info, context);
}
/// Returns a that represents this instance.
/// A that represents this instance.
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Node::SetMember::Disposed}";
return string.Format("({0}.{1} = {2})", Host.Sketch(), Name.Sketch(), Value.Sketch());
}
/// Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.
/// If set to true dispose object.
public override void Dispose(bool disposing)
{
if (disposing)
{
try
{
if (Value != null)
{
var node = Value as Node;
if (node != null)
{
if (node.IsNodeAncestor(this))
node.Host = null;
node.Dispose(disposing);
}
Value = null;
}
}
catch
{
}
}
base.Dispose(disposing);
}
}
#endregion SetMember
#region GetIndex
///
/// Describe a 'get indexed' operation, as in 'x => x.Member[...]'.
///
[Serializable]
public class GetIndex : Node, ISerializable
{
/// Gets the indexes.
public object[] Indexes { get; internal set; }
///
/// Initializes a new instance of the class.
///
/// The host.
/// The indexes.
/// Indexes array cannot be null.
/// Indexes array cannot be empty.
public GetIndex(Node host, object[] indexes)
: base(host)
{
if (indexes == null)
throw new ArgumentNullException("indexes", "Indexes array cannot be null.");
if (indexes.Length == 0)
throw new ArgumentException("Indexes array cannot be empty.");
Indexes = indexes;
}
///
/// Initializes a new instance of the class.
///
/// The info.
/// The context.
protected GetIndex(SerializationInfo info, StreamingContext context)
: base(info, context)
{
int count = (int)info.GetValue("IndexCount", typeof(int));
if (count != 0)
{
Indexes = new object[count]; for (int i = 0; i < count; i++)
{
string typeName = info.GetString("IndexType" + i);
object obj = typeName == "NULL" ? null : info.GetValue("IndexValue" + i, Type.GetType(typeName));
Indexes[i] = obj;
}
}
}
///
/// Gets the object data.
///
/// The info.
/// The context.
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
int count = Indexes == null ? 0 : Indexes.Length; info.AddValue("IndexCount", count);
for (int i = 0; i < count; i++)
{
info.AddValue("IndexType" + i, Indexes[i] == null ? "NULL" : Indexes[i].GetType().AssemblyQualifiedName);
if (Indexes[i] != null) info.AddValue("IndexValue" + i, Indexes[i]);
}
base.GetObjectData(info, context);
}
/// Returns a that represents this instance.
/// A that represents this instance.
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Node::GetIndex::Disposed}";
return string.Format("{0}{1}", Host.Sketch(), Indexes == null ? "[empty]" : Indexes.Sketch());
}
/// Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.
/// If set to true dispose object.
public override void Dispose(bool disposing)
{
if (disposing)
{
try
{
if (Indexes != null)
{
for (int i = 0; i < Indexes.Length; i++)
{
var node = Indexes[i] as Node;
if (node != null)
{
if (node.IsNodeAncestor(this))
node.Host = null;
node.Dispose(disposing);
}
}
Array.Clear(Indexes, 0, Indexes.Length);
}
}
catch
{
}
Indexes = null;
}
base.Dispose(disposing);
}
}
#endregion GetIndex
#region SetIndex
///
/// Describe a 'set indexed' operation, as in 'x => x.Member[...] = Value'.
///
[Serializable]
public class SetIndex : GetIndex, ISerializable
{
///
/// Gets the value that has been (virtually) assigned to this member. It might be null if the null value has been
/// assigned to this instance, or if this instance is disposed.
///
public object Value { get; private set; }
///
/// Initializes a new instance of the class.
///
/// The host.
/// The indexes.
/// The value.
public SetIndex(Node host, object[] indexes, object value)
: base(host, indexes)
{
Value = value;
}
///
/// Initializes a new instance of the class.
///
/// The info.
/// The context.
protected SetIndex(SerializationInfo info, StreamingContext context)
: base(info, context)
{
string type = info.GetString("MemberType");
Value = type == "NULL" ? null : info.GetValue("MemberValue", Type.GetType(type));
}
///
/// Gets the object data.
///
/// The info.
/// The context.
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("MemberType", Value == null ? "NULL" : Value.GetType().AssemblyQualifiedName);
if (Value != null) info.AddValue("MemberValue", Value);
base.GetObjectData(info, context);
}
/// Returns a that represents this instance.
/// A that represents this instance.
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Node::SetIndex::Disposed}";
return string.Format("({0}{1} = {2})", Host.Sketch(), Indexes == null ? "[empty]" : Indexes.Sketch(), Value.Sketch());
}
/// Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.
/// If set to true dispose object.
public override void Dispose(bool disposing)
{
if (disposing)
{
try
{
if (Value != null)
{
var node = Value as Node;
if (node != null)
{
if (node.IsNodeAncestor(this))
node.Host = null;
node.Dispose(disposing);
}
Value = null;
}
}
catch
{
}
}
base.Dispose(disposing);
}
}
#endregion SetIndex
#region Invoke
///
/// Describe a method invocation operation, as in 'x => x.Method(...)".
///
[Serializable]
public class Invoke : Node, ISerializable
{
/// Gets the arguments.
public object[] Arguments { get; internal set; }
///
/// Initializes a new instance of the class.
///
/// The host.
/// The arguments.
public Invoke(Node host, object[] arguments)
: base(host)
{
Arguments = arguments == null || arguments.Length == 0 ? null : arguments;
}
///
/// Initializes a new instance of the class.
///
/// The info.
/// The context.
protected Invoke(SerializationInfo info, StreamingContext context)
: base(info, context)
{
int count = (int)info.GetValue("ArgumentCount", typeof(int));
if (count != 0)
{
Arguments = new object[count]; for (int i = 0; i < count; i++)
{
string typeName = info.GetString("ArgumentType" + i);
object obj = typeName == "NULL" ? null : info.GetValue("ArgumentValue" + i, Type.GetType(typeName));
Arguments[i] = obj;
}
}
}
///
/// Gets the object data.
///
/// The info.
/// The context.
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
int count = Arguments == null ? 0 : Arguments.Length; info.AddValue("ArgumentCount", count);
for (int i = 0; i < count; i++)
{
info.AddValue("ArgumentType" + i, Arguments[i] == null ? "NULL" : Arguments[i].GetType().AssemblyQualifiedName);
if (Arguments[i] != null) info.AddValue("ArgumentValue" + i, Arguments[i]);
}
base.GetObjectData(info, context);
}
/// Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.
/// If set to true dispose object.
public override void Dispose(bool disposing)
{
if (disposing)
{
try
{
if (Arguments != null)
{
for (int i = 0; i < Arguments.Length; i++)
{
var node = Arguments[i] as Node;
if (node != null)
{
if (node.IsNodeAncestor(this))
node.Host = null;
node.Dispose(disposing);
}
}
Array.Clear(Arguments, 0, Arguments.Length);
}
}
catch
{
}
Arguments = null;
}
base.Dispose(disposing);
}
/// Returns a that represents this instance.
/// A that represents this instance.
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Node::Invoke::Disposed}";
return string.Format("{0}{1}", Host.Sketch(), Arguments == null ? "()" : Arguments.Sketch(brackets: "()".ToCharArray()));
}
}
#endregion Invoke
#region Method
///
/// Describe a method invocation operation, as in 'x => x.Method(...)".
///
[Serializable]
public class Method : Node, ISerializable
{
/// Gets the arguments.
public object[] Arguments { get; internal set; }
///
/// Initializes a new instance of the class.
///
/// The host.
/// The name.
/// The arguments.
public Method(Node host, string name, object[] arguments)
: base(host, name)
{
Arguments = arguments == null || arguments.Length == 0 ? null : arguments;
}
///
/// Initializes a new instance of the class.
///
/// The info.
/// The context.
protected Method(SerializationInfo info, StreamingContext context)
: base(info, context)
{
int count = (int)info.GetValue("ArgumentCount", typeof(int));
if (count != 0)
{
Arguments = new object[count]; for (int i = 0; i < count; i++)
{
string typeName = info.GetString("ArgumentType" + i);
object obj = typeName == "NULL" ? null : info.GetValue("ArgumentValue" + i, Type.GetType(typeName));
Arguments[i] = obj;
}
}
}
///
/// Gets the object data.
///
/// The info.
/// The context.
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
int count = Arguments == null ? 0 : Arguments.Length; info.AddValue("ArgumentCount", count);
for (int i = 0; i < count; i++)
{
info.AddValue("ArgumentType" + i, Arguments[i] == null ? "NULL" : Arguments[i].GetType().AssemblyQualifiedName);
if (Arguments[i] != null) info.AddValue("ArgumentValue" + i, Arguments[i]);
}
base.GetObjectData(info, context);
}
/// Returns a that represents this instance.
/// A that represents this instance.
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Node::Method::Disposed}";
return string.Format("{0}.{1}{2}", Host.Sketch(), Name.Sketch(), Arguments == null ? "()" : Arguments.Sketch(brackets: "()".ToCharArray()));
}
/// Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.
/// If set to true dispose object.
public override void Dispose(bool disposing)
{
if (disposing)
{
try
{
if (Arguments != null)
{
for (int i = 0; i < Arguments.Length; i++)
{
var node = Arguments[i] as Node;
if (node != null)
{
if (node.IsNodeAncestor(this))
node.Host = null;
node.Dispose(disposing);
}
}
Array.Clear(Arguments, 0, Arguments.Length);
}
}
catch
{
}
Arguments = null;
}
base.Dispose(disposing);
}
}
#endregion Method
#region Binary
///
/// Represents a binary operation between a dynamic element and an arbitrary object, including null ones, as in
/// 'x => (x && null)'. The left operand must be an instance of , whereas the right one
/// can be any object, including null values.
///
[Serializable]
public class Binary : Node, ISerializable
{
/// Gets the operation.
public ExpressionType Operation { get; private set; }
/// Gets host of the .
public Node Left { get { return Host; } }
/// Gets the right side value.
public object Right { get; private set; }
///
/// Initializes a new instance of the class.
///
/// The left.
/// The operation.
/// The right.
public Binary(Node left, ExpressionType operation, object right)
: base(left)
{
Operation = operation;
Right = right;
}
///
/// Initializes a new instance of the class.
///
/// The info.
/// The context.
protected Binary(SerializationInfo info, StreamingContext context)
: base(info, context)
{
Operation = (ExpressionType)info.GetValue("Operation", typeof(ExpressionType));
string type = info.GetString("RightType");
Right = type == "NULL" ? null : (Node)info.GetValue("RightItem", Type.GetType(type));
}
///
/// Gets the object data.
///
/// The info.
/// The context.
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Operation", Operation);
info.AddValue("RightType", Right == null ? "NULL" : Right.GetType().AssemblyQualifiedName);
if (Right != null)
info.AddValue("RightItem", Right);
base.GetObjectData(info, context);
}
/// Returns a that represents this instance.
/// A that represents this instance.
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Node::Binary::Disposed}";
return string.Format("({0} {1} {2})", Host.Sketch(), Operation, Right.Sketch());
}
/// Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.
/// If set to true dispose object.
public override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
if (Left != null)
{
if (Left is Node)
{
Node n = (Node)Left;
if (!n.IsDisposed)
n.Dispose(disposing);
}
}
if (Right != null)
{
if (Right is Node)
{
Node n = (Node)Right;
if (!n.IsDisposed)
n.Dispose(disposing);
}
Right = null;
}
}
}
}
#endregion Binary
#region Unary
///
/// Represents an unary operation, as in 'x => !x'. The target must be a instance. There
/// is no distinction between pre- and post- version of the same operation.
///
[Serializable]
public class Unary : Node, ISerializable
{
/// Gets the operation.
public ExpressionType Operation { get; private set; }
/// Gets host of the .
public Node Target { get; private set; }
///
/// Initializes a new instance of the class.
///
/// The target.
/// The operation.
public Unary(Node target, ExpressionType operation)
: base(target)
{
Operation = operation;
Target = target;
}
///
/// Initializes a new instance of the class.
///
/// The info.
/// The context.
protected Unary(SerializationInfo info, StreamingContext context)
: base(info, context)
{
Operation = (ExpressionType)info.GetValue("Operation", typeof(ExpressionType));
}
///
/// Gets the object data.
///
/// The info.
/// The context.
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Operation", Operation);
base.GetObjectData(info, context);
}
/// Returns a that represents this instance.
/// A that represents this instance.
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Node::Binary::Disposed}";
return string.Format("({0} {1})", Operation, Host.Sketch());
}
/// Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.
/// If set to true dispose object.
public override void Dispose(bool disposing)
{
if (disposing)
{
try
{
if (Target != null)
{
var node = Target as Node;
if (node != null)
{
if (node.IsNodeAncestor(this))
node.Host = null;
node.Dispose(disposing);
}
Target = null;
}
}
catch
{
}
}
base.Dispose(disposing);
}
}
#endregion Unary
#region Convert
///
/// Represents a conversion operation, as in 'x => (string)x'.
///
[Serializable]
public class Convert : Node, ISerializable
{
/// Gets the new type to which value will be converted.
public Type NewType { get; private set; }
/// Gets host of the .
public Node Target { get { return Host; } }
///
/// Initializes a new instance of the class.
///
/// The target.
/// The new type.
public Convert(Node target, Type newType)
: base(target)
{
NewType = newType;
}
///
/// Initializes a new instance of the class.
///
/// The info.
/// The context.
protected Convert(SerializationInfo info, StreamingContext context)
: base(info, context)
{
NewType = (Type)info.GetValue("NewType", typeof(Type));
}
///
/// Gets the object data.
///
/// The info.
/// The context.
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("NewType", NewType);
base.GetObjectData(info, context);
}
}
#endregion Convert
///
/// Gets the name of the member. It might be null if this instance is disposed.
///
public string Name { get; internal set; }
/// Gets host of the .
public Node Host { get; internal set; }
/// Gets reference to the parser.
public DynamicParser Parser
{
get { return _parser; }
internal set
{
_parser = value;
if (_parser != null)
_parser._allNodes.Add(this);
}
}
///
/// Initializes a new instance of the class.
///
internal Node()
{
IsDisposed = false;
}
///
/// Initializes a new instance of the class.
///
/// The host.
internal Node(Node host)
: this()
{
if (host == null)
throw new ArgumentNullException("host", "Host cannot be null.");
Host = host;
}
///
/// Initializes a new instance of the class.
///
/// The name.
internal Node(string name)
: this()
{
Name = name.Validated("Name");
}
///
/// Initializes a new instance of the class.
///
/// The host.
/// The name.
/// Host cannot be null.
internal Node(Node host, string name)
: this(host)
{
Name = name.Validated("Name");
}
///
/// Initializes a new instance of the class.
///
/// The info.
/// The context.
protected Node(SerializationInfo info, StreamingContext context)
{
Name = info.GetString("MemberName");
string type = info.GetString("HostType");
Host = type == "NULL" ? null : (Node)info.GetValue("HostItem", Type.GetType(type));
}
/// Returns whether the given node is an ancestor of this instance.
/// The node to test.
/// True if the given node is an ancestor of this instance.
public bool IsNodeAncestor(Node node)
{
if (node != null)
{
Node parent = Host;
while (parent != null)
{
if (object.ReferenceEquals(parent, node))
return true;
parent = parent.Host;
}
}
return false;
}
/// Returns a that represents this instance.
/// A that represents this instance.
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Node::Disposed}";
return "{DynamicParser::Node::Empty}";
}
#region Implementation of IDynamicMetaObjectProvider
/// Returns the responsible
/// for binding operations performed on this object.
/// The expression tree representation of the runtime value.
/// The to bind this object.
/// Thrown if this instance is disposed.
public DynamicMetaObject GetMetaObject(System.Linq.Expressions.Expression parameter)
{
if (IsDisposed)
throw new ObjectDisposedException("DynamicParser.Node");
return new MetaNode(
parameter,
BindingRestrictions.GetInstanceRestriction(parameter, this),
this);
}
#endregion Implementation of IDynamicMetaObjectProvider
#region Implementation of IFinalizerDisposable
/// Finalizes an instance of the class.
~Node()
{
Dispose(false);
}
/// Gets a value indicating whether this instance is disposed.
public bool IsDisposed { get; private set; }
/// Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.
public virtual void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.
/// If set to true dispose object.
public virtual void Dispose(bool disposing)
{
if (disposing)
{
IsDisposed = true;
if (Host != null && !Host.IsDisposed)
Host.Dispose();
Host = null;
Parser = null;
}
}
#endregion Implementation of IFinalizerDisposable
#region Implementation of ISerializable
///
/// Populates a with the data needed to serialize the target object.
///
/// The to populate with data.
/// The destination (see ) for this serialization.
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (!string.IsNullOrEmpty(Name))
info.AddValue("MemberName", Name);
info.AddValue("HostType", Host == null ? "NULL" : Host.GetType().AssemblyQualifiedName);
if (Host != null)
info.AddValue("HostItem", Host);
}
#endregion Implementation of ISerializable
}
#endregion Node
#region Data
private List _arguments = new List();
private List _allNodes = new List();
private object _uncertainResult;
#endregion Data
#region Properties
/// Gets the last node (root of the tree).
public Node Last { get; internal set; }
///
/// Gets an enumeration containing the dynamic arguments used in the dynamic lambda expression parsed.
///
public IEnumerable Arguments
{
get
{
List list = new List();
if (!IsDisposed && _arguments != null)
list.AddRange(_arguments);
foreach (Node.Argument arg in list)
yield return arg;
list.Clear();
list = null;
}
}
///
/// Gets the number of dynamic arguments used in the dynamic lambda expression parsed.
///
public int Count
{
get { return _arguments == null ? 0 : _arguments.Count; }
}
///
/// Gets the result of the parsing of the dynamic lambda expression. This result can be either an arbitrary object,
/// including null, if the expression resolves to it, or an instance of the class that
/// contains the last logic expression evaluated when parsing the dynamic lambda expression.
///
public object Result
{
get { return _uncertainResult ?? Last; }
}
#endregion Properties
private DynamicParser(Delegate f)
{
// I know this can be almost a one liner
// but it causes memory leaks when so.
ParameterInfo[] pars = f.Method.GetParameters();
foreach (ParameterInfo p in pars)
{
int attrs = p.GetCustomAttributes(typeof(DynamicAttribute), true).Length;
if (attrs != 0)
{
Node.Argument par = new Node.Argument(p.Name) { Parser = this };
this._arguments.Add(par);
}
else
throw new ArgumentException(string.Format("Argument '{0}' must be dynamic.", p.Name));
}
try
{
_uncertainResult = f.DynamicInvoke(_arguments.ToArray());
}
catch (TargetInvocationException e)
{
if (e.InnerException != null) throw e.InnerException;
else throw e;
}
}
///
/// Parses the dynamic lambda expression given in the form of a delegate, and returns a new instance of the
/// class that holds the dynamic arguments used in the dynamic lambda expression, and
/// the result of the parsing.
///
/// The dynamic lambda expression to parse.
/// A new instance of .
public static DynamicParser Parse(Delegate f)
{
return new DynamicParser(f);
}
/// Returns a that represents this instance.
/// A that represents this instance.
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Disposed}";
StringBuilder sb = new StringBuilder();
sb.Append("(");
bool first = true;
if (_arguments != null)
{
foreach (Node.Argument arg in _arguments)
{
if (!first) sb.Append(", "); else first = false;
sb.Append(arg);
}
}
sb.Append(")");
sb.AppendFormat(" => {0}", Result.Sketch());
return sb.ToString();
}
#region Implementation of IExtendedDisposable
/// Gets a value indicating whether this instance is disposed.
public bool IsDisposed { get; private set; }
///
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
///
public void Dispose()
{
IsDisposed = true;
if (_uncertainResult != null)
{
if (_uncertainResult is Node)
((Node)_uncertainResult).Dispose();
_uncertainResult = null;
}
if (Last != null)
{
if (!Last.IsDisposed)
Last.Dispose();
Last = null;
}
if (_arguments != null)
{
_arguments.ForEach(x => { if (!x.IsDisposed) x.Dispose(); });
_arguments.Clear();
_arguments = null;
}
if (_allNodes != null)
{
_allNodes.ForEach(x => { if (!x.IsDisposed) x.Dispose(); });
_allNodes.Clear();
_allNodes = null;
}
}
#endregion Implementation of IExtendedDisposable
}
}