/* * Copyright 2000-2014 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.util.diff; import java.util.BitSet; class PatienceIntLCS { private final int[] myFirst; private final int[] mySecond; private final int myStart1; private final int myStart2; private final int myCount1; private final int myCount2; private final BitSet myChanges1; private final BitSet myChanges2; public PatienceIntLCS(int[] first, int[] second) { this(first, second, 0, first.length, 0, second.length, new BitSet(first.length), new BitSet(second.length)); } public PatienceIntLCS(int[] first, int[] second, int start1, int count1, int start2, int count2, BitSet changes1, BitSet changes2) { myFirst = first; mySecond = second; myStart1 = start1; myStart2 = start2; myCount1 = count1; myCount2 = count2; myChanges1 = changes1; myChanges2 = changes2; } public void execute() throws FilesTooBigForDiffException { execute(false); } public void execute(boolean failOnSmallReduction) throws FilesTooBigForDiffException { int thresholdCheckCounter = failOnSmallReduction ? 2 : -1; execute(myStart1, myCount1, myStart2, myCount2, thresholdCheckCounter); } private void execute(int start1, int count1, int start2, int count2, int thresholdCheckCounter) throws FilesTooBigForDiffException { if (count1 == 0 && count2 == 0) { return; } if (count1 == 0 || count2 == 0) { addChange(start1, count1, start2, count2); return; } int startOffset = matchForward(start1, count1, start2, count2); start1 += startOffset; start2 += startOffset; count1 -= startOffset; count2 -= startOffset; int endOffset = matchBackward(start1, count1, start2, count2); count1 -= endOffset; count2 -= endOffset; if (count1 == 0 || count2 == 0) { addChange(start1, count1, start2, count2); } else { if (thresholdCheckCounter == 0) checkReduction(count1, count2); thresholdCheckCounter = Math.max(-1, thresholdCheckCounter - 1); UniqueLCS uniqueLCS = new UniqueLCS(myFirst, mySecond, start1, count1, start2, count2); int[][] matching = uniqueLCS.execute(); if (matching == null) { if (thresholdCheckCounter >= 0) checkReduction(count1, count2); IntLCS intLCS = new IntLCS(myFirst, mySecond, start1, count1, start2, count2, myChanges1, myChanges2); intLCS.execute(); } else { int s1, s2, c1, c2; int matched = matching[0].length; assert matched > 0; c1 = matching[0][0]; c2 = matching[1][0]; execute(start1, c1, start2, c2, thresholdCheckCounter); for (int i = 1; i < matching[0].length; i++) { s1 = matching[0][i - 1] + 1; s2 = matching[1][i - 1] + 1; c1 = matching[0][i] - s1; c2 = matching[1][i] - s2; if (c1 > 0 || c2 > 0) { execute(start1 + s1, c1, start2 + s2, c2, thresholdCheckCounter); } } if (matching[0][matched - 1] == count1 - 1) { s1 = count1 - 1; c1 = 0; } else { s1 = matching[0][matched - 1] + 1; c1 = count1 - s1; } if (matching[1][matched - 1] == count2 - 1) { s2 = count2 - 1; c2 = 0; } else { s2 = matching[1][matched - 1] + 1; c2 = count2 - s2; } execute(start1 + s1, c1, start2 + s2, c2, thresholdCheckCounter); } } } private int matchForward(int start1, int count1, int start2, int count2) { final int size = Math.min(count1, count2); int idx = 0; for (int i = 0; i < size; i++) { if (!(myFirst[start1 + i] == mySecond[start2 + i])) break; ++idx; } return idx; } private int matchBackward(int start1, int count1, int start2, int count2) { final int size = Math.min(count1, count2); int idx = 0; for (int i = 1; i <= size; i++) { if (!(myFirst[start1 + count1 - i] == mySecond[start2 + count2 - i])) break; ++idx; } return idx; } private void addChange(int start1, int count1, int start2, int count2) { myChanges1.set(start1, start1 + count1); myChanges2.set(start2, start2 + count2); } public BitSet[] getChanges() { return new BitSet[]{myChanges1, myChanges2}; } private void checkReduction(int count1, int count2) throws FilesTooBigForDiffException { if (count1 * 2 < myCount1) return; if (count2 * 2 < myCount2) return; throw new FilesTooBigForDiffException(0); } }