Files
DynamORM/DynamORM/Helpers/Dynamics/DynamicParser.cs
grzegorz.russek 2f19bf5c49
2014-11-29 21:29:02 +00:00

1239 lines
54 KiB
C#

/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012, 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;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Text;
namespace DynamORM.Helpers.Dynamics
{
/// <summary>
/// Class able to parse dynamic lambda expressions. Allows to create dynamic logic.
/// </summary>
public class DynamicParser : IExtendedDisposable
{
#region Node
/// <summary>
/// Generic bindable operation where some of its operands is a dynamic argument, or a dynamic member or
/// a method of that argument.
/// </summary>
[Serializable]
public class Node : IDynamicMetaObjectProvider, IFinalizerDisposable, ISerializable
{
private DynamicParser _parser = null;
#region MetaNode
/// <summary>
/// Represents the dynamic binding and a binding logic of
/// an object participating in the dynamic binding.
/// </summary>
internal class MetaNode : DynamicMetaObject
{
/// <summary>
/// Initializes a new instance of the <see cref="MetaNode"/> class.
/// </summary>
/// <param name="parameter">The parameter.</param>
/// <param name="rest">The restrictions.</param>
/// <param name="value">The value.</param>
public MetaNode(Expression parameter, BindingRestrictions rest, object value)
: base(parameter, rest, value)
{
}
// Func was cool but caused memory leaks
private DynamicMetaObject GetBinder(Node node)
{
var o = (Node)this.Value;
node.Parser = o.Parser;
o.Parser.Last = node;
var p = Expression.Variable(typeof(Node), "ret");
var exp = Expression.Block(new ParameterExpression[] { p }, Expression.Assign(p, Expression.Constant(node)));
return new MetaNode(exp, this.Restrictions, node);
}
/// <summary>
/// Performs the binding of the dynamic get member operation.
/// </summary>
/// <param name="binder">An instance of the <see cref="T:System.Dynamic.GetMemberBinder" /> that represents the details of the dynamic operation.</param>
/// <returns>
/// The new <see cref="T:System.Dynamic.DynamicMetaObject" /> representing the result of the binding.
/// </returns>
public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
{
return GetBinder(new GetMember((Node)this.Value, binder.Name));
}
/// <summary>
/// Performs the binding of the dynamic set member operation.
/// </summary>
/// <param name="binder">An instance of the <see cref="T:System.Dynamic.SetMemberBinder" /> that represents the details of the dynamic operation.</param>
/// <param name="value">The <see cref="T:System.Dynamic.DynamicMetaObject" /> representing the value for the set member operation.</param>
/// <returns>
/// The new <see cref="T:System.Dynamic.DynamicMetaObject" /> representing the result of the binding.
/// </returns>
public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
{
return GetBinder(new SetMember((Node)this.Value, binder.Name, value.Value));
}
/// <summary>
/// Performs the binding of the dynamic get index operation.
/// </summary>
/// <param name="binder">An instance of the <see cref="T:System.Dynamic.GetIndexBinder" /> that represents the details of the dynamic operation.</param>
/// <param name="indexes">An array of <see cref="T:System.Dynamic.DynamicMetaObject" /> instances - indexes for the get index operation.</param>
/// <returns>
/// The new <see cref="T:System.Dynamic.DynamicMetaObject" /> representing the result of the binding.
/// </returns>
public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes)
{
return GetBinder(new GetIndex((Node)this.Value, MetaList2List(indexes)));
}
/// <summary>
/// Performs the binding of the dynamic set index operation.
/// </summary>
/// <param name="binder">An instance of the <see cref="T:System.Dynamic.SetIndexBinder" /> that represents the details of the dynamic operation.</param>
/// <param name="indexes">An array of <see cref="T:System.Dynamic.DynamicMetaObject" /> instances - indexes for the set index operation.</param>
/// <param name="value">The <see cref="T:System.Dynamic.DynamicMetaObject" /> representing the value for the set index operation.</param>
/// <returns>
/// The new <see cref="T:System.Dynamic.DynamicMetaObject" /> representing the result of the binding.
/// </returns>
public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value)
{
return GetBinder(new SetIndex((Node)this.Value, MetaList2List(indexes), value.Value));
}
/// <summary>
/// Performs the binding of the dynamic invoke operation.
/// </summary>
/// <param name="binder">An instance of the <see cref="T:System.Dynamic.InvokeBinder" /> that represents the details of the dynamic operation.</param>
/// <param name="args">An array of <see cref="T:System.Dynamic.DynamicMetaObject" /> instances - arguments to the invoke operation.</param>
/// <returns>
/// The new <see cref="T:System.Dynamic.DynamicMetaObject" /> representing the result of the binding.
/// </returns>
public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args)
{
return GetBinder(new Invoke((Node)this.Value, MetaList2List(args)));
}
/// <summary>
/// Performs the binding of the dynamic invoke member operation.
/// </summary>
/// <param name="binder">An instance of the <see cref="T:System.Dynamic.InvokeMemberBinder" /> that represents the details of the dynamic operation.</param>
/// <param name="args">An array of <see cref="T:System.Dynamic.DynamicMetaObject" /> instances - arguments to the invoke member operation.</param>
/// <returns>
/// The new <see cref="T:System.Dynamic.DynamicMetaObject" /> representing the result of the binding.
/// </returns>
public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
{
return GetBinder(new Method((Node)this.Value, binder.Name, MetaList2List(args)));
}
/// <summary>
/// Performs the binding of the dynamic binary operation.
/// </summary>
/// <param name="binder">An instance of the <see cref="T:System.Dynamic.BinaryOperationBinder" /> that represents the details of the dynamic operation.</param>
/// <param name="arg">An instance of the <see cref="T:System.Dynamic.DynamicMetaObject" /> representing the right hand side of the binary operation.</param>
/// <returns>
/// The new <see cref="T:System.Dynamic.DynamicMetaObject" /> representing the result of the binding.
/// </returns>
public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg)
{
return GetBinder(new Binary((Node)this.Value, binder.Operation, arg.Value));
}
/// <summary>
/// Performs the binding of the dynamic unary operation.
/// </summary>
/// <param name="binder">An instance of the <see cref="T:System.Dynamic.UnaryOperationBinder" /> that represents the details of the dynamic operation.</param>
/// <returns>
/// The new <see cref="T:System.Dynamic.DynamicMetaObject" /> representing the result of the binding.
/// </returns>
public override DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder)
{
var o = (Node)this.Value;
var 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;
var p = Expression.Variable(ret.GetType(), "ret"); // the type is now obtained from "ret"
var 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);
}
/// <summary>
/// Performs the binding of the dynamic conversion operation.
/// </summary>
/// <param name="binder">An instance of the <see cref="T:System.Dynamic.ConvertBinder" /> that represents the details of the dynamic operation.</param>
/// <returns>
/// The new <see cref="T:System.Dynamic.DynamicMetaObject" /> representing the result of the binding.
/// </returns>
public override DynamicMetaObject BindConvert(ConvertBinder binder)
{
var o = (Node)this.Value;
var 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();
}
}
var p = Expression.Variable(binder.ReturnType, "ret");
var 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
/// <summary>
/// Describe a dynamic argument used in a dynamic lambda expression.
/// </summary>
[Serializable]
public class Argument : Node, ISerializable
{
/// <summary>
/// Initializes a new instance of the <see cref="Argument"/> class.
/// </summary>
/// <param name="name">The name.</param>
public Argument(string name)
: base(name)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Argument"/> class.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
protected Argument(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
/// <summary>Returns a <see cref="System.String" /> that represents this instance.</summary>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Node::Argument::Disposed}";
return Name;
}
}
#endregion Argument
#region GetMember
/// <summary>
/// Describe a 'get member' operation, as in 'x => x.Member'.
/// </summary>
[Serializable]
public class GetMember : Node, ISerializable
{
/// <summary>
/// Initializes a new instance of the <see cref="GetMember"/> class.
/// </summary>
/// <param name="host">The host.</param>
/// <param name="name">The name.</param>
public GetMember(Node host, string name)
: base(host, name)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="GetMember"/> class.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
protected GetMember(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
/// <summary>Returns a <see cref="System.String" /> that represents this instance.</summary>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Node::GetMember::Disposed}";
return string.Format("{0}.{1}", Host.Sketch(), Name.Sketch());
}
}
#endregion GetMember
#region SetMember
/// <summary>
/// Describe a 'set member' operation, as in 'x => x.Member = y'.
/// </summary>
[Serializable]
public class SetMember : Node, ISerializable
{
/// <summary>
/// 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.
/// </summary>
public object Value { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="SetMember"/> class.
/// </summary>
/// <param name="host">The host.</param>
/// <param name="name">The name.</param>
/// <param name="value">The value.</param>
public SetMember(Node host, string name, object value)
: base(host, name)
{
Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="SetMember"/> class.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
protected SetMember(SerializationInfo info, StreamingContext context)
: base(info, context)
{
string type = info.GetString("MemberType");
Value = type == "NULL" ? null : info.GetValue("MemberValue", Type.GetType(type));
}
/// <summary>
/// Gets the object data.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
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);
}
/// <summary>Returns a <see cref="System.String" /> that represents this instance.</summary>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Node::SetMember::Disposed}";
return string.Format("({0}.{1} = {2})", Host.Sketch(), Name.Sketch(), Value.Sketch());
}
}
#endregion SetMember
#region GetIndex
/// <summary>
/// Describe a 'get indexed' operation, as in 'x => x.Member[...]'.
/// </summary>
[Serializable]
public class GetIndex : Node, ISerializable
{
/// <summary>Gets the indexes.</summary>
public object[] Indexes { get; internal set; }
/// <summary>
/// Initializes a new instance of the <see cref="GetIndex"/> class.
/// </summary>
/// <param name="host">The host.</param>
/// <param name="indexes">The indexes.</param>
/// <exception cref="System.ArgumentNullException">Indexes array cannot be null.</exception>
/// <exception cref="System.ArgumentException">Indexes array cannot be empty.</exception>
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;
}
/// <summary>
/// Initializes a new instance of the <see cref="GetIndex"/> class.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
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;
}
}
}
/// <summary>
/// Gets the object data.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
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);
}
/// <summary>Returns a <see cref="System.String" /> that represents this instance.</summary>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Node::GetIndex::Disposed}";
return string.Format("{0}{1}", Host.Sketch(), Indexes == null ? "[empty]" : Indexes.Sketch());
}
}
#endregion GetIndex
#region SetIndex
/// <summary>
/// Describe a 'set indexed' operation, as in 'x => x.Member[...] = Value'.
/// </summary>
[Serializable]
public class SetIndex : GetIndex, ISerializable
{
/// <summary>
/// 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.
/// </summary>
public object Value { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="SetIndex"/> class.
/// </summary>
/// <param name="host">The host.</param>
/// <param name="indexes">The indexes.</param>
/// <param name="value">The value.</param>
public SetIndex(Node host, object[] indexes, object value)
: base(host, indexes)
{
Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="SetIndex"/> class.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
protected SetIndex(SerializationInfo info, StreamingContext context)
: base(info, context)
{
string type = info.GetString("MemberType");
Value = type == "NULL" ? null : info.GetValue("MemberValue", Type.GetType(type));
}
/// <summary>
/// Gets the object data.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
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);
}
/// <summary>Returns a <see cref="System.String" /> that represents this instance.</summary>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
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());
}
}
#endregion SetIndex
#region Invoke
/// <summary>
/// Describe a method invocation operation, as in 'x => x.Method(...)".
/// </summary>
[Serializable]
public class Invoke : Node, ISerializable
{
/// <summary>Gets the arguments.</summary>
public object[] Arguments { get; internal set; }
/// <summary>
/// Initializes a new instance of the <see cref="Invoke"/> class.
/// </summary>
/// <param name="host">The host.</param>
/// <param name="arguments">The arguments.</param>
public Invoke(Node host, object[] arguments)
: base(host)
{
Arguments = arguments == null || arguments.Length == 0 ? null : arguments;
}
/// <summary>
/// Initializes a new instance of the <see cref="Invoke"/> class.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
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;
}
}
}
/// <summary>
/// Gets the object data.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
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);
}
/// <summary>Returns a <see cref="System.String" /> that represents this instance.</summary>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
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
/// <summary>
/// Describe a method invocation operation, as in 'x => x.Method(...)".
/// </summary>
[Serializable]
public class Method : Node, ISerializable
{
/// <summary>Gets the arguments.</summary>
public object[] Arguments { get; internal set; }
/// <summary>
/// Initializes a new instance of the <see cref="Method"/> class.
/// </summary>
/// <param name="host">The host.</param>
/// <param name="name">The name.</param>
/// <param name="arguments">The arguments.</param>
public Method(Node host, string name, object[] arguments)
: base(host, name)
{
Arguments = arguments == null || arguments.Length == 0 ? null : arguments;
}
/// <summary>
/// Initializes a new instance of the <see cref="Method"/> class.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
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;
}
}
}
/// <summary>
/// Gets the object data.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
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);
}
/// <summary>Returns a <see cref="System.String" /> that represents this instance.</summary>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
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()));
}
}
#endregion Method
#region Binary
/// <summary>
/// Represents a binary operation between a dynamic element and an arbitrary object, including null ones, as in
/// 'x =&gt; (x &amp;&amp; null)'. The left operand must be an instance of <see cref="Node"/>, whereas the right one
/// can be any object, including null values.
/// </summary>
[Serializable]
public class Binary : Node, ISerializable
{
/// <summary>Gets the operation.</summary>
public ExpressionType Operation { get; private set; }
/// <summary>Gets host of the <see cref="Node"/>.</summary>
public Node Left { get { return Host; } }
/// <summary>Gets the right side value.</summary>
public object Right { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="Binary"/> class.
/// </summary>
/// <param name="left">The left.</param>
/// <param name="operation">The operation.</param>
/// <param name="right">The right.</param>
public Binary(Node left, ExpressionType operation, object right)
: base(left)
{
Operation = operation;
Right = right;
}
/// <summary>
/// Initializes a new instance of the <see cref="Binary"/> class.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
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));
}
/// <summary>
/// Gets the object data.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
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);
}
/// <summary>Returns a <see cref="System.String" /> that represents this instance.</summary>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Node::Binary::Disposed}";
return string.Format("({0} {1} {2})", Host.Sketch(), Operation, Right.Sketch());
}
/// <summary>Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.</summary>
/// <param name="disposing">If set to <c>true</c> dispose object.</param>
public override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing && Right != null && Right is Node)
{
Node n = (Node)Right;
if (!n.IsDisposed)
n.Dispose(disposing);
Right = null;
}
}
}
#endregion Binary
#region Unary
/// <summary>
/// Represents an unary operation, as in 'x => !x'. The target must be a <see cref="Node"/> instance. There
/// is no distinction between pre- and post- version of the same operation.
/// </summary>
[Serializable]
public class Unary : Node, ISerializable
{
/// <summary>Gets the operation.</summary>
public ExpressionType Operation { get; private set; }
/// <summary>Gets host of the <see cref="Node"/>.</summary>
public Node Target { get { return Host; } }
/// <summary>
/// Initializes a new instance of the <see cref="Unary"/> class.
/// </summary>
/// <param name="target">The target.</param>
/// <param name="operation">The operation.</param>
public Unary(Node target, ExpressionType operation)
: base(target)
{
Operation = operation;
}
/// <summary>
/// Initializes a new instance of the <see cref="Unary"/> class.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
protected Unary(SerializationInfo info, StreamingContext context)
: base(info, context)
{
Operation = (ExpressionType)info.GetValue("Operation", typeof(ExpressionType));
}
/// <summary>
/// Gets the object data.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Operation", Operation);
base.GetObjectData(info, context);
}
/// <summary>Returns a <see cref="System.String" /> that represents this instance.</summary>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Node::Binary::Disposed}";
return string.Format("({0} {1})", Operation, Host.Sketch());
}
}
#endregion Unary
#region Convert
/// <summary>
/// Represents a conversion operation, as in 'x => (string)x'.
/// </summary>
[Serializable]
public class Convert : Node, ISerializable
{
/// <summary>Gets the new type to which value will be converted.</summary>
public Type NewType { get; private set; }
/// <summary>Gets host of the <see cref="Node"/>.</summary>
public Node Target { get { return Host; } }
/// <summary>
/// Initializes a new instance of the <see cref="Convert"/> class.
/// </summary>
/// <param name="target">The target.</param>
/// <param name="newType">The new type.</param>
public Convert(Node target, Type newType)
: base(target)
{
NewType = newType;
}
/// <summary>
/// Initializes a new instance of the <see cref="Convert"/> class.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
protected Convert(SerializationInfo info, StreamingContext context)
: base(info, context)
{
NewType = (Type)info.GetValue("NewType", typeof(Type));
}
/// <summary>
/// Gets the object data.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("NewType", NewType);
base.GetObjectData(info, context);
}
}
#endregion Convert
/// <summary>
/// Gets the name of the member. It might be null if this instance is disposed.
/// </summary>
public string Name { get; internal set; }
/// <summary>Gets host of the <see cref="Node"/>.</summary>
public Node Host { get; internal set; }
/// <summary>Gets reference to the parser.</summary>
public DynamicParser Parser
{
get { return _parser; }
internal set
{
_parser = value;
if (_parser != null)
_parser._allNodes.Add(this);
}
}
/// <summary>
/// Initializes a new instance of the <see cref="Node"/> class.
/// </summary>
internal Node()
{
IsDisposed = false;
}
/// <summary>
/// Initializes a new instance of the <see cref="Node"/> class.
/// </summary>
/// <param name="host">The host.</param>
internal Node(Node host)
: this()
{
if (host == null)
throw new ArgumentNullException("host", "Host cannot be null.");
Host = host;
}
/// <summary>
/// Initializes a new instance of the <see cref="Node"/> class.
/// </summary>
/// <param name="name">The name.</param>
internal Node(string name)
: this()
{
Name = name.Validated("Name");
}
/// <summary>
/// Initializes a new instance of the <see cref="Node"/> class.
/// </summary>
/// <param name="host">The host.</param>
/// <param name="name">The name.</param>
/// <exception cref="System.ArgumentNullException">Host cannot be null.</exception>
internal Node(Node host, string name)
: this(host)
{
Name = name.Validated("Name");
}
/// <summary>
/// Initializes a new instance of the <see cref="Node"/> class.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="context">The context.</param>
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));
}
/// <summary>Returns a <see cref="System.String" /> that represents this instance.</summary>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Node::Disposed}";
return "{DynamicParser::Node::Empty}";
}
#region Implementation of IDynamicMetaObjectProvider
/// <summary>Returns the <see cref="T:System.Dynamic.DynamicMetaObject" /> responsible
/// for binding operations performed on this object.</summary>
/// <param name="parameter">The expression tree representation of the runtime value.</param>
/// <returns>The <see cref="T:System.Dynamic.DynamicMetaObject" /> to bind this object.</returns>
/// <exception cref="System.ObjectDisposedException">Thrown if this instance is disposed.</exception>
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
/// <summary>Releases unmanaged resources and performs other cleanup operations before
/// the <see cref="DynamORM.Helpers.Dynamics.DynamicParser.Node"/> is reclaimed by
/// garbage collection.</summary>
~Node()
{
Dispose(false);
}
/// <summary>Gets a value indicating whether this instance is disposed.</summary>
public bool IsDisposed { get; private set; }
/// <summary>Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.</summary>
public virtual void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.</summary>
/// <param name="disposing">If set to <c>true</c> dispose object.</param>
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
/// <summary>
/// Populates a <see cref="T:System.Runtime.Serialization.SerializationInfo" /> with the data needed to serialize the target object.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> to populate with data.</param>
/// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext" />) for this serialization.</param>
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<Node.Argument> _arguments = new List<Node.Argument>();
private List<Node> _allNodes = new List<Node>();
private object _uncertainResult;
#endregion Data
#region Properties
/// <summary>Gets the last node (root of the tree).</summary>
public Node Last { get; internal set; }
/// <summary>
/// Gets an enumeration containing the dynamic arguments used in the dynamic lambda expression parsed.
/// </summary>
public IEnumerable<Node.Argument> Arguments
{
get
{
List<Node.Argument> list = new List<Node.Argument>();
if (!IsDisposed && _arguments != null)
list.AddRange(_arguments);
foreach (var arg in list)
yield return arg;
list.Clear();
list = null;
}
}
/// <summary>
/// Gets the number of dynamic arguments used in the dynamic lambda expression parsed.
/// </summary>
public int Count
{
get { return _arguments == null ? 0 : _arguments.Count; }
}
/// <summary>
/// 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 <see cref="Node"/> class that
/// contains the last logic expression evaluated when parsing the dynamic lambda expression.
/// </summary>
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.
var pars = f.Method.GetParameters();
foreach (var p in pars)
{
var attrs = p.GetCustomAttributes(typeof(DynamicAttribute), true).Length;
if (attrs != 0)
{
var 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));
}
_uncertainResult = f.DynamicInvoke(_arguments.ToArray());
}
/// <summary>
/// Parses the dynamic lambda expression given in the form of a delegate, and returns a new instance of the
/// <see cref="DynamicParser"/> class that holds the dynamic arguments used in the dynamic lambda expression, and
/// the result of the parsing.
/// </summary>
/// <param name="f">The dynamic lambda expression to parse.</param>
/// <returns>A new instance of <see cref="DynamicParser"/>.</returns>
public static DynamicParser Parse(Delegate f)
{
return new DynamicParser(f);
}
/// <summary>Returns a <see cref="System.String" /> that represents this instance.</summary>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public override string ToString()
{
if (IsDisposed)
return "{DynamicParser::Disposed}";
StringBuilder sb = new StringBuilder();
sb.Append("(");
bool first = true;
if (_arguments != null)
{
foreach (var 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
/// <summary>Gets a value indicating whether this instance is disposed.</summary>
public bool IsDisposed { get; private set; }
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
IsDisposed = true;
if (_uncertainResult != null && _uncertainResult is Node)
{
((Node)_uncertainResult).Dispose();
_uncertainResult = null;
}
if (Last != null && !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
}
}