001    /*
002     *  Copyright 2001-2009 Stephen Colebourne
003     *
004     *  Licensed under the Apache License, Version 2.0 (the "License");
005     *  you may not use this file except in compliance with the License.
006     *  You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     *  Unless required by applicable law or agreed to in writing, software
011     *  distributed under the License is distributed on an "AS IS" BASIS,
012     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     *  See the License for the specific language governing permissions and
014     *  limitations under the License.
015     */
016    package org.joda.time;
017    
018    import java.io.Serializable;
019    import java.util.Comparator;
020    
021    import org.joda.time.convert.ConverterManager;
022    import org.joda.time.convert.InstantConverter;
023    
024    /**
025     * DateTimeComparator provides comparators to compare one date with another.
026     * <p>
027     * Dates may be specified using any object recognised by the
028     * {@link org.joda.time.convert.ConverterManager ConverterManager} class.
029     * <p>
030     * The default objects recognised by the comparator are:
031     * <ul>
032     * <li>ReadableInstant
033     * <li>String
034     * <li>Calendar
035     * <li>Date
036     * <li>Long (milliseconds)
037     * <li>null (now)
038     * </ul>
039     *
040     * <p>
041     * DateTimeComparator is thread-safe and immutable.
042     *
043     * @author Guy Allard
044     * @author Stephen Colebourne
045     * @author Brian S O'Neill
046     * @since 1.0
047     */
048    public class DateTimeComparator implements Comparator<Object>, Serializable {
049    
050        /** Serialization lock */
051        private static final long serialVersionUID = -6097339773320178364L;
052    
053        /** Singleton instance */
054        private static final DateTimeComparator ALL_INSTANCE = new DateTimeComparator(null, null);
055        /** Singleton instance */
056        private static final DateTimeComparator DATE_INSTANCE = new DateTimeComparator(DateTimeFieldType.dayOfYear(), null);
057        /** Singleton instance */
058        private static final DateTimeComparator TIME_INSTANCE = new DateTimeComparator(null, DateTimeFieldType.dayOfYear());
059    
060        /** The lower limit of fields to compare, null if no limit */
061        private final DateTimeFieldType iLowerLimit;
062        /** The upper limit of fields to compare, null if no limit */
063        private final DateTimeFieldType iUpperLimit;
064    
065        //-----------------------------------------------------------------------
066        /**
067         * Returns a DateTimeComparator the compares the entire date time value.
068         * 
069         * @return a comparator over all fields
070         */
071        public static DateTimeComparator getInstance() {
072            return ALL_INSTANCE;
073        }
074    
075        /**
076         * Returns a DateTimeComparator with a lower limit only. Fields of a
077         * magnitude less than the lower limit are excluded from comparisons.
078         *
079         * @param lowerLimit  inclusive lower limit for fields to be compared, null means no limit
080         * @return a comparator over all fields above the lower limit
081         */
082        public static DateTimeComparator getInstance(DateTimeFieldType lowerLimit) {
083            return getInstance(lowerLimit, null);
084        }
085    
086        /**
087         * Returns a DateTimeComparator with a lower and upper limit. Fields of a
088         * magnitude less than the lower limit are excluded from comparisons.
089         * Fields of a magnitude greater than or equal to the upper limit are also
090         * excluded from comparisons. Either limit may be specified as null, which
091         * indicates an unbounded limit.
092         *
093         * @param lowerLimit  inclusive lower limit for fields to be compared, null means no limit
094         * @param upperLimit  exclusive upper limit for fields to be compared, null means no limit
095         * @return a comparator over all fields between the limits
096         */
097        public static DateTimeComparator getInstance(DateTimeFieldType lowerLimit, DateTimeFieldType upperLimit) {
098            if (lowerLimit == null && upperLimit == null) {
099                return ALL_INSTANCE;
100            }
101            if (lowerLimit == DateTimeFieldType.dayOfYear() && upperLimit == null) {
102                return DATE_INSTANCE;
103            }
104            if (lowerLimit == null && upperLimit == DateTimeFieldType.dayOfYear()) {
105                return TIME_INSTANCE;
106            }
107            return new DateTimeComparator(lowerLimit, upperLimit);
108        }
109    
110        /**
111         * Returns a comparator that only considers date fields.
112         * Time of day is ignored.
113         * 
114         * @return a comparator over all date fields
115         */
116        public static DateTimeComparator getDateOnlyInstance() {
117            return DATE_INSTANCE;
118        }
119    
120        /**
121         * Returns a comparator that only considers time fields.
122         * Date is ignored.
123         * 
124         * @return a comparator over all time fields
125         */
126        public static DateTimeComparator getTimeOnlyInstance() {
127            return TIME_INSTANCE;
128        }
129    
130        /**
131         * Restricted constructor.
132         * 
133         * @param lowerLimit  the lower field limit, null means no limit
134         * @param upperLimit  the upper field limit, null means no limit
135         */
136        protected DateTimeComparator(DateTimeFieldType lowerLimit, DateTimeFieldType upperLimit) {
137            super();
138            iLowerLimit = lowerLimit;
139            iUpperLimit = upperLimit;
140        }
141    
142        //-----------------------------------------------------------------------
143        /**
144         * Gets the field type that represents the lower limit of comparison.
145         * 
146         * @return the field type, null if no upper limit
147         */
148        public DateTimeFieldType getLowerLimit() {
149            return iLowerLimit;
150        }
151    
152        /**
153         * Gets the field type that represents the upper limit of comparison.
154         * 
155         * @return the field type, null if no upper limit
156         */
157        public DateTimeFieldType getUpperLimit() {
158            return iUpperLimit;
159        }
160    
161        /**
162         * Compare two objects against only the range of date time fields as
163         * specified in the constructor.
164         * 
165         * @param lhsObj  the first object,
166         *      logically on the left of a &lt; comparison, null means now
167         * @param rhsObj  the second object,
168         *      logically on the right of a &lt; comparison, null means now
169         * @return zero if order does not matter,
170         *      negative value if lhsObj &lt; rhsObj, positive value otherwise.
171         * @throws IllegalArgumentException if either argument is not supported
172         */
173        public int compare(Object lhsObj, Object rhsObj) {
174            InstantConverter conv = ConverterManager.getInstance().getInstantConverter(lhsObj);
175            Chronology lhsChrono = conv.getChronology(lhsObj, (Chronology) null);
176            long lhsMillis = conv.getInstantMillis(lhsObj, lhsChrono);
177            
178            conv = ConverterManager.getInstance().getInstantConverter(rhsObj);
179            Chronology rhsChrono = conv.getChronology(rhsObj, (Chronology) null);
180            long rhsMillis = conv.getInstantMillis(rhsObj, rhsChrono);
181    
182            if (iLowerLimit != null) {
183                lhsMillis = iLowerLimit.getField(lhsChrono).roundFloor(lhsMillis);
184                rhsMillis = iLowerLimit.getField(rhsChrono).roundFloor(rhsMillis);
185            }
186    
187            if (iUpperLimit != null) {
188                lhsMillis = iUpperLimit.getField(lhsChrono).remainder(lhsMillis);
189                rhsMillis = iUpperLimit.getField(rhsChrono).remainder(rhsMillis);
190            }
191    
192            if (lhsMillis < rhsMillis) {
193                return -1;
194            } else if (lhsMillis > rhsMillis) {
195                return 1;
196            } else {
197                return 0;
198            }
199        }
200    
201        //-----------------------------------------------------------------------
202        /**
203         * Support serialization singletons.
204         * 
205         * @return the resolved singleton instance
206         */
207        private Object readResolve() {
208            return getInstance(iLowerLimit, iUpperLimit);
209        }
210    
211        /**
212         * Compares this comparator to another.
213         * 
214         * @param object  the object to compare to
215         * @return true if equal
216         */
217        public boolean equals(Object object) {
218            if (object instanceof DateTimeComparator) {
219                DateTimeComparator other = (DateTimeComparator) object;
220                return (iLowerLimit == other.getLowerLimit() ||
221                        (iLowerLimit != null && iLowerLimit.equals(other.getLowerLimit()))) &&
222                       (iUpperLimit == other.getUpperLimit() ||
223                        (iUpperLimit != null && iUpperLimit.equals(other.getUpperLimit())));
224            }
225            return false;
226        }
227    
228        /**
229         * Gets a suitable hashcode.
230         * 
231         * @return the hashcode
232         */
233        public int hashCode() {
234            return (iLowerLimit == null ? 0 : iLowerLimit.hashCode()) +
235                   (123 * (iUpperLimit == null ? 0 : iUpperLimit.hashCode()));
236        }
237    
238        /**
239         * Gets a debugging string.
240         * 
241         * @return a debugging string
242         */
243        public String toString() {
244            if (iLowerLimit == iUpperLimit) {
245                return "DateTimeComparator["
246                    + (iLowerLimit == null ? "" : iLowerLimit.getName())
247                    + "]";
248            } else {
249                return "DateTimeComparator["
250                    + (iLowerLimit == null ? "" : iLowerLimit.getName())
251                    + "-"
252                    + (iUpperLimit == null ? "" : iUpperLimit.getName())
253                    + "]";
254            }
255        }
256    
257    }