1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.collections.comparators;
18
19 import java.io.Serializable;
20 import java.util.Comparator;
21
22 import org.apache.commons.collections.ComparatorUtils;
23
24 /**
25 * A Comparator that will compare nulls to be either lower or higher than
26 * other objects.
27 *
28 * @since 2.0
29 * @version $Id: NullComparator.java 1429905 2013-01-07 17:15:14Z ggregory $
30 */
31 public class NullComparator<E> implements Comparator<E>, Serializable {
32
33 /** Serialization version. */
34 private static final long serialVersionUID = -5820772575483504339L;
35
36 /**
37 * The comparator to use when comparing two non-<code>null</code> objects.
38 **/
39 private final Comparator<E> nonNullComparator;
40
41 /**
42 * Specifies whether a <code>null</code> are compared as higher than
43 * non-<code>null</code> objects.
44 **/
45 private final boolean nullsAreHigh;
46
47 //-----------------------------------------------------------------------
48 /**
49 * Construct an instance that sorts <code>null</code> higher than any
50 * non-<code>null</code> object it is compared with. When comparing two
51 * non-<code>null</code> objects, the {@link ComparableComparator} is
52 * used.
53 **/
54 @SuppressWarnings("unchecked")
55 public NullComparator() {
56 this(ComparatorUtils.NATURAL_COMPARATOR, true);
57 }
58
59 /**
60 * Construct an instance that sorts <code>null</code> higher than any
61 * non-<code>null</code> object it is compared with. When comparing two
62 * non-<code>null</code> objects, the specified {@link Comparator} is
63 * used.
64 *
65 * @param nonNullComparator the comparator to use when comparing two
66 * non-<code>null</code> objects. This argument cannot be
67 * <code>null</code>
68 *
69 * @exception NullPointerException if <code>nonNullComparator</code> is
70 * <code>null</code>
71 **/
72 public NullComparator(final Comparator<E> nonNullComparator) {
73 this(nonNullComparator, true);
74 }
75
76 /**
77 * Construct an instance that sorts <code>null</code> higher or lower than
78 * any non-<code>null</code> object it is compared with. When comparing
79 * two non-<code>null</code> objects, the {@link ComparableComparator} is
80 * used.
81 *
82 * @param nullsAreHigh a <code>true</code> value indicates that
83 * <code>null</code> should be compared as higher than a
84 * non-<code>null</code> object. A <code>false</code> value indicates
85 * that <code>null</code> should be compared as lower than a
86 * non-<code>null</code> object.
87 **/
88 @SuppressWarnings("unchecked")
89 public NullComparator(final boolean nullsAreHigh) {
90 this(ComparatorUtils.NATURAL_COMPARATOR, nullsAreHigh);
91 }
92
93 /**
94 * Construct an instance that sorts <code>null</code> higher or lower than
95 * any non-<code>null</code> object it is compared with. When comparing
96 * two non-<code>null</code> objects, the specified {@link Comparator} is
97 * used.
98 *
99 * @param nonNullComparator the comparator to use when comparing two
100 * non-<code>null</code> objects. This argument cannot be
101 * <code>null</code>
102 *
103 * @param nullsAreHigh a <code>true</code> value indicates that
104 * <code>null</code> should be compared as higher than a
105 * non-<code>null</code> object. A <code>false</code> value indicates
106 * that <code>null</code> should be compared as lower than a
107 * non-<code>null</code> object.
108 *
109 * @exception NullPointerException if <code>nonNullComparator</code> is
110 * <code>null</code>
111 **/
112 public NullComparator(final Comparator<E> nonNullComparator, final boolean nullsAreHigh) {
113 this.nonNullComparator = nonNullComparator;
114 this.nullsAreHigh = nullsAreHigh;
115
116 if (nonNullComparator == null) {
117 throw new NullPointerException("null nonNullComparator");
118 }
119 }
120
121 //-----------------------------------------------------------------------
122 /**
123 * Perform a comparison between two objects. If both objects are
124 * <code>null</code>, a <code>0</code> value is returned. If one object
125 * is <code>null</code> and the other is not, the result is determined on
126 * whether the Comparator was constructed to have nulls as higher or lower
127 * than other objects. If neither object is <code>null</code>, an
128 * underlying comparator specified in the constructor (or the default) is
129 * used to compare the non-<code>null</code> objects.
130 *
131 * @param o1 the first object to compare
132 * @param o2 the object to compare it to.
133 * @return <code>-1</code> if <code>o1</code> is "lower" than (less than,
134 * before, etc.) <code>o2</code>; <code>1</code> if <code>o1</code> is
135 * "higher" than (greater than, after, etc.) <code>o2</code>; or
136 * <code>0</code> if <code>o1</code> and <code>o2</code> are equal.
137 **/
138 public int compare(final E o1, final E o2) {
139 if(o1 == o2) { return 0; }
140 if(o1 == null) { return this.nullsAreHigh ? 1 : -1; }
141 if(o2 == null) { return this.nullsAreHigh ? -1 : 1; }
142 return this.nonNullComparator.compare(o1, o2);
143 }
144
145 //-----------------------------------------------------------------------
146 /**
147 * Implement a hash code for this comparator that is consistent with
148 * {@link #equals(Object)}.
149 *
150 * @return a hash code for this comparator.
151 **/
152 @Override
153 public int hashCode() {
154 return (nullsAreHigh ? -1 : 1) * nonNullComparator.hashCode();
155 }
156
157 /**
158 * Determines whether the specified object represents a comparator that is
159 * equal to this comparator.
160 *
161 * @param obj the object to compare this comparator with.
162 *
163 * @return <code>true</code> if the specified object is a NullComparator
164 * with equivalent <code>null</code> comparison behavior
165 * (i.e. <code>null</code> high or low) and with equivalent underlying
166 * non-<code>null</code> object comparators.
167 **/
168 @Override
169 public boolean equals(final Object obj) {
170 if(obj == null) { return false; }
171 if(obj == this) { return true; }
172 if(!obj.getClass().equals(this.getClass())) { return false; }
173
174 final NullComparator<?> other = (NullComparator<?>) obj;
175
176 return this.nullsAreHigh == other.nullsAreHigh &&
177 this.nonNullComparator.equals(other.nonNullComparator);
178 }
179
180 }