| /* |
| * Copyright 2000-2009 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. |
| */ |
| |
| /* |
| * Class DiffFragmentBuilder |
| * @author Jeka |
| */ |
| package com.intellij.openapi.diff.impl; |
| |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.diff.impl.string.DiffString; |
| import com.intellij.openapi.diff.ex.DiffFragment; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.util.diff.Diff; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| /** |
| Builds a sequence of DiffFragment objects thus diffing 2 files |
| Parses the output of CVS 'diff' command assumed to be in the RCS Normal Format |
| Format of the output chunks for the command: 'diff file1 file2': |
| change-command |
| < from-file-line |
| < from-file-line... |
| --- |
| > to-file-line |
| > to-file-line... |
| |
| Where: |
| Change-Command -> Line a Range |
| Change-Command -> Range c Range |
| Change-Command -> Range d Line |
| Range -> Line , Line |
| Range -> Line |
| Line -> number-of-line |
| |
| The commands are: |
| a: append a range of lines from the file2 after line Line of the file1 |
| c: change the range of lines in the file1 to the range from file2 |
| d: Delete the lines in range Range from the file1; line Line is where they would have appeared in the file2 had they not been deleted |
| */ |
| public class DiffFragmentBuilder { |
| |
| private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.DiffFragmentBuilder"); |
| |
| @NotNull private final DiffString[] mySource1; |
| @NotNull private final DiffString[] mySource2; |
| private int myLastLine1 = 1; |
| private int myLastLine2 = 1; |
| @NotNull private final List<DiffFragment> myData = new LinkedList<DiffFragment>(); |
| |
| public DiffFragmentBuilder(@NotNull DiffString[] source1, @NotNull DiffString[] source2) { |
| mySource1 = source1; |
| mySource2 = source2; |
| init(); |
| } |
| |
| @NotNull |
| private List<DiffFragment> getFragments() { |
| return myData; |
| } |
| |
| private void finish() { |
| DiffString text1 = null; |
| DiffString text2 = null; |
| if (myLastLine1 <= mySource1.length) { |
| text1 = concatenate(mySource1, myLastLine1, mySource1.length); |
| } |
| if (myLastLine2 <= mySource2.length) { |
| text2 = concatenate((mySource2), myLastLine2, mySource2.length); |
| } |
| if (text1 != null || text2 != null) { |
| myData.add(DiffFragment.unchanged(text1, text2)); |
| } |
| } |
| |
| private void init() { |
| myData.clear(); |
| myLastLine1 = myLastLine2 = 1; |
| } |
| |
| private void append(int line, @NotNull TextRange range) { |
| LOG.debug("DiffFragmentBuilder.append(" + line + "," + range + "), modified:"); |
| DiffString text1 = null; |
| DiffString text2 = null; |
| int start = range.getStartOffset(); |
| int end = range.getEndOffset(); |
| if (myLastLine1 <= line) { |
| text1 = concatenate(mySource1, myLastLine1, line); |
| } |
| if (myLastLine2 < start) { |
| text2 = concatenate(mySource2, myLastLine2, start - 1); |
| } |
| if (text1 != null || text2 != null) { |
| myData.add(DiffFragment.unchanged(text1, text2)); |
| } |
| myData.add(new DiffFragment(null, concatenate(mySource2, start, end))); |
| myLastLine1 = line + 1; |
| myLastLine2 = end + 1; |
| } |
| |
| private void change(@NotNull TextRange range1, @NotNull TextRange range2) { |
| LOG.debug("DiffFragmentBuilder.change(" + range1 + "," + range2 + ")"); |
| |
| DiffString text1 = null, text2 = null; |
| int start1 = range1.getStartOffset(); |
| int end1 = range1.getEndOffset(); |
| int start2 = range2.getStartOffset(); |
| int end2 = range2.getEndOffset(); |
| if (myLastLine1 < start1) { |
| text1 = concatenate(mySource1, myLastLine1, start1 - 1); |
| } |
| if (myLastLine2 < start2) { |
| text2 = concatenate(mySource2, myLastLine2, start2 - 1); |
| } |
| if (text1 != null || text2 != null) { |
| myData.add(DiffFragment.unchanged(text1, text2)); |
| } |
| myData.add(new DiffFragment(concatenate(mySource1, start1, end1), |
| concatenate(mySource2, start2, end2))); |
| myLastLine1 = end1 + 1; |
| myLastLine2 = end2 + 1; |
| } |
| |
| private void delete(@NotNull TextRange range, int line) { |
| LOG.debug("DiffFragmentBuilder.delete(" + range + "," + line + ")"); |
| |
| DiffString text1 = null; |
| DiffString text2 = null; |
| int start = range.getStartOffset(); |
| int end = range.getEndOffset(); |
| if (myLastLine1 < start) { |
| text1 = concatenate(mySource1, myLastLine1, start - 1); |
| } |
| if (myLastLine2 <= line) { |
| text2 = concatenate(mySource2, myLastLine2, line); |
| } |
| if (text1 != null || text2 != null) { |
| myData.add(DiffFragment.unchanged(text1, text2)); |
| } |
| myData.add(new DiffFragment(concatenate(mySource1, start, end), null)); |
| myLastLine1 = end + 1; |
| myLastLine2 = line + 1; |
| } |
| |
| @NotNull |
| private static DiffString concatenate(@NotNull DiffString[] strings, int start, int end) { |
| return DiffString.concatenate(strings, start - 1, end - start + 1); |
| } |
| |
| @NotNull |
| public DiffFragment[] buildFragments(@Nullable Diff.Change change) { |
| while (change != null) { |
| if (change.inserted > 0 && change.deleted > 0) { |
| change( |
| new TextRange(change.line0 + 1, change.line0 + change.deleted), |
| new TextRange(change.line1 + 1, change.line1 + change.inserted) |
| ); |
| } |
| else if (change.inserted > 0) { |
| append(change.line0, new TextRange(change.line1 + 1, change.line1 + change.inserted)); |
| } |
| else if (change.deleted > 0) { |
| delete(new TextRange(change.line0 + 1, change.line0 + change.deleted), change.line1); |
| } |
| change = change.link; |
| } |
| finish(); |
| |
| final List<DiffFragment> fragments = getFragments(); |
| return fragments.toArray(new DiffFragment[myData.size()]); |
| } |
| } |