cz.hobrasoft.pdfmu.PreferenceListComparator.java Source code

Java tutorial

Introduction

Here is the source code for cz.hobrasoft.pdfmu.PreferenceListComparator.java

Source

/* 
 * Copyright (C) 2016 Hobrasoft s.r.o.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package cz.hobrasoft.pdfmu;

import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.commons.collections.IteratorUtils;

/**
 * Orders the items by a preference list.
 *
 * <p>
 * The items at the start of the preference list are placed before (that is
 * deemed smaller than) the other items. The items that appear in the preference
 * list are placed before the items that do not appear in the preference list.
 * The order of items that do not appear in the preference list is not specified
 * (and is application specific in practice). Thus the induced order is not
 * guaranteed to be linear.
 *
 * <p>
 * Usage example:
 * <pre>
 * {@code
 * import java.util.Comparator;
 * import java.util.List;
 * import java.util.Map;
 * import java.util.SortedMap;
 * import java.util.TreeMap;
 * }
 * </pre>
 * <pre>
 * {@code
 * Map<K, V> unsorted;
 * List<K> preferenceList;
 * Comparator<K> comparator = new PreferenceListComparator<>(preferenceList);
 * SortedMap<K, V> sorted = new TreeMap<>(comparator);
 * sorted.putAll(unsorted);
 * }
 * </pre>
 *
 * @param <T> the type of objects that may be compared by this comparator
 *
 * @author <a href="mailto:filip.bartek@hobrasoft.cz">Filip Bartek</a>
 */
public class PreferenceListComparator<T> implements Comparator<T>, MapSorter<T> {

    private final List<T> preferred;

    /**
     * Creates a new comparator.
     *
     * @param preferred the preference list
     */
    public PreferenceListComparator(List<T> preferred) {
        this.preferred = preferred;
    }

    /**
     * Creates a new comparator.
     *
     * @param preferred the preference list specified by an array
     */
    public PreferenceListComparator(T[] preferred) {
        this(Arrays.asList(preferred));
    }

    /**
     * Creates a new comparator.
     *
     * @param preferredIterator an iterator that iterates over the items in the
     * order of preference
     */
    public PreferenceListComparator(Iterator<T> preferredIterator) {
        this(IteratorUtils.toList(preferredIterator));
    }

    /**
     * Compares its two arguments for order. Returns a negative integer, zero,
     * or a positive integer as the first argument is less than, equal to, or
     * greater than the second.
     *
     * <p>
     * Note that this implementation violates some of the requirements imposed
     * by the {@link Comparator} interface, so care should be taken when using
     * it. In practice, it at least allows a {@link TreeMap} initialized by a
     * {@link Comparator} to be used for one-time ordering of elements of a
     * {@link Map} (using the method {@link TreeMap#putAll(Map)}). Other uses of
     * the comparator have not been tested.
     *
     * @param o1 the first object to be compared.
     * @param o2 the second object to be compared.
     * @return a negative integer, zero, or a positive integer as the first
     * argument is less than, equal to, or greater than the second.
     */
    @Override
    public int compare(T o1, T o2) {
        if (o1.equals(o2)) {
            return 0;
        }

        int i1 = preferred.indexOf(o1);
        int i2 = preferred.indexOf(o2);

        if (i1 == -1 && i2 != -1) {
            // `o1` is not in `preferred` but `o2` is.
            // "Prefer" `o2`, that is claim it smaller than `o1`.
            return 1;
        }
        if (i2 == -1 && i1 != -1) {
            return -1;
        }
        if (i1 == -1 && i2 == -1) {
            // HACK:
            // None of `o1` and `o2` is in `preferred`.
            // Prefer `o1` (the former), preserving the order.
            // With this hack, it may happen that `a < b` and `b < a`,
            // so the comparator does not provide a <em>linear</em> order.
            return -1;
        }

        // `i1 == i2` can only occur if `o1.equals(o1)`
        // or when none of `o1` and `o2` is in `preferred`.
        assert i1 != i2;

        // If `o1` comes before `o1` in `preferred`, the result will be negative.
        return i1 - i2;
    }

    /**
     * Sorts a map by its keys using a comparator.
     *
     * @param <K> the type of keys
     * @param <V> the type of values
     * @param unsorted the original (unsorted) map
     * @param comparator a comparator on K
     * @return a sorted map
     */
    public static <K, V> SortedMap<K, V> sort(Map<K, V> unsorted, Comparator<K> comparator) {
        SortedMap<K, V> sorted = new TreeMap<>(comparator);
        sorted.putAll(unsorted);
        return sorted;
    }

    @Override
    public <V> SortedMap<T, V> sort(Map<T, V> unsorted) {
        return sort(unsorted, this);
    }
}