public override GraphNode CreateObjectGraph(object value)
        {
            // Queue of pending nodes
            Queue<GraphNode> pendingQueue = new Queue<GraphNode>();

            // Dictionary of < object hashcode, node > - to lookup already visited objects
            Dictionary<int, GraphNode> visitedObjects = new Dictionary<int, GraphNode>();

            // Build the root node and enqueue it
            GraphNode root = new GraphNode()
            {
                Name = "RootObject",
                ObjectValue = value,
            };

            pendingQueue.Enqueue(root);

            while (pendingQueue.Count != 0)
            {
                GraphNode currentNode = pendingQueue.Dequeue();
                object nodeData = currentNode.ObjectValue;
                Type nodeType = currentNode.ObjectType;

                // If we have reached a leaf node -
                // no more processing is necessary
                if (nodeData == null || nodeData.GetType().IsPrimitive)
                {
                    continue;
                }

                // Handle loops by checking the visted objects
                if (visitedObjects.Keys.Contains(nodeData.GetHashCode()))
                {
                    // Caused by a cycle - we have alredy seen this node so
                    // use the existing node instead of creating a new one
                    GraphNode prebuiltNode = visitedObjects[nodeData.GetHashCode()];
                    currentNode.Children.Add(prebuiltNode);
                    continue;
                }
                else
                {
                    visitedObjects.Add(nodeData.GetHashCode(), currentNode);
                }

                // Extract and add child nodes for current object //
                Collection<GraphNode> childNodes = GetChildNodes(nodeData);
                foreach (GraphNode childNode in childNodes)
                {
                    childNode.Parent = currentNode;
                    currentNode.Children.Add(childNode);

                    pendingQueue.Enqueue(childNode);
                }
            }

            return root;
        }
 private bool IsAllowed(IEnumerable<PropertyComparator> comparators, GraphNode leftNode, GraphNode rightNode)
 {
     if (comparators.Select(c => c.Property).Contains(rightNode.Property))
     {
         PropertyComparator comparator =
             comparators.First(c => c.Property == rightNode.Property);
         return ((bool)comparator.Comparator.DynamicInvoke(leftNode.ObjectValue, rightNode.ObjectValue));
     }
     return false;
 }
        private bool IsAllowed(GraphNode leftNode, GraphNode rightNode)
        {
            if (leftNode == null || rightNode == null)
                return false;

            if (IsAllowed(_customTypePropertyComparisons, leftNode, rightNode))
                return true;

            if(rightNode.ObjectValue != null &&
               rightNode.Parent != null &&
               _customInstancePropertyComparisons.ContainsKey(rightNode.Parent.ObjectValue))
            {
                List<PropertyComparator> comparators = _customInstancePropertyComparisons[rightNode.Parent.ObjectValue];
                if (IsAllowed(comparators, leftNode, rightNode))
                    return true;
            }
            return false;
        }
 private List<MemberInfo> FindPropertiesToIgnore(GraphNode node)
 {
     var propertiesToIgnore = new List<MemberInfo>();
     IEnumerable<MemberInfo> properties =
         node.ObjectType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
     propertiesToIgnore.AddRange(properties.Intersect(_typePropertiesToIgnore));
     if (node.ObjectValue != null && _instancePropertiesToIgnore.ContainsKey(node.ObjectValue))
     {
         IEnumerable<MemberInfo> instanceProperties = _instancePropertiesToIgnore[node.ObjectValue];
         propertiesToIgnore.AddRange(properties.Intersect(instanceProperties));
     }
     return propertiesToIgnore;
 }
        private ObjectComparisonMismatch CompareNodes(GraphNode leftNode, GraphNode rightNode)
        {
            // Check if there is the node difference is within the allowed range
            if (IsAllowed(leftNode, rightNode))
                return null;

            // Check if both are null
            if (IsNull(leftNode) && IsNull(rightNode))
                return null;

            // Check if left is null and right is not
            if (IsNull(leftNode))
                return new ObjectComparisonMismatch(leftNode,
                                                    rightNode,
                                                    ObjectComparisonMismatchType.MissingLeftNode);

            // Check if right is null and left is not
            if (IsNull(rightNode))
                return new ObjectComparisonMismatch(leftNode,
                                                    rightNode,
                                                    ObjectComparisonMismatchType.MissingRightNode);

            // Compare type names of the properties
            if (!leftNode.ObjectType.Equals(rightNode.ObjectType))
                return new ObjectComparisonMismatch(leftNode,
                                                    rightNode,
                                                    ObjectComparisonMismatchType.ObjectTypesDoNotMatch);

            // Compare type names of instances
            if (leftNode.ObjectValue.GetType() != rightNode.ObjectValue.GetType())
                return new ObjectComparisonMismatch(leftNode,
                                                    rightNode,
                                                    ObjectComparisonMismatchType.ObjectTypesDoNotMatch);

            // Compare primitives, strings
            if (leftNode.ObjectType.IsPrimitive || leftNode.ObjectType.IsValueType || leftNode.ObjectType == typeof(string))
            {
                if (!leftNode.ObjectValue.Equals(rightNode.ObjectValue))
                    return new ObjectComparisonMismatch(leftNode,
                                                        rightNode,
                                                        ObjectComparisonMismatchType.ObjectValuesDoNotMatch);
                return null;
            }

            // Compare the child count
            if (leftNode.Children.Count != rightNode.Children.Count)
            {
                var mismatchType = leftNode.Children.Count > rightNode.Children.Count ?
                                   ObjectComparisonMismatchType.RightNodeHasFewerChildren :
                                   ObjectComparisonMismatchType.LeftNodeHasFewerChildren;

                return new ObjectComparisonMismatch(leftNode,
                                                    rightNode,
                                                    mismatchType);
            }

            return null;
        }
 private static bool IsNull(GraphNode node)
 {
     return node == null || node.ObjectValue == null;
 }
 private static Queue<GraphNode> CreatePendingQueue(GraphNode root)
 {
     var queue = new Queue<GraphNode>();
     queue.Enqueue(root);
     return queue;
 }
 /// <summary>
 /// Creates an instance of the ObjectComparisonMismatch class.
 /// </summary>
 /// <param name="leftObjectNode">The node from the left object.</param>
 /// <param name="rightObjectNode">The node from the right object.</param>
 /// <param name="mismatchType">Represents the type of mismatch.</param>
 public ObjectComparisonMismatch(GraphNode leftObjectNode, GraphNode rightObjectNode, ObjectComparisonMismatchType mismatchType)
 {
     this.leftObjectNode = leftObjectNode;
     this.rightObjectNode = rightObjectNode;
     this.mismatchType = mismatchType;
 }
        private static string StringFromGraph(GraphNode graph)
        {
            StringBuilder stringBuilder = new StringBuilder();
            IEnumerable<GraphNode> nodes = graph.GetNodesInDepthFirstOrder();
            foreach (GraphNode node in nodes)
            {
                string type = "Null";
                if (node.ObjectValue != null)
                {
                    type = node.ObjectType.FullName;
                }

                stringBuilder.AppendLine("".PadLeft(node.Depth*4) + node.Name + "Value = '" + node.ObjectValue + "'" + " Type=" + type);
            }
            return stringBuilder.ToString();
        }
        private ObjectComparisonMismatch CompareNodes(GraphNode leftNode, GraphNode rightNode)
        {
            if (IsAllowed(leftNode, rightNode))
                return null;

            // Check if both are null
            if (leftNode.ObjectValue == null && rightNode.ObjectValue == null)
                return null;

            // Check if left is null and right is not
            if (leftNode.ObjectValue == null)
                return new ObjectComparisonMismatch(leftNode,
                                                    rightNode,
                                                    ObjectComparisonMismatchType.MissingLeftNode);

            // Check if left is null and right is not
            if (rightNode.ObjectValue == null)
                return new ObjectComparisonMismatch(leftNode,
                                                    rightNode,
                                                    ObjectComparisonMismatchType.MissingRightNode);

            // Compare type names //
            if (!leftNode.ObjectType.Equals(rightNode.ObjectType))
                return new ObjectComparisonMismatch(leftNode,
                                                    rightNode,
                                                    ObjectComparisonMismatchType.ObjectTypesDoNotMatch);

            // Compare primitives, strings
            if (leftNode.ObjectType.IsPrimitive || leftNode.ObjectType.IsValueType || leftNode.ObjectType == typeof(string))
            {
                if (!leftNode.ObjectValue.Equals(rightNode.ObjectValue))
                    return new ObjectComparisonMismatch(leftNode,
                                                        rightNode,
                                                        ObjectComparisonMismatchType.ObjectValuesDoNotMatch);
                return null;
            }

            // Compare the child count
            if (leftNode.Children.Count != rightNode.Children.Count)
            {
                var mismatchType = leftNode.Children.Count > rightNode.Children.Count ?
                                   ObjectComparisonMismatchType.RightNodeHasFewerChildren :
                                   ObjectComparisonMismatchType.LeftNodeHasFewerChildren;

                return new ObjectComparisonMismatch(leftNode,
                                                    rightNode,
                                                    mismatchType);
            }

            return null;
        }