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.collections4.comparators;
18
19 import java.io.Serializable;
20 import java.util.Comparator;
21 import java.util.Objects;
22
23 import org.apache.commons.collections4.ComparatorUtils;
24
25 /**
26 * A Comparator that will compare nulls to be either lower or higher than
27 * other objects.
28 *
29 * @param <E> the type of objects compared by this comparator
30 * @since 2.0
31 */
32 public class NullComparator<E> implements Comparator<E>, Serializable {
33
34 /** Serialization version. */
35 private static final long serialVersionUID = -5820772575483504339L;
36
37 /**
38 * The comparator to use when comparing two non-{@code null} objects.
39 **/
40 private final Comparator<? super E> nonNullComparator;
41
42 /**
43 * Specifies whether a {@code null} are compared as higher than
44 * non-{@code null} objects.
45 **/
46 private final boolean nullsAreHigh;
47
48 /**
49 * Construct an instance that sorts {@code null} higher than any
50 * non-{@code null} object it is compared with. When comparing two
51 * non-{@code null} objects, the {@link ComparableComparator} is
52 * used.
53 **/
54 public NullComparator() {
55 this(ComparatorUtils.NATURAL_COMPARATOR, true);
56 }
57
58 /**
59 * Construct an instance that sorts {@code null} higher or lower than
60 * any non-{@code null} object it is compared with. When comparing
61 * two non-{@code null} objects, the {@link ComparableComparator} is
62 * used.
63 *
64 * @param nullsAreHigh a {@code true} value indicates that
65 * {@code null} should be compared as higher than a
66 * non-{@code null} object. A {@code false} value indicates
67 * that {@code null} should be compared as lower than a
68 * non-{@code null} object.
69 **/
70 public NullComparator(final boolean nullsAreHigh) {
71 this(ComparatorUtils.NATURAL_COMPARATOR, nullsAreHigh);
72 }
73
74 /**
75 * Construct an instance that sorts {@code null} higher than any
76 * non-{@code null} object it is compared with. When comparing two
77 * non-{@code null} objects, the specified {@link Comparator} is
78 * used.
79 *
80 * @param nonNullComparator the comparator to use when comparing two
81 * non-{@code null} objects. This argument cannot be
82 * {@code null}
83 *
84 * @throws NullPointerException if {@code nonNullComparator} is
85 * {@code null}
86 **/
87 public NullComparator(final Comparator<? super E> nonNullComparator) {
88 this(nonNullComparator, true);
89 }
90
91 /**
92 * Construct an instance that sorts {@code null} higher or lower than
93 * any non-{@code null} object it is compared with. When comparing
94 * two non-{@code null} objects, the specified {@link Comparator} is
95 * used.
96 *
97 * @param nonNullComparator the comparator to use when comparing two
98 * non-{@code null} objects. This argument cannot be
99 * {@code null}
100 *
101 * @param nullsAreHigh a {@code true} value indicates that
102 * {@code null} should be compared as higher than a
103 * non-{@code null} object. A {@code false} value indicates
104 * that {@code null} should be compared as lower than a
105 * non-{@code null} object.
106 *
107 * @throws NullPointerException if {@code nonNullComparator} is
108 * {@code null}
109 **/
110 public NullComparator(final Comparator<? super E> nonNullComparator, final boolean nullsAreHigh) {
111 this.nonNullComparator = Objects.requireNonNull(nonNullComparator, "nonNullComparator");
112 this.nullsAreHigh = nullsAreHigh;
113 }
114
115 /**
116 * Perform a comparison between two objects. If both objects are
117 * {@code null}, a {@code 0} value is returned. If one object
118 * is {@code null} and the other is not, the result is determined on
119 * whether the Comparator was constructed to have nulls as higher or lower
120 * than other objects. If neither object is {@code null}, an
121 * underlying comparator specified in the constructor (or the default) is
122 * used to compare the non-{@code null} objects.
123 *
124 * @param o1 the first object to compare
125 * @param o2 the object to compare it to.
126 * @return {@code -1} if {@code o1} is "lower" than (less than,
127 * before, etc.) {@code o2}; {@code 1} if {@code o1} is
128 * "higher" than (greater than, after, etc.) {@code o2}; or
129 * {@code 0} if {@code o1} and {@code o2} are equal.
130 **/
131 @Override
132 public int compare(final E o1, final E o2) {
133 if (o1 == o2) {
134 return 0;
135 }
136 if (o1 == null) {
137 return nullsAreHigh ? 1 : -1;
138 }
139 if (o2 == null) {
140 return nullsAreHigh ? -1 : 1;
141 }
142 return nonNullComparator.compare(o1, o2);
143 }
144
145 /**
146 * Determines whether the specified object represents a comparator that is
147 * equal to this comparator.
148 *
149 * @param obj the object to compare this comparator with.
150 *
151 * @return {@code true} if the specified object is a NullComparator
152 * with equivalent {@code null} comparison behavior
153 * (i.e. {@code null} high or low) and with equivalent underlying
154 * non-{@code null} object comparators.
155 **/
156 @Override
157 public boolean equals(final Object obj) {
158 if (obj == null) {
159 return false;
160 }
161 if (obj == this) {
162 return true;
163 }
164 if (!obj.getClass().equals(this.getClass())) {
165 return false;
166 }
167
168 final NullComparator<?> other = (NullComparator<?>) obj;
169
170 return nullsAreHigh == other.nullsAreHigh &&
171 nonNullComparator.equals(other.nonNullComparator);
172 }
173
174 /**
175 * Implement a hash code for this comparator that is consistent with
176 * {@link #equals(Object)}.
177 *
178 * @return a hash code for this comparator.
179 **/
180 @Override
181 public int hashCode() {
182 return (nullsAreHigh ? -1 : 1) * nonNullComparator.hashCode();
183 }
184 }