### Eclipse Workspace Patch 1.0 #P org.eclipse.compare Index: compare/org/eclipse/compare/internal/patch/FileDiffResult.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/FileDiffResult.java,v retrieving revision 1.11 diff -u -r1.11 FileDiffResult.java --- compare/org/eclipse/compare/internal/patch/FileDiffResult.java 16 May 2007 18:17:07 -0000 1.11 +++ compare/org/eclipse/compare/internal/patch/FileDiffResult.java 12 Oct 2007 11:03:30 -0000 @@ -31,7 +31,7 @@ private List fBeforeLines, fAfterLines; private final PatchConfiguration configuration; private String charset; - private int fuzz; + private int fFuzz; public FileDiffResult(FileDiff diff, PatchConfiguration configuration) { super(); @@ -146,7 +146,7 @@ fBeforeLines = new ArrayList(); fBeforeLines.addAll(lines); if (getConfiguration().getFuzz() == -1) { - fuzz = calculateFuzz(fBeforeLines, monitor); + fFuzz = calculateFuzz(fBeforeLines, monitor); } int shift= 0; Hunk[] hunks = fDiff.getHunks(); @@ -208,8 +208,7 @@ public int calculateFuzz(List lines, IProgressMonitor monitor) { if (monitor == null) monitor = new NullProgressMonitor(); - fBeforeLines = new ArrayList(); - fBeforeLines.addAll(lines); + fBeforeLines = new ArrayList(lines); // TODO: What about deletions? if (fDiff.getDiffType(getConfiguration().isReversed()) == Differencer.ADDITION) { // Additions don't need to adjust the fuzz factor @@ -217,7 +216,7 @@ return -1; } int shift= 0; - int fuzz1 = 0; + int highestFuzz = -1; // the maximum fuzz factor for all hunks String name = getTargetPath() != null ? getTargetPath().lastSegment() : ""; //$NON-NLS-1$ Hunk[] hunks = fDiff.getHunks(); for (int j = 0; j < hunks.length; j++) { @@ -225,16 +224,21 @@ monitor.subTask(NLS.bind(PatchMessages.PreviewPatchPage_GuessFuzzProgress_format, new String[] {name, Integer.toString(j + 1)})); HunkResult result = getHunkResult(h); result.setShift(shift); - int f= result.calculateFuzz(lines, monitor); - if (f >= 0) { - shift = result.getShift(); - } - if (f>fuzz1) - fuzz1= f; + int fuzz = result.calculateFuzz(lines, monitor); + /* + * TODO (tzarna): I believe this is not needed anymore as the + * calculateFuzz does what it's supposed to, instead of calculate + * shifting + */ + // if (fuzz >= 0) { + // shift = result.getShift(); + // } + if (fuzz > highestFuzz) + highestFuzz = fuzz; monitor.worked(1); } fAfterLines = lines; - return fuzz1; + return highestFuzz; } public IPath getTargetPath() { @@ -330,7 +334,8 @@ public int getFuzz() { int cf = configuration.getFuzz(); if (cf == -1) { - return fuzz; + // TODO (tzarna): fFuzz can be -1 if not found during calculateFuzz + return fFuzz; } return cf; } Index: compare/org/eclipse/compare/internal/patch/HunkResult.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/HunkResult.java,v retrieving revision 1.7 diff -u -r1.7 HunkResult.java --- compare/org/eclipse/compare/internal/patch/HunkResult.java 16 Mar 2007 19:53:10 -0000 1.7 +++ compare/org/eclipse/compare/internal/patch/HunkResult.java 12 Oct 2007 11:03:30 -0000 @@ -21,6 +21,13 @@ public class HunkResult implements IHunk { private static final boolean DEBUG= false; + + /** + * Default maximum fuzz factor equals 2. This is related to the default + * number of context lines, which is 3. + */ + // TODO (tzarna): we could use lines.size() instead but it's highly inefficient + private static final int MAXIMUM_FUZZ_FACTOR = 2; private Hunk fHunk; private boolean fMatches; @@ -41,36 +48,39 @@ /** * Try to apply the specified hunk to the given lines. * If the hunk cannot be applied at the original position - * the methods tries fuzz lines before and after. + * the method tries shift lines up and down. * @param lines the lines to be patched * @return whether the hunk could be applied */ public boolean patch(List lines) { fMatches = false; PatchConfiguration configuration = getConfiguration(); + int fuzz = fDiffResult.getFuzz();// configuration.getFuzz(); if (isEnabled(configuration)) { - if (fHunk.tryPatch(configuration, lines, fShift)) { - fShift+= fHunk.doPatch(configuration, lines, fShift); + if (fHunk.tryPatch(configuration, lines, fShift, fuzz)) { + // it's a perfect match, no adjustment is needed + fShift += fHunk.doPatch(configuration, lines, fShift, fuzz); fMatches = true; } else { boolean found= false; int oldShift= fShift; - for (int i= 1; i <= fDiffResult.getFuzz(); i++) { - if (fHunk.tryPatch(configuration, lines, fShift-i)) { + int hugeShift = lines.size(); + for (int i = 1; i <= hugeShift; i++) { + if (fHunk.tryPatch(configuration, lines, fShift - i, fuzz)) { if (isAdjustShift()) - fShift-= i; - found= true; + fShift -= i; + found = true; break; } } - if (! found) { - for (int i= 1; i <= fDiffResult.getFuzz(); i++) { - if (fHunk.tryPatch(configuration, lines, fShift+i)) { + if (!found) { + for (int i = 1; i <= hugeShift/* fDiffResult.getFuzz()*/; i++) { + if (fHunk.tryPatch(configuration, lines, fShift + i, fuzz)) { if (isAdjustShift()) - fShift+= i; - found= true; + fShift += i; + found = true; break; } } @@ -78,7 +88,7 @@ if (found) { if (DEBUG) System.out.println("patched hunk at offset: " + (fShift-oldShift)); //$NON-NLS-1$ - fShift+= fHunk.doPatch(configuration, lines, fShift); + fShift+= fHunk.doPatch(configuration, lines, fShift, fuzz); fMatches = true; } } @@ -95,55 +105,67 @@ } /** - * Calculate the fuzz factor that will allow the most hunks to be matched. - * @param lines the lines of the target file - * @param monitor a progress monitor + * Calculate the fuzz that will allow the most hunks to be matched. Even + * though we're interested only in the value of the fuzz, the shifting is + * done anyway. + * + * @param lines + * the lines of the target file + * @param monitor + * a progress monitor * @return the fuzz factor or -1 if the hunk could not be matched */ public int calculateFuzz(List lines, IProgressMonitor monitor) { - - fMatches= false; - int fuzz = 0; + fMatches = false; PatchConfiguration configuration = getConfiguration(); - if (fHunk.tryPatch(configuration, lines, fShift)) { - fShift+= fHunk.doPatch(configuration, lines, fShift); - fMatches = true; - } else { - int hugeFuzz= lines.size(); // the maximum we need for this file - fuzz= -1; // not found + int fuzz = 0; + for (; fuzz <= MAXIMUM_FUZZ_FACTOR; fuzz++) { + // try to apply using lines coordinates from the patch + if (fHunk.tryPatch(configuration, lines, fShift, fuzz)) { + // it's a perfect match, no adjustment is needed + fShift += fHunk.doPatch(configuration, lines, fShift, fuzz); + fMatches = true; + break; + } + //TODO (tzarna): hugeShift=lines.size() is to much, lines to the beg/end of a + // file would be enough the maximum shift we can use for this file + // this can result in matching hunks out of order + int hugeShift = lines.size(); - for (int i= 1; i <= hugeFuzz; i++) { + // shift up + for (int i = 1; i <= hugeShift; i++) { if (monitor.isCanceled()) { throw new OperationCanceledException(); } - if (fHunk.tryPatch(configuration, lines, fShift-i)) { - fuzz= i; + if (fHunk.tryPatch(configuration, lines, fShift - i, fuzz)) { if (isAdjustShift()) - fShift-= i; - fMatches= true; + fShift -= i; + fMatches = true; break; } } - - if (! fMatches) { - for (int i= 1; i <= hugeFuzz; i++) { + + // shift down + if (!fMatches) { + for (int i = 1; i <= hugeShift; i++) { if (monitor.isCanceled()) { throw new OperationCanceledException(); } - if (fHunk.tryPatch(configuration, lines, fShift+i)) { - fuzz= i; + if (fHunk.tryPatch(configuration, lines, fShift + i, fuzz)) { if (isAdjustShift()) - fShift+= i; - fMatches= true; + fShift += i; + fMatches = true; break; } } } - - if (fMatches) - fShift+= fHunk.doPatch(configuration, lines, fShift); + + if (fMatches) { + fShift += fHunk.doPatch(configuration, lines, fShift, fuzz); + break; + } } - return fuzz; + return fMatches ? fuzz : -1; } /** Index: compare/org/eclipse/compare/internal/patch/Hunk.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.compare/compare/org/eclipse/compare/internal/patch/Hunk.java,v retrieving revision 1.22 diff -u -r1.22 Hunk.java --- compare/org/eclipse/compare/internal/patch/Hunk.java 5 Sep 2007 15:26:47 -0000 1.22 +++ compare/org/eclipse/compare/internal/patch/Hunk.java 12 Oct 2007 11:03:30 -0000 @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.compare.internal.patch; +import java.util.ArrayList; import java.util.List; import org.eclipse.compare.patch.PatchConfiguration; @@ -167,50 +168,128 @@ * The parameter shift is added to the line numbers given * in the hunk. */ - public boolean tryPatch(PatchConfiguration configuration, List lines, int shift) { + public boolean tryPatch(PatchConfiguration configuration, List lines, int shift, int fuzz) { boolean reverse = configuration.isReversed(); - int pos= getStart(reverse) + shift; - int deleteMatches= 0; + int pos = getStart(reverse) + shift; + int deleteMatches = 0; + List contextLines = new ArrayList(); + boolean contextLinesMatched = true; + boolean precedingLinesChecked = false; for (int i= 0; i < fLines.length; i++) { - String s= fLines[i]; + String s = fLines[i]; Assert.isTrue(s.length() > 0); - String line= s.substring(1); - char controlChar= s.charAt(0); + String line = s.substring(1); + char controlChar = s.charAt(0); + if (controlChar == ' ') { // context lines - while (true) { - if (pos < 0 || pos >= lines.size()) - return false; - if (linesMatch(configuration, line, (String) lines.get(pos))) { - pos++; - break; - } + + if (pos < 0 || pos >= lines.size()) return false; - } + contextLines.add(line); + if (linesMatch(configuration, line, (String) lines.get(pos))) { + pos++; + continue; + } else if (fuzz > 0) { + // doesn't match, use the fuzz factor + contextLinesMatched = false; + pos++; + continue; + } + return false; } else if (isDeletedDelimeter(controlChar, reverse)) { // deleted lines - while (true) { - if (pos < 0 || pos >= lines.size()) - return false; - if (linesMatch(configuration, line, (String) lines.get(pos))) { - deleteMatches++; - pos++; - break; - } - - // We must remove all lines at once, return false if this - // fails. In other words, all lines considered for deletion - // must be found one by one. - - // if (deleteMatches <= 0) - return false; - // pos++; + + if (precedingLinesChecked && !contextLinesMatched && contextLines.size() > 0) + // context lines inside hunk don't match + return false; + + // check following context lines if exist + // use the fuzz factor if needed + if (!precedingLinesChecked + && !contextLinesMatched + && contextLines.size() >= fuzz + && !checkPrecedingContextLines(configuration, lines, + fuzz, pos, contextLines)) + return false; + // else if there is less or equal context line to the fuzz + // factor we ignore them all and treat as matching + + precedingLinesChecked = true; + contextLines.clear(); + contextLinesMatched = true; + + if (pos < 0 || pos >= lines.size()) // out of the file + return false; + if (linesMatch(configuration, line, (String) lines.get(pos))) { + deleteMatches++; + pos++; + continue; // line matched, continue with the next one } + + // We must remove all lines at once, return false if this + // fails. In other words, all lines considered for deletion + // must be found one by one. + + // if (deleteMatches <= 0) + return false; + // pos++; } else if (isAddedDelimeter(controlChar, reverse)) { - // added lines - // we don't have to do anything for a 'try' + + if (precedingLinesChecked && !contextLinesMatched && contextLines.size() > 0) + // context lines inside hunk don't match + return false; + + if (!precedingLinesChecked + && !contextLinesMatched + && contextLines.size() >= fuzz + && !checkPrecedingContextLines(configuration, lines, + fuzz, pos, contextLines)) + return false; + + precedingLinesChecked = true; + contextLines.clear(); + contextLinesMatched = true; + + // we don't have to do anything more for a 'try' } else Assert.isTrue(false, "tryPatch: unknown control character: " + controlChar); //$NON-NLS-1$ } + + // check following context lines if exist + if (!contextLinesMatched + && fuzz > 0 + && contextLines.size() > fuzz + && !checkFollowingContextLines(configuration, lines, fuzz, pos, + contextLines)) + return false; + + return true; + } + + private boolean checkPrecedingContextLines( + PatchConfiguration configuration, List lines, int fuzz, int pos, + List contextLines) { + + // ignore from the beginning + for (int j = fuzz; j < contextLines.size(); j++) { + if (!linesMatch(configuration, (String) contextLines.get(j), + (String) lines.get(pos - contextLines.size() + j))) + return false; + } + return true; + } + + private boolean checkFollowingContextLines( + PatchConfiguration configuration, List lines, int fuzz, int pos, + List contextLines) { + if (!contextLines.isEmpty()) { + // ignore from the end + for (int j = 0; j < contextLines.size() - fuzz; j++) { + if (!linesMatch(configuration, (String) contextLines.get(j), + (String) lines.get(pos - contextLines.size() + j))) + return false; + } + } return true; } @@ -235,35 +314,70 @@ return fNewLength - fOldLength; } - int doPatch(PatchConfiguration configuration, List lines, int shift) { + int doPatch(PatchConfiguration configuration, List lines, int shift, int fuzz) { boolean reverse = configuration.isReversed(); int pos = getStart(reverse) + shift; + List contextLines = new ArrayList(); + boolean contextLinesMatched = true; + boolean precedingLinesChecked = false; for (int i= 0; i < fLines.length; i++) { String s= fLines[i]; Assert.isTrue(s.length() > 0); String line= s.substring(1); char controlChar= s.charAt(0); - if (controlChar == ' ') { // context lines - while (true) { + if (controlChar == ' ') { + // context lines Assert.isTrue(pos < lines.size(), "doPatch: inconsistency in context"); //$NON-NLS-1$ + contextLines.add(line); if (linesMatch(configuration, line, (String) lines.get(pos))) { pos++; - break; + continue; + } else if (fuzz > 0) { + // doesn't match, use the fuzz factor + contextLinesMatched = false; + pos++; + continue; } - pos++; - } + Assert.isTrue(false, "doPatch: context doesn't match"); //$NON-NLS-1$ +// pos++; } else if (isDeletedDelimeter(controlChar, reverse)) { - // deleted lines - while (true) { - Assert.isTrue(pos < lines.size(), "doPatch: inconsistency in deleted lines"); //$NON-NLS-1$ - if (linesMatch(configuration, line, (String) lines.get(pos))) { - break; - } - pos++; - } + // deleted lines + if (precedingLinesChecked && !contextLinesMatched && contextLines.size() > 0) + // context lines inside hunk don't match + Assert.isTrue(false, "doPatch: context lines inside hunk don't match"); //$NON-NLS-1$ + + // check following context lines if exist + // use the fuzz factor if needed + if (!precedingLinesChecked + && !contextLinesMatched + && contextLines.size() >= fuzz + && !checkPrecedingContextLines(configuration, lines, + fuzz, pos, contextLines)) + Assert.isTrue(false, "doPatch: preceding context lines don't match, even though fuzz factor has been used"); //$NON-NLS-1$; + // else if there is less or equal context line to the fuzz + // factor we ignore them all and treat as matching + + precedingLinesChecked = true; + contextLines.clear(); + contextLinesMatched = true; + lines.remove(pos); } else if (isAddedDelimeter(controlChar, reverse)) { // added lines + if (precedingLinesChecked && !contextLinesMatched && contextLines.size() > 0) + Assert.isTrue(false, "doPatch: context lines inside hunk don't match"); //$NON-NLS-1$ + + if (!precedingLinesChecked + && !contextLinesMatched + && contextLines.size() >= fuzz + && !checkPrecedingContextLines(configuration, lines, + fuzz, pos, contextLines)) + Assert.isTrue(false, "doPatch: preceding context lines don't match, even though fuzz factor has been used"); //$NON-NLS-1$; + + precedingLinesChecked = true; + contextLines.clear(); + contextLinesMatched = true; + if (getLength(reverse) == 0 && pos+1 < lines.size()) lines.add(pos+1, line); else