View Javadoc

1   /*
2    *  Copyright 2001-2004 The Apache Software Foundation
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
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 }