com.day.util.diff.DocumentDiff3 Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
The newest version!
/* * * ADOBE CONFIDENTIAL * __________________ * * Copyright 2017 Adobe Systems Incorporated * All Rights Reserved. * * NOTICE: All information contained herein is, and remains * the property of Adobe Systems Incorporated and its suppliers, * if any. The intellectual and technical concepts contained * herein are proprietary to Adobe Systems Incorporated and its * suppliers and are protected by trade secret or copyright law. * Dissemination of this information or reproduction of this material * is strictly forbidden unless prior written permission is obtained * from Adobe Systems Incorporated. */ package com.day.util.diff; import java.io.IOException; import java.io.StringWriter; /** * Implements a tree-way diff between a base document and 2 derived ones. * The result of the diff operation is a list of {@link Hunk3} that can * record the changes and conflicts. */ public class DocumentDiff3 { /** * the base document */ private final Document base; /** * the left document */ private final Document left; /** * the right documents */ private final Document right; /** * the script of changes from the base to the left document */ private final Diff.Change leftChanges; /** * the script of changes from the base to the right document */ private final Diff.Change rightChanges; /** * the chain of {@link Hunk3}s of this diff. this hunk here is just a * sentinel so that the chain is never empty. */ private final Hunk3 hunks = new Hunk3(null, null, null, null); /** * flag that indicates that this diff has conflicts */ private boolean hasConflicts; /** * Creates a new document diff 3 object. * @param base the base document * @param left the left document * @param right the right document */ public DocumentDiff3(Document base, Document left, Document right) { this.base = base; this.left = left; this.right = right; Document.Element[] baseElems = base.getElements(); Document.Element[] leftElems = left.getElements(); Document.Element[] rightElems = right.getElements(); leftChanges = new Diff(baseElems, leftElems).diff_2(false); rightChanges = new Diff(baseElems, rightElems).diff_2(false); initHunks(); } /** * Returns a chain of {@link Hunk3}s that contain the modifications. * @return a chain of {@link Hunk3}s. */ public Hunk3 getHunks() { return hunks.next(); } /** * Indicates if any of the hunks has a conflict. * @return
if this is a deletion. */ public boolean isDelete() { return low1 == high1; } /** * Checks if this is an insertion * @returntrue
if any of the hunks has a conflict. */ public boolean hasConflicts() { return hasConflicts; } /** * Writes the resulting document to the given string buffer. this may include * conflicting regions. * * @param buf the string buffer to write to * @param lineSeparator the line separator to use * @param showBase if set totrue
the base section of a conflict * is also included in the output. */ public void write(StringBuffer buf, String lineSeparator, boolean showBase) { try { StringWriter w = new StringWriter(); write(new DiffWriter(w, lineSeparator), showBase); buf.append(w.getBuffer()); } catch (IOException e) { throw new IllegalStateException(e.toString()); } } /** * Writes the resulting document to the given write. this may include * conflicting regions. * * @param w the writer to write to * @param showBase if set totrue
the base section of a conflict * is also included in the output. * @throws IOException if an I/O error occurs */ public void write(DiffWriter w, boolean showBase) throws IOException { for (Hunk3 hunk = hunks.next(); hunk != null; hunk = hunk.next()) { hunk.write(w, showBase); } w.flush(); } /** * initializes the hunks */ private void initHunks() { MyChange[] changes = { wrap(leftChanges), wrap(rightChanges) }; int basePos = 0; Hunk3 hunk = hunks; // the last hunk while (changes[0] != null || changes[1] != null) { MyChange[] using = {null, null}; MyChange[] lastUsing = {null, null}; int baseIdx; if (changes[0] == null) { baseIdx = 1; } else if (changes[1] == null) { baseIdx = 0; } else { // use the change that is smaller baseIdx = changes[0].low0 < changes[1].low0 ? 0 : 1; } int highIdx = baseIdx; int highMark = changes[highIdx].high0; // add the change to the using set and unchain it using[highIdx] = lastUsing[highIdx] = changes[highIdx]; changes[highIdx] = changes[highIdx].next; lastUsing[highIdx].next = null; int otherIdx = highIdx^1; MyChange other = changes[otherIdx]; // search for region that ends in a 'void' of changes // i.e. when the end of the 'other' is greater than the high mark. // // a a a // a a b a b // b b a // b while (other != null && other.low0 <= highMark) { // add this change to the using set if (using[otherIdx] == null) { using[otherIdx] = other; } else { using[otherIdx].next = other; } lastUsing[otherIdx] = other; // advance other and unchain it changes[otherIdx] = changes[otherIdx].next; other.next = null; // if the high mark is beyond the end of the other diff // we're finished if (other.high0 > highMark) { // switch roles highIdx ^= 1; highMark = other.high0; } otherIdx = highIdx^1; other = changes[otherIdx]; } // now build the hunks from the set of changes in 'using' // first deal with the stuff that was common before the first change int lowMark = using[baseIdx].low0; if (basePos < lowMark) { hunk = new Hunk3( new Range(base, basePos, lowMark), null, null, hunk); basePos = lowMark; //System.out.println(hunks.getLast().toString()); } // get the ranges for the changsets int[] deltaLow = {0,0}; if (using[baseIdx^1] != null) { deltaLow[baseIdx^1] = using[baseIdx^1].low0 - using[baseIdx].low0; } int[] deltaHigh = {0,0}; if (using[highIdx^1] != null) { deltaHigh[highIdx^1] = using[highIdx].high0 - using[highIdx^1].high0; } Range leftRange = null; Range rightRange = null; if (using[0] != null) { leftRange = new Range(left, using[0].low1 - deltaLow[0], lastUsing[0].high1 + deltaHigh[0]); } if (using[1] != null) { rightRange = new Range(right, using[1].low1 - deltaLow[1], lastUsing[1].high1 + deltaHigh[1]); } // check if the conflict is really one boolean conflict = false; if (leftRange != null && rightRange != null) { if (leftRange.len() == rightRange.len()) { for (int i=0; i< leftRange.len(); i++) { if (!left.getElements()[leftRange.low + i].equals(right.getElements()[rightRange.low + i])) { // yes, it is conflict = true; break; } } // if all lines match, we can discard one of the ranges if (!conflict) { rightRange = null; } } else { conflict = true; } } // get the range for the base int baseHigh = using[highIdx].high0; Range baseRange = new Range(base, basePos, baseHigh); basePos = baseHigh; // and create new hunk hunk = new Hunk3(baseRange, leftRange, rightRange, hunk); hasConflicts |= conflict; //System.out.println(hunks.getLast().toString()); } /* while */ // deal with last hunk if (basePos < base.getElements().length) { new Hunk3( new Range(base, basePos, base.getElements().length), null, null, hunk); //System.out.println(hunks.getLast().toString()); } } /** * Wraps a chain of {@link Diff.Change}s with a list of {@link MyChange}s. * this is rather a convencience wrapping so that the algorithm above is * easier to understand. * * @param df the chain of changes * @return the wrapped chain of changes */ private MyChange wrap(Diff.Change df) { // init sentinel MyChange first = null; MyChange c = null; while (df != null) { c = new MyChange(df.line0, df.line1, df.deleted, df.inserted, c); if (first == null) { first = c; } df = df.nextChange; } return first; } /** * Dumps a chain of my changes. * @param c the change * @param left the left doc * @param right the right doc */ private void dump(MyChange c, Document left, Document right) { while (c != null) { if (c.isInsert()) { for (int i=0; itrue false
if this is an insertion. */ public boolean isInsert() { return low0 == high0; } /** * Returns a debug string for this change. * @return a debug string for this change. */ public String toString() { return "(" + low0 + "-" + high0 + "),(" + low1 + "-" + high1 + ")"; } } }