1 package eu.simuline.util;
2
3 import java.util.Comparator;
4 import java.util.Collection;
5 import java.util.List;
6 import java.util.Set;
7
8 import java.io.Serializable;
9
10 /**
11 * Collection of static methods related with {@link Comparator}s.
12 *
13 * @param <E>
14 * the type to be compared.
15 *
16 * Created: Sat May 21 16:30:58 2005
17 *
18 * @author <a href="mailto:ernst.reissner@simuline.eu">Ernst Reissner</a>
19 * @version 1.0
20 */
21 public abstract class Comparators<E> { // NOPMD
22
23 /* -------------------------------------------------------------------- *
24 * inner classes. *
25 * -------------------------------------------------------------------- */
26
27 /**
28 * Represents a comparator which is a cascade of comparators:
29 * Starting with the first comparator in {@link #seq}
30 * asks the next comparator if the current one detects equality.
31 *
32 * @param <E>
33 * the type to be compared.
34 */
35 private static class Cascade<E> implements Comparator<E>, Serializable {
36 private static final long serialVersionUID = -2479143000061671589L;
37
38 private final Collection<Comparator<E>> seq;
39 Cascade(Collection<Comparator<E>> seq) {
40 this.seq = seq;
41 }
42 public int compare(E obj1, E obj2) {
43 int res;
44 for (Comparator<E> cmp : seq) {
45 res = cmp.compare(obj1, obj2);
46 if (res != 0) {
47 return res;
48 }
49 }
50 // non the comparators could decide ordering.
51
52 return 0;
53 }
54 } // class Cascade<E>
55
56 /**
57 * Implements an ordering given by the list {@link #seq}.
58 * Elements not in the list are all minimal
59 * and pairwise equal with respect to this comparator.
60 * They are less than all elements in the list.
61 * The ordering on the list is ascending.
62 * <p>
63 * CAUTION: This ordering is consistent with equals only
64 * if the elements of {@link #seq} are pairwise non-equal.
65 * If this condition is hurt,
66 * an element <code>a</code> may satisfy <code>a>a</code>.
67 * Even if this condition is satisfied,
68 * two elements <code>a</code>, <code>b</code> not in {@link #seq}
69 * satisfy <code>a=b</code> with respect to the ordering
70 * even if they are not equal
71 * with respect to the method {@link #equals(Object)}.
72 * Thus the ordering is consistent with equals only,
73 * if restricted to the elements of {@link #seq}
74 * plus one single object not in {@link #seq}.
75 * This suffices to make the method {@link Set#add(Object)}
76 * to work properly.
77 * <p>
78 * The ordering is always total,
79 * i.e. either <code>a\le b</code> or <code>b\le a</code> holds.
80 * This is clear if both elements are in {@link #seq}
81 * and also if one is not.
82 * Also the ordering is always transitive:
83 * Assume <code>a\le b</code> or <code>b\le c</code>.
84 * If <code>a\not\in seq</code>, then <code>a\le c</code>.
85 * If <code>b\not\in seq</code> then also <code>a\not\in seq</code>.
86 * If<code>c\not\in seq</code> then also <code>b\not\in seq</code>.
87 * So, if one of the elements is not in {@link #seq},
88 * transitivity holds.
89 * Otherwise, all elements are in {@link #seq}
90 * and transitivity follows directly.
91 *
92 * @param <E>
93 * the type to be compared.
94 */
95 private static class AsListed<E> implements Comparator<E>, Serializable {
96
97 private static final long serialVersionUID = -2479143000061671589L;
98
99 /* ----------------------------------------------------------------- *
100 * fields. *
101 * ----------------------------------------------------------------- */
102
103 private final List<E> seq;
104
105 /* ----------------------------------------------------------------- *
106 * constructors. *
107 * ----------------------------------------------------------------- */
108
109 // **** does not check that the elements of seq are pairwise different.
110 AsListed(List<E> seq) {
111 if (seq == null) {
112 throw new NullPointerException(); // NOPMD
113 }
114 this.seq = seq;
115 assert this.seq != null;
116 }
117
118 /* ----------------------------------------------------------------- *
119 * methods. *
120 * ----------------------------------------------------------------- */
121
122 public int compare(E obj1, E obj2) {
123 // The result is 0, if neither object is in seq,
124 // because the indices are both -1.
125 // The result is the difference of the indices
126 // if both objects are in seq.
127 // This defines an ascending ordering on seq.
128 // If the second object is not in seq, whereas the first one is,
129 // the result is strictly negative.
130 // Accordingly,
131 // if the first object is not in seq, whereas the second one is,
132 // the result is strictly positive.
133 // Thus all elements in seq are smaller
134 // than all elements not in seq.
135
136 int idx1 = this.seq.indexOf(obj1);
137 int idx2 = this.seq.indexOf(obj2);
138 if (idx1 == -1 || idx2 == -1) {
139 // If neither object is in seq, returns 0=-1-(-1)
140 // If obj1 is in seq whereas obj2 is not, returns <0
141 // If obj2 is in seq whereas obj1 is not, returns >0
142 return this.seq.indexOf(obj2) - this.seq.indexOf(obj1);
143 } else {
144 // Here, both obj1 and obj2 are in seq.
145 // Returns the signed difference of the indices.
146 return this.seq.indexOf(obj1) - this.seq.indexOf(obj2);
147 }
148 }
149 } // class AsListed<E>
150
151 /* -------------------------------------------------------------------- *
152 * methods. *
153 * -------------------------------------------------------------------- */
154
155
156 @edu.umd.cs.findbugs.annotations.SuppressWarnings
157 (value = "IL_INFINITE_RECURSIVE_LOOP",
158 justification = "bug in findbugs")
159 public static <E> Comparator<E> getNegative(Comparator<E> cmp) {
160 return new Comparator<E>() {
161 public int compare(E obj1, E obj2) {
162 return cmp.compare(obj2, obj1);
163 }
164 };
165 }
166
167 public static <E> Comparator<E> getInv(Comparator<E> cmp) {
168 return new Comparator<E>() {
169 public int compare(E obj1, E obj2) {
170 return cmp.compare(obj2, obj1);
171 }
172 };
173 }
174
175 public static <E> Comparator<E> getCascade(Collection<Comparator<E>> seq) {
176 return new Cascade<E>(seq);
177 }
178
179 /**
180 * Returns an ordering given by the list {@link Comparators.AsListed#seq}.
181 * Elements not in the list are all minimal
182 * and pairwise equal with respect to this comparator.
183 * They are less than all elements in the list.
184 * The ordering on the list is ascending.
185 * <p>
186 * CAUTION: This ordering is consistent with equals only
187 * under certain conditions.
188 * The other ordering axioms hold.
189 * For more details see {@link Comparators.AsListed}.
190 * <p>
191 * Caution: changing the list, affects the comparator.
192 *
193 * @param seq
194 * a list which determines the ordering.
195 * @throws NullPointerException
196 * if <code>seq</code> is <code>null</code>.
197 */
198 public static <E> Comparator<E> getAsListed(List<E> seq) {
199 return new AsListed<E>(seq);
200 }
201 } // Comparators