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

net.sf.javagimmicks.collections.diff.DifferenceUtils Maven / Gradle / Ivy

package net.sf.javagimmicks.collections.diff;

import java.util.AbstractList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.ListIterator;

import net.sf.javagimmicks.collections.diff.Difference.Range;

/**
 * This class serves as central entry point into the diff API and provides some
 * additional helper methods around it.
 * 

* The main methods findDifferences() take as arguments two (typed) * {@link List}s and internally compare them with the LCS (longest common * subsequences) algorithm. The resulting differences between the {@link List}s * are returned in form of a {@link DifferenceList} object, which is actually a * {@link List} of {@link Difference} objects (but containing some more specific * logic). Each one of the {@link Difference} objects carries detailed * information about one single difference between the two compared {@link List} * s, like the start and end index of a deletion and/or addition or the changed * elements. *

* A {@link Comparator} may be passed as an additional parameter, in order to * compare elements of the respective type. If the elements are not comparable * and no {@link Comparator} is passed, they must implement * {@link #equals(Object)} and {@link #hashCode()} in order to make the * comparison work. *

* This implementation class for the actual algorithm is strongly based upon the * Diff class from the java-diff project on http://www.incava.org (which seems to be * offline in between). That class was refactored, adapted to Java 5 and is now * used here. *

*/ public class DifferenceUtils { private DifferenceUtils() {} /** * Finds the differences between two provided {@link List}s using the * "longest common subsequences" algorithm. * * @param * the element type for the provided {@link List}s * @param fromList * the first {@link List} to be analyzed (called 'from' list) * @param toList * the second {@link List} to be analyzed (called 'to' list) * @return the differences between the two {@link List}s encapsulated in a * {@link DifferenceList} object */ public static DifferenceList findDifferences(final List fromList, final List toList) { return new DifferenceAlgorithm(fromList, toList, null).getDifferences(); } /** * Finds the differences between two provided arrays using the * "longest common subsequences" algorithm. * * @param * the element type for the provided arrays * @param fromArray * the first arrays to be analyzed (called 'from' list) * @param toArray * the second arrays to be analyzed (called 'to' list) * @return the differences between the two arrays encapsulated in a * {@link DifferenceList} object */ public static DifferenceList findDifferences(final T[] fromArray, final T[] toArray) { return findDifferences(Arrays.asList(fromArray), Arrays.asList(toArray)); } /** * Finds the differences between two provided {@link List}s using the * "longest common subsequences" algorithm. * * @param * the element type for the provided {@link List}s * @param fromList * the first {@link List} to be analyzed (called 'from' list) * @param toList * the second {@link List} to be analyzed (called 'to' list) * @param comparator * a {@link Comparator} able to compare elements of the respective * type * @return the differences between the two {@link List}s encapsulated in a * {@link DifferenceList} object */ public static DifferenceList findDifferences(final List fromList, final List toList, final Comparator comparator) { return new DifferenceAlgorithm(fromList, toList, comparator).getDifferences(); } /** * Finds the differences between two provided arrays using the * "longest common subsequences" algorithm. * * @param * the element type for the provided arrays * @param fromArray * the first arrays to be analyzed (called 'from' list) * @param toArray * the second arrays to be analyzed (called 'to' list) * @param comparator * a {@link Comparator} able to compare elements of the respective * type * @return the differences between the two arrays encapsulated in a * {@link DifferenceList} object */ public static DifferenceList findDifferences(final T[] fromArray, final T[] toArray, final Comparator comparator) { return findDifferences(Arrays.asList(fromArray), Arrays.asList(toArray), comparator); } /** * Returns an inverted {@link Difference} object for a given one. Inverted * means that delete and add information are exchanged. * * @param * the element type of the {@link Difference} object * @param difference * the {@link Difference} object to invert * @return an inverted version of the provided {@link Difference} object * using the original one in background */ public static Difference getInvertedDifference(final Difference difference) { return new InvertedDifference(difference); } /** * Returns an inverted {@link DifferenceList} object for a given one. * Inverted means that delete and add information are exchanged. * * @param * the element type of the {@link DifferenceList} object * @param differenceList * the {@link DifferenceList} object to invert * @return an inverted version of the provided {@link DifferenceList} object * using the original one in background */ public static DifferenceList getInvertedDifferenceList(final DifferenceList differenceList) { return new InvertedDifferenceList(differenceList); } /** * Applies the difference information contained in a given {@link Difference} * object to a target {@link List}. The means: remove there all elements * denoted by the "delete" information of the {@link Difference} object at * the right position and add all elements from the "add" {@link List} at the * same position. * * @param * the element type of the {@link Difference} object and target * {@link List} * @param d * the {@link Difference} object to apply * @param targetList * the {@link List} where to apply the changes */ public static void applyDifference(final Difference d, final List targetList) { final Range deleteRange = d.deleteRange(); final Range addRange = d.addRange(); if (deleteRange.exists()) { // Get a ListIterator at the delete position final ListIterator iterator = targetList.listIterator(deleteRange.getStartIndex()); // Delete as many elements as are contained in the delete list for (int i = 0; i < deleteRange.size(); ++i) { iterator.next(); iterator.remove(); } } if (addRange.exists()) { // Adding is easy as we have addAll(); just use the right index and add // the complete AddList targetList.addAll(deleteRange.getStartIndex(), addRange); } } /** * Applies the difference information contained in a given * {@link DifferenceList} object to a target {@link List}. The means: apply * step by step (in reverse order) all {@link Difference} object inside of * the {@link DifferenceList}. If a {@link List} with the same elements like * the original from {@link List} is provided here, it will have after * execution the same elements like the original to {@link List}. * * @param * the element type of the {@link DifferenceList} object and target * {@link List} * @param diffList * the {@link DifferenceList} object to apply * @param targetList * the {@link List} where to apply the changes */ public static void applyDifferenceList(final DifferenceList diffList, final List targetList) { // ATTENTION: In any case, apply in reverse order; otherwise would cause // data corruption final ListIterator> iterator = diffList.listIterator(diffList.size()); while (iterator.hasPrevious()) { applyDifference(iterator.previous(), targetList); } } public static String toString(final Difference d) { final Range deleteRange = d.deleteRange(); final Range addRange = d.addRange(); return new StringBuilder() .append("del(") .append(deleteRange.getStartIndex()) .append(", ") .append(deleteRange.getEndIndex()) .append(")") .append("|") .append("add(") .append(addRange.getStartIndex()) .append(", ") .append(addRange.getEndIndex()) .append(")") .toString(); } protected static class InvertedDifference implements Difference { protected final Difference _original; protected InvertedDifference(final Difference original) { _original = original; } @Override public Range deleteRange() { return _original.addRange(); } @Override public Range addRange() { return _original.deleteRange(); } @Override public Difference invert() { return _original; } @Override public String toString() { return DifferenceUtils.toString(this); } } protected static class InvertedDifferenceList extends AbstractList> implements DifferenceList { protected final DifferenceList _original; protected InvertedDifferenceList(final DifferenceList original) { _original = original; } @Override public void add(final int index, final Difference element) { _original.add(index, element.invert()); } @Override public Difference get(final int index) { return _original.get(index).invert(); } @Override public Difference remove(final int index) { return _original.remove(index).invert(); } @Override public Difference set(final int index, final Difference element) { return _original.set(index, element.invert()).invert(); } @Override public void applyTo(final List list) { DifferenceUtils.applyDifferenceList(this, list); } @Override public DifferenceList invert() { return _original; } @Override public int size() { return _original.size(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy