private IEnumerable <DocumentChange <T> > GetForwardChanges(DocumentVersion <T> other) { // Return changes from this(inclusive) to other(exclusive). for (DocumentVersion <T> node = this; node != other; node = node.next) { foreach (DocumentChange <T> change in node.changes) { yield return(change); } } }
/// <summary> /// Compares the age of this checkpoint to the other checkpoint. /// </summary> /// <remarks>This method is thread-safe.</remarks> /// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this version.</exception> /// <returns>-1 if this version is older than <paramref name="other"/>. /// 0 if <c>this</c> version instance represents the same version as <paramref name="other"/>. /// 1 if this version is newer than <paramref name="other"/>.</returns> public int CompareAge(DocumentVersion <T> other) { if (other == null) { throw new ArgumentNullException("other"); } DocumentVersion <T> o = other as DocumentVersion <T>; if (o == null || provider != o.provider) { throw new ArgumentException("Versions do not belong to the same document"); } // We will allow overflows, but assume that the maximum distance between checkpoints is 2^31-1. // This is guaranteed on x86 because so many checkpoints don't fit into memory. return(Math.Sign(unchecked (this.id - o.id))); }
/// <summary> /// Gets the changes from this checkpoint to the other checkpoint. /// If 'other' is older than this checkpoint, an exception is thrown. /// </summary> /// <remarks>This method is thread-safe.</remarks> /// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this checkpoint, or 'other' is older than this checkpoint.</exception> public IEnumerable <DocumentChange <T> > GetChangesTo(DocumentVersion <T> other) { int result = CompareAge(other); if (result < 0) { return(GetForwardChanges(other)); } else if (result > 0) { return(other.GetForwardChanges(this).Reverse().Select(change => change.Invert())); } else { return(NO_CHANGES); } }
public DocumentChangedEvent(DocumentVersion <T> before, DocumentVersion <T> after) { DocumentVersionBefore = before; DocumentVersionAfter = after; }
/// <summary> /// Gets whether this checkpoint belongs to the same document as the other checkpoint. /// </summary> /// <remarks> /// Returns false when given <c>null</c>. /// </remarks> public bool BelongsToSameDocumentAs(DocumentVersion <T> other) { DocumentVersion <T> o = other as DocumentVersion <T>; return(o != null && provider == o.provider); }
/// <summary> /// New version of an existing document /// </summary> internal DocumentVersion(DocumentVersion <T> prev) { this.provider = prev.provider; this.id = unchecked (prev.id + 1); }
/// <summary> /// Get changes between older version v1 and more recent version v2 : /// - with redundant changes applied on the same line merged in one single change /// - with line indexes translated to be valid in the v2 document /// - with changes sorted in the order of the line indexes in the v2 document /// </summary> public IList <DocumentChange <T> > GetReducedAndOrderedChangesInNewerVersion(DocumentVersion <T> other) { // Ensure the version received as parameter is more recent than teh current version int result = CompareAge(other); if (result >= 0) { throw new InvalidOperationException("other version must be more recent than this version"); } // Merge the redundant changes and translate the line indexes to the last version List <DocumentChange <T> > reducedDocumentChanges = new List <DocumentChange <T> >(); foreach (DocumentChange <T> documentChange in GetChangesTo(other)) { switch (documentChange.Type) { case DocumentChangeType.DocumentCleared: // Ignore all previous document changes : they are meaningless now that the document was completely cleared reducedDocumentChanges.Clear(); // Add the clear event reducedDocumentChanges.Add(documentChange); break; case DocumentChangeType.LineInserted: // Recompute the line indexes of all the changes prevously applied foreach (DocumentChange <T> documentChangeToAdjust in reducedDocumentChanges) { if (documentChangeToAdjust.LineIndex >= documentChange.LineIndex) { documentChangeToAdjust.LineIndex = documentChangeToAdjust.LineIndex + 1; } } // Add the insert change reducedDocumentChanges.Add(documentChange); break; case DocumentChangeType.LineUpdated: // Check to see if this change can be merged with a previous one bool changeAlreadyApplied = false; foreach (DocumentChange <T> documentChangeToAdjust in reducedDocumentChanges) { if (documentChangeToAdjust.LineIndex == documentChange.LineIndex) { changeAlreadyApplied = true; break; } } if (!changeAlreadyApplied) { // Add the update change reducedDocumentChanges.Add(documentChange); } break; case DocumentChangeType.LineRemoved: // Recompute the line indexes of all the changes prevously applied DocumentChange <T> documentChangeToRemove = null; foreach (DocumentChange <T> documentChangeToAdjust in reducedDocumentChanges) { if (documentChangeToAdjust.LineIndex > documentChange.LineIndex) { documentChangeToAdjust.LineIndex = documentChangeToAdjust.LineIndex - 1; } else if (documentChangeToAdjust.LineIndex == documentChange.LineIndex) { documentChangeToRemove = documentChangeToAdjust; } } // Ignore all previous changes applied to a line now removed if (documentChangeToRemove != null) { reducedDocumentChanges.Remove(documentChangeToRemove); } break; } } // Sort all changes by line index reducedDocumentChanges.Sort((documentChange1, documentChange2) => documentChange1.LineIndex - documentChange2.LineIndex); return(reducedDocumentChanges); }