All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.compare.rangedifferencer.RangeDifferencer Maven / Gradle / Ivy

There is a newer version: 11.5.0
Show newest version
/*******************************************************************************
 * Copyright (c) 2000, 2007 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;
import java.util.List;

import org.eclipse.compare.internal.CompareMessages;
import org.eclipse.compare.internal.LCSSettings;

/**
 * A RangeDifferencer finds the differences between two or three
 * IRangeComparators.
 * 

* To use the differencer, clients provide an IRangeComparator * that breaks their input data into a sequence of comparable entities. The * differencer returns the differences among these sequences as an array of * RangeDifference objects (findDifferences * methods). Every RangeDifference represents a single kind of * difference and the corresponding ranges of the underlying comparable entities * in the left, right, and optionally ancestor sides. *

* Alternatively, the findRanges methods not only return objects * for the differing ranges but for non-differing ranges too. *

* 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. * * @see IRangeComparator * @see RangeDifference */ public final class RangeDifferencer { private static final RangeDifference[] EMPTY_RESULT = new RangeDifference[0]; /* * (non Javadoc) Cannot be instantiated! */ private RangeDifferencer() { // nothing to do } /** * Finds the differences between two IRangeComparators. The * differences are returned as an array of RangeDifferences. * If no differences are detected an empty array is returned. * * @param left * the left range comparator * @param right * the right range comparator * @return an array of range differences, or an empty array if no * differences were found */ // public static RangeDifference[] findDifferences(LCSSettings settings, // IRangeComparator left, IRangeComparator right) { // return findDifferences( settings, left, right); // } /** * Finds the differences between two IRangeComparators. The * differences are returned as an array of RangeDifferences. * If no differences are detected an empty array is returned. * * @param left * the left range comparator * @param right * the right range comparator * @return an array of range differences, or an empty array if no * differences were found */ public static RangeDifference[] findDifferences(IRangeComparator left, IRangeComparator right) { return findDifferences( new LCSSettings(), left, right); } /** * Finds the differences between two IRangeComparators. The * differences are returned as an array of RangeDifferences. * If no differences are detected an empty array is returned. * * @param pm * if not null used to report progress * @param left * the left range comparator * @param right * the right range comparator * @return an array of range differences, or an empty array if no * differences were found * @since 2.0 */ public static RangeDifference[] findDifferences( LCSSettings settings, IRangeComparator left, IRangeComparator right) { if (!settings.isUseGreedyMethod()) { return OldDifferencer.findDifferences( left, right); } return RangeComparatorLCS.findDifferences( settings, left, right); } /** * Finds the differences among three IRangeComparators. The * differences are returned as a list of RangeDifferences. * If no differences are detected an empty list is returned. If the ancestor * range comparator is null, a two-way comparison is * performed. * * @param pm * if not null used to report progress * @param ancestor * the ancestor range comparator or null * @param left * the left range comparator * @param right * the right range comparator * @return an array of range differences, or an empty array if no * differences were found * @since 2.0 */ public static RangeDifference[] findDifferences( LCSSettings settings, IRangeComparator ancestor, IRangeComparator left, IRangeComparator right) { if (ancestor == null) return findDifferences( settings, left, right); RangeDifference[] leftAncestorScript = null; RangeDifference[] rightAncestorScript = findDifferences( settings, ancestor, right); if (rightAncestorScript != null) { leftAncestorScript = findDifferences(settings, ancestor, left); } if (rightAncestorScript == null || leftAncestorScript == null) return null; DifferencesIterator myIter = new DifferencesIterator( rightAncestorScript); DifferencesIterator yourIter = new DifferencesIterator( leftAncestorScript); List diff3 = new ArrayList(); diff3.add(new RangeDifference(RangeDifference.ERROR)); // add a // sentinel int changeRangeStart = 0; int changeRangeEnd = 0; // // Combine the two two-way edit scripts into one // while (myIter.fDifference != null || yourIter.fDifference != null) { DifferencesIterator startThread; myIter.removeAll(); yourIter.removeAll(); // // take the next diff that is closer to the start // if (myIter.fDifference == null) startThread = yourIter; else if (yourIter.fDifference == null) startThread = myIter; else { // not at end of both scripts take the lowest range if (myIter.fDifference.fLeftStart <= yourIter.fDifference.fLeftStart) // 2 -> // common // (Ancestor) // change // range startThread = myIter; else startThread = yourIter; } changeRangeStart = startThread.fDifference.fLeftStart; changeRangeEnd = startThread.fDifference.leftEnd(); startThread.next(); // // check for overlapping changes with other thread // merge overlapping changes with this range // DifferencesIterator other = startThread.other(myIter, yourIter); while (other.fDifference != null && other.fDifference.fLeftStart <= changeRangeEnd) { int newMax = other.fDifference.leftEnd(); other.next(); if (newMax >= changeRangeEnd) { changeRangeEnd = newMax; other = other.other(myIter, yourIter); } } diff3.add(createRangeDifference3(myIter, yourIter, diff3, right, left, changeRangeStart, changeRangeEnd)); } // remove sentinel diff3.remove(0); return (RangeDifference[]) diff3.toArray(EMPTY_RESULT); } /** * Finds the differences among two IRangeComparators. In * contrast to findDifferences, the result contains * RangeDifference elements for non-differing ranges too. * * @param left * the left range comparator * @param right * the right range comparator * @return an array of range differences * @since 2.0 */ public static RangeDifference[] findRanges( LCSSettings settings, IRangeComparator left, IRangeComparator right) { RangeDifference[] in = findDifferences( settings, left, right); List out = new ArrayList(); RangeDifference rd; int mstart = 0; int ystart = 0; for (int i = 0; i < in.length; i++) { RangeDifference es = in[i]; rd = new RangeDifference(RangeDifference.NOCHANGE, mstart, es .rightStart() - mstart, ystart, es.leftStart() - ystart); if (rd.maxLength() != 0) out.add(rd); out.add(es); mstart = es.rightEnd(); ystart = es.leftEnd(); } rd = new RangeDifference(RangeDifference.NOCHANGE, mstart, right .getRangeCount() - mstart, ystart, left.getRangeCount() - ystart); if (rd.maxLength() > 0) out.add(rd); return (RangeDifference[]) out.toArray(EMPTY_RESULT); } /** * Finds the differences among three IRangeComparators. In * contrast to findDifferences, the result contains * RangeDifference elements for non-differing ranges too. If * the ancestor range comparator is null, a two-way * comparison is performed. * * @param ancestor * the ancestor range comparator or null * @param left * the left range comparator * @param right * the right range comparator * @return an array of range differences */ // public static RangeDifference[] findRanges(LCSSettings settings, // IRangeComparator ancestor, IRangeComparator left, // IRangeComparator right) { // return findRanges( settings, ancestor, left, right); // } /** * Finds the differences among three IRangeComparators. In * contrast to findDifferences, the result contains * RangeDifference elements for non-differing ranges too. If * the ancestor range comparator is null, a two-way * comparison is performed. * * @param pm * if not null used to report progress * @param ancestor * the ancestor range comparator or null * @param left * the left range comparator * @param right * the right range comparator * @return an array of range differences * @since 2.0 */ public static RangeDifference[] findRanges( LCSSettings settings, IRangeComparator ancestor, IRangeComparator left, IRangeComparator right) { if (ancestor == null) return findRanges(settings, left, right); RangeDifference[] in = findDifferences(settings, ancestor, left, right); List out = new ArrayList(); RangeDifference rd; int mstart = 0; int ystart = 0; int astart = 0; for (int i = 0; i < in.length; i++) { RangeDifference es = in[i]; rd = new RangeDifference(RangeDifference.NOCHANGE, mstart, es .rightStart() - mstart, ystart, es.leftStart() - ystart, astart, es .ancestorStart() - astart); if (rd.maxLength() > 0) out.add(rd); out.add(es); mstart = es.rightEnd(); ystart = es.leftEnd(); astart = es.ancestorEnd(); } rd = new RangeDifference(RangeDifference.NOCHANGE, mstart, right .getRangeCount() - mstart, ystart, left.getRangeCount() - ystart, astart, ancestor.getRangeCount() - astart); if (rd.maxLength() > 0) out.add(rd); return (RangeDifference[]) out.toArray(EMPTY_RESULT); } // ---- private methods /* * Creates a RangeDifference3 given the state of two * DifferenceIterators. */ private static RangeDifference createRangeDifference3( DifferencesIterator myIter, DifferencesIterator yourIter, List diff3, IRangeComparator right, IRangeComparator left, int changeRangeStart, int changeRangeEnd) { int rightStart, rightEnd; int leftStart, leftEnd; int kind = RangeDifference.ERROR; RangeDifference last = (RangeDifference) diff3.get(diff3.size() - 1); // Assert.isTrue((myIter.getCount() != 0 || yourIter.getCount() != 0)); // At // least // one // range // array // must // be // non-empty // // find corresponding lines to fChangeRangeStart/End in right and left // if (myIter.getCount() == 0) { // only left changed rightStart = changeRangeStart - last.ancestorEnd() + last.rightEnd(); rightEnd = changeRangeEnd - last.ancestorEnd() + last.rightEnd(); kind = RangeDifference.LEFT; } else { RangeDifference f = (RangeDifference) myIter.fRange.get(0); RangeDifference l = (RangeDifference) myIter.fRange .get(myIter.fRange.size() - 1); rightStart = changeRangeStart - f.fLeftStart + f.fRightStart; rightEnd = changeRangeEnd - l.leftEnd() + l.rightEnd(); } if (yourIter.getCount() == 0) { // only right changed leftStart = changeRangeStart - last.ancestorEnd() + last.leftEnd(); leftEnd = changeRangeEnd - last.ancestorEnd() + last.leftEnd(); kind = RangeDifference.RIGHT; } else { RangeDifference f = (RangeDifference) yourIter.fRange.get(0); RangeDifference l = (RangeDifference) yourIter.fRange .get(yourIter.fRange.size() - 1); leftStart = changeRangeStart - f.fLeftStart + f.fRightStart; leftEnd = changeRangeEnd - l.leftEnd() + l.rightEnd(); } if (kind == RangeDifference.ERROR) { // overlapping change (conflict) // -> compare the changed ranges if (rangeSpansEqual(right, rightStart, rightEnd - rightStart, left, leftStart, leftEnd - leftStart)) kind = RangeDifference.ANCESTOR; else kind = RangeDifference.CONFLICT; } return new RangeDifference(kind, rightStart, rightEnd - rightStart, leftStart, leftEnd - leftStart, changeRangeStart, changeRangeEnd - changeRangeStart); } /* * Tests whether right and left changed in the * same way */ private static boolean rangeSpansEqual(IRangeComparator right, int rightStart, int rightLen, IRangeComparator left, int leftStart, int leftLen) { if (rightLen == leftLen) { int i = 0; for (i = 0; i < rightLen; i++) { if (!rangesEqual(right, rightStart + i, left, leftStart + i)) break; } if (i == rightLen) return true; } return false; } /* * Tests if two ranges are equal */ private static boolean rangesEqual(IRangeComparator a, int ai, IRangeComparator b, int bi) { return a.rangesEqual(ai, b, bi); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy