// ==============================================
// #define UNDER_CONSTRUCTION
namespace MBLib.SystemTools.Dynamic
{
using System;
using System.Text;
using System.Collections.Generic;
using System.Dynamic;
using System.Diagnostics;
// ===========================================
public class DeepObject : DynamicObject, ICloneable
{
public DeepObject( string name = null, bool caseSensitive = true )
{
__name = name == null ? null : name.Validated( "DeepObject Name", invalidChars: TypeHelper.InvalidMemberNameChars );
__caseSensitive = caseSensitive;
}
#region
private string __name = null;
private bool __isIndex = false;
private bool __caseSensitive = true;
private DeepObject __parent = null;
private List<DeepObject> __members = null;
private object __value = null;
private bool __hasValue = false;
private bool __cloneOnCopying = true;
#endregion
public string DeepName()
{
return __name;
}
public void DeepSetName( string name )
{
if( name == null ) {
if( __parent == null ) { __name = null; return; }
throw new InvalidOperationException( "Name cannot be cleaned in DeepObjects that are members of a tree." );
}
if( __parent != null ) {
if( __parent.DeepMember( name, raise: false ) != null )
throw new InvalidOperationException( "Cannot change name because it already exists as a member in the parent tree." );
}
__name = name;
}
public bool DeepCaseSensitive()
{
return __caseSensitive;
}
public bool DeepIsIndex()
{
return __isIndex;
}
public object DeepValue()
{
return __value;
}
public bool DeepHasValue()
{
return __hasValue;
}
public void DeepSetValue( object value )
{
__hasValue = true;
__value = value;
}
public void DeepClearValue()
{
__hasValue = false;
__value = null;
}
public bool DeepIsWrapper()
{
return ( __hasValue && __members == null ) ? true : false;
}
public DeepObject DeepParent()
{
return __parent;
}
public int DeepLevel()
{
if( __parent == null ) return 0;
return __parent.DeepLevel() + 1;
}
public bool DeepIsTree()
{
return __members == null ? false : true;
}
public int DeepCount()
{
return __members == null ? 0 : __members.Count;
}
public IEnumerable<DeepObject> DeepMembers()
{
return __members.AsReadOnly();
}
public DeepObject DeepMember( int n, bool raise = true )
{
if( __members == null ) {
if( raise ) throw new InvalidOperationException( "This DeepObject is not a tree." );
return null;
}
if( n < 0 || n >= __members.Count ) {
if( raise ) throw new IndexOutOfRangeException( "Index (" + n + ") is invalid." );
return null;
}
return __members[ n ];
}
public DeepObject DeepMember( string name, bool raise = true )
{
name = name.Validated( "Member name" );
if( __members == null ) {
if( raise ) throw new InvalidOperationException( "This DeepObject is not a tree." );
return null;
}
StringComparison comparison = __caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
DeepObject r = __members.Find( x => name.Equals( x.__name, comparison ) );
if( r == null && raise ) throw new KeyNotFoundException( "Cannot find member: " + name );
return r;
}
public DeepObject DeepAddMember( string name )
{
name = name.Validated( "Member name" );
if( __members == null ) __members = new List<DeepObject>();
if( DeepMember( name, raise: false ) != null ) throw new InvalidOperationException( "Member already exists: " + name );
DeepObject r = new DeepObject( name, __caseSensitive ) { __parent = this }; __members.Add( r );
return r;
}
public DeepObject DeepAddIndex( object[] indexes )
{
string name = DeepIndexesToString( indexes );
if( __members == null ) __members = new List<DeepObject>();
if( DeepMember( name, raise: false ) != null ) throw new InvalidOperationException( "Member already exists: " + name );
DeepObject r = new DeepObject( null, __caseSensitive ) { __name = name, __parent = this }; __members.Add( r );
r.__isIndex = true;
return r;
}
public static string DeepIndexesToString( object[] indexes )
{
StringBuilder sb = new StringBuilder( "[" ); if( indexes != null ) {
bool first = true; foreach( object index in indexes ) {
if( !first ) sb.Append( "//" ); else first = false;
sb.AppendFormat( "{0}", index == null ? "NULL" : index.ToString() );
}
}
sb.Append( "]" ); return sb.ToString();
}
public DeepObject DeepRemoveMember( int n )
{
DeepObject r = DeepMember( n, raise: true );
if( r != null ) { __members.Remove( r ); r.__parent = null; }
return r;
}
public DeepObject DeepRemoveMember( string name )
{
DeepObject r = DeepMember( name, raise: true );
if( r != null ) { __members.Remove( r ); r.__parent = null; }
return r;
}
public void DeepClearMembers()
{
if( __members != null ) {
foreach( DeepObject r in __members ) r.__parent = null;
__members.Clear();
__members = null;
}
}
public string DeepToString()
{
StringBuilder sb = new StringBuilder();
if( __name != null || __hasValue ) sb.Append( "[" );
sb.AppendFormat( "{0}", __name );
if( __hasValue ) sb.AppendFormat( ":{0}", __value == null ? "NULL" : __value.ToString() );
if( __members != null ) {
if( __name != null || __hasValue ) sb.Append( ", " );
bool first = true; foreach( DeepObject member in __members ) {
if( !first ) sb.Append( ", " ); else first = false;
sb.Append( member.ToString() );
}
}
if( __name != null || __hasValue ) sb.Append( "]" );
return sb.ToString();
}
public override string ToString()
{
return DeepToString();
}
public bool DeepCloneOnCopying
{
get { return __cloneOnCopying; }
set { __cloneOnCopying = value; }
}
public object Clone()
{
DeepObject cloned = new DeepObject( __name, __caseSensitive );
// The deep value...
if( __hasValue ) {
if( __value != null ) {
if( __value is ICloneable && __cloneOnCopying )
cloned.__value = ( (ICloneable)__value ).Clone();
else cloned.__value = __value;
}
cloned.__hasValue = true;
}
// The deep tree...
if( __members != null ) foreach( DeepObject member in __members ) {
member.__cloneOnCopying = __cloneOnCopying;
DeepObject neo = (DeepObject)member.Clone();
cloned.__members.Add( neo ); neo.__parent = cloned;
}
return cloned;
}
// ----------------------------------------
#region
[Conditional( "UNDER_CONSTRUCTION" )]
static public void TraceBind( string format, params object[] args ) { Console.Write( format, args ); }
#endregion
public override IEnumerable<string> GetDynamicMemberNames()
{
List<string> list = new List<string>(); if( __members != null ) {
foreach( DeepObject member in __members ) list.Add( member.__name );
}
return list;
}
public override bool TryGetMember( GetMemberBinder binder, out object result )
{
// If exists AND is a wrapper, return the value.
// Otherwise, returns the member itself (assuring it is created ad-hoc if needed)
TraceBind( "- GETMEMBER: {0} {1}\n", this.ToString(), binder.Name );
DeepObject member = DeepMember( binder.Name, raise: false );
if( member != null ) {
if( member.DeepIsWrapper() ) result = member.DeepValue();
else result = member;
}
else {
member = DeepAddMember( binder.Name );
result = member;
}
return true;
}
public override bool TrySetMember( SetMemberBinder binder, object value )
{
// Assign the value to the correct member (assuring it is created ad-hoc if needed).
TraceBind( "- SETMEMBER: {0} {1} = {2}\n", this.ToString(), binder.Name, value == null ? "NULL" : value.ToString() );
DeepObject member = DeepMember( binder.Name, raise: false );
if( member == null ) member = DeepAddMember( binder.Name );
member.DeepSetValue( value );
return true;
}
public override bool TryGetIndex( GetIndexBinder binder, object[] indexes, out object result )
{
// If exists AND is a wrapper, return the value.
// Otherwise, returns the member itself (assuring it is created ad-hoc if needed)
// The member's name is composed by a deterministic internal function
string name = DeepIndexesToString( indexes );
TraceBind( "- GETINDEX: {0} {1}\n", this.ToString(), name );
DeepObject member = DeepMember( name, raise: false );
if( member != null ) {
if( member.DeepIsWrapper() ) result = member.DeepValue();
else result = member;
}
else {
member = DeepAddIndex( indexes );
result = member;
}
return true;
}
public override bool TrySetIndex( SetIndexBinder binder, object[] indexes, object value )
{
// Assign the value to the correct member (assuring it is created ad-hoc if needed).
// The member's name is composed by a deterministic internal function
string name = DeepIndexesToString( indexes );
TraceBind( "- SETINDEX: {0} {1}\n", this.ToString(), name, value == null ? "NULL" : value.ToString() );
DeepObject member = DeepMember( name, raise: false );
if( member == null ) member = DeepAddIndex( indexes );
member.DeepSetValue( value );
return true;
}
public override bool TryConvert( ConvertBinder binder, out object result )
{
TraceBind( "- CONVERT: {0} to {1}\n", this.ToString(), binder.ReturnType );
result = TypeHelper.ConvertTo( DeepIsWrapper() ? DeepValue() : this, binder.ReturnType );
return true;
}
public override bool TryInvokeMember( InvokeMemberBinder binder, object[] args, out object result )
{
// If the member holds a delegate to a function, invoke it.
TraceBind( "- INVOKEMEMBER: {0} {1}\n", this.ToString(), binder.Name );
DeepObject member = DeepMember( binder.Name, raise: true );
Delegate del = member.DeepValue() as Delegate;
if( del == null ) throw new InvalidOperationException( "Member '" + binder.Name + "' is not an executable delegate." );
// If the delegate's return type is 'void', returning null...
if( del.GetType().Name.StartsWith( "Action" ) ) { del.DynamicInvoke( args ); result = null; }
else result = del.DynamicInvoke( args );
return true;
}
}
}
// ==============================================