1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.collections15.comparators;
17
18 import java.io.Serializable;
19 import java.util.Comparator;
20
21 /***
22 * A <code>Comparator</code> that will compare <code>null</code> values to be
23 * either lower or higher than other objects.
24 *
25 * @author Michael A. Smith
26 * @author Chris Lambrou (port to Java 5.0)
27 * @since Collections15 1.0
28 */
29 public class NullComparator <E> implements Comparator<E>, Serializable
30 {
31
32 static final long serialVersionUID = 7935803601304930086L;
33
34 /***
35 * The <code>Comparator</code> to use when comparing two
36 * non-<code>null</code> objects.
37 */
38 private Comparator<E> nonNullComparator;
39
40 /***
41 * Specifies whether a <code>null</code> object is compared as higher than a
42 * non-<code>null</code> object.
43 */
44 private boolean nullsAreHigh;
45
46 /***
47 * Returns an instance that uses the sorting order of an existing
48 * <code>Comparator</code>, but which additionally sorts <code>null</code>
49 * higher or lower than any non-<code>null</code> object it is compared
50 * with.
51 *
52 * @param nonNullComparator The <code>Comparator</code> to use when
53 * comparing two non-<code>null</code> objects.
54 * This argument cannot be <code>null</code>.
55 * @param nullsAreHigh A <code>true</code> value indicates that
56 * <code>null</code> should be compared as higher
57 * than a non-<code>null</code> object. A
58 * <code>false</code> value indicates that
59 * <code>null</code> should be compared as lower
60 * than a non-<code>null</code> object.
61 *
62 * @throws IllegalArgumentException Thrown if <code>nonNullComparator</code>
63 * is <code>null</code>.
64 */
65 protected NullComparator(Comparator<E> nonNullComparator, boolean nullsAreHigh)
66 {
67 if (nonNullComparator == null) {
68 throw new IllegalArgumentException("null nonNullComparator");
69 }
70 this.nonNullComparator = nonNullComparator;
71 this.nullsAreHigh = nullsAreHigh;
72 }
73
74 /***
75 * Gets an instance that sorts <code>null</code> higher than any
76 * non-<code>null</code> <code>Comparable</code> object it is compared with.
77 * When comparing two non-<code>null</code> <code>Comparable</code> objects,
78 * their natural ordering is used.
79 */
80 public static <T extends Comparable> NullComparator<T> getInstance()
81 {
82 return getInstance(true);
83 }
84
85 /***
86 * Gets an instance that sorts non-<code>null</code> <code>Comparable</code>
87 * objects according to their natural ordering, but which also which sorts
88 * <code>null</code> objects higher or lower than any non-<code>null</code>
89 * <code>Comparable</code> object it is compared with.
90 *
91 * @param nullsAreHigh A <code>true</code> value indicates that
92 * <code>null</code> should be compared as higher than a
93 * non-<code>null</code> object. A <code>false</code>
94 * value indicates that <code>null</code> should be
95 * compared as lower than a non-<code>null</code>
96 * object.
97 */
98 public static <T extends Comparable> NullComparator<T> getInstance(boolean nullsAreHigh)
99 {
100 Comparator<T> comparator = ComparableComparator.getInstance();
101 return new NullComparator<T>(comparator, nullsAreHigh);
102 }
103
104 /***
105 * Decorator method that uses the sorting order of an existing
106 * <code>Comparator</code>, but which additionally sorts <code>null</code>
107 * higher or lower than any non-<code>null</code> object it is compared
108 * with.
109 *
110 * @param nonNullComparator The <code>Comparator</code> to use when
111 * comparing two non-<code>null</code> objects.
112 * This argument cannot be <code>null</code>.
113 * @param nullsAreHigh A <code>true</code> value indicates that
114 * <code>null</code> should be compared as higher
115 * than a non-<code>null</code> object. A
116 * <code>false</code> value indicates that
117 * <code>null</code> should be compared as lower
118 * than a non-<code>null</code> object.
119 *
120 * @throws IllegalArgumentException Thrown if <code>nonNullComparator</code>
121 * is <code>null</code>.
122 */
123 public static <T> NullComparator<T> decorate(Comparator<T> nonNullComparator, boolean nullsAreHigh)
124 {
125 return new NullComparator<T>(nonNullComparator, nullsAreHigh);
126 }
127
128 /***
129 * Perform a comparison between two objects. If both objects are
130 * <code>null</code>, a <code>0</code> value is returned. If one object is
131 * <code>null</code> and the other is not, the result is determined on
132 * whether the Comparator was constructed to have nulls as higher or lower
133 * than other objects. If neither object is <code>null</code>, an
134 * underlying comparator specified in the constructor is used to compare the
135 * non-<code>null</code> objects.
136 *
137 * @param o1 The first object to compare
138 * @param o2 The object to compare it to.
139 *
140 * @return <code>-1</code> if <code>o1</code> is "lower" than (less than,
141 * before, etc.) <code>o2</code>; <code>1</code> if <code>o1</code>
142 * is "higher" than (greater than, after, etc.) <code>o2</code>; or
143 * <code>0</code> if <code>o1</code> and <code>o2</code> are equal.
144 */
145 public int compare(E o1, E o2)
146 {
147 if (o1 == o2) {
148 return 0;
149 }
150 if (o1 == null) {
151 return (this.nullsAreHigh ? 1 : -1);
152 }
153 if (o2 == null) {
154 return (this.nullsAreHigh ? -1 : 1);
155 }
156 return this.nonNullComparator.compare(o1, o2);
157 }
158
159 /***
160 * Implement a hash code for this <code>Comparator</code> that is consistent
161 * with {@link #equals(Object)}.
162 *
163 * @return A hash code for this <code>Comparator</code>.
164 */
165 public int hashCode()
166 {
167 return nullsAreHigh ? -nonNullComparator.hashCode() : nonNullComparator.hashCode();
168 }
169
170 /***
171 * Determines whether the specified object represents a
172 * <code>Comparator</code> that is equal to this <code>Comparator</code>.
173 *
174 * @param obj The object to compare this <code>Comparator</code> with.
175 *
176 * @return <code>true</code> if the specified object is a
177 * <code>NullComparator</code> with equivalent <code>null</code>
178 * comparison behavior (i.e. <code>null</code> high or low) and with
179 * an equivalent underlying non-<code>null</code> object
180 * <code>Comparator</code>.
181 */
182 public boolean equals(Object obj)
183 {
184 if (obj == null) {
185 return false;
186 }
187 if (obj == this) {
188 return true;
189 }
190 if (!obj.getClass().equals(this.getClass())) {
191 return false;
192 }
193
194 NullComparator other = (NullComparator) obj;
195
196 return ((this.nullsAreHigh == other.nullsAreHigh) &&
197 (this.nonNullComparator.equals(other.nonNullComparator)));
198 }
199
200 }