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

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

package org.incava.diff;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.TreeMap;

public class LCS {
    /**
     * The source array, AKA the "from" values.
     */
    private final List from;

    /**
     * The target array, AKA the "to" values.
     */
    private final List to;

    /**
     * The comparator used, if any.
     */
    private final Comparator comparator;

    /**
     * Constructs an LCS for the two arrays, using the given comparator.
     */
    public LCS(ObjectType[] from, ObjectType[] to, Comparator comp) {
        this(Arrays.asList(from), Arrays.asList(to), comp);
    }

    /**
     * Constructs an LCS for the two arrays, using the default comparison
     * mechanism between the objects, such as equals and
     * compareTo.
     */
    public LCS(ObjectType[] from, ObjectType[] to) {
        this(from, to, null);
    }

    /**
     * Constructs an LCS for the two collections, using the default comparison
     * mechanism between the objects, such as equals and
     * compareTo.
     */
    public LCS(List from, List to) {
        this(from, to, null);
    }

    /**
     * Constructs an LCS for the two collections, using the given comparator.
     */
    public LCS(List from, List to, Comparator comp) {
        this.from = from;
        this.to = to;
        this.comparator = comp;
    }

    /**
     * Returns an array of the longest common subsequences.
     */
    public List getMatches() {
        int fromStart = 0;
        int fromEnd = from.size() - 1;

        int toStart = 0;
        int toEnd = to.size() - 1;

        TreeMap matches = new TreeMap();

        // common beginning and ending elements:
        while (fromStart <= fromEnd && toStart <= toEnd && equals(comparator, from.get(fromStart), to.get(toStart))) {
            matches.put(fromStart++, toStart++);
        }

        while (fromStart <= fromEnd && toStart <= toEnd && equals(comparator, from.get(fromEnd), to.get(toEnd))) {
            matches.put(fromEnd--, toEnd--);
        }

        addMatches(matches, fromStart, fromEnd, toStart, toEnd);
        
        return toList(matches);
    }

    public void addMatches(TreeMap matches, int fromStart, int fromEnd, int toStart, int toEnd) {
        Map> toMatches = getToMatches(toStart, toEnd);

        LCSTable links = new LCSTable();
        Thresholds thresh = new Thresholds();

        for (int idx = fromStart; idx <= fromEnd; ++idx) {
            ObjectType fromElement = from.get(idx); // keygen here.
            List positions = toMatches.get(fromElement);

            if (positions == null) {
                continue;
            }
            
            Integer k = 0;
            ListIterator pit = positions.listIterator(positions.size());
            while (pit.hasPrevious()) {
                Integer j = pit.previous();
                k = thresh.insert(j, k);
                if (k != null) {
                    links.update(idx, j, k);
                }   
            }
        }

        if (!thresh.isEmpty()) {
            Integer ti = thresh.lastKey();
            Map chain = links.getChain(ti);
            matches.putAll(chain);
        }
    }

    public Map> createMatchesMap() {
        if (comparator == null) {
            if (from.size() > 0 && from.get(0) instanceof Comparable) {
                // this uses the Comparable interface
                return new TreeMap>();
            }
            else {
                // this just uses hashCode()
                return new HashMap>();
            }
        }
        else {
            // we don't really want them sorted, but this is the only Map
            // implementation (as of JDK 1.4) that takes a comparator.
            return new TreeMap>(comparator);
        }
    }

    public Map> getToMatches(int toStart, int toEnd) {
        Map> toMatches = createMatchesMap();

        for (int toIdx = toStart; toIdx <= toEnd; ++toIdx) {
            ObjectType key = to.get(toIdx);
            List positions = toMatches.get(key);
            if (positions == null) {
                positions = new ArrayList();
                toMatches.put(key, positions);
            }
            positions.add(toIdx);
        }

        return toMatches;
    }

    /**
     * Compares the two objects, using the comparator provided with the
     * constructor, if any.
     */
    protected boolean equals(Comparator comparator, ObjectType x, ObjectType y) {
        return comparator == null ? x.equals(y) : comparator.compare(x, y) == 0;
    }

    /**
     * Converts the map into a list.
     */
    protected static List toList(TreeMap map) {
        int size = map.isEmpty() ? 0 : 1 + map.lastKey();
        ArrayList list = new ArrayList(Collections.nCopies(size, (Integer)null));
        for (Map.Entry entry : map.entrySet()) {
            list.set(entry.getKey(), entry.getValue());
        }
        return list;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy