001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.commons.collections.comparators;
018    
019    import java.io.Serializable;
020    import java.util.Comparator;
021    
022    /**
023     * A Comparator that will compare nulls to be either lower or higher than
024     * other objects.
025     *
026     * @since Commons Collections 2.0
027     * @version $Revision: 647116 $ $Date: 2008-04-11 12:23:08 +0100 (Fri, 11 Apr 2008) $ 
028     *
029     * @author Michael A. Smith
030     */
031    public class NullComparator implements Comparator, Serializable {
032    
033        /** Serialization version. */
034        private static final long serialVersionUID = -5820772575483504339L;
035    
036        /**
037         *  The comparator to use when comparing two non-<code>null</code> objects.
038         **/
039        private Comparator nonNullComparator;
040    
041        /**
042         *  Specifies whether a <code>null</code> are compared as higher than
043         *  non-<code>null</code> objects.
044         **/
045        private boolean nullsAreHigh;
046    
047        //-----------------------------------------------------------------------
048        /**
049         *  Construct an instance that sorts <code>null</code> higher than any
050         *  non-<code>null</code> object it is compared with. When comparing two
051         *  non-<code>null</code> objects, the {@link ComparableComparator} is
052         *  used.
053         **/
054        public NullComparator() {
055            this(ComparableComparator.getInstance(), true);
056        }
057    
058        /**
059         *  Construct an instance that sorts <code>null</code> higher than any
060         *  non-<code>null</code> object it is compared with.  When comparing two
061         *  non-<code>null</code> objects, the specified {@link Comparator} is
062         *  used.
063         *
064         *  @param nonNullComparator the comparator to use when comparing two
065         *  non-<code>null</code> objects.  This argument cannot be
066         *  <code>null</code>
067         *
068         *  @exception NullPointerException if <code>nonNullComparator</code> is
069         *  <code>null</code>
070         **/
071        public NullComparator(Comparator nonNullComparator) {
072            this(nonNullComparator, true);
073        }
074    
075        /**
076         *  Construct an instance that sorts <code>null</code> higher or lower than
077         *  any non-<code>null</code> object it is compared with.  When comparing
078         *  two non-<code>null</code> objects, the {@link ComparableComparator} is
079         *  used.
080         *
081         *  @param nullsAreHigh a <code>true</code> value indicates that
082         *  <code>null</code> should be compared as higher than a
083         *  non-<code>null</code> object.  A <code>false</code> value indicates
084         *  that <code>null</code> should be compared as lower than a
085         *  non-<code>null</code> object.
086         **/
087        public NullComparator(boolean nullsAreHigh) {
088            this(ComparableComparator.getInstance(), nullsAreHigh);
089        }
090        
091        /**
092         *  Construct an instance that sorts <code>null</code> higher or lower than
093         *  any non-<code>null</code> object it is compared with.  When comparing
094         *  two non-<code>null</code> objects, the specified {@link Comparator} is
095         *  used.
096         *
097         *  @param nonNullComparator the comparator to use when comparing two
098         *  non-<code>null</code> objects. This argument cannot be
099         *  <code>null</code>
100         *
101         *  @param nullsAreHigh a <code>true</code> value indicates that
102         *  <code>null</code> should be compared as higher than a
103         *  non-<code>null</code> object.  A <code>false</code> value indicates
104         *  that <code>null</code> should be compared as lower than a
105         *  non-<code>null</code> object.
106         *
107         *  @exception NullPointerException if <code>nonNullComparator</code> is
108         *  <code>null</code>
109         **/
110        public NullComparator(Comparator nonNullComparator, boolean nullsAreHigh) {
111            this.nonNullComparator = nonNullComparator;
112            this.nullsAreHigh = nullsAreHigh;
113            
114            if(nonNullComparator == null) {
115                throw new NullPointerException("null nonNullComparator");
116            }
117        }
118    
119        //-----------------------------------------------------------------------
120        /**
121         *  Perform a comparison between two objects.  If both objects are
122         *  <code>null</code>, a <code>0</code> value is returned.  If one object
123         *  is <code>null</code> and the other is not, the result is determined on
124         *  whether the Comparator was constructed to have nulls as higher or lower
125         *  than other objects.  If neither object is <code>null</code>, an
126         *  underlying comparator specified in the constructor (or the default) is
127         *  used to compare the non-<code>null</code> objects.
128         *
129         *  @param o1  the first object to compare
130         *  @param o2  the object to compare it to.
131         *  @return <code>-1</code> if <code>o1</code> is "lower" than (less than,
132         *  before, etc.) <code>o2</code>; <code>1</code> if <code>o1</code> is
133         *  "higher" than (greater than, after, etc.) <code>o2</code>; or
134         *  <code>0</code> if <code>o1</code> and <code>o2</code> are equal.
135         **/
136        public int compare(Object o1, Object o2) {
137            if(o1 == o2) { return 0; }
138            if(o1 == null) { return (this.nullsAreHigh ? 1 : -1); }
139            if(o2 == null) { return (this.nullsAreHigh ? -1 : 1); }
140            return this.nonNullComparator.compare(o1, o2);
141        }
142    
143        //-----------------------------------------------------------------------
144        /**
145         *  Implement a hash code for this comparator that is consistent with
146         *  {@link #equals(Object)}.
147         *
148         *  @return a hash code for this comparator.
149         **/
150        public int hashCode() {
151            return (nullsAreHigh ? -1 : 1) * nonNullComparator.hashCode();
152        }
153    
154        /**
155         *  Determines whether the specified object represents a comparator that is
156         *  equal to this comparator.
157         *
158         *  @param obj  the object to compare this comparator with.
159         *
160         *  @return <code>true</code> if the specified object is a NullComparator
161         *  with equivalent <code>null</code> comparison behavior
162         *  (i.e. <code>null</code> high or low) and with equivalent underlying
163         *  non-<code>null</code> object comparators.
164         **/
165        public boolean equals(Object obj) {
166            if(obj == null) { return false; }
167            if(obj == this) { return true; }
168            if(!obj.getClass().equals(this.getClass())) { return false; }
169    
170            NullComparator other = (NullComparator)obj;
171        
172            return ((this.nullsAreHigh == other.nullsAreHigh) &&
173                    (this.nonNullComparator.equals(other.nonNullComparator)));
174        }
175    
176    }