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; }