View Javadoc
1   /* 
2    * Copyright (C) 2016 Hobrasoft s.r.o.
3    *
4    * This program is free software: you can redistribute it and/or modify
5    * it under the terms of the GNU Affero General Public License as published by
6    * the Free Software Foundation, either version 3 of the License, or
7    * (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU Affero General Public License for more details.
13   *
14   * You should have received a copy of the GNU Affero General Public License
15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16   */
17  package cz.hobrasoft.pdfmu;
18  
19  import java.util.Arrays;
20  import java.util.Comparator;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.SortedMap;
25  import java.util.TreeMap;
26  import org.apache.commons.collections.IteratorUtils;
27  
28  /**
29   * Orders the items by a preference list.
30   *
31   * <p>
32   * The items at the start of the preference list are placed before (that is
33   * deemed smaller than) the other items. The items that appear in the preference
34   * list are placed before the items that do not appear in the preference list.
35   * The order of items that do not appear in the preference list is not specified
36   * (and is application specific in practice). Thus the induced order is not
37   * guaranteed to be linear.
38   *
39   * <p>
40   * Usage example:
41   * <pre>
42   * {@code
43   * import java.util.Comparator;
44   * import java.util.List;
45   * import java.util.Map;
46   * import java.util.SortedMap;
47   * import java.util.TreeMap;
48   * }
49   * </pre>
50   * <pre>
51   * {@code
52   * Map<K, V> unsorted;
53   * List<K> preferenceList;
54   * Comparator<K> comparator = new PreferenceListComparator<>(preferenceList);
55   * SortedMap<K, V> sorted = new TreeMap<>(comparator);
56   * sorted.putAll(unsorted);
57   * }
58   * </pre>
59   *
60   * @param <T> the type of objects that may be compared by this comparator
61   *
62   * @author <a href="mailto:filip.bartek@hobrasoft.cz">Filip Bartek</a>
63   */
64  public class PreferenceListComparator<T> implements Comparator<T>, MapSorter<T> {
65  
66      private final List<T> preferred;
67  
68      /**
69       * Creates a new comparator.
70       *
71       * @param preferred the preference list
72       */
73      public PreferenceListComparator(List<T> preferred) {
74          this.preferred = preferred;
75      }
76  
77      /**
78       * Creates a new comparator.
79       *
80       * @param preferred the preference list specified by an array
81       */
82      public PreferenceListComparator(T[] preferred) {
83          this(Arrays.asList(preferred));
84      }
85  
86      /**
87       * Creates a new comparator.
88       *
89       * @param preferredIterator an iterator that iterates over the items in the
90       * order of preference
91       */
92      public PreferenceListComparator(Iterator<T> preferredIterator) {
93          this(IteratorUtils.toList(preferredIterator));
94      }
95  
96      /**
97       * Compares its two arguments for order. Returns a negative integer, zero,
98       * or a positive integer as the first argument is less than, equal to, or
99       * greater than the second.
100      *
101      * <p>
102      * Note that this implementation violates some of the requirements imposed
103      * by the {@link Comparator} interface, so care should be taken when using
104      * it. In practice, it at least allows a {@link TreeMap} initialized by a
105      * {@link Comparator} to be used for one-time ordering of elements of a
106      * {@link Map} (using the method {@link TreeMap#putAll(Map)}). Other uses of
107      * the comparator have not been tested.
108      *
109      * @param o1 the first object to be compared.
110      * @param o2 the second object to be compared.
111      * @return a negative integer, zero, or a positive integer as the first
112      * argument is less than, equal to, or greater than the second.
113      */
114     @Override
115     public int compare(T o1, T o2) {
116         if (o1.equals(o2)) {
117             return 0;
118         }
119 
120         int i1 = preferred.indexOf(o1);
121         int i2 = preferred.indexOf(o2);
122 
123         if (i1 == -1 && i2 != -1) {
124             // `o1` is not in `preferred` but `o2` is.
125             // "Prefer" `o2`, that is claim it smaller than `o1`.
126             return 1;
127         }
128         if (i2 == -1 && i1 != -1) {
129             return -1;
130         }
131         if (i1 == -1 && i2 == -1) {
132             // HACK:
133             // None of `o1` and `o2` is in `preferred`.
134             // Prefer `o1` (the former), preserving the order.
135             // With this hack, it may happen that `a < b` and `b < a`,
136             // so the comparator does not provide a <em>linear</em> order.
137             return -1;
138         }
139 
140         // `i1 == i2` can only occur if `o1.equals(o1)`
141         // or when none of `o1` and `o2` is in `preferred`.
142         assert i1 != i2;
143 
144         // If `o1` comes before `o1` in `preferred`, the result will be negative.
145         return i1 - i2;
146     }
147 
148     /**
149      * Sorts a map by its keys using a comparator.
150      *
151      * @param <K> the type of keys
152      * @param <V> the type of values
153      * @param unsorted the original (unsorted) map
154      * @param comparator a comparator on K
155      * @return a sorted map
156      */
157     public static <K, V> SortedMap<K, V> sort(Map<K, V> unsorted, Comparator<K> comparator) {
158         SortedMap<K, V> sorted = new TreeMap<>(comparator);
159         sorted.putAll(unsorted);
160         return sorted;
161     }
162 
163     @Override
164     public <V> SortedMap<T, V> sort(Map<T, V> unsorted) {
165         return sort(unsorted, this);
166     }
167 }