public static DiffTree FromEnumerator(IEnumerator <DiffNode> treeEnumerator, DateTime?timeStamp = null) { DiffNode current = null; while (treeEnumerator.MoveNext()) { current = treeEnumerator.Current; } return(new DiffTree(current, timeStamp ?? DateTime.UtcNow)); }
public DiffTree(DiffNode root, DateTime?timeStamp = null) { Root = root; TimeStamp = timeStamp ?? DateTime.UtcNow; }
public DiffNodeNamePair(DiffNode node, PropertyName name, bool allowReflection) { Node = node; Name = name; AllowReflection = allowReflection; }
public static LogMessage ToMessage(DiffNode node, PropertyName name, LogLevel level, bool allowReflection) { return(node.ToMessage(name, level, allowReflection)); }
public DiffNodeNamePair FindFirstMultiChildParent(DiffTree tree, PropertyName name, bool shortenName, bool allowReflection, DiffNode parentNode = null) { var oneNode = Nodes.Count == 1; var propertyNameString = Property.GetName(tree.Root, this, parentNode); // Collection elements should be referred to by their name or string representation var propName = IsCollectionElement ? new PropertyElementName(ObjectToString(allowReflection)) : (Property.IsTab ? new PropertyTabName(propertyNameString) : new PropertyName(propertyNameString)); // If the node can't be displayed and its name cant be ignored, // we can't go further down the tree. This can happen theoretically but hasn't occured anywhere yet if (propName.Name == null && !Property.IgnoreName) { return(new DiffNodeNamePair(parentNode?.ChangeExpanded(false), name, allowReflection)); } var newName = name.SubProperty(propName); var elemName = Property.GetElementName(); if (shortenName && Property.GetElementName() != null && !IsCollectionElement) { if (oneNode) { // Remove everything from the path and replace it with the element name, e.g "Settings > DataSettings > GroupComparisons" // becomes "GroupComparison:" newName = PropertyName.ROOT.SubProperty(new PropertyElementName(elemName)); } else { // Multiple changes have been made to the collection newName = propName; } } if (Property.IgnoreName && !IsCollectionElement) { newName = name; } var objects = Objects.Select(AuditLogObject.GetAuditLogObject) .Where(o => o == null || o.IsName).ToArray(); if (objects.Length == 2) { var type = Property.GetPropertyType(ObjectPair.Create(objects[1], objects[0])); if (((objects[0] != null) != (objects[1] != null) || (objects[0] != null && objects[1] != null && objects[0].AuditLogText != objects[1].AuditLogText && !typeof(DocNode).IsAssignableFrom(type))) && !Property.IsRoot) { oneNode = false; // Stop recursion, since in the undo-redo/summary log we don't want to go deeper for objects where the name changed } } return(oneNode && !IsFirstExpansionNode ? Nodes[0].FindFirstMultiChildParent(tree, newName, shortenName, allowReflection, this) : new DiffNodeNamePair(ChangeExpanded(false), newName, allowReflection)); }
public List <DiffNodeNamePair> FindAllLeafNodes(DiffTree tree, PropertyName name, bool allowReflection, DiffNode parentNode = null) { var result = new List <DiffNodeNamePair>(); var propertyNameString = Property.GetName(tree.Root, this, parentNode); var isName = false; // Collection elements should be referred to by their name or string representation var propName = IsCollectionElement ? new PropertyElementName(ObjectToString(allowReflection, Objects.FirstOrDefault(o => o != null), out isName)) : (Property.IsTab ? new PropertyTabName(propertyNameString) : new PropertyName(propertyNameString)); // The name can not be ignored if the node is a collection change (since then the child nodes of the collection change // have the same attribute as the collection change node) var canIgnoreName = (Property.IgnoreName && !IsCollectionElement); if (!canIgnoreName) { name = name != null?name.SubProperty(propName) : propName; } // We can't display sub changes of an element if it's unnamed, so we display its // string representation as a change if (IsCollectionElement && !isName && !canIgnoreName) { result.Add(new DiffNodeNamePair(this, name, allowReflection)); return(result); } var obj = Objects.FirstOrDefault(); var isNamedChange = IsFirstExpansionNode || (obj != null && AuditLogObject.IsNameObject(obj)) && Expanded && !canIgnoreName; if (isNamedChange) { result.Add(new DiffNodeNamePair(this, name, allowReflection)); } if (Nodes.Count == 0) { if (!isNamedChange) { result.Add(new DiffNodeNamePair(this, name, allowReflection)); } } else { var collectionPropDiffNode = this as CollectionPropertyDiffNode; if (collectionPropDiffNode != null && collectionPropDiffNode.RemovedAll) { result.Add(new DiffNodeNamePair(this, name, allowReflection)); } else { foreach (var n in Nodes) { result.AddRange(n.FindAllLeafNodes(tree, name, allowReflection, this)); } } } return(result); }
/// <summary> /// Converts the given object to a string, showing each of its properties values. /// </summary> /// <param name="rootPair">old and new document, can be null</param> /// <param name="rootNode">diff node describing root object change</param> /// <param name="state">describes how to format the string</param> /// <returns>String representation</returns> public static string ToString(ObjectPair <object> rootPair, DiffNode rootNode, ToStringState state) { return(Reflector.ToString(rootPair, rootNode, null, state).Trim()); }
public string GetName(ObjectPair <object> docPair, DiffNode node, DiffNode parent) { return(GetName(new ObjectInfo <object>(node.Objects.LastOrDefault(), node.Objects.FirstOrDefault(), parent != null ? parent.Objects.LastOrDefault() : null, parent != null ? parent.Objects.FirstOrDefault() : null, docPair.OldObject, docPair.NewObject))); }
public string GetName(DiffNode root, DiffNode node, DiffNode parent) { return(GetName(ObjectPair.Create(root.Objects.LastOrDefault(), root.Objects.FirstOrDefault()), node, parent)); }
/// <summary> /// Converts the given object to a string, showing each of its properties values. /// </summary> /// <param name="rootPair">old and new document, can be null</param> /// <param name="docType">May determine whether human readable version requires "peptide"->"molecule" translation</param> // CONSIDER: does this belong in ToStringState? /// <param name="rootNode">diff node describing root object change</param> /// <param name="state">describes how to format the string</param> /// <returns>String representation</returns> public static string ToString(ObjectPair <object> rootPair, SrmDocument.DOCUMENT_TYPE docType, DiffNode rootNode, ToStringState state) { return(Reflector.ToString(rootPair, docType, rootNode, null, state).Trim()); }
// TODO: make this function and its overloads in nongeneric Reflector class simpler by introducing new class storing parameters private static IEnumerator <DiffNode> EnumerateDiffNodes(ObjectInfo <object> objectInfo, Property thisProperty, bool expand, PropertyPath propertyPath, object elementKey, IList <object> defaults, Func <DiffNode, bool> nodeSelector, DiffNode resultNode, int stackDepth) { nodeSelector = nodeSelector ?? (n => true); if (objectInfo.ObjectPair.ReferenceEquals() && !expand) { yield break; } resultNode = resultNode ?? (thisProperty.IsCollectionElement ? new ElementPropertyDiffNode(thisProperty, propertyPath, objectInfo.ObjectPair, elementKey, null, expand) : new PropertyDiffNode(thisProperty, propertyPath, objectInfo.ObjectPair, null, expand)); var expandAnyways = false; var auditObjPair = objectInfo.ObjectPair.Transform(AuditLogObject.GetAuditLogObject); var nameChanged = auditObjPair.OldObject.IsName && auditObjPair.NewObject.IsName && auditObjPair.OldObject.AuditLogText != auditObjPair.NewObject.AuditLogText; if (ReferenceEquals(objectInfo.OldObject, null) && !ReferenceEquals(objectInfo.NewObject, null) && auditObjPair.NewObject.IsName || nameChanged && // IIdentiyContainer name changes are actually displayed, since we match IIdentiyContainers by their global indices !typeof(IIdentiyContainer).IsAssignableFrom(thisProperty.GetPropertyType(objectInfo.ObjectPair))) { if (!thisProperty.IsRoot) { if (!expand) { resultNode.IsFirstExpansionNode = true; } expandAnyways = expand = true; } } // If the object is an IAuditLogObject but not a name one, we don't care about subproperties //if (expand && typeof(IAuditLogObject).IsAssignableFrom(thisProperty.GetPropertyType(objectInfo.ObjectPair)) && // !auditObjPair.OldObject.IsName && !auditObjPair.NewObject.IsName) // expand = false; defaults = defaults ?? new List <object>(); if (expand && Reflector.ProcessDefaults(objectInfo, thisProperty, ref defaults, out var ignore)) { // Don't expand if we changed to a default object if (expandAnyways && !ignore && nodeSelector(resultNode)) // Only show this object if it shouldn't be fully ignored if it's a default object (for instance: small molecule only properties) { yield return(resultNode); } yield break; } // We only compare sub properties if both objects are non null, unless we're expanding // and only the old object is null if (ReferenceEquals(objectInfo.OldObject, null) && !expand || ReferenceEquals(objectInfo.NewObject, null)) { if (nodeSelector(resultNode)) { yield return(resultNode); } yield break; } // Deal with Collections var collection = Reflector.GetCollectionInfo(thisProperty.GetPropertyType(objectInfo.ObjectPair), objectInfo.ObjectPair); if (collection != null) { var nodeIter = Reflector.EnumerateCollectionDiffNodes(objectInfo.ChangeObjectPair(collection.Collections), collection, thisProperty, propertyPath, expand, defaults, nodeSelector, resultNode, stackDepth); while (nodeIter.MoveNext()) { yield return(nodeIter.Current); } yield break; } // Properties that should directly be checked for equality -- stop recursion if (!thisProperty.DiffProperties) { // Properties that are not TrackParents and not collections are simply compared // Also make sure that new val doesn't equal any of the default objects if (nodeSelector(resultNode) && (!objectInfo.ObjectPair.Equals() || expand) && (!expand || thisProperty.IgnoreDefaultParent || !defaults.Any(d => Equals(d, objectInfo.ObjectPair.NewObject)))) { yield return(resultNode); } yield break; } objectInfo = objectInfo.ChangeParentPair(objectInfo.ObjectPair); // Compare properties foreach (var property in _properties) { var newPropertyPath = property.AddProperty(propertyPath); var newObjectInfo = objectInfo .ChangeOldObject(ReferenceEquals(objectInfo.OldObject, null) ? null : property.GetValue(objectInfo.OldObject)) .ChangeNewObject(property.GetValue(objectInfo.NewObject)); var valType = property.GetPropertyType(newObjectInfo.ObjectPair); var nodeIter = Reflector.EnumerateDiffNodes(valType, newObjectInfo, property, expand, newPropertyPath, null, defaults, nodeSelector, null, stackDepth); DiffNode current = null; while (nodeIter.MoveNext()) { current = nodeIter.Current; yield return(current); } if (current != null) { resultNode.Nodes.Add(current); } } // If there are child nodes, we always want to return the parent node, // but if it's just the root node or a diffparent (meaning only the object reference changed) // we return null (meaning the objects are equivalent) if (resultNode.Nodes.Count == 0 && thisProperty.DiffProperties && !expandAnyways) { yield break; } if (nodeSelector(resultNode)) { yield return(resultNode); } }