VCS: patch creation separated from patch application: 2 more steps
authorirengrig <Irina.Chernushina@jetbrains.com>
Mon, 15 Mar 2010 16:08:47 +0000 (19:08 +0300)
committerirengrig <Irina.Chernushina@jetbrains.com>
Mon, 15 Mar 2010 16:08:47 +0000 (19:08 +0300)
platform/lvcs-impl/testSrc/com/intellij/historyIntegrTests/PatchingTestCase.java
platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchHunk.java
platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchReader.java
platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchVirtualFileReader.java [new file with mode: 0644]
platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatchBase.java
platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyPatchHunk.java [new file with mode: 0644]
platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyTextFilePatch.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java

index 4b7be8efdcfd90f324fb0dc1f9a4c65db3407220..a36384218eb1fe98cc7fc54841af14f68ca3c8f3 100644 (file)
@@ -19,6 +19,7 @@ package com.intellij.historyIntegrTests;
 import com.intellij.openapi.diff.impl.patch.BinaryFilePatch;
 import com.intellij.openapi.diff.impl.patch.FilePatch;
 import com.intellij.openapi.diff.impl.patch.PatchReader;
+import com.intellij.openapi.diff.impl.patch.PatchVirtualFileReader;
 import com.intellij.openapi.diff.impl.patch.formove.PatchApplier;
 import com.intellij.openapi.vfs.VirtualFile;
 
