### 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 1 Oct 2007 13:55:24 -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 = 0; // 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,7 @@ public int getFuzz() { int cf = configuration.getFuzz(); if (cf == -1) { - return fuzz; + 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 1 Oct 2007 13:55:24 -0000 @@ -41,36 +41,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); 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 start = fHunk.getStart(configuration.isReversed()); + for (int i = 1; i <= start; 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 <= lines.size()-start/* fDiffResult.getFuzz()*/; i++) { + if (fHunk.tryPatch(configuration, lines, fShift + i, fuzz)) { if (isAdjustShift()) - fShift+= i; - found= true; + fShift += i; + found = true; break; } } @@ -95,54 +98,71 @@ } /** - * 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; + // int shift = 0; // we're not interested in the shift value right now 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; + // TODO (tzarna): the default maximum fuzz factor is 2 + for (; fuzz <= 2; 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); + fMatches = true; + break; + } + //TODO (tzarna): hugeShift it to much, lines to the beg/end of a + // file would be enough the maximum shift we can use for this file + int hugeShift = lines.size(); + // shift = -1; // not found - 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)) { + // shift = i; 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)) { + // shift = i; 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); + break; + } } + // TODO (tzarna): return -1 if the hunk could not be matched? return fuzz; } 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 1 Oct 2007 13:55:24 -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,135 @@ * 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; 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()) // out of the file 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; + + // use the fuzz factor if needed + // TODO (tzarna): extract method + if (!contextLinesMatched && fuzz > 0 && contextLines.size() > fuzz) { + // three lines of context is typically the default + if (contextLines.size() > 3) { + // two "groups" of context lines -- both preceding and following + if (contextLines.size() % 2 == 0) { + // if the number of context lines is even treat the + // first half part as following and the second one + // as preceding... + List followingContextLines = new ArrayList(contextLines.subList(0, contextLines.size() / 2 - 1)); + if (!checkFollowingContextLines(configuration, lines, fuzz, pos, followingContextLines)) + return false; + List precedingContextLines = new ArrayList(contextLines.subList(contextLines.size() / 2 - 1, contextLines.size())); + if (!checkPrecedingContextLines(configuration, lines, fuzz, pos, precedingContextLines)) + return false; + } else { + // ... otherwise, if the number is odd give them two + // tries (e.g. for 5 context lines try both 2+3 and + // 3+2) + List followingContextLines = new ArrayList(contextLines.subList(0, contextLines.size() / 2)); + List precedingContextLines = new ArrayList(contextLines.subList(contextLines.size() / 2, contextLines.size())); + if (checkPrecedingContextLines(configuration, lines, fuzz, pos, precedingContextLines) + && checkFollowingContextLines(configuration, lines, fuzz, pos - contextLines.size() / 2 - 1, followingContextLines)) { + // to omit the check of following of the context lines at the end of this method + contextLinesMatched = true; + break; + } + // else give it another try + precedingContextLines = new ArrayList(contextLines.subList(0, contextLines.size() / 2 + 1)); + if (!checkPrecedingContextLines(configuration, lines, fuzz, pos, precedingContextLines)) + return false; + followingContextLines = new ArrayList(contextLines.subList(contextLines.size() / 2 + 1, contextLines.size())); + if (!checkFollowingContextLines(configuration, lines, fuzz, pos, followingContextLines)) + return false; + } + } else { + //ignore from the beginning + if (!checkPrecedingContextLines(configuration, lines, + fuzz, pos, contextLines)) return false; } - - // 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 there is less or equal context line to the fuzz + // factor we ignore them all and treat as matching + + contextLines.clear(); + + 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' + contextLines.clear(); } else Assert.isTrue(false, "tryPatch: unknown control character: " + controlChar); //$NON-NLS-1$ } + if (!contextLinesMatched && fuzz > 0 && contextLines.size() > fuzz) + if (!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; } @@ -243,15 +329,22 @@ Assert.isTrue(s.length() > 0); String line= s.substring(1); char controlChar= s.charAt(0); + // TODO (tzarna): don't process context lines as they are already + // checked by the tryPatch method (with fuzz factor) or add the fuzz + // factor processing here too if (controlChar == ' ') { // context lines - while (true) { Assert.isTrue(pos < lines.size(), "doPatch: inconsistency in context"); //$NON-NLS-1$ - if (linesMatch(configuration, line, (String) lines.get(pos))) { - pos++; - break; - } +// if (linesMatch(configuration, line, (String) lines.get(pos))) { +// pos++; +// break; +// }else if (fuzz > 0) { +// // doesn't match, use the fuzz factor +// fuzz--; +// pos++; +// break; +// } pos++; - } +// } } else if (isDeletedDelimeter(controlChar, reverse)) { // deleted lines while (true) { @@ -259,6 +352,7 @@ if (linesMatch(configuration, line, (String) lines.get(pos))) { break; } + // TODO (tzarna): does it ever happen? pos++; } lines.remove(pos);