diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/patches/PatchCreator.java b/platform/lvcs-impl/src/com/intellij/history/integration/patches/PatchCreator.java index bdd3dd3d6247..5f68e2e15d30 100644 --- a/platform/lvcs-impl/src/com/intellij/history/integration/patches/PatchCreator.java +++ b/platform/lvcs-impl/src/com/intellij/history/integration/patches/PatchCreator.java @@ -41,7 +41,7 @@ public class PatchCreator { Writer writer = new OutputStreamWriter(new FileOutputStream(filePath)); try { String lineSeparator = CodeStyleSettingsManager.getInstance(p).getCurrentSettings().getLineSeparator(); - UnifiedDiffWriter.write(patches, writer, lineSeparator); + UnifiedDiffWriter.write(p, patches, writer, lineSeparator); } finally { writer.close(); diff --git a/platform/lvcs-impl/testSrc/com/intellij/history/integration/PatchingTestCase.java b/platform/lvcs-impl/testSrc/com/intellij/history/integration/PatchingTestCase.java index c1f03d8ca360..0b62f4d5b802 100644 --- a/platform/lvcs-impl/testSrc/com/intellij/history/integration/PatchingTestCase.java +++ b/platform/lvcs-impl/testSrc/com/intellij/history/integration/PatchingTestCase.java @@ -16,18 +16,15 @@ package com.intellij.history.integration; -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.*; import com.intellij.openapi.diff.impl.patch.formove.PatchApplier; +import com.intellij.openapi.vcs.ObjectsConvertor; import com.intellij.openapi.vcs.changes.LocalChangeList; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.List; public abstract class PatchingTestCase extends IntegrationTestCase { @@ -47,14 +44,9 @@ public abstract class PatchingTestCase extends IntegrationTestCase { } protected void applyPatch() throws Exception { - List patches = new ArrayList(); PatchReader reader = PatchVirtualFileReader.create(LocalFileSystem.getInstance().refreshAndFindFileByPath(patchFilePath)); - while (true) { - FilePatch p = reader.readNextPatch(); - if (p == null) break; - patches.add(p); - } + List patches = ObjectsConvertor.downcast(reader.readAllPatches()); new PatchApplier(myProject, myRoot, patches, (LocalChangeList) null, null).execute(); } diff --git a/platform/platform-resources/src/META-INF/VcsExtensionPoints.xml b/platform/platform-resources/src/META-INF/VcsExtensionPoints.xml index fbbe601e7d16..f89219a978e6 100644 --- a/platform/platform-resources/src/META-INF/VcsExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/VcsExtensionPoints.xml @@ -39,5 +39,6 @@ + \ No newline at end of file diff --git a/platform/util/src/com/intellij/openapi/util/text/LineTokenizer.java b/platform/util/src/com/intellij/openapi/util/text/LineTokenizer.java index 050b5262ec83..8206638a2f8a 100644 --- a/platform/util/src/com/intellij/openapi/util/text/LineTokenizer.java +++ b/platform/util/src/com/intellij/openapi/util/text/LineTokenizer.java @@ -19,6 +19,7 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.text.CharArrayCharSequence; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -36,8 +37,17 @@ public class LineTokenizer { } private static String[] tokenize(final CharSequence chars, final boolean includeSeparators, final boolean skipLastEmptyLine) { + final List strings = tokenizeIntoList(chars, includeSeparators, skipLastEmptyLine); + return strings.isEmpty() ? ArrayUtil.EMPTY_STRING_ARRAY : ArrayUtil.toStringArray(strings); + } + + public static List tokenizeIntoList(final CharSequence chars, final boolean includeSeparators) { + return tokenizeIntoList(chars, includeSeparators, true); + } + + public static List tokenizeIntoList(final CharSequence chars, final boolean includeSeparators, final boolean skipLastEmptyLine) { if (chars == null || chars.length() == 0){ - return ArrayUtil.EMPTY_STRING_ARRAY; + return Collections.emptyList(); } LineTokenizer tokenizer = new LineTokenizer(chars); @@ -57,7 +67,7 @@ public class LineTokenizer { if (!skipLastEmptyLine && stringEndsWithSeparator(tokenizer)) lines.add(""); - return ArrayUtil.toStringArray(lines); + return lines; } public static int calcLineCount(final CharSequence chars, final boolean skipLastEmptyLine) { diff --git a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java index 06504f05e897..7be333959159 100644 --- a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java +++ b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java @@ -911,6 +911,10 @@ public class StringUtil { return s == null || s.length() == 0; } + public static boolean isEmpty(final CharSequence cs) { + return cs == null || cs.length() == 0; + } + @NotNull public static String notNullize(final String s) { return notNullize(s, ""); diff --git a/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchEP.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchEP.java new file mode 100644 index 000000000000..57424c22057c --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchEP.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2011 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.extensions.ExtensionPointName; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author irengrig + * Date: 7/11/11 + * Time: 11:43 AM + */ +public interface PatchEP { + ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.patch.extension"); + @NotNull + String getName(); + /** + * @param path - before path, if exist, otherwise after path + */ + @Nullable + CharSequence provideContent(@NotNull final String path); + /** + * @param path - before path, if exist, otherwise after path + */ + void consumeContent(@NotNull final String path, @NotNull final CharSequence content); +} diff --git a/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchReader.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchReader.java index 288b39586d53..8c7d476e9f09 100644 --- a/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchReader.java +++ b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchReader.java @@ -23,22 +23,25 @@ package com.intellij.openapi.diff.impl.patch; import com.intellij.openapi.util.text.LineTokenizer; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.changes.TransparentlyFailedValue; +import com.intellij.util.SmartList; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; public class PatchReader { @NonNls public static final String NO_NEWLINE_SIGNATURE = "\\ No newline at end of file"; + private final List myLines; + private final PatchReader.PatchContentParser myPatchContentParser; + private final AdditionalInfoParser myAdditionalInfoParser; + private List myPatches; private enum DiffFormat { CONTEXT, UNIFIED } - private final String[] myLines; - private int myLineIndex = 0; - private DiffFormat myDiffFormat = null; @NonNls private static final String CONTEXT_HUNK_PREFIX = "***************"; @NonNls private static final String CONTEXT_FILE_PREFIX = "*** "; @NonNls private static final Pattern ourUnifiedHunkStartPattern = Pattern.compile("@@ -(\\d+)(,(\\d+))? \\+(\\d+)(,(\\d+))? @@.*"); @@ -46,279 +49,455 @@ public class PatchReader { @NonNls private static final Pattern ourContextAfterHunkStartPattern = Pattern.compile("--- (\\d+),(\\d+) ----"); public PatchReader(CharSequence patchContent) { - myLines = LineTokenizer.tokenize(patchContent, false); + myLines = LineTokenizer.tokenizeIntoList(patchContent, false); + myAdditionalInfoParser = new AdditionalInfoParser(); + myPatchContentParser = new PatchContentParser(); } public List readAllPatches() throws PatchSyntaxException { - List result = new ArrayList(); - while(true) { - TextFilePatch patch = readNextPatch(); - if (patch == null) break; - result.add(patch); - } - return result; + parseAllPatches(); + return myPatches; } - @Nullable - public TextFilePatch readNextPatch() throws PatchSyntaxException { - while (myLineIndex < myLines.length) { - String curLine = myLines [myLineIndex]; - if (curLine.startsWith("--- ") && (myDiffFormat == null || myDiffFormat == DiffFormat.UNIFIED)) { + public List getPatches() { + return myPatches; + } + + public void parseAllPatches() throws PatchSyntaxException { + final ListIterator iterator = myLines.listIterator(); + if (! iterator.hasNext()) { + myPatches = Collections.emptyList(); + return; + } + + String next; + boolean containsAdditional = false; + while (iterator.hasNext()) { + next = iterator.next(); + final boolean containsAdditionalNow = myAdditionalInfoParser.testIsStart(next); + if (containsAdditionalNow && containsAdditional) { + myAdditionalInfoParser.acceptError(new PatchSyntaxException(iterator.previousIndex(), "Contains additional information without patch itself")); + } + if (containsAdditionalNow) { + containsAdditional = containsAdditionalNow; + myAdditionalInfoParser.parse(next, iterator); + if (! iterator.hasNext()) { + myAdditionalInfoParser.acceptError(new PatchSyntaxException(iterator.previousIndex(), "Contains additional information without patch itself")); + break; + } + next = iterator.next(); + } + + if (myPatchContentParser.testIsStart(next)) { + myPatchContentParser.parse(next, iterator); + //iterator.previous(); // to correctly initialize next + if (containsAdditional) { + final String lastName = myPatchContentParser.getLastName(); + if (lastName == null) { + myAdditionalInfoParser.acceptError(new PatchSyntaxException(iterator.previousIndex(), "Contains additional information without patch itself")); + } else { + myAdditionalInfoParser.copyToResult(lastName); + } + } + containsAdditional = false; + } + } + myPatches = myPatchContentParser.getResult(); + } + + public TransparentlyFailedValue>, PatchSyntaxException> getAdditionalInfo(final Set filterByPaths) { + final TransparentlyFailedValue>, PatchSyntaxException> + value = new TransparentlyFailedValue>, PatchSyntaxException>(); + + final Map> map = myAdditionalInfoParser.getResultMap(); + final Map>newMap = new HashMap>(); + + for (Map.Entry> entry : map.entrySet()) { + final Map innerMap = entry.getValue(); + if (filterByPaths == null || filterByPaths.contains(entry.getKey())) { + newMap.put(entry.getKey(), innerMap); + } + } + value.set(newMap); + final PatchSyntaxException e = myAdditionalInfoParser.getSyntaxException(); + if (e != null) { + value.fail(e); + } + return value; + } + + private static class AdditionalInfoParser implements Parser { + // first is path! + private final Map> myResultMap; + private Map myAddMap; + private PatchSyntaxException mySyntaxException; + + private AdditionalInfoParser() { + myAddMap = new HashMap(); + myResultMap = new HashMap>(); + } + + public PatchSyntaxException getSyntaxException() { + return mySyntaxException; + } + + public Map> getResultMap() { + return myResultMap; + } + + public void copyToResult(final String filePath) { + if (myAddMap != null && ! myAddMap.isEmpty()) { + myResultMap.put(filePath, myAddMap); + myAddMap = new HashMap(); + } + } + + @Override + public boolean testIsStart(String start) { + if (mySyntaxException != null) return false; // stop on first error + return start != null && start.contains(UnifiedDiffWriter.ADDITIONAL_PREFIX); + } + + @Override + public void parse(String start, ListIterator iterator) { + if (! iterator.hasNext()) { + mySyntaxException = new PatchSyntaxException(iterator.previousIndex(), "Empty additional info header"); + return; + } + while (true) { + final String header = iterator.next(); + final int idxHead = header.indexOf(UnifiedDiffWriter.ADD_INFO_HEADER); + if (idxHead == -1) { + if (myAddMap.isEmpty()) { + mySyntaxException = new PatchSyntaxException(iterator.previousIndex(), "Empty additional info header"); + } + iterator.previous(); + return; + } + + final String subsystem = header.substring(idxHead + UnifiedDiffWriter.ADD_INFO_HEADER.length()).trim(); + if (! iterator.hasNext()) { + mySyntaxException = new PatchSyntaxException(iterator.previousIndex(), "Empty '" + subsystem + "' data section"); + return; + } + + final StringBuilder sb = new StringBuilder(); + myAddMap.put(subsystem, sb); + while (iterator.hasNext()) { + final String line = iterator.next(); + if (! line.startsWith(UnifiedDiffWriter.ADD_INFO_LINE_START)) { + iterator.previous(); + break; + } + if (sb.length() > 0) { + sb.append("\n"); + } + sb.append(StringUtil.unescapeStringCharacters(line.substring(UnifiedDiffWriter.ADD_INFO_LINE_START.length()))); + } + } + } + + public void acceptError(PatchSyntaxException e) { + mySyntaxException = e; + } + } + + + private static class PatchContentParser implements Parser { + private DiffFormat myDiffFormat = null; + private final List myPatches; + + private PatchContentParser() { + myPatches = new SmartList(); + } + + @Override + public boolean testIsStart(String start) { + if (start.startsWith("--- ") && (myDiffFormat == null || myDiffFormat == DiffFormat.UNIFIED)) { myDiffFormat = DiffFormat.UNIFIED; - return readPatch(curLine); + return true; } - else if (curLine.startsWith(CONTEXT_FILE_PREFIX) && (myDiffFormat == null || myDiffFormat == DiffFormat.CONTEXT)) { + else if (start.startsWith(CONTEXT_FILE_PREFIX) && (myDiffFormat == null || myDiffFormat == DiffFormat.CONTEXT)) { myDiffFormat = DiffFormat.CONTEXT; - return readPatch(curLine); + return true; } - myLineIndex++; + return false; } - return null; - } - private TextFilePatch readPatch(String curLine) throws PatchSyntaxException { - final TextFilePatch curPatch; - curPatch = new TextFilePatch(); - extractFileName(curLine, curPatch, true); - myLineIndex++; - curLine = myLines [myLineIndex]; - String secondNamePrefix = myDiffFormat == DiffFormat.UNIFIED ? "+++ " : "--- "; - if (!curLine.startsWith(secondNamePrefix)) { - throw new PatchSyntaxException(myLineIndex, "Second file name expected"); + @Override + public void parse(String start, ListIterator iterator) throws PatchSyntaxException { + final TextFilePatch patch = readPatch(start, iterator); + if (patch != null) { + myPatches.add(patch); + } } - extractFileName(curLine, curPatch, false); - myLineIndex++; - while(myLineIndex < myLines.length) { - PatchHunk hunk; - if (myDiffFormat == DiffFormat.UNIFIED) { - hunk = readNextHunkUnified(); + + public List getResult() throws PatchSyntaxException { + return myPatches; + } + + private TextFilePatch readPatch(String curLine, ListIterator iterator) throws PatchSyntaxException { + final TextFilePatch curPatch = new TextFilePatch(); + extractFileName(curLine, curPatch, true); + + if (! iterator.hasNext()) throw new PatchSyntaxException(iterator.previousIndex(), "Second file name expected"); + curLine = iterator.next(); + String secondNamePrefix = myDiffFormat == DiffFormat.UNIFIED ? "+++ " : "--- "; + if (! curLine.startsWith(secondNamePrefix)) { + throw new PatchSyntaxException(iterator.previousIndex(), "Second file name expected"); + } + extractFileName(curLine, curPatch, false); + + while (iterator.hasNext()) { + PatchHunk hunk; + if (myDiffFormat == DiffFormat.UNIFIED) { + hunk = readNextHunkUnified(iterator); + } + else { + hunk = readNextHunkContext(iterator); + } + if (hunk == null) break; + curPatch.addHunk(hunk); + } + return curPatch; + } + + @Nullable + private PatchHunk readNextHunkUnified(ListIterator iterator) throws PatchSyntaxException { + String curLine = null; + int numIncrements = 0; + while (iterator.hasNext()) { + curLine = iterator.next(); + ++ numIncrements; + if (curLine.startsWith("--- ")) { + for (int i = 0; i < numIncrements; i++) { + iterator.previous(); + } + return null; + } + if (curLine.startsWith("@@ ")) { + break; + } + } + if (! iterator.hasNext()) return null; + + Matcher m = ourUnifiedHunkStartPattern.matcher(curLine); + if (!m.matches()) { + throw new PatchSyntaxException(iterator.previousIndex(), "Unknown hunk start syntax"); + } + int startLineBefore = Integer.parseInt(m.group(1)); + final String linesBeforeText = m.group(3); + int linesBefore = linesBeforeText == null ? 1 : Integer.parseInt(linesBeforeText); + int startLineAfter = Integer.parseInt(m.group(4)); + final String linesAfterText = m.group(6); + int linesAfter = linesAfterText == null ? 1 : Integer.parseInt(linesAfterText); + PatchHunk hunk = new PatchHunk(startLineBefore-1, startLineBefore+linesBefore-1, startLineAfter-1, startLineAfter+linesAfter-1); + + PatchLine lastLine = null; + while (iterator.hasNext()) { + String hunkCurLine = iterator.next(); + if (lastLine != null && hunkCurLine.startsWith(NO_NEWLINE_SIGNATURE)) { + lastLine.setSuppressNewLine(true); + continue; + } + if (hunkCurLine.startsWith("--- ")) { + iterator.previous(); + break; + } + lastLine = parsePatchLine(hunkCurLine, 1); + if (lastLine == null) { + iterator.previous(); + break; + } + hunk.addLine(lastLine); + } + return hunk; + } + + @Nullable + public String getLastName() { + if (myPatches.isEmpty()) { + return null; } else { - hunk = readNextHunkContext(); + final TextFilePatch patch = myPatches.get(myPatches.size() - 1); + return patch.getBeforeName() == null ? patch.getAfterName() : patch.getBeforeName(); } - if (hunk == null) break; - curPatch.addHunk(hunk); } - return curPatch; - } - @Nullable - private PatchHunk readNextHunkUnified() throws PatchSyntaxException { - while(myLineIndex < myLines.length) { - String curLine = myLines [myLineIndex]; - if (curLine.startsWith("--- ") && myLineIndex < myLines.length-1 && myLines [myLineIndex+1].startsWith("+++ ")) { + @Nullable + private static PatchLine parsePatchLine(final String line, final int prefixLength) { + PatchLine.Type type; + if (line.startsWith("+")) { + type = PatchLine.Type.ADD; + } + else if (line.startsWith("-")) { + type = PatchLine.Type.REMOVE; + } + else if (line.startsWith(" ")) { + type = PatchLine.Type.CONTEXT; + } + else { return null; } - if (curLine.startsWith("@@ ")) { - break; + String lineText; + if (line.length() < prefixLength) { + lineText = ""; } - myLineIndex++; - } - if (myLineIndex == myLines.length) { - return null; + else { + lineText = line.substring(prefixLength); + } + return new PatchLine(type, lineText); } - Matcher m = ourUnifiedHunkStartPattern.matcher(myLines [myLineIndex]); - if (!m.matches()) { - throw new PatchSyntaxException(myLineIndex, "Unknown hunk start syntax"); - } - int startLineBefore = Integer.parseInt(m.group(1)); - final String linesBeforeText = m.group(3); - int linesBefore = linesBeforeText == null ? 1 : Integer.parseInt(linesBeforeText); - int startLineAfter = Integer.parseInt(m.group(4)); - final String linesAfterText = m.group(6); - int linesAfter = linesAfterText == null ? 1 : Integer.parseInt(linesAfterText); - PatchHunk hunk = new PatchHunk(startLineBefore-1, startLineBefore+linesBefore-1, startLineAfter-1, startLineAfter+linesAfter-1); - myLineIndex++; - PatchLine lastLine = null; - while(myLineIndex < myLines.length) { - String curLine = myLines [myLineIndex]; - if (lastLine != null && curLine.startsWith(NO_NEWLINE_SIGNATURE)) { - lastLine.setSuppressNewLine(true); - myLineIndex++; - continue; + @Nullable + private PatchHunk readNextHunkContext(ListIterator iterator) throws PatchSyntaxException { + while (iterator.hasNext()) { + String curLine = iterator.next(); + if (curLine.startsWith(CONTEXT_FILE_PREFIX)) { + return null; + } + if (curLine.startsWith(CONTEXT_HUNK_PREFIX)) { + break; + } } - if (curLine.startsWith("--- ")) { - break; - } - lastLine = parsePatchLine(curLine, 1); - if (lastLine == null) { - break; - } - hunk.addLine(lastLine); - myLineIndex++; - } - return hunk; - } - - @Nullable - private static PatchLine parsePatchLine(final String line, final int prefixLength) { - PatchLine.Type type; - if (line.startsWith("+")) { - type = PatchLine.Type.ADD; - } - else if (line.startsWith("-")) { - type = PatchLine.Type.REMOVE; - } - else if (line.startsWith(" ")) { - type = PatchLine.Type.CONTEXT; - } - else { - return null; - } - String lineText; - if (line.length() < prefixLength) { - lineText = ""; - } - else { - lineText = line.substring(prefixLength); - } - return new PatchLine(type, lineText); - } - - @Nullable - private PatchHunk readNextHunkContext() throws PatchSyntaxException { - while(myLineIndex < myLines.length) { - String curLine = myLines [myLineIndex]; - if (curLine.startsWith(CONTEXT_FILE_PREFIX)) { + if (! iterator.hasNext()) { return null; } - if (curLine.startsWith(CONTEXT_HUNK_PREFIX)) { - break; + Matcher beforeMatcher = ourContextBeforeHunkStartPattern.matcher(iterator.next()); + if (! beforeMatcher.matches()) { + throw new PatchSyntaxException(iterator.previousIndex(), "Unknown before hunk start syntax"); } - myLineIndex++; - } - if (myLineIndex == myLines.length) { - return null; - } - myLineIndex++; - Matcher beforeMatcher = ourContextBeforeHunkStartPattern.matcher(myLines [myLineIndex]); - if (!beforeMatcher.matches()) { - throw new PatchSyntaxException(myLineIndex, "Unknown before hunk start syntax"); - } - myLineIndex++; - List beforeLines = readContextDiffLines(); - if (myLineIndex == myLines.length) { - throw new PatchSyntaxException(myLineIndex, "Missing after hunk"); - } - Matcher afterMatcher = ourContextAfterHunkStartPattern.matcher(myLines [myLineIndex]); - if (!afterMatcher.matches()) { - throw new PatchSyntaxException(myLineIndex, "Unknown after hunk start syntax"); - } - myLineIndex++; - List afterLines = readContextDiffLines(); - int startLineBefore = Integer.parseInt(beforeMatcher.group(1)); - int endLineBefore = Integer.parseInt(beforeMatcher.group(2)); - int startLineAfter = Integer.parseInt(afterMatcher.group(1)); - int endLineAfter = Integer.parseInt(afterMatcher.group(2)); - PatchHunk hunk = new PatchHunk(startLineBefore-1, endLineBefore-1, startLineAfter-1, endLineAfter-1); + List beforeLines = readContextDiffLines(iterator); + if (! iterator.hasNext()) { + throw new PatchSyntaxException(iterator.previousIndex(), "Missing after hunk"); + } + Matcher afterMatcher = ourContextAfterHunkStartPattern.matcher(iterator.next()); + if (! afterMatcher.matches()) { + throw new PatchSyntaxException(iterator.previousIndex(), "Unknown after hunk start syntax"); + } + //if (! iterator.hasNext()) { + //throw new PatchSyntaxException(iterator.previousIndex(), "Unexpected patch end"); + //} + List afterLines = readContextDiffLines(iterator); + int startLineBefore = Integer.parseInt(beforeMatcher.group(1)); + int endLineBefore = Integer.parseInt(beforeMatcher.group(2)); + int startLineAfter = Integer.parseInt(afterMatcher.group(1)); + int endLineAfter = Integer.parseInt(afterMatcher.group(2)); + PatchHunk hunk = new PatchHunk(startLineBefore-1, endLineBefore-1, startLineAfter-1, endLineAfter-1); - int beforeLineIndex = 0; - int afterLineIndex = 0; - PatchLine lastBeforePatchLine = null; - PatchLine lastAfterPatchLine = null; - if (beforeLines.size() == 0) { - for(String line: afterLines) { - hunk.addLine(parsePatchLine(line, 2)); + int beforeLineIndex = 0; + int afterLineIndex = 0; + PatchLine lastBeforePatchLine = null; + PatchLine lastAfterPatchLine = null; + if (beforeLines.size() == 0) { + for(String line: afterLines) { + hunk.addLine(parsePatchLine(line, 2)); + } } - } - else if (afterLines.size() == 0) { - for(String line: beforeLines) { - hunk.addLine(parsePatchLine(line, 2)); + else if (afterLines.size() == 0) { + for(String line: beforeLines) { + hunk.addLine(parsePatchLine(line, 2)); + } } - } - else { - while(beforeLineIndex < beforeLines.size() || afterLineIndex < afterLines.size()) { - String beforeLine = beforeLineIndex >= beforeLines.size() ? null : beforeLines.get(beforeLineIndex); - String afterLine = afterLineIndex >= afterLines.size() ? null : afterLines.get(afterLineIndex); - if (startsWith(beforeLine, NO_NEWLINE_SIGNATURE) && lastBeforePatchLine != null) { - lastBeforePatchLine.setSuppressNewLine(true); - beforeLineIndex++; - } - else if (startsWith(afterLine, NO_NEWLINE_SIGNATURE) && lastAfterPatchLine != null) { - lastAfterPatchLine.setSuppressNewLine(true); - afterLineIndex++; - } - else if (startsWith(beforeLine, " ") && - (startsWith(afterLine, " ") || afterLine == null /* handle some weird cases with line breaks truncated at EOF */ )) { - addContextDiffLine(hunk, beforeLine, PatchLine.Type.CONTEXT); - beforeLineIndex++; - afterLineIndex++; - } - else if (startsWith(beforeLine, "-")) { - lastBeforePatchLine = addContextDiffLine(hunk, beforeLine, PatchLine.Type.REMOVE); - beforeLineIndex++; - } - else if (startsWith(afterLine, "+")) { - lastAfterPatchLine = addContextDiffLine(hunk, afterLine, PatchLine.Type.ADD); - afterLineIndex++; - } - else if (startsWith(beforeLine, "!") && startsWith(afterLine, "!")) { - while(beforeLineIndex < beforeLines.size() && beforeLines.get(beforeLineIndex).startsWith("! ")) { - lastBeforePatchLine = addContextDiffLine(hunk, beforeLines.get(beforeLineIndex), PatchLine.Type.REMOVE); + else { + while(beforeLineIndex < beforeLines.size() || afterLineIndex < afterLines.size()) { + String beforeLine = beforeLineIndex >= beforeLines.size() ? null : beforeLines.get(beforeLineIndex); + String afterLine = afterLineIndex >= afterLines.size() ? null : afterLines.get(afterLineIndex); + if (startsWith(beforeLine, NO_NEWLINE_SIGNATURE) && lastBeforePatchLine != null) { + lastBeforePatchLine.setSuppressNewLine(true); beforeLineIndex++; } - - while(afterLineIndex < afterLines.size() && afterLines.get(afterLineIndex).startsWith("! ")) { - lastAfterPatchLine = addContextDiffLine(hunk, afterLines.get(afterLineIndex), PatchLine.Type.ADD); + else if (startsWith(afterLine, NO_NEWLINE_SIGNATURE) && lastAfterPatchLine != null) { + lastAfterPatchLine.setSuppressNewLine(true); afterLineIndex++; } - } - else { - throw new PatchSyntaxException(-1, "Unknown line prefix"); + else if (startsWith(beforeLine, " ") && + (startsWith(afterLine, " ") || afterLine == null /* handle some weird cases with line breaks truncated at EOF */ )) { + addContextDiffLine(hunk, beforeLine, PatchLine.Type.CONTEXT); + beforeLineIndex++; + afterLineIndex++; + } + else if (startsWith(beforeLine, "-")) { + lastBeforePatchLine = addContextDiffLine(hunk, beforeLine, PatchLine.Type.REMOVE); + beforeLineIndex++; + } + else if (startsWith(afterLine, "+")) { + lastAfterPatchLine = addContextDiffLine(hunk, afterLine, PatchLine.Type.ADD); + afterLineIndex++; + } + else if (startsWith(beforeLine, "!") && startsWith(afterLine, "!")) { + while(beforeLineIndex < beforeLines.size() && beforeLines.get(beforeLineIndex).startsWith("! ")) { + lastBeforePatchLine = addContextDiffLine(hunk, beforeLines.get(beforeLineIndex), PatchLine.Type.REMOVE); + beforeLineIndex++; + } + + while(afterLineIndex < afterLines.size() && afterLines.get(afterLineIndex).startsWith("! ")) { + lastAfterPatchLine = addContextDiffLine(hunk, afterLines.get(afterLineIndex), PatchLine.Type.ADD); + afterLineIndex++; + } + } + else { + throw new PatchSyntaxException(-1, "Unknown line prefix"); + } } } + return hunk; } - return hunk; - } - private static boolean startsWith(@Nullable final String line, final String prefix) { - return line != null && line.startsWith(prefix); - } - - private static PatchLine addContextDiffLine(final PatchHunk hunk, final String line, final PatchLine.Type type) { - final PatchLine patchLine = new PatchLine(type, line.length() < 2 ? "" : line.substring(2)); - hunk.addLine(patchLine); - return patchLine; - } - - private List readContextDiffLines() { - ArrayList result = new ArrayList(); - while(myLineIndex < myLines.length) { - final String line = myLines[myLineIndex]; - if (!line.startsWith(" ") && !line.startsWith("+ ") && !line.startsWith("- ") && !line.startsWith("! ") && - !line.startsWith(NO_NEWLINE_SIGNATURE)) { - break; - } - result.add(line); - myLineIndex++; + private static boolean startsWith(@Nullable final String line, final String prefix) { + return line != null && line.startsWith(prefix); } - return result; - } - private static void extractFileName(final String curLine, final FilePatch patch, final boolean before) { - String fileName = curLine.substring(4); - int pos = fileName.indexOf('\t'); - if (pos < 0) { - pos = fileName.indexOf(' '); + private static PatchLine addContextDiffLine(final PatchHunk hunk, final String line, final PatchLine.Type type) { + final PatchLine patchLine = new PatchLine(type, line.length() < 2 ? "" : line.substring(2)); + hunk.addLine(patchLine); + return patchLine; } - if (pos >= 0) { - String versionId = fileName.substring(pos).trim(); - fileName = fileName.substring(0, pos); - if (versionId.length() > 0) { - if (before) { - patch.setBeforeVersionId(versionId); + + private List readContextDiffLines(ListIterator iterator) { + ArrayList result = new ArrayList(); + while (iterator.hasNext()) { + final String line = iterator.next(); + if (!line.startsWith(" ") && !line.startsWith("+ ") && !line.startsWith("- ") && !line.startsWith("! ") && + !line.startsWith(NO_NEWLINE_SIGNATURE)) { + iterator.previous(); + break; } - else { - patch.setAfterVersionId(versionId); + result.add(line); + } + return result; + } + + private static void extractFileName(final String curLine, final FilePatch patch, final boolean before) { + String fileName = curLine.substring(4); + int pos = fileName.indexOf('\t'); + if (pos < 0) { + pos = fileName.indexOf(' '); + } + if (pos >= 0) { + String versionId = fileName.substring(pos).trim(); + fileName = fileName.substring(0, pos); + if (versionId.length() > 0) { + if (before) { + patch.setBeforeVersionId(versionId); + } + else { + patch.setAfterVersionId(versionId); + } } } + if (before) { + patch.setBeforeName(fileName); + } + else { + patch.setAfterName(fileName); + } } - if (before) { - patch.setBeforeName(fileName); - } - else { - patch.setAfterName(fileName); - } + } + + private interface Parser { + boolean testIsStart(final String start); + void parse(final String start, final ListIterator iterator) throws PatchSyntaxException; } } diff --git a/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/UnifiedDiffWriter.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/UnifiedDiffWriter.java index 037cbb0c925e..656858f70410 100644 --- a/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/UnifiedDiffWriter.java +++ b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/UnifiedDiffWriter.java @@ -22,25 +22,48 @@ */ package com.intellij.openapi.diff.impl.patch; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NonNls; import java.io.IOException; import java.io.Writer; import java.text.MessageFormat; import java.util.Collection; +import java.util.List; +import java.util.Map; public class UnifiedDiffWriter { @NonNls private static final String INDEX_SIGNATURE = "Index: {0}{1}"; + @NonNls public static final String ADDITIONAL_PREFIX = "IDEA additional info:"; + @NonNls public static final String ADD_INFO_HEADER = "Subsystem: "; + @NonNls public static final String ADD_INFO_LINE_START = "<+>"; private static final String HEADER_SEPARATOR = "==================================================================="; private UnifiedDiffWriter() { } - public static void write(Collection patches, Writer writer, final String lineSeparator) throws IOException { + public static void write(Project project, Collection patches, Writer writer, final String lineSeparator) throws IOException { + final PatchEP[] extensions = project == null ? new PatchEP[0] : Extensions.getExtensions(PatchEP.EP_NAME, project); + write(patches, writer, lineSeparator, extensions); + } + + public static void write(Collection patches, Writer writer, final String lineSeparator, + final PatchEP[] extensions) throws IOException { for(FilePatch filePatch: patches) { if (!(filePatch instanceof TextFilePatch)) continue; TextFilePatch patch = (TextFilePatch) filePatch; - writeFileHeading(patch, writer, lineSeparator); + final String path = patch.getBeforeName() == null ? patch.getAfterName() : patch.getBeforeName(); + final Map additionalMap = new HashMap(); + for (PatchEP extension : extensions) { + final CharSequence charSequence = extension.provideContent(path); + if (! StringUtil.isEmpty(charSequence)) { + additionalMap.put(extension.getName(), charSequence); + } + } + writeFileHeading(patch, writer, lineSeparator, additionalMap); for(PatchHunk hunk: patch.getHunks()) { writeHunkStart(writer, hunk.getStartLineBefore(), hunk.getEndLineBefore(), hunk.getStartLineAfter(), hunk.getEndLineAfter(), lineSeparator); @@ -67,8 +90,26 @@ public class UnifiedDiffWriter { } } - private static void writeFileHeading(final FilePatch patch, final Writer writer, final String lineSeparator) throws IOException { + private static void writeFileHeading(final FilePatch patch, + final Writer writer, + final String lineSeparator, + Map additionalMap) throws IOException { writer.write(MessageFormat.format(INDEX_SIGNATURE, patch.getBeforeName(), lineSeparator)); + if (additionalMap != null && ! additionalMap.isEmpty()) { + writer.write(ADDITIONAL_PREFIX); + writer.write(lineSeparator); + for (Map.Entry entry : additionalMap.entrySet()) { + writer.write(ADD_INFO_HEADER + entry.getKey()); + writer.write(lineSeparator); + final String value = StringUtil.escapeStringCharacters(entry.getValue().toString()); + final List lines = StringUtil.split(value, "\n"); + for (String line : lines) { + writer.write(ADD_INFO_LINE_START); + writer.write(line); + writer.write(lineSeparator); + } + } + } writer.write(HEADER_SEPARATOR + lineSeparator); writeRevisionHeading(writer, "---", patch.getBeforeName(), patch.getBeforeVersionId(), lineSeparator); writeRevisionHeading(writer, "+++", patch.getAfterName(), patch.getAfterVersionId(), lineSeparator); @@ -98,4 +139,4 @@ public class UnifiedDiffWriter { writer.write(prefix); writer.write(line); } -} \ No newline at end of file +} diff --git a/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/formove/TestPatchEP.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/formove/TestPatchEP.java new file mode 100644 index 000000000000..e837b57177fb --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/formove/TestPatchEP.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2011 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.formove; + +import com.intellij.openapi.diff.impl.patch.PatchEP; +import org.jetbrains.annotations.NotNull; + +/** + * @author irengrig + * Date: 7/12/11 + * Time: 1:15 PM + */ +public class TestPatchEP implements PatchEP { + private final static String ourName = "com.intellij.openapi.diff.impl.patch.formove.TestPatchEP"; + private final static String ourContent = "ourContent\nseveral\nlines\twith\u0142\u0001 different symbols"; + + @NotNull + @Override + public String getName() { + return ourName; + } + + @Override + public CharSequence provideContent(@NotNull String path) { + return ourContent + path; + } + + @Override + public void consumeContent(@NotNull String path, @NotNull CharSequence content) { + assert (ourContent + path).equals(content.toString()); + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/TransparentlyFailedValue.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/TransparentlyFailedValue.java similarity index 89% rename from platform/vcs-impl/src/com/intellij/openapi/vcs/changes/TransparentlyFailedValue.java rename to platform/vcs-api/src/com/intellij/openapi/vcs/changes/TransparentlyFailedValue.java index dfb33375808f..d34da8fd984a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/TransparentlyFailedValue.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/TransparentlyFailedValue.java @@ -36,4 +36,9 @@ public class TransparentlyFailedValue { if (this.e != null) throw this.e; return this.t; } + + public void take(final TransparentlyFailedValue value) { + this.t = value.t; + this.e = value.e; + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ObjectsConvertor.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ObjectsConvertor.java index efc5a8a12739..282a4900a12a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ObjectsConvertor.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ObjectsConvertor.java @@ -27,6 +27,19 @@ import java.util.Collection; import java.util.List; public class ObjectsConvertor { + private final static DownCast DOWN_CAST = new DownCast(); + + public static class DownCast implements Convertor { + @Override + public Sup convert(Sub o) { + return o; + } + } + + public static List downcast(List list) { + return convert(list, (Convertor) DOWN_CAST); + } + public static final Convertor FILEPATH_TO_VIRTUAL = new Convertor() { public VirtualFile convert(FilePath fp) { return fp.getVirtualFile(); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDefaultExecutor.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDefaultExecutor.java index 2261b7039e8a..af1f7c7dd641 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDefaultExecutor.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDefaultExecutor.java @@ -17,16 +17,25 @@ package com.intellij.openapi.vcs.changes.patch; import com.intellij.openapi.diff.impl.patch.BinaryFilePatch; import com.intellij.openapi.diff.impl.patch.FilePatch; +import com.intellij.openapi.diff.impl.patch.PatchEP; +import com.intellij.openapi.diff.impl.patch.PatchSyntaxException; import com.intellij.openapi.diff.impl.patch.formove.PatchApplier; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.vcs.ObjectsConvertor; import com.intellij.openapi.vcs.changes.LocalChangeList; +import com.intellij.openapi.vcs.changes.TransparentlyFailedValue; +import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.Convertor; +import com.intellij.util.containers.HashSet; import com.intellij.util.containers.MultiMap; import java.util.Collection; import java.util.LinkedList; +import java.util.Map; +import java.util.Set; /** * @author irengrig @@ -47,7 +56,10 @@ public class ApplyPatchDefaultExecutor implements ApplyPatchExecutor { } @Override - public void apply(MultiMap patchGroups, LocalChangeList localList, String fileName) { + public void apply(MultiMap patchGroups, + LocalChangeList localList, + String fileName, + TransparentlyFailedValue>, PatchSyntaxException> additionalInfo) { final Collection appliers = new LinkedList(); for (VirtualFile base : patchGroups.keySet()) { final PatchApplier patchApplier = @@ -60,5 +72,43 @@ public class ApplyPatchDefaultExecutor implements ApplyPatchExecutor { appliers.add(patchApplier); } PatchApplier.executePatchGroup(appliers); + + applyAdditionalInfo(myProject, additionalInfo); + } + + public static void applyAdditionalInfo(final Project project, + TransparentlyFailedValue>, PatchSyntaxException> additionalInfo) { + final PatchEP[] extensions = Extensions.getExtensions(PatchEP.EP_NAME, project); + if (extensions == null && extensions.length == 0) return; + if (additionalInfo != null) { + try { + final Map> map = additionalInfo.get(); + for (Map.Entry> entry : map.entrySet()) { + final String path = entry.getKey(); + final Map innerMap = entry.getValue(); + + for (PatchEP extension : extensions) { + final CharSequence charSequence = innerMap.get(extension.getName()); + if (charSequence != null) { + extension.consumeContent(path, charSequence); + } + } + } + } + catch (PatchSyntaxException e) { + VcsBalloonProblemNotifier + .showOverChangesView(project, "Can not apply additional patch info: " + e.getMessage(), MessageType.ERROR); + } + } + } + + public static Set pathsFromGroups(MultiMap patchGroups) { + final Set selectedPaths = new HashSet(); + final Collection values = patchGroups.values(); + for (FilePatchInProgress value : values) { + final String path = value.getPatch().getBeforeName() == null ? value.getPatch().getAfterName() : value.getPatch().getBeforeName(); + selectedPaths.add(path); + } + return selectedPaths; } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java index d83ba267f863..36f0ecacfa36 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java @@ -17,7 +17,10 @@ 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.*; +import com.intellij.openapi.diff.impl.patch.PatchReader; +import com.intellij.openapi.diff.impl.patch.PatchSyntaxException; +import com.intellij.openapi.diff.impl.patch.PatchVirtualFileReader; +import com.intellij.openapi.diff.impl.patch.TextFilePatch; import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.fileTypes.FileTypes; @@ -86,6 +89,7 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { private boolean myContainBasedChanges; private JLabel myPatchFileLabel; + private PatchReader myReader; public ApplyPatchDifferentiatedDialog(final Project project, final ApplyPatchExecutor callback, final List executors, @NotNull final ApplyPatchMode applyPatchMode, @NotNull final VirtualFile patchFile) { @@ -196,7 +200,9 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { for (FilePatchInProgress patchInProgress : included) { patchGroups.putValue(patchInProgress.getBase(), patchInProgress); } - executor.apply(patchGroups, getSelectedChangeList(), myRecentPathFileChange.get().getVf().getName()); + final LocalChangeList selected = getSelectedChangeList(); + executor.apply(patchGroups, selected, myRecentPathFileChange.get().getVf().getName(), + myReader == null ? null : myReader.getAdditionalInfo(ApplyPatchDefaultExecutor.pathsFromGroups(patchGroups))); } @Override @@ -229,64 +235,42 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { } final VirtualFile file = filePresentation.getVf(); - final List patches = loadPatches(file); - final AutoMatchIterator autoMatchIterator = new AutoMatchIterator(myProject); - final List matchedPathes = autoMatchIterator.execute(patches); + final PatchReader patchReader = loadPatches(file); + final List matchedPathes = patchReader == null ? Collections.emptyList() : + new AutoMatchIterator(myProject).execute(patchReader.getPatches()); SwingUtilities.invokeLater(new Runnable() { public void run() { myChangeListChooser.setDefaultName(file.getNameWithoutExtension().replace('_', ' ').trim()); myPatches.clear(); myPatches.addAll(matchedPathes); - + myReader = patchReader; updateTree(true); } }); } } - private List loadPatches(final VirtualFile patchFile) { + @Nullable + private PatchReader loadPatches(final VirtualFile patchFile) { if (! patchFile.isValid()) { - //todo - //queueUpdateStatus("Cannot find patch file"); - return Collections.emptyList(); + return null; } PatchReader reader; try { reader = PatchVirtualFileReader.create(patchFile); } catch (IOException e) { - //todo - //queueUpdateStatus(VcsBundle.message("patch.apply.open.error", e.getMessage())); - return Collections.emptyList(); + return null; + } + try { + reader.parseAllPatches(); + } + catch (PatchSyntaxException e) { + return null; } - final List result = new LinkedList(); - while(true) { - FilePatch patch; - try { - patch = reader.readNextPatch(); - } - catch (PatchSyntaxException e) { - // todo - if (e.getLine() >= 0) { - //queueUpdateStatus(VcsBundle.message("patch.apply.load.error.line", e.getMessage(), e.getLine())); - } - else { - //queueUpdateStatus(VcsBundle.message("patch.apply.load.error", e.getMessage())); - } - return Collections.emptyList(); - } - if (patch == null) { - break; - } - result.add((TextFilePatch) patch); - } - if (myPatches.isEmpty()) { - // todo - //queueUpdateStatus(VcsBundle.message("patch.apply.no.patches.found")); - } - return result; + return reader; } private static class FilePresentation { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchExecutor.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchExecutor.java index db506e7577bf..4166204b9060 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchExecutor.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchExecutor.java @@ -15,10 +15,14 @@ */ package com.intellij.openapi.vcs.changes.patch; +import com.intellij.openapi.diff.impl.patch.PatchSyntaxException; import com.intellij.openapi.vcs.changes.LocalChangeList; +import com.intellij.openapi.vcs.changes.TransparentlyFailedValue; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.MultiMap; +import java.util.Map; + /** * @author irengrig * Date: 2/25/11 @@ -26,5 +30,8 @@ import com.intellij.util.containers.MultiMap; */ public interface ApplyPatchExecutor { String getName(); - void apply(final MultiMap patchGroups, final LocalChangeList localList, String fileName); + void apply(final MultiMap patchGroups, + final LocalChangeList localList, + String fileName, + TransparentlyFailedValue>, PatchSyntaxException> additionalInfo); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ImportToShelfExecutor.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ImportToShelfExecutor.java index 008375f36973..1ce381a09c12 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ImportToShelfExecutor.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ImportToShelfExecutor.java @@ -16,26 +16,33 @@ package com.intellij.openapi.vcs.changes.patch; import com.intellij.openapi.diff.impl.patch.FilePatch; +import com.intellij.openapi.diff.impl.patch.PatchEP; +import com.intellij.openapi.diff.impl.patch.PatchSyntaxException; import com.intellij.openapi.diff.impl.patch.TextFilePatch; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.AbstractVcsHelper; import com.intellij.openapi.vcs.ObjectsConvertor; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.LocalChangeList; +import com.intellij.openapi.vcs.changes.TransparentlyFailedValue; import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager; import com.intellij.openapi.vcs.changes.shelf.ShelvedChangeList; import com.intellij.openapi.vcs.changes.shelf.ShelvedChangesViewManager; +import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.Convertor; import com.intellij.util.containers.MultiMap; import com.intellij.vcsUtil.VcsCatchingRunnable; +import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * @author irengrig @@ -56,7 +63,10 @@ public class ImportToShelfExecutor implements ApplyPatchExecutor { } @Override - public void apply(final MultiMap patchGroups, LocalChangeList localList, final String fileName) { + public void apply(final MultiMap patchGroups, + LocalChangeList localList, + final String fileName, + final TransparentlyFailedValue>, PatchSyntaxException> additionalInfo) { final VcsCatchingRunnable vcsCatchingRunnable = new VcsCatchingRunnable() { @Override public void runImpl() throws VcsException { @@ -77,9 +87,24 @@ public class ImportToShelfExecutor implements ApplyPatchExecutor { } })); } - if (!allPatches.isEmpty()) { + if (! allPatches.isEmpty()) { + PatchEP[] patchTransitExtensions = null; + if (additionalInfo != null) { + try { + final List list = new ArrayList(); + for (Map.Entry> entry : additionalInfo.get().entrySet()) { + list.add(new TransitExtension(entry.getKey(), entry.getValue())); + } + patchTransitExtensions = list.toArray(new PatchEP[list.size()]); + } + catch (PatchSyntaxException e) { + VcsBalloonProblemNotifier + .showOverChangesView(myProject, "Can not import additional patch info: " + e.getMessage(), MessageType.ERROR); + } + } try { - final ShelvedChangeList shelvedChangeList = ShelveChangesManager.getInstance(myProject).importFilePatches(fileName, allPatches); + final ShelvedChangeList shelvedChangeList = ShelveChangesManager.getInstance(myProject). + importFilePatches(fileName, allPatches, patchTransitExtensions); ShelvedChangesViewManager.getInstance(myProject).activateView(shelvedChangeList); } catch (IOException e) { @@ -93,4 +118,30 @@ public class ImportToShelfExecutor implements ApplyPatchExecutor { AbstractVcsHelper.getInstance(myProject).showErrors(vcsCatchingRunnable.get(), IMPORT_TO_SHELF); } } + + private static class TransitExtension implements PatchEP { + private final String myName; + private final Map myMap; + + private TransitExtension(String name, Map map) { + myName = name; + myMap = map; + } + + @NotNull + @Override + public String getName() { + return myName; + } + + @Override + public CharSequence provideContent(@NotNull String path) { + return myMap.get(path); + } + + @Override + public void consumeContent(@NotNull String path, @NotNull CharSequence content) { + throw new UnsupportedOperationException(); + } + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/PatchWriter.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/PatchWriter.java index 5c68cd0c64d0..7f11b62f0040 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/PatchWriter.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/PatchWriter.java @@ -39,7 +39,7 @@ public class PatchWriter { Writer writer = new OutputStreamWriter(new FileOutputStream(fileName)); try { final String lineSeparator = CodeStyleFacade.getInstance(project).getLineSeparator(); - UnifiedDiffWriter.write(patches, writer, lineSeparator); + UnifiedDiffWriter.write(project, patches, writer, lineSeparator); } finally { writer.close(); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java index be8821d03544..be20b3f4b31b 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java @@ -178,7 +178,7 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl myFileProcessor.savePathFile( new CompoundShelfFileProcessor.ContentProvider(){ public void writeContentTo(final Writer writer) throws IOException { - UnifiedDiffWriter.write(patches, writer, "\n"); + UnifiedDiffWriter.write(myProject, patches, writer, "\n"); } }, patchPath); @@ -198,13 +198,13 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl return changeList; } - public ShelvedChangeList importFilePatches(final String fileName, final List patches) throws IOException { + public ShelvedChangeList importFilePatches(final String fileName, final List patches, final PatchEP[] patchTransitExtensions) throws IOException { try { final File patchPath = getPatchPath(fileName); myFileProcessor.savePathFile( new CompoundShelfFileProcessor.ContentProvider(){ public void writeContentTo(final Writer writer) throws IOException { - UnifiedDiffWriter.write(patches, writer, "\n"); + UnifiedDiffWriter.write(patches, writer, "\n", patchTransitExtensions); } }, patchPath); @@ -484,12 +484,12 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl return false; } - private static void writePatchesToFile(final String path, final List remainingPatches) { + private static void writePatchesToFile(final Project project, final String path, final List remainingPatches) { OutputStreamWriter writer; try { writer = new OutputStreamWriter(new FileOutputStream(path)); try { - UnifiedDiffWriter.write(remainingPatches, writer, "\n"); + UnifiedDiffWriter.write(project, remainingPatches, writer, "\n"); } finally { writer.close(); @@ -514,7 +514,7 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl new ArrayList(changeList.getBinaryFiles())); listCopy.DATE = (changeList.DATE == null) ? null : new Date(changeList.DATE.getTime()); - writePatchesToFile(changeList.PATH, remainingPatches); + writePatchesToFile(myProject, changeList.PATH, remainingPatches); changeList.getBinaryFiles().retainAll(remainingBinaries); changeList.clearLoadedChanges(); @@ -569,7 +569,7 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl for (ShelvedChange change : listCopy.getChanges()) { patches.add(change.loadFilePatch()); } - writePatchesToFile(listCopy.PATH, patches); + writePatchesToFile(myProject, listCopy.PATH, patches); } catch (IOException e) { LOG.info(e);