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;

/**
 * This class serves as central entry point into the diff API and provides some additional
 * helper methods around it. The main methods findDifferences() take two 
 * (typed) {@link List}s as arguments 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. * That class was refactored, adapted to Java 5 and is now used here. *

*/ public class 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(List fromList, 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(T[] fromArray, 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(List fromList, List toList, 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(T[] fromArray, T[] toArray, 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(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(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(Difference d, List targetList) { // We need this index for deleting AND adding (to know where to add) int deleteStartIndex = d.getDeleteStartIndex(); if(d.isDelete()) { // Get a ListIterator at the delete position ListIterator iterator = targetList.listIterator(deleteStartIndex); // Delete as many elements as are contained in the delete list for(int i = 0; i < d.getDeleteList().size(); ++i) { iterator.next(); iterator.remove(); } } if(d.isAdd()) { // Adding is easy as we have addAll(); just use the right index and add the complete AddList targetList.addAll(deleteStartIndex, d.getAddList()); } } /** * 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(DifferenceList diffList, List targetList) { // ATTENTION: In any case, apply in reverse order; otherwise would cause data corruption ListIterator> iterator = diffList.listIterator(diffList.size()); while(iterator.hasPrevious()) { applyDifference(iterator.previous(), targetList); } } public static String toString(Difference d) { return new StringBuilder() .append("del(") .append(d.getDeleteStartIndex()) .append(", ") .append(d.getDeleteEndIndex()) .append(")") .append("|") .append("add(") .append(d.getAddStartIndex()) .append(", ") .append(d.getAddEndIndex()) .append(")") .toString(); } protected static class InvertedDifference implements Difference { protected final Difference _original; protected InvertedDifference(Difference original) { _original = original; } public int getAddStartIndex() { return _original.getDeleteStartIndex(); } public int getAddEndIndex() { return _original.getDeleteEndIndex(); } public List getAddList() { return _original.getDeleteList(); } public boolean isAdd() { return _original.isDelete(); } public int getDeleteStartIndex() { return _original.getAddStartIndex(); } public int getDeleteEndIndex() { return _original.getAddEndIndex(); } public List getDeleteList() { return _original.getAddList(); } public boolean isDelete() { return _original.isAdd(); } public Difference invert() { return _original; } public String toString() { return DifferenceUtils.toString(this); } } protected static class InvertedDifferenceList extends AbstractList> implements DifferenceList { protected final DifferenceList _original; protected InvertedDifferenceList(DifferenceList original) { _original = original; } @Override public void add(int index, Difference element) { _original.add(index, element.invert()); } @Override public Difference get(int index) { return _original.get(index).invert(); } @Override public Difference remove(int index) { return _original.remove(index).invert(); } @Override public Difference set(int index, Difference element) { return _original.set(index, element.invert()).invert(); } public void applyTo(List list) { DifferenceUtils.applyDifferenceList(this, list); } public DifferenceList invert() { return _original; } @Override public int size() { return _original.size(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy