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 * https://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 * @throws NullPointerException if {@code nonNullComparator} is
84 * {@code null}
85 */
86 public NullComparator(final Comparator<? super E> nonNullComparator) {
87 this(nonNullComparator, true);
88 }
89
90 /**
91 * Construct an instance that sorts {@code null} higher or lower than
92 * any non-{@code null} object it is compared with. When comparing
93 * two non-{@code null} objects, the specified {@link Comparator} is
94 * used.
95 *
96 * @param nonNullComparator The comparator to use when comparing two
97 * non-{@code null} objects. This argument cannot be
98 * {@code null}
99 * @param nullsAreHigh A {@code true} value indicates that
100 * {@code null} should be compared as higher than a
101 * non-{@code null} object. A {@code false} value indicates
102 * that {@code null} should be compared as lower than a
103 * non-{@code null} object.
104 * @throws NullPointerException if {@code nonNullComparator} is
105 * {@code null}
106 */
107 public NullComparator(final Comparator<? super E> nonNullComparator, final boolean nullsAreHigh) {
108 this.nonNullComparator = Objects.requireNonNull(nonNullComparator, "nonNullComparator");
109 this.nullsAreHigh = nullsAreHigh;
110 }
111
112 /**
113 * Perform a comparison between two objects. If both objects are
114 * {@code null}, a {@code 0} value is returned. If one object
115 * is {@code null} and the other is not, the result is determined on
116 * whether the Comparator was constructed to have nulls as higher or lower
117 * than other objects. If neither object is {@code null}, an
118 * underlying comparator specified in the constructor (or the default) is
119 * used to compare the non-{@code null} objects.
120 *
121 * @param o1 The first object to compare
122 * @param o2 The object to compare it to.
123 * @return {@code -1} if {@code o1} is "lower" than (less than,
124 * before, etc.) {@code o2}; {@code 1} if {@code o1} is
125 * "higher" than (greater than, after, etc.) {@code o2}; or
126 * {@code 0} if {@code o1} and {@code o2} are equal.
127 */
128 @Override
129 public int compare(final E o1, final E o2) {
130 if (o1 == o2) {
131 return 0;
132 }
133 if (o1 == null) {
134 return nullsAreHigh ? 1 : -1;
135 }
136 if (o2 == null) {
137 return nullsAreHigh ? -1 : 1;
138 }
139 return nonNullComparator.compare(o1, o2);
140 }
141
142 /**
143 * Determines whether the specified object represents a comparator that is
144 * equal to this comparator.
145 *
146 * @param obj The object to compare this comparator with.
147 * @return {@code true} if the specified object is a NullComparator
148 * with equivalent {@code null} comparison behavior
149 * (i.e. {@code null} high or low) and with equivalent underlying
150 * non-{@code null} object comparators.
151 */
152 @Override
153 public boolean equals(final Object obj) {
154 if (obj == null) {
155 return false;
156 }
157 if (obj == this) {
158 return true;
159 }
160 if (!obj.getClass().equals(this.getClass())) {
161 return false;
162 }
163
164 final NullComparator<?> other = (NullComparator<?>) obj;
165
166 return nullsAreHigh == other.nullsAreHigh &&
167 nonNullComparator.equals(other.nonNullComparator);
168 }
169
170 /**
171 * Implement a hash code for this comparator that is consistent with
172 * {@link #equals(Object)}.
173 *
174 * @return A hash code for this comparator.
175 */
176 @Override
177 public int hashCode() {
178 return (nullsAreHigh ? -1 : 1) * nonNullComparator.hashCode();
179 }
180 }