using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic;
using System.Linq;
using System.Reflection;
using System.Text;
using Simple.Data.Commands;
namespace Simple.Data
{
///
/// Represents a qualified reference to a data store object, such as a table or column.
///
public class ObjectReference : SimpleReference, IEquatable
{
private readonly string _name;
private readonly ObjectReference _owner;
private readonly DataStrategy _dataStrategy;
internal ObjectReference(string name) : this(name, null, null, null)
{
}
public ObjectReference(string name, ObjectReference owner) : this(name, owner, null, null)
{
}
internal ObjectReference(string name, DataStrategy dataStrategy) : this(name, null, dataStrategy, null)
{
}
internal ObjectReference(string name, ObjectReference owner, DataStrategy dataStrategy) : this(name, owner, dataStrategy, null)
{
}
internal ObjectReference(string name, ObjectReference owner, DataStrategy dataStrategy, string alias) : base(alias)
{
_name = name;
_owner = owner;
_dataStrategy = dataStrategy;
}
///
/// Gets the owner of the referenced object; null if the object is top-level.
///
/// The owner.
public ObjectReference GetOwner()
{
return _owner;
}
public ObjectReference GetTop()
{
return ReferenceEquals(_owner, null) ? this : _owner.GetTop();
}
protected internal override DataStrategy FindDataStrategyInHierarchy()
{
return _dataStrategy ?? (ReferenceEquals(_owner, null) ? null : _owner.FindDataStrategyInHierarchy());
}
///
/// Gets the name of the referenced object.
///
/// The name.
public string GetName()
{
return _name;
}
public ObjectReference As(string alias)
{
return new ObjectReference(_name, _owner, _dataStrategy, alias);
}
public override string GetAliasOrName()
{
return GetAlias() ?? _name;
}
public AllColumnsSpecialReference AllColumns()
{
return new AllColumnsSpecialReference(this);
}
public AllColumnsSpecialReference Star()
{
return new AllColumnsSpecialReference(this);
}
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
{
if (_dataStrategy != null)
{
if (TryInvokeDataStrategyMethod(args, out result)) return true;
if (_dataStrategy.TryInvokeFunction(_name, () => binder.ArgumentsToDictionary(args), out result)) return true;
}
throw new InvalidOperationException();
}
private bool TryInvokeDataStrategyMethod(object[] args, out object result)
{
var methodInfo = _dataStrategy.GetType().GetMethod(_name, args.Select(a => (a ?? new object()).GetType()).ToArray());
if (methodInfo != null)
{
result = methodInfo.Invoke(_dataStrategy, args);
return true;
}
result = null;
return false;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (base.TryInvokeMember(binder, args, out result)) return true;
if (_dataStrategy != null)
{
var table = new DynamicTable(_name, _dataStrategy);
try
{
if (table.TryInvokeMember(binder, args, out result))
{
_dataStrategy.SetMemberAsTable(this, table);
return true;
}
}
catch (InvalidOperationException ex)
{
if (!ex.Message.StartsWith("Method"))
{
throw;
}
}
// Or it could be a schema reference...
var schema = new DynamicSchema(_name, _dataStrategy);
try
{
if (schema.TryInvokeMember(binder, args, out result))
{
_dataStrategy.SetMemberAsSchema(this);
return true;
}
}
catch (KeyNotFoundException)
{
throw new InvalidOperationException(string.Format("Method {0} not recognised", binder.Name));
}
}
var dataStrategy = FindDataStrategyInHierarchy();
if (dataStrategy != null)
{
var command = CommandFactory.GetCommandFor(binder.Name);
if (command != null)
{
if (!ReferenceEquals(_owner, null))
{
var schema = dataStrategy.SetMemberAsSchema(_owner);
var table = schema.GetTable(_name);
table.TryInvokeMember(binder, args, out result);
}
else
{
throw new InvalidOperationException(string.Format("Method {0} not recognised", binder.Name));
}
//result = command.Execute(dataStrategy, table, binder, args);
}
else
{
if (dataStrategy.IsExpressionFunction(binder.Name, args))
{
result = new SimpleExpression(this, new SimpleFunction(binder.Name, args), SimpleExpressionType.Function);
}
else
{
result = new FunctionReference(binder.Name, this, args);
}
}
return true;
}
throw new InvalidOperationException(string.Format("Method {0} not recognised", binder.Name));
}
///
/// Provides the implementation for operations that get member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as getting a value for a property.
///
/// Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive.
/// The result of the get operation. For example, if the method is called for a property, you can assign the property value to .
///
/// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a run-time exception is thrown.)
///
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = new ObjectReference(binder.Name, this);
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (base.TrySetMember(binder, value))
{
return true;
}
throw new BadExpressionException("Cannot assign values to object references.");
}
public dynamic this[string name]
{
get { return new ObjectReference(name, this); }
}
///
/// Gets the names of all objects included in the reference as an array, with the uppermost object first.
///
///
public string[] GetAllObjectNames()
{
if (ReferenceEquals(GetOwner(), null)) return new[] {_name};
return _owner.GetAllObjectNames().Concat(new[] {_name}).ToArray();
}
///
/// Gets the names of all objects included in the reference as an array, with the uppermost object first.
///
///
public Tuple[] GetAllObjectNamesAndAliases()
{
if (ReferenceEquals(GetOwner(), null)) return new[] {Tuple.Create(_name, GetAlias())};
return _owner.GetAllObjectNamesAndAliases().Concat(new[] {Tuple.Create(_name, GetAlias())}).ToArray();
}
public string GetAllObjectNamesDotted()
{
return string.Join(".", GetAllObjectNames());
}
public static ObjectReference FromString(string source)
{
return FromStrings(source.Split('.'));
}
public static ObjectReference FromStrings(params string[] source)
{
return source.Where(current => !String.IsNullOrEmpty(current))
.Aggregate(null, (current, element) => new ObjectReference(element, current));
}
///
/// Indicates whether the current object is equal to another object of the same type.
///
///
/// true if the current object is equal to the parameter; otherwise, false.
///
/// An object to compare with this object.
public bool Equals(ObjectReference other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(other._name, _name) && Equals(other._owner, _owner);
}
///
/// Determines whether the specified is equal to the current .
///
///
/// true if the specified is equal to the current ; otherwise, false.
///
/// The to compare with the current . 2
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof (ObjectReference)) return false;
return Equals((ObjectReference) obj);
}
///
/// Serves as a hash function for a particular type.
///
///
/// A hash code for the current .
///
/// 2
public override int GetHashCode()
{
unchecked
{
return ((_name != null ? _name.GetHashCode() : 0)*397) ^ (!ReferenceEquals(_owner, null) ? _owner.GetHashCode() : 0);
}
}
public override string ToString()
{
if (!ReferenceEquals(_owner, null))
{
return string.Concat(_owner, ".", _name);
}
return _name;
}
///
/// Implements the operator == to create a with the type .
///
/// The column.
/// The value.
/// The expression.
public static SimpleExpression operator ==(ObjectReference column, object value)
{
return new SimpleExpression(column, value, SimpleExpressionType.Equal);
}
///
/// Implements the operator != to create a with the type .
///
/// The column.
/// The value.
/// The expression.
public static SimpleExpression operator !=(ObjectReference column, object value)
{
return new SimpleExpression(column, value, SimpleExpressionType.NotEqual);
}
///
/// Implements the operator < to create a with the type .
///
/// The column.
/// The value.
/// The expression.
public static SimpleExpression operator <(ObjectReference column, object value)
{
return new SimpleExpression(column, value, SimpleExpressionType.LessThan);
}
///
/// Implements the operator > to create a with the type .
///
/// The column.
/// The value.
/// The expression.
public static SimpleExpression operator >(ObjectReference column, object value)
{
return new SimpleExpression(column, value, SimpleExpressionType.GreaterThan);
}
///
/// Implements the operator <= to create a with the type .
///
/// The column.
/// The value.
/// The expression.
public static SimpleExpression operator <=(ObjectReference column, object value)
{
return new SimpleExpression(column, value, SimpleExpressionType.LessThanOrEqual);
}
///
/// Implements the operator >= to create a with the type .
///
/// The column.
/// The value.
/// The expression.
public static SimpleExpression operator >=(ObjectReference column, object value)
{
return new SimpleExpression(column, value, SimpleExpressionType.GreaterThanOrEqual);
}
public static MathReference operator +(ObjectReference column, object value)
{
return new MathReference(column, value, MathOperator.Add);
}
public static MathReference operator -(ObjectReference column, object value)
{
return new MathReference(column, value, MathOperator.Subtract);
}
public static MathReference operator *(ObjectReference column, object value)
{
return new MathReference(column, value, MathOperator.Multiply);
}
public static MathReference operator /(ObjectReference column, object value)
{
return new MathReference(column, value, MathOperator.Divide);
}
public static MathReference operator %(ObjectReference column, object value)
{
return new MathReference(column, value, MathOperator.Modulo);
}
public bool HasOwner()
{
return !ReferenceEquals(null, _owner);
}
}
}