org.eclipse.compare.rangedifferencer.OldDifferencer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of docx4j Show documentation
Show all versions of docx4j Show documentation
docx4j is a library which helps you to work with the Office Open
XML file format as used in docx
documents, pptx presentations, and xlsx spreadsheets.
/*******************************************************************************
* Copyright (c) 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.compare.rangedifferencer;
import java.util.ArrayList;
/**
* The algorithm used is an objectified version of one described in: A File
* Comparison Program, by Webb Miller and Eugene W. Myers, Software
* Practice and Experience, Vol. 15, Nov. 1985.
*/
/* package */class OldDifferencer {
private static final RangeDifference[] EMPTY_RESULT = new RangeDifference[0];
private static class LinkedRangeDifference extends RangeDifference {
static final int INSERT = 0;
static final int DELETE = 1;
LinkedRangeDifference fNext;
/*
* Creates a LinkedRangeDifference an initializes it to the error state
*/
LinkedRangeDifference() {
super(ERROR);
fNext = null;
}
/*
* Constructs and links a LinkeRangeDifference to another
* LinkedRangeDifference
*/
LinkedRangeDifference(LinkedRangeDifference next, int operation) {
super(operation);
fNext = next;
}
/*
* Follows the next link
*/
LinkedRangeDifference getNext() {
return fNext;
}
boolean isDelete() {
return kind() == DELETE;
}
boolean isInsert() {
return kind() == INSERT;
}
/*
* Sets the next link of this LinkedRangeDifference
*/
void setNext(LinkedRangeDifference next) {
fNext = next;
}
}
public static RangeDifference[] findDifferences(IRangeComparator left, IRangeComparator right) {
// assert that both IRangeComparators are of the same class
// Assert.isTrue(right.getClass().equals(left.getClass()));
int rightSize = right.getRangeCount();
int leftSize = left.getRangeCount();
//
// Differences matrix:
// only the last d of each diagonal is stored, i.e., lastDiagonal[k] =
// row of d
//
int diagLen = 2 * Math.max(rightSize, leftSize); // bound on the size
// of edit script
int maxDiagonal = diagLen;
int lastDiagonal[] = new int[diagLen + 1]; // the row containing the
// last d
// on diagonal k (lastDiagonal[k] = row)
int origin = diagLen / 2; // origin of diagonal 0
// script corresponding to d[k]
LinkedRangeDifference script[] = new LinkedRangeDifference[diagLen + 1];
int row, col;
// find common prefix
for (row = 0; row < rightSize && row < leftSize
&& rangesEqual(right, row, left, row) == true;)
row++;
lastDiagonal[origin] = row;
script[origin] = null;
int lower = (row == rightSize) ? origin + 1 : origin - 1;
int upper = (row == leftSize) ? origin - 1 : origin + 1;
if (lower > upper)
return EMPTY_RESULT;
// System.out.println("findDifferences: " + maxDiagonal + " " + lower +
// " " + upper);
// for each value of the edit distance
for (int d = 1; d <= maxDiagonal; ++d) { // d is the current edit
// distance
if (right.skipRangeComparison(d, maxDiagonal, left))
return EMPTY_RESULT; // should be something we already found
// for each relevant diagonal (-d, -d+2 ..., d-2, d)
for (int k = lower; k <= upper; k += 2) { // k is the current
// diagonal
LinkedRangeDifference edit;
if (k == origin - d || k != origin + d
&& lastDiagonal[k + 1] >= lastDiagonal[k - 1]) {
//
// move down
//
row = lastDiagonal[k + 1] + 1;
edit = new LinkedRangeDifference(script[k + 1],
LinkedRangeDifference.DELETE);
} else {
//
// move right
//
row = lastDiagonal[k - 1];
edit = new LinkedRangeDifference(script[k - 1],
LinkedRangeDifference.INSERT);
}
col = row + k - origin;
edit.fRightStart = row;
edit.fLeftStart = col;
// Assert.isTrue(k >= 0 && k <= maxDiagonal);
script[k] = edit;
// slide down the diagonal as far as possible
while (row < rightSize && col < leftSize
&& rangesEqual(right, row, left, col) == true) {
++row;
++col;
}
// Assert.isTrue(k >= 0 && k <= maxDiagonal); // Unreasonable
// value for
// diagonal index
lastDiagonal[k] = row;
if (row == rightSize && col == leftSize) {
// showScript(script[k], right, left);
return createDifferencesRanges(script[k]);
}
if (row == rightSize)
lower = k + 2;
if (col == leftSize)
upper = k - 2;
}
--lower;
++upper;
}
// too many differences
// Assert.isTrue(false);
return null;
}
/*
* Tests if two ranges are equal
*/
private static boolean rangesEqual(IRangeComparator a, int ai,
IRangeComparator b, int bi) {
return a.rangesEqual(ai, b, bi);
}
/*
* Creates a Vector of DifferencesRanges out of the LinkedRangeDifference.
* It coalesces adjacent changes. In addition, indices are changed such that
* the ranges are 1) open, i.e, the end of the range is not included, and 2)
* are zero based.
*/
private static RangeDifference[] createDifferencesRanges(
LinkedRangeDifference start) {
LinkedRangeDifference ep = reverseDifferences(start);
ArrayList result = new ArrayList();
RangeDifference es = null;
while (ep != null) {
es = new RangeDifference(RangeDifference.CHANGE);
if (ep.isInsert()) {
es.fRightStart = ep.fRightStart + 1;
es.fLeftStart = ep.fLeftStart;
RangeDifference b = ep;
do {
ep = ep.getNext();
es.fLeftLength++;
} while (ep != null && ep.isInsert()
&& ep.fRightStart == b.fRightStart);
} else {
es.fRightStart = ep.fRightStart;
es.fLeftStart = ep.fLeftStart;
RangeDifference a = ep;
//
// deleted lines
//
do {
a = ep;
ep = ep.getNext();
es.fRightLength++;
} while (ep != null && ep.isDelete()
&& ep.fRightStart == a.fRightStart + 1);
boolean change = (ep != null && ep.isInsert() && ep.fRightStart == a.fRightStart);
if (change) {
RangeDifference b = ep;
//
// replacement lines
//
do {
ep = ep.getNext();
es.fLeftLength++;
} while (ep != null && ep.isInsert()
&& ep.fRightStart == b.fRightStart);
} else {
es.fLeftLength = 0;
}
es.fLeftStart++; // meaning of range changes from "insert
// after", to "replace with"
}
//
// the script commands are 1 based, subtract one to make them zero
// based
//
es.fRightStart--;
es.fLeftStart--;
result.add(es);
}
return (RangeDifference[]) result.toArray(EMPTY_RESULT);
}
/*
* Reverses the range differences
*/
private static LinkedRangeDifference reverseDifferences(
LinkedRangeDifference start) {
LinkedRangeDifference ep, behind, ahead;
ahead = start;
ep = null;
while (ahead != null) {
behind = ep;
ep = ahead;
ahead = ahead.getNext();
ep.setNext(behind);
}
return ep;
}
}