/** * If possible, append a few lines of unchanged text that appears after to the changed line in * order to add context to the current list of WikiDiff objects. * * @param currentDiff The current diff object. * @param nextDiff The diff object that immediately follows this object (if any). * @param oldArray The original array of string objects that was compared from in order to * generate the diff. * @param newArray The original array of string objects that was compared to in order to generate * the diff. * @param bufferAmount The number of unchanged elements to display after the diff, or -1 if all * unchanged lines should be displayed. */ private static List<WikiDiff> postBufferDifference( Difference currentDiff, Difference nextDiff, String[] oldArray, String[] newArray, int bufferAmount) { List<WikiDiff> wikiDiffs = new ArrayList<WikiDiff>(); if (bufferAmount == 0) { // do not buffer return wikiDiffs; } int deletedCurrent = (currentDiff.getDeletedEnd() == -1) ? currentDiff.getDeletedStart() : (currentDiff.getDeletedEnd() + 1); int addedCurrent = (currentDiff.getAddedEnd() == -1) ? currentDiff.getAddedStart() : (currentDiff.getAddedEnd() + 1); int numIterations = bufferAmount; if (bufferAmount == -1) { // buffer everything numIterations = (nextDiff != null) ? Math.max( nextDiff.getAddedStart() - addedCurrent, nextDiff.getDeletedStart() - deletedCurrent) : Math.max(oldArray.length - deletedCurrent, newArray.length - addedCurrent); } String oldText = null; String newText = null; for (int i = 0; i < numIterations; i++) { int position = (deletedCurrent < 0) ? 0 : deletedCurrent; oldText = null; newText = null; if (canPostBuffer(nextDiff, deletedCurrent, oldArray, false)) { oldText = oldArray[deletedCurrent]; deletedCurrent++; } if (canPostBuffer(nextDiff, addedCurrent, newArray, true)) { newText = newArray[addedCurrent]; addedCurrent++; } if (oldText == null && newText == null) { logger.fine( "Possible DIFF bug: no elements post-buffered. position: " + position + " / deletedCurrent: " + deletedCurrent + " / addedCurrent " + addedCurrent + " / numIterations: " + numIterations); break; } wikiDiffs.add(new WikiDiff(oldText, newText, position)); } return wikiDiffs; }
/** Utility method for determining whether or not to prepend lines of context around a diff. */ private static boolean canPreBuffer( Difference previousDiff, int current, int currentStart, String[] replacementArray, int bufferAmount, boolean adding) { if (current < 0 || current >= replacementArray.length) { // current position is out of range for buffering return false; } if (previousDiff == null) { // if no previous diff, buffer away return true; } if (bufferAmount == -1) { // if everything is being buffered and there was a previous diff do not pre-buffer return false; } int previousEnd = (adding) ? previousDiff.getAddedEnd() : previousDiff.getDeletedEnd(); if (previousEnd != -1) { // if there was a previous diff but it was several lines previous, buffer away. // if there was a previous diff, and it overlaps with the current diff, don't buffer. return (current > (previousEnd + bufferAmount)); } int previousStart = (adding) ? previousDiff.getAddedStart() : previousDiff.getDeletedStart(); if (current <= (previousStart + bufferAmount)) { // the previous diff did not specify an end, and the current diff would overlap with // buffering from its start, don't buffer return false; } // the previous diff did not specify an end, and the current diff will not overlap // with buffering from its start, buffer away. otherwise the default is not to buffer. return (currentStart > current); }
private static boolean hasMoreDiffInfo( int addedCurrent, int deletedCurrent, Difference currentDiff) { if (addedCurrent == -1) { addedCurrent = 0; } if (deletedCurrent == -1) { deletedCurrent = 0; } return (addedCurrent <= currentDiff.getAddedEnd() || deletedCurrent <= currentDiff.getDeletedEnd()); }
/** * Process the diff object and add it to the output. Text will either have been deleted or added * (it cannot have remained the same, since a diff object represents a change). This method steps * through the diff result and converts it into an array of objects that can be used to easily * represent the diff. */ private static List<WikiDiff> processDifference( Difference currentDiff, String[] oldArray, String[] newArray) { List<WikiDiff> wikiDiffs = new ArrayList<WikiDiff>(); // if text was deleted then deletedCurrent represents the starting position of the deleted text. int deletedCurrent = currentDiff.getDeletedStart(); // if text was added then addedCurrent represents the starting position of the added text. int addedCurrent = currentDiff.getAddedStart(); // count is simply used to ensure that the loop is not infinite, which should never happen int count = 0; // the text of the element that changed String oldText = null; // the text of what the element was changed to String newText = null; while (hasMoreDiffInfo(addedCurrent, deletedCurrent, currentDiff)) { // the position within the diff array (line number, character, etc) at which the change // started (starting at 0) int position = ((deletedCurrent < 0) ? 0 : deletedCurrent); oldText = null; newText = null; if (currentDiff.getDeletedEnd() >= 0 && currentDiff.getDeletedEnd() >= deletedCurrent) { oldText = oldArray[deletedCurrent]; deletedCurrent++; } if (currentDiff.getAddedEnd() >= 0 && currentDiff.getAddedEnd() >= addedCurrent) { newText = newArray[addedCurrent]; addedCurrent++; } wikiDiffs.add(new WikiDiff(oldText, newText, position)); // FIXME - this shouldn't be necessary count++; if (count > 5000) { logger.warning("Infinite loop in DiffUtils.processDifference"); break; } } return wikiDiffs; }
/** * If possible, prepend a few lines of unchanged text that before after to the changed line in * order to add context to the current list of WikiDiff objects. * * @param currentDiff The current diff object. * @param previousDiff The diff object that immediately preceded this object (if any). * @param oldArray The original array of string objects that was compared from in order to * generate the diff. * @param newArray The original array of string objects that was compared to in order to generate * the diff. * @param bufferAmount The number of unchanged elements to display after the diff, or -1 if all * unchanged lines should be displayed. */ private static List<WikiDiff> preBufferDifference( Difference currentDiff, Difference previousDiff, String[] oldArray, String[] newArray, int bufferAmount) { List<WikiDiff> wikiDiffs = new ArrayList<WikiDiff>(); if (bufferAmount == 0) { return wikiDiffs; } if (bufferAmount == -1 && previousDiff != null) { // when buffering everything, only pre-buffer for the first element as the post-buffer code // will handle everything else. return wikiDiffs; } // deletedCurrent is the current position in oldArray to start buffering from int deletedCurrent = (bufferAmount == -1 || bufferAmount > currentDiff.getDeletedStart()) ? 0 : (currentDiff.getDeletedStart() - bufferAmount); // addedCurrent is the current position in newArray to start buffering from int addedCurrent = (bufferAmount == -1 || bufferAmount > currentDiff.getAddedStart()) ? 0 : (currentDiff.getAddedStart() - bufferAmount); if (previousDiff != null) { // if there was a previous diff make sure that it is not being overlapped deletedCurrent = Math.max(previousDiff.getDeletedEnd() + 1, deletedCurrent); addedCurrent = Math.max(previousDiff.getAddedEnd() + 1, addedCurrent); } // number of iterations is number of loops required to fully buffer the added and deleted diff int numIterations = Math.max( currentDiff.getDeletedStart() - deletedCurrent, currentDiff.getAddedStart() - addedCurrent); String oldText = null; String newText = null; for (int i = 0; i < numIterations; i++) { int position = (deletedCurrent < 0) ? 0 : deletedCurrent; oldText = null; newText = null; // if diffs are close together, do not allow buffers to overlap if (canPreBuffer( previousDiff, deletedCurrent, currentDiff.getDeletedStart(), oldArray, bufferAmount, false)) { oldText = oldArray[deletedCurrent]; deletedCurrent++; } if (canPreBuffer( previousDiff, addedCurrent, currentDiff.getAddedStart(), newArray, bufferAmount, true)) { newText = newArray[addedCurrent]; addedCurrent++; } if (oldText == null && newText == null) { logger.fine( "Possible DIFF bug: no elements pre-buffered. position: " + position + " / deletedCurrent: " + deletedCurrent + " / addedCurrent " + addedCurrent + " / numIterations: " + numIterations); break; } wikiDiffs.add(new WikiDiff(oldText, newText, position)); } return wikiDiffs; }