@@ -45,7 +46,7 @@ public abstract class PatchingTestCase extends IntegrationTestCase {
 
   protected void applyPatch() throws Exception {
     List<FilePatch> patches = new ArrayList<FilePatch>();
-    PatchReader reader = new PatchReader(getFS().refreshAndFindFileByPath(patchFilePath));
+    PatchReader reader = PatchVirtualFileReader.create(getFS().refreshAndFindFileByPath(patchFilePath));
 
     while (true) {
       FilePatch p = reader.readNextPatch();
index a899bee960bd062120ee2f5c7ab8585791e3986b..3ca09d8d68f3d2fc4604649dd4d82c0e8d76886c 100644 (file)
@@ -64,121 +64,6 @@ public class PatchHunk {
     return Collections.unmodifiableList(myLines);
   }
 
-  public ApplyPatchStatus apply(final List<String> lines) throws ApplyPatchException {
-    List<String> originalLines = new ArrayList<String>(lines);
-    try {
-      return tryApply(lines, false);
-    }
-    catch(ApplyPatchException ex) {
-      lines.clear();
-      lines.addAll(originalLines);
-      return tryApply(lines, true);
-    }
-  }
-
-  private ApplyPatchStatus tryApply(final List<String> lines, boolean acceptPartial) throws ApplyPatchException {
-    ApplyPatchStatus result = null;
-    int curLine = findStartLine(lines);
-    for(PatchLine line: myLines) {
-      final String patchLineText = line.getText();
-      switch (line.getType()) {
-        case CONTEXT:
-          checkContextMismatch(lines, curLine, patchLineText);
-          curLine++;
-          break;
-
-        case ADD:
-          if (curLine < lines.size() && lines.get(curLine).equals(patchLineText) && acceptPartial) {
-            result = ApplyPatchStatus.and(result, ApplyPatchStatus.ALREADY_APPLIED);
-          }
-          else {
-            lines.add(curLine, patchLineText);
-            result = ApplyPatchStatus.and(result, ApplyPatchStatus.SUCCESS);
-          }
-          curLine++;
-          break;
-
-        case REMOVE:
-          if (curLine >= lines.size() || !patchLineText.equals(lines.get(curLine))) {
-            if (acceptPartial) {
-              // we'll get a context mismatch exception later if it's actually a conflict and not an already applied line
-              result = ApplyPatchStatus.and(result, ApplyPatchStatus.ALREADY_APPLIED);
-            }
-            else {
-              checkContextMismatch(lines, curLine, patchLineText);
-            }
-          }
-          else {
-            lines.remove(curLine);
-            result = ApplyPatchStatus.and(result, ApplyPatchStatus.SUCCESS);
-          }
-          break;
-      }
-    }
-    if (result != null) {
-      return result;
-    }
-    return ApplyPatchStatus.SUCCESS;
-  }
-
-  private static void checkContextMismatch(final List<String> lines, final int curLine, final String patchLineText) throws ApplyPatchException {
-    if (curLine >= lines.size()) {
-      throw new ApplyPatchException("Unexpected end of document. Expected line:\n" + patchLineText);
-    }
-    if (!patchLineText.equals(lines.get(curLine))) {
-      throw new ApplyPatchException("Context mismatch. Expected line:\n" + patchLineText + "\nFound line:\n" + lines.get(curLine));
-    }
-  }
-
-  private int findStartLine(final List<String> lines) throws ApplyPatchException {
-    int totalContextLines = countContextLines();
-    if (getLinesProcessingContext(lines, myStartLineBefore) == totalContextLines) {
-      return myStartLineBefore;
-    }
-    int maxContextStartLine = -1;
-    int maxContextLines = 0;
-    for(int i=0;i< lines.size(); i++) {
-      int contextLines = getLinesProcessingContext(lines, i);
-      if (contextLines == totalContextLines) {
-        return i;
-      }
-      if (contextLines > maxContextLines) {
-        maxContextLines = contextLines;
-        maxContextStartLine = i;
-      }
-    }
-    if (maxContextLines < 2) {
-      throw new ApplyPatchException("couldn't find context");
-    }
-    return maxContextStartLine;
-  }
-
-  private int countContextLines() {
-    int count = 0;
-    for(PatchLine line: myLines) {
-      if (line.getType() == PatchLine.Type.CONTEXT || line.getType() == PatchLine.Type.REMOVE) {
-        count++;
-      }
-    }
-    return count;
-  }
-
-  private int getLinesProcessingContext(final List<String> lines, int startLine) {
-    int count = 0;
-    for(PatchLine line: myLines) {
-      PatchLine.Type type = line.getType();
-      if (type == PatchLine.Type.REMOVE || type == PatchLine.Type.CONTEXT) {
-        // TODO: smarter algorithm (search outward from non-context lines)
-        if (startLine >= lines.size() || !line.getText().equals(lines.get(startLine))) {
-          return count;
-        }
-        count++;
-        startLine++;
-      }
-    }
-    return count;
-  }
-
   public boolean isNewContent() {
     return myStartLineBefore == -1 && myEndLineBefore == -1;
   }
index 7baf2fe36210cdb49d481d6d4f25a0e05c701fe9..288b39586d53ae20f7dca23520d438d7f756c36e 100644 (file)
  */
 package com.intellij.openapi.diff.impl.patch;
 
-import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
 import com.intellij.openapi.util.text.LineTokenizer;
-import com.intellij.openapi.vfs.VirtualFile;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.Nullable;
 
-import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import java.util.List;
-import java.util.ArrayList;
 
 public class PatchReader {
   @NonNls public static final String NO_NEWLINE_SIGNATURE = "\\ No newline at end of file";
@@ -48,12 +45,6 @@ public class PatchReader {
   @NonNls private static final Pattern ourContextBeforeHunkStartPattern = Pattern.compile("\\*\\*\\* (\\d+),(\\d+) \\*\\*\\*\\*");
   @NonNls private static final Pattern ourContextAfterHunkStartPattern = Pattern.compile("--- (\\d+),(\\d+) ----");
 
-  public PatchReader(VirtualFile virtualFile) throws IOException {
-    byte[] patchContents = virtualFile.contentsToByteArray();
-    CharSequence patchText = LoadTextUtil.getTextByBinaryPresentation(patchContents, virtualFile);
-    myLines = LineTokenizer.tokenize(patchText, false);
-  }
-
   public PatchReader(CharSequence patchContent) {
     myLines = LineTokenizer.tokenize(patchContent, false);
   }
diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchVirtualFileReader.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchVirtualFileReader.java
new file mode 100644 (file)
index 0000000..d416c38
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2000-2010 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.openapi.diff.impl.patch;
+
+import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+
+import java.io.IOException;
+
+public class PatchVirtualFileReader {
+  private PatchVirtualFileReader() {
+  }
+
+  public static PatchReader create(final VirtualFile virtualFile) throws IOException {
+    final byte[] patchContents = virtualFile.contentsToByteArray();
+    final CharSequence patchText = LoadTextUtil.getTextByBinaryPresentation(patchContents, virtualFile);
+    return new PatchReader(patchText);
+  }
+}
index 0b368a9e09bad331406be9c27ad1d930efccb940..b6fda11b4e0b1d557a984bab7966cff482764bbb 100644 (file)
@@ -186,7 +186,7 @@ public abstract class ApplyFilePatchBase<T extends FilePatch> implements ApplyFi
     Collections.addAll(lines, LineTokenizer.tokenize(text, false));
     ApplyPatchStatus result = null;
     for(PatchHunk hunk: hunks) {
-      result = ApplyPatchStatus.and(result, hunk.apply(lines));
+      result = ApplyPatchStatus.and(result, new ApplyPatchHunk(hunk).apply(lines));
     }
     for(int i=0; i<lines.size(); i++) {
       newText.append(lines.get(i));
diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyPatchHunk.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyPatchHunk.java
new file mode 100644 (file)
index 0000000..2b81d32
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2000-2010 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.openapi.diff.impl.patch.apply;
+
+import com.intellij.openapi.diff.impl.patch.ApplyPatchException;
+import com.intellij.openapi.diff.impl.patch.ApplyPatchStatus;
+import com.intellij.openapi.diff.impl.patch.PatchHunk;
+import com.intellij.openapi.diff.impl.patch.PatchLine;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ApplyPatchHunk {
+  private final PatchHunk myHunk;
+
+  public ApplyPatchHunk(final PatchHunk hunk) {
+    myHunk = hunk;
+  }
+  
+  public ApplyPatchStatus apply(final List<String> lines) throws ApplyPatchException {
+    List<String> originalLines = new ArrayList<String>(lines);
+    try {
+      return tryApply(lines, false);
+    }
+    catch(ApplyPatchException ex) {
+      lines.clear();
+      lines.addAll(originalLines);
+      return tryApply(lines, true);
+    }
+  }
+
+  private ApplyPatchStatus tryApply(final List<String> lines, boolean acceptPartial) throws ApplyPatchException {
+    final List<PatchLine> hunkLines = myHunk.getLines();
+    ApplyPatchStatus result = null;
+    int curLine = findStartLine(hunkLines, lines);
+    for(PatchLine line: hunkLines) {
+      final String patchLineText = line.getText();
+      switch (line.getType()) {
+        case CONTEXT:
+          checkContextMismatch(lines, curLine, patchLineText);
+          curLine++;
+          break;
+
+        case ADD:
+          if (curLine < lines.size() && lines.get(curLine).equals(patchLineText) && acceptPartial) {
+            result = ApplyPatchStatus.and(result, ApplyPatchStatus.ALREADY_APPLIED);
+          }
+          else {
+            lines.add(curLine, patchLineText);
+            result = ApplyPatchStatus.and(result, ApplyPatchStatus.SUCCESS);
+          }
+          curLine++;
+          break;
+
+        case REMOVE:
+          if (curLine >= lines.size() || !patchLineText.equals(lines.get(curLine))) {
+            if (acceptPartial) {
+              // we'll get a context mismatch exception later if it's actually a conflict and not an already applied line
+              result = ApplyPatchStatus.and(result, ApplyPatchStatus.ALREADY_APPLIED);
+            }
+            else {
+              checkContextMismatch(lines, curLine, patchLineText);
+            }
+          }
+          else {
+            lines.remove(curLine);
+            result = ApplyPatchStatus.and(result, ApplyPatchStatus.SUCCESS);
+          }
+          break;
+      }
+    }
+    if (result != null) {
+      return result;
+    }
+    return ApplyPatchStatus.SUCCESS;
+  }
+
+  private static void checkContextMismatch(final List<String> lines, final int curLine, final String patchLineText) throws ApplyPatchException {
+    if (curLine >= lines.size()) {
+      throw new ApplyPatchException("Unexpected end of document. Expected line:\n" + patchLineText);
+    }
+    if (!patchLineText.equals(lines.get(curLine))) {
+      throw new ApplyPatchException("Context mismatch. Expected line:\n" + patchLineText + "\nFound line:\n" + lines.get(curLine));
+    }
+  }
+
+  private int findStartLine(final List<PatchLine> hunkLines, final List<String> lines) throws ApplyPatchException {
+    int totalContextLines = countContextLines(hunkLines);
+    final int startLineBefore = myHunk.getStartLineBefore();
+    if (getLinesProcessingContext(hunkLines, lines, startLineBefore) == totalContextLines) {
+      return startLineBefore;
+    }
+    int maxContextStartLine = -1;
+    int maxContextLines = 0;
+    for(int i=0;i< lines.size(); i++) {
+      int contextLines = getLinesProcessingContext(hunkLines, lines, i);
+      if (contextLines == totalContextLines) {
+        return i;
+      }
+      if (contextLines > maxContextLines) {
+        maxContextLines = contextLines;
+        maxContextStartLine = i;
+      }
+    }
+    if (maxContextLines < 2) {
+      throw new ApplyPatchException("couldn't find context");
+    }
+    return maxContextStartLine;
+  }
+
+  private int countContextLines(final List<PatchLine> hunkLines) {
+    int count = 0;
+    for(PatchLine line: hunkLines) {
+      if (line.getType() == PatchLine.Type.CONTEXT || line.getType() == PatchLine.Type.REMOVE) {
+        count++;
+      }
+    }
+    return count;
+  }
+
+  private int getLinesProcessingContext(final List<PatchLine> hunkLines, final List<String> lines, int startLine) {
+    int count = 0;
+    for(PatchLine line: hunkLines) {
+      PatchLine.Type type = line.getType();
+      if (type == PatchLine.Type.REMOVE || type == PatchLine.Type.CONTEXT) {
+        // TODO: smarter algorithm (search outward from non-context lines)
+        if (startLine >= lines.size() || !line.getText().equals(lines.get(startLine))) {
+          return count;
+        }
+        count++;
+        startLine++;
+      }
+    }
+    return count;
+  }
+}
index 0fb9298d9b8f07b32dbf659ef4a1a78cc56c88c5..d59542a67df5852ec2ab815257e14ec7cdde7480 100644 (file)
@@ -64,7 +64,7 @@ public class ApplyTextFilePatch extends ApplyFilePatchBase<TextFilePatch> {
     Collections.addAll(lines, LineTokenizer.tokenize(text, false));
     ApplyPatchStatus result = null;
     for(PatchHunk hunk: hunks) {
-      result = ApplyPatchStatus.and(result, hunk.apply(lines));
+      result = ApplyPatchStatus.and(result, new ApplyPatchHunk(hunk).apply(lines));
     }
     for(int i=0; i<lines.size(); i++) {
       newText.append(lines.get(i));
index 1a840a924823a9fe5acd19217d77e72d39c568d0..d0a1e0f6508a79b27dbfbf4f17b5adf8755fdeb2 100644 (file)
@@ -17,10 +17,7 @@ package com.intellij.openapi.vcs.changes.patch;
 
 import com.intellij.ide.util.PropertiesComponent;
 import com.intellij.openapi.actionSystem.*;
-import com.intellij.openapi.diff.impl.patch.FilePatch;
-import com.intellij.openapi.diff.impl.patch.PatchReader;
-import com.intellij.openapi.diff.impl.patch.PatchSyntaxException;
-import com.intellij.openapi.diff.impl.patch.TextFilePatch;
+import com.intellij.openapi.diff.impl.patch.*;
 import com.intellij.openapi.fileChooser.FileChooser;
 import com.intellij.openapi.fileChooser.FileChooserDescriptor;
 import com.intellij.openapi.fileChooser.FileChooserDialog;
@@ -213,7 +210,7 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper {
     }
     PatchReader reader;
     try {
-      reader = new PatchReader(patchFile);
+      reader = PatchVirtualFileReader.create(patchFile);
     }
     catch (IOException e) {
       //todo