using System; using System.Collections; using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; using System.Text; using NHibernate.Proxy; namespace Atlanta.Application.Domain.DomainBase { /// <summary> /// Extension class to allow graphing of objects using fluent syntax /// </summary> public static class GraphExtensions { /// <summary> /// Create a Graph<T> for an object"/> /// </summary> public static Graph<T> Graph<T>(this T source) { return new Graph<T>(source); } /// <summary> /// Create a Graph<T> for an IEnumerable object"/> /// </summary> public static Graph<T> GraphList<T>(this IList<T> source) { return new Graph<T>(source); } } /// <summary> /// interface to allow non-generic use of Graph /// </summary> public interface IGraph { /// <summary> Set the source object for this graph </summary> void SetSource(object source); /// <summary> Make a copy of the source object </summary> object Copy(); } /// <summary> /// Class to allow creation of copies of a graph of objects without circular references /// </summary> public class Graph<T> : IGraph { private object _source; private IDictionary<MemberInfo, IGraph> _subGraphs = new Dictionary<MemberInfo, IGraph>(); /// <summary> Constructor </summary> public Graph() { } /// <summary> Constructor </summary> public Graph(T source) { (this as IGraph).SetSource(source); } /// <summary> Constructor </summary> public Graph(IEnumerable<T> source) { (this as IGraph).SetSource(source); } /// <summary> /// Return a copy of the selected graph of objects /// </summary> public T Copy() { DomainRegistry.Repository.Flush(); return (T)((IGraph)this).Copy(); } /// <summary> /// Return a copy of the selected graph of objects as an IList /// </summary> public IList<T> CopyList() { DomainRegistry.Repository.Flush(); return (IList<T>)((IGraph)this).Copy(); } /// <summary> /// Add a copy of the selected property to the graph /// </summary> public Graph<T> Add<U>(Expression<Func<T, U>> property) { return Add(property, new Graph<U>()); } /// <summary> /// Add a copy of the selected property to the graph using the supplied Graph /// </summary> public Graph<T> Add<U>(Expression<Func<T, U>> property, IGraph subGraph) { _subGraphs.Add(FindMember(property.Body), subGraph); return this; } object IGraph.Copy() { if (_source == null) return default(T); if (_source is IList) return CopyIntoNewList(); return CopySingleObject(typeof(T), _source); } void IGraph.SetSource(object source) { if (source is INHibernateProxy) { INHibernateProxy proxy = (INHibernateProxy)source; _source = proxy.HibernateLazyInitializer.GetImplementation(); } else { _source = source; } } private object CopySingleObject(Type type, object source) { object target = Activator.CreateInstance(type, true); CopyValues(source, target); CopySubGraphs(source, target); return target; } private Type FindListType() { if (typeof(T).IsGenericType) return typeof(T).GetGenericArguments()[0]; return typeof(T); } private IList CreateGenericList(Type type) { return null; } private object CopyIntoNewList() { Type targetType = FindListType(); IList listCopy = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(new Type[] { targetType })); foreach (object source in (IList)_source) { listCopy.Add(CopySingleObject(targetType, source)); } return listCopy; } private MemberInfo FindMember(Expression expression) { MemberExpression me = null; if (expression is MemberExpression) me = (MemberExpression)expression; if (expression is UnaryExpression) { UnaryExpression unaryExpression = (UnaryExpression)expression; if (unaryExpression.NodeType != ExpressionType.Convert) throw new Exception("Cannot interpret member from " + expression.ToString()); me = (MemberExpression)unaryExpression.Operand; } if (me == null) throw new Exception("Could not determine member from " + expression.ToString()); return me.Member; } private void CopySubGraphs(object source, object target) { foreach (MemberInfo member in _subGraphs.Keys) { IGraph graph = _subGraphs[member]; if (member is PropertyInfo) { PropertyInfo property = (PropertyInfo)member; if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType)) { PropertyInfo listProperty = property.DeclaringType.GetProperty(property.Name + "List", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (listProperty != null) property = listProperty; } graph.SetSource(property.GetValue(source, null)); property.SetValue(target, graph.Copy(), null); } } } private void CopyValues(object source, object target) { Type targetType = target.GetType(); while (targetType != null) { foreach (FieldInfo field in targetType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { if (field.FieldType.IsValueType || typeof(string).IsAssignableFrom(field.FieldType)) { field.SetValue(target, field.GetValue(source)); } } targetType = targetType.BaseType; } } } }
http://code.google.com/p/atlanta/source/browse/trunk/Source/Application/Domain/DomainBase/Graph.cs