/* * Copyright 2007 Guy Van den Broeck * * 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 org.outerj.daisy.diff.html; import java.util.LinkedList; import java.util.List; import org.eclipse.compare.internal.LCSSettings; import org.eclipse.compare.rangedifferencer.RangeDifference; import org.eclipse.compare.rangedifferencer.RangeDifferencer; import org.outerj.daisy.diff.html.modification.ModificationType; import org.outerj.daisy.diff.output.DiffOutput; import org.outerj.daisy.diff.output.Differ; import org.xml.sax.SAXException; /** * Takes two or three {@link TextNodeComparator} instances, computes the difference * between them, marks the changes, and outputs a merged tree to a * {@link HtmlSaxDiffOutput} instance. */ public class HTMLDiffer implements Differ{ private DiffOutput output; public HTMLDiffer(DiffOutput dm) { output = dm; } public void diff(TextNodeComparator ancestorComparator, TextNodeComparator leftComparator, TextNodeComparator rightComparator) throws SAXException { LCSSettings settings = new LCSSettings(); settings.setUseGreedyMethod(false); // settings.setPowLimit(1.5); // settings.setTooLong(100000*100000); org.eclipse.compare.rangedifferencer.RangeDifference[] differences = RangeDifferencer.findDifferences(settings, ancestorComparator, leftComparator, rightComparator); List<RangeDifference> pdifferences = preProcess(differences); int currentIndexAncestor = 0; int currentIndexLeft = 0; int currentIndexRight = 0; for (RangeDifference d : pdifferences) { int tempKind = d.kind(); if (tempKind == RangeDifference.ANCESTOR) { // ignore, we won't show pseudo-conflicts currently (left and right have the same change) continue; } if (d.leftStart() > currentIndexLeft) { ancestorComparator.handlePossibleChangedPart(currentIndexLeft, d .leftStart(), currentIndexAncestor, d.ancestorStart(), leftComparator); } if (d.rightStart() > currentIndexRight) { ancestorComparator.handlePossibleChangedPart(currentIndexRight, d .rightStart(), currentIndexAncestor, d.ancestorStart(), rightComparator); } if (tempKind == RangeDifference.CONFLICT || tempKind == RangeDifference.LEFT) { // conflicts and changes on the left side if (d.leftLength() > 0) { ancestorComparator.markAsDeleted(d.leftStart(), d.leftEnd(), leftComparator, d.ancestorStart(), d.ancestorEnd(), ModificationType.ADDED); } } if (tempKind == RangeDifference.CONFLICT || tempKind == RangeDifference.RIGHT) { // conflicts and changes on the right side if (d.rightLength() > 0) { ancestorComparator.markAsDeleted(d.rightStart(), d.rightEnd(), rightComparator, d.ancestorStart(), d.ancestorEnd(), ModificationType.ADDED); } } ancestorComparator.markAsNew(d.ancestorStart(), d.ancestorEnd(), ModificationType.REMOVED); currentIndexAncestor = d.ancestorEnd(); currentIndexLeft = d.leftEnd(); currentIndexRight = d.rightEnd(); } if (currentIndexLeft < leftComparator.getRangeCount()) { ancestorComparator.handlePossibleChangedPart(currentIndexLeft, leftComparator.getRangeCount(), currentIndexAncestor, ancestorComparator.getRangeCount(), leftComparator); } if (currentIndexRight < rightComparator.getRangeCount()) { ancestorComparator.handlePossibleChangedPart(currentIndexRight, rightComparator.getRangeCount(), currentIndexAncestor, ancestorComparator.getRangeCount(), rightComparator); } ancestorComparator.expandWhiteSpace(); output.generateOutput(ancestorComparator.getBodyNode()); } /** * {@inheritDoc} */ public void diff(TextNodeComparator leftComparator, TextNodeComparator rightComparator) throws SAXException { LCSSettings settings = new LCSSettings(); settings.setUseGreedyMethod(false); // settings.setPowLimit(1.5); // settings.setTooLong(100000*100000); RangeDifference[] differences = RangeDifferencer.findDifferences( settings, leftComparator, rightComparator); List<RangeDifference> pdifferences = preProcess(differences); int currentIndexLeft = 0; int currentIndexRight = 0; for (RangeDifference d : pdifferences) { if (d.leftStart() > currentIndexLeft) { rightComparator.handlePossibleChangedPart(currentIndexLeft, d .leftStart(), currentIndexRight, d.rightStart(), leftComparator); } if (d.leftLength() > 0) { rightComparator.markAsDeleted(d.leftStart(), d.leftEnd(), leftComparator, d.rightStart(), d.rightEnd()); } rightComparator.markAsNew(d.rightStart(), d.rightEnd()); currentIndexLeft = d.leftEnd(); currentIndexRight = d.rightEnd(); } if (currentIndexLeft < leftComparator.getRangeCount()) { rightComparator.handlePossibleChangedPart(currentIndexLeft, leftComparator.getRangeCount(), currentIndexRight, rightComparator.getRangeCount(), leftComparator); } rightComparator.expandWhiteSpace(); output.generateOutput(rightComparator.getBodyNode()); } private List<RangeDifference> preProcess(RangeDifference[] differences) { List<RangeDifference> newRanges = new LinkedList<RangeDifference>(); for (int i = 0; i < differences.length; i++) { int ancestorStart = differences[i].ancestorStart(); int ancestorEnd = differences[i].ancestorEnd(); int leftStart = differences[i].leftStart(); int leftEnd = differences[i].leftEnd(); int rightStart = differences[i].rightStart(); int rightEnd = differences[i].rightEnd(); int kind = differences[i].kind(); int ancestorLength = ancestorEnd - ancestorStart; int leftLength = leftEnd - leftStart; int rightLength = rightEnd - rightStart; while (i + 1 < differences.length && differences[i + 1].kind() == kind && score(leftLength, differences[i + 1].leftLength(), rightLength, differences[i + 1].rightLength()) > (differences[i + 1] .leftStart() - leftEnd)) { leftEnd = differences[i + 1].leftEnd(); rightEnd = differences[i + 1].rightEnd(); ancestorEnd = differences[i + 1].ancestorEnd(); leftLength = leftEnd - leftStart; rightLength = rightEnd - rightStart; ancestorLength = ancestorEnd - ancestorStart; i++; } newRanges.add(new RangeDifference(kind, rightStart, rightLength, leftStart, leftLength, ancestorStart, ancestorLength)); } return newRanges; } public static double score(int... numbers) { if ((numbers[0] == 0 && numbers[1] == 0) || (numbers[2] == 0 && numbers[3] == 0)) return 0; double d = 0; for (double number : numbers) { while (number > 3) { d += 3; number -= 3; number *= 0.5; } d += number; } return d / (1.5 * numbers.length); } }