/* * Copyright 2000-2015 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.diff.comparison; import com.intellij.diff.comparison.ByLine.Line; import com.intellij.diff.comparison.iterables.DiffIterableUtil; import com.intellij.diff.comparison.iterables.FairDiffIterable; import com.intellij.diff.util.Range; import com.intellij.openapi.progress.ProgressIndicator; import gnu.trove.TIntArrayList; import org.jetbrains.annotations.NotNull; import java.util.List; import static com.intellij.diff.comparison.TrimUtil.expand; import static com.intellij.diff.comparison.iterables.DiffIterableUtil.diff; import static com.intellij.diff.comparison.iterables.DiffIterableUtil.fair; /* * Base class for two-step diff algorithms. * Given matching between some sub-sequences of base sequences - build matching on whole base sequences */ abstract class ChangeCorrector { private final int myLength1; private final int myLength2; @NotNull private final FairDiffIterable myChanges; @NotNull protected final ProgressIndicator myIndicator; @NotNull protected final DiffIterableUtil.ChangeBuilder myBuilder; public ChangeCorrector(int length1, int length2, @NotNull FairDiffIterable changes, @NotNull ProgressIndicator indicator) { myLength1 = length1; myLength2 = length2; myChanges = changes; myIndicator = indicator; myBuilder = new DiffIterableUtil.ChangeBuilder(length1, length2); } @NotNull public FairDiffIterable build() { execute(); return fair(myBuilder.finish()); } protected void execute() { int last1 = 0; int last2 = 0; for (Range ch : myChanges.iterateUnchanged()) { int count = ch.end1 - ch.start1; for (int i = 0; i < count; i++) { int index1 = getOriginalIndex1(ch.start1 + i); int index2 = getOriginalIndex2(ch.start2 + i); matchGap(last1, index1, last2, index2); myBuilder.markEqual(index1, index2); last1 = index1 + 1; last2 = index2 + 1; } } matchGap(last1, myLength1, last2, myLength2); } // match elements in range [start1 - end1) -> [start2 - end2) protected abstract void matchGap(int start1, int end1, int start2, int end2); protected abstract int getOriginalIndex1(int index); protected abstract int getOriginalIndex2(int index); // // Implementations // public static class DefaultCharChangeCorrector extends ChangeCorrector { @NotNull private final ByChar.CharOffsets myChars1; @NotNull private final ByChar.CharOffsets myChars2; @NotNull private final CharSequence myText1; @NotNull private final CharSequence myText2; public DefaultCharChangeCorrector(@NotNull ByChar.CharOffsets chars1, @NotNull ByChar.CharOffsets chars2, @NotNull CharSequence text1, @NotNull CharSequence text2, @NotNull FairDiffIterable changes, @NotNull ProgressIndicator indicator) { super(text1.length(), text2.length(), changes, indicator); myChars1 = chars1; myChars2 = chars2; myText1 = text1; myText2 = text2; } @Override protected void matchGap(int start1, int end1, int start2, int end2) { Range expand = expand(myText1, myText2, start1, start2, end1, end2); CharSequence inner1 = myText1.subSequence(expand.start1, expand.end1); CharSequence inner2 = myText2.subSequence(expand.start2, expand.end2); FairDiffIterable innerChanges = ByChar.compare(inner1, inner2, myIndicator); myBuilder.markEqual(start1, start2, expand.start1, expand.start2); for (Range chunk : innerChanges.iterateUnchanged()) { myBuilder.markEqual(expand.start1 + chunk.start1, expand.start2 + chunk.start2, chunk.end1 - chunk.start1); } myBuilder.markEqual(expand.end1, expand.end2, end1, end2); } @Override protected int getOriginalIndex1(int index) { return myChars1.offsets[index]; } @Override protected int getOriginalIndex2(int index) { return myChars2.offsets[index]; } } public static class SmartLineChangeCorrector extends ChangeCorrector { @NotNull private final TIntArrayList myIndexes1; @NotNull private final TIntArrayList myIndexes2; @NotNull private final List<Line> myLines1; @NotNull private final List<Line> myLines2; public SmartLineChangeCorrector(@NotNull TIntArrayList indexes1, @NotNull TIntArrayList indexes2, @NotNull List<Line> lines1, @NotNull List<Line> lines2, @NotNull FairDiffIterable changes, @NotNull ProgressIndicator indicator) { super(lines1.size(), lines2.size(), changes, indicator); myIndexes1 = indexes1; myIndexes2 = indexes2; myLines1 = lines1; myLines2 = lines2; } @Override protected void matchGap(int start1, int end1, int start2, int end2) { Range expand = expand(myLines1, myLines2, start1, start2, end1, end2); List<Line> inner1 = myLines1.subList(expand.start1, expand.end1); List<Line> inner2 = myLines2.subList(expand.start2, expand.end2); FairDiffIterable innerChanges = diff(inner1, inner2, myIndicator); myBuilder.markEqual(start1, start2, expand.start1, expand.start2); for (Range chunk : innerChanges.iterateUnchanged()) { myBuilder.markEqual(expand.start1 + chunk.start1, expand.start2 + chunk.start2, chunk.end1 - chunk.start1); } myBuilder.markEqual(expand.end1, expand.end2, end1, end2); } @Override protected int getOriginalIndex1(int index) { return myIndexes1.get(index); } @Override protected int getOriginalIndex2(int index) { return myIndexes2.get(index); } } }