public static TPropertiesEnum DiffProperties <TPropertiesEnum, TDiffGram>(this ParentedRecursiveType <IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> > self, ParentedRecursiveType <IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> > other) where TPropertiesEnum : struct { TPropertiesEnum changes = self.Value.DiffProperties(other.Value); if ((self.Parent == null ^ other.Parent == null) || (self.Parent != null && other.Parent != null && self.Parent.Identity != other.Parent.Identity)) { changes = self.Value.Union(changes, self.Value.ParentProperty); } else if (self.Value != other.Value && self.Parent != null && other.Parent != null) { var selfParentOrdered = self.Parent as IRecursiveParentWithOrderedChildren; if (selfParentOrdered != null) { var selfParentSorted = selfParentOrdered as IRecursiveParentWithSortedChildren; if (selfParentSorted != null) { if (selfParentSorted.Compare(self.Value, other.Value) != 0) { // Calculate where the node was, and where it would go in the old tree. var otherParentSorted = (IRecursiveParentWithSortedChildren)other.Parent; int beforeIndex = otherParentSorted.IndexOf(other.Value); int afterIndex = ~otherParentSorted.IndexOf(self.Value); // If the indices are the same, the new one would come "just before" the old one. // If the new index is just 1 greater than the old index, the new one would come "just after" the old one. // In either of these cases, since the old one will be gone in the new tree, the position hasn't changed. if (afterIndex != beforeIndex && afterIndex != beforeIndex + 1) { changes = self.Value.Union(changes, self.Value.PositionUnderParentProperty); } } } else { // Calculate whether items were reordered without leveraging a sorting comparer. var otherParentOrdered = (IRecursiveParentWithOrderedChildren)other.Parent; int beforeIndex = otherParentOrdered.IndexOf(other.Value); int afterIndex = selfParentOrdered.IndexOf(self.Value); // TODO: review (and add tests for) cases where items are inserted/removed from the parent, // causing all other items after it to apparently shift in position. We probably don't want // to consider that a change in PositionUnderParent, since the removal or add will take care of it. if (afterIndex != beforeIndex) { changes = self.Value.Union(changes, self.Value.PositionUnderParentProperty); } } } } return(changes); }
public int GetHashCode(ParentedRecursiveType <TRecursiveParent, TRecursiveType> obj) { return((int)obj.Value.Identity); }
public static IReadOnlyList <TDiffGram> ChangesSince <TPropertiesEnum, TDiffGram>(this IRecursiveDiffingType <TPropertiesEnum, TDiffGram> current, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> priorVersion) where TPropertiesEnum : struct where TDiffGram : struct { Requires.NotNull(current, "current"); Requires.NotNull(priorVersion, "priorVersion"); if (current == priorVersion) { return(System.Collections.Immutable.ImmutableList.Create <TDiffGram>()); } if (priorVersion.Identity != current.Identity) { throw new System.ArgumentException("Not another version of the same node.", nameof(priorVersion)); } var currentAsParent = current as IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >; var currentAsRecursiveType = (IRecursiveDiffingType <TPropertiesEnum, TDiffGram>)current; var before = new HashSet <ParentedRecursiveType <IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> > >(Comparers.Parented <IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >()); var after = new HashSet <ParentedRecursiveType <IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> > >(Comparers.Parented <IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >()); var priorVersionAsParent = priorVersion as IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >; if (priorVersionAsParent != null) { before.UnionWith(priorVersionAsParent.GetSelfAndDescendentsWithParents <IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >()); } else { before.Add(priorVersion.WithParent()); } if (currentAsParent != null) { after.UnionWith(currentAsParent.GetSelfAndDescendentsWithParents <IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >()); } else { after.Add(current.WithParent()); } var added = new HashSet <ParentedRecursiveType <IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> > >(Comparers.Parented <IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >()); var removed = new HashSet <ParentedRecursiveType <IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> > >(Comparers.Parented <IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >()); var changed = new Dictionary <ParentedRecursiveType <IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, ParentedRecursiveType <IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> > >(Comparers.Parented <IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >()); var descendentsOfAddOrRemove = new HashSet <IRecursiveType>(Comparers.Identity); foreach (var fromBefore in before) { if (after.Contains(fromBefore)) { ParentedRecursiveType <IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> > fromAfter; if (currentAsParent != null) { var parent = currentAsParent.GetParentedNode(fromBefore.Value.Identity); fromAfter = new ParentedRecursiveType <IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >( (IRecursiveDiffingType <TPropertiesEnum, TDiffGram>)parent.Value, (IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >)parent.Parent); } else { fromAfter = new ParentedRecursiveType <IRecursiveParent <IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >, IRecursiveDiffingType <TPropertiesEnum, TDiffGram> >( fromBefore.Value.Identity == current.Identity ? (IRecursiveDiffingType <TPropertiesEnum, TDiffGram>)current : null); } if (!object.ReferenceEquals(fromBefore.Value, fromAfter.Value) || fromBefore.Parent.Identity != fromAfter.Parent.Identity) { changed.Add(fromBefore, fromAfter); } } else { removed.Add(fromBefore); } } foreach (var fromAfter in after) { if (!before.Contains(fromAfter)) { added.Add(fromAfter); } } foreach (var topLevelOperation in added.Concat(removed)) { descendentsOfAddOrRemove.UnionWith(topLevelOperation.Value.GetSelfAndDescendents().Skip(1)); } var history = new List <TDiffGram>(); history.AddRange(removed.Where(r => !descendentsOfAddOrRemove.Contains(r.Value)).Select(r => currentAsRecursiveType.Remove(r.Value))); foreach (var changedNode in changed) { var oldNode = changedNode.Key; var newNode = changedNode.Value; var diff = newNode.DiffProperties(oldNode); if (!currentAsRecursiveType.Equals(diff, default(TPropertiesEnum))) { history.Add(currentAsRecursiveType.Change(oldNode.Value, newNode.Value, diff)); } } history.AddRange(added.Where(a => !descendentsOfAddOrRemove.Contains(a.Value)).Select(a => currentAsRecursiveType.Add(a.Value))); return(history); }
public bool Equals(ParentedRecursiveType <TRecursiveParent, TRecursiveType> x, ParentedRecursiveType <TRecursiveParent, TRecursiveType> y) { return(x.Value.Identity == y.Value.Identity); }