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

org.incava.diff.Differ Maven / Gradle / Ivy

package org.incava.diff;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

/**
 * Compares two collections, returning a list of the additions, changes, and
 * deletions between them. A Comparator may be passed as an
 * argument to the constructor, and will thus be used. If not provided, the
 * initial value in the from collection will be looked at to see if
 * it supports the Comparable interface. If so, its
 * equals and compareTo methods will be invoked on the
 * instances in the "from" and "to" collections; otherwise, for speed, hash
 * codes from the objects will be used instead for comparison.
 *
 * 

The file FileDiff.java shows an example usage of this class, in an * application similar to the Unix "diff" program.

*/ public abstract class Differ { /** * The source array, AKA the "from" values. */ private final List from; /** * The target array, AKA the "to" values. */ private final List to; /** * The list of differences, as DiffType instances. */ private final List diffs; /** * The point at which the pending deletion starts. */ private Integer delStart = null; /** * The point at which the pending deletion ends. */ private Integer delEnd = null; /** * The point at which the pending addition starts. */ private Integer addStart = null; /** * The point at which the pending addition ends. */ private Integer addEnd = null; /** * The comparator used, if any. */ private final Comparator comparator; /** * Constructs the Differ object for the two arrays, using the given comparator. */ public Differ(ObjectType[] from, ObjectType[] to, Comparator comp) { this(Arrays.asList(from), Arrays.asList(to), comp); } /** * Constructs the Differ object for the two arrays, using the default * comparison mechanism between the objects, such as equals and * compareTo. */ public Differ(ObjectType[] from, ObjectType[] to) { this(from, to, null); } /** * Constructs the Differ object for the two collections, using the default * comparison mechanism between the objects, such as equals and * compareTo. */ public Differ(List from, List to) { this(from, to, null); } /** * Constructs the Differ object for the two collections, using the given * comparator. */ public Differ(List from, List to, Comparator comp) { this.from = from; this.to = to; this.comparator = comp; this.diffs = new ArrayList(); } /** * Runs diff and returns the results. */ public List execute() { traverseSequences(); addPending(); return diffs; } /** * Runs diff and returns the results. * * @deprecated execute is a more accurate and descriptive name. */ @Deprecated public List diff() { return execute(); } /** * Subclasses implement this to return their own subclass of * Difference. */ public abstract DiffType createDifference(Integer delStart, Integer delEnd, Integer addStart, Integer addEnd); /** * Adds the last difference, if pending. */ protected void addPending() { if (delStart != null) { diffs.add(createDifference(delStart, delEnd, addStart, addEnd)); delStart = null; delEnd = null; addStart = null; addEnd = null; } } /** * Traverses the sequences, seeking the longest common subsequences, * invoking the methods finishedFrom, finishedTo, * onFromNotTo, and onToNotFrom. */ protected void traverseSequences() { LCS lcs = new LCS(from, to, comparator); List matches = lcs.getMatches(); int toIdx = 0; int fromIdx = 0; int lastMatch = matches.size() - 1; while (fromIdx <= lastMatch) { Integer toElement = matches.get(fromIdx); if (toElement == null) { onFromNotTo(fromIdx, toIdx); } else { while (toIdx < toElement.intValue()) { onToNotFrom(fromIdx, toIdx++); } onMatch(fromIdx, toIdx++); } fromIdx++; } traverseEndOfSequences(fromIdx, toIdx); } protected void traverseEndOfSequences(int fromIdx, int toIdx) { int lastFrom = from.size() - 1; int lastTo = to.size() - 1; while (fromIdx <= lastFrom || toIdx <= lastTo) { // last from? if (fromIdx == lastFrom + 1 && toIdx <= lastTo) { while (toIdx <= lastTo) { onToNotFrom(fromIdx, toIdx++); } } // last to? if (toIdx == lastTo + 1 && fromIdx <= lastFrom) { while (fromIdx <= lastFrom) { onFromNotTo(fromIdx++, toIdx); } } if (fromIdx <= lastFrom) { onFromNotTo(fromIdx++, toIdx); } if (toIdx <= lastTo) { onToNotFrom(fromIdx, toIdx++); } } } /** * Invoked for elements in from and not in to. */ protected void onFromNotTo(int fromIdx, int toIdx) { if (delStart == null) { setIndices(fromIdx, fromIdx, toIdx, Difference.NONE); } else { delStart = Math.min(fromIdx, delStart); delEnd = Math.max(fromIdx, delEnd); } } /** * Invoked for elements in to and not in from. */ protected void onToNotFrom(int fromIdx, int toIdx) { if (delStart == null) { setIndices(fromIdx, Difference.NONE, toIdx, toIdx); } else { addStart = Math.min(toIdx, addStart); addEnd = Math.max(toIdx, addEnd); } } private void setIndices(int delSt, int delEn, int addSt, int addEn) { delStart = delSt; delEnd = delEn; addStart = addSt; addEnd = addEn; } /** * Invoked for elements matching in from and to. */ protected void onMatch(int fromIdx, int toIdx) { addPending(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy