001: /*
002: * Copyright 2001-2005 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, 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(
055: null, null);
056: /** Singleton instance */
057: private static final DateTimeComparator DATE_INSTANCE = new DateTimeComparator(
058: DateTimeFieldType.dayOfYear(), null);
059: /** Singleton instance */
060: private static final DateTimeComparator TIME_INSTANCE = new DateTimeComparator(
061: null, DateTimeFieldType.dayOfYear());
062:
063: /** The lower limit of fields to compare, null if no limit */
064: private final DateTimeFieldType iLowerLimit;
065: /** The upper limit of fields to compare, null if no limit */
066: private final DateTimeFieldType iUpperLimit;
067:
068: //-----------------------------------------------------------------------
069: /**
070: * Returns a DateTimeComparator the compares the entire date time value.
071: *
072: * @return a comparator over all fields
073: */
074: public static DateTimeComparator getInstance() {
075: return ALL_INSTANCE;
076: }
077:
078: /**
079: * Returns a DateTimeComparator with a lower limit only. Fields of a
080: * magnitude less than the lower limit are excluded from comparisons.
081: *
082: * @param lowerLimit inclusive lower limit for fields to be compared, null means no limit
083: * @return a comparator over all fields above the lower limit
084: */
085: public static DateTimeComparator getInstance(
086: DateTimeFieldType lowerLimit) {
087: return getInstance(lowerLimit, null);
088: }
089:
090: /**
091: * Returns a DateTimeComparator with a lower and upper limit. Fields of a
092: * magnitude less than the lower limit are excluded from comparisons.
093: * Fields of a magnitude greater than or equal to the upper limit are also
094: * excluded from comparisons. Either limit may be specified as null, which
095: * indicates an unbounded limit.
096: *
097: * @param lowerLimit inclusive lower limit for fields to be compared, null means no limit
098: * @param upperLimit exclusive upper limit for fields to be compared, null means no limit
099: * @return a comparator over all fields between the limits
100: */
101: public static DateTimeComparator getInstance(
102: DateTimeFieldType lowerLimit, DateTimeFieldType upperLimit) {
103: if (lowerLimit == null && upperLimit == null) {
104: return ALL_INSTANCE;
105: }
106: if (lowerLimit == DateTimeFieldType.dayOfYear()
107: && upperLimit == null) {
108: return DATE_INSTANCE;
109: }
110: if (lowerLimit == null
111: && upperLimit == DateTimeFieldType.dayOfYear()) {
112: return TIME_INSTANCE;
113: }
114: return new DateTimeComparator(lowerLimit, upperLimit);
115: }
116:
117: /**
118: * Returns a comparator that only considers date fields.
119: * Time of day is ignored.
120: *
121: * @return a comparator over all date fields
122: */
123: public static DateTimeComparator getDateOnlyInstance() {
124: return DATE_INSTANCE;
125: }
126:
127: /**
128: * Returns a comparator that only considers time fields.
129: * Date is ignored.
130: *
131: * @return a comparator over all time fields
132: */
133: public static DateTimeComparator getTimeOnlyInstance() {
134: return TIME_INSTANCE;
135: }
136:
137: /**
138: * Restricted constructor.
139: *
140: * @param lowerLimit the lower field limit, null means no limit
141: * @param upperLimit the upper field limit, null means no limit
142: */
143: protected DateTimeComparator(DateTimeFieldType lowerLimit,
144: DateTimeFieldType upperLimit) {
145: super ();
146: iLowerLimit = lowerLimit;
147: iUpperLimit = upperLimit;
148: }
149:
150: //-----------------------------------------------------------------------
151: /**
152: * Gets the field type that represents the lower limit of comparison.
153: *
154: * @return the field type, null if no upper limit
155: */
156: public DateTimeFieldType getLowerLimit() {
157: return iLowerLimit;
158: }
159:
160: /**
161: * Gets the field type that represents the upper limit of comparison.
162: *
163: * @return the field type, null if no upper limit
164: */
165: public DateTimeFieldType getUpperLimit() {
166: return iUpperLimit;
167: }
168:
169: /**
170: * Compare two objects against only the range of date time fields as
171: * specified in the constructor.
172: *
173: * @param lhsObj the first object,
174: * logically on the left of a < comparison, null means now
175: * @param rhsObj the second object,
176: * logically on the right of a < comparison, null means now
177: * @return zero if order does not matter,
178: * negative value if lhsObj < rhsObj, positive value otherwise.
179: * @throws IllegalArgumentException if either argument is not supported
180: */
181: public int compare(Object lhsObj, Object rhsObj) {
182: InstantConverter conv = ConverterManager.getInstance()
183: .getInstantConverter(lhsObj);
184: Chronology lhsChrono = conv.getChronology(lhsObj,
185: (Chronology) null);
186: long lhsMillis = conv.getInstantMillis(lhsObj, lhsChrono);
187:
188: conv = ConverterManager.getInstance().getInstantConverter(
189: rhsObj);
190: Chronology rhsChrono = conv.getChronology(rhsObj,
191: (Chronology) null);
192: long rhsMillis = conv.getInstantMillis(rhsObj, rhsChrono);
193:
194: if (iLowerLimit != null) {
195: lhsMillis = iLowerLimit.getField(lhsChrono).roundFloor(
196: lhsMillis);
197: rhsMillis = iLowerLimit.getField(rhsChrono).roundFloor(
198: rhsMillis);
199: }
200:
201: if (iUpperLimit != null) {
202: lhsMillis = iUpperLimit.getField(lhsChrono).remainder(
203: lhsMillis);
204: rhsMillis = iUpperLimit.getField(rhsChrono).remainder(
205: rhsMillis);
206: }
207:
208: if (lhsMillis < rhsMillis) {
209: return -1;
210: } else if (lhsMillis > rhsMillis) {
211: return 1;
212: } else {
213: return 0;
214: }
215: }
216:
217: //-----------------------------------------------------------------------
218: /**
219: * Support serialization singletons.
220: *
221: * @return the resolved singleton instance
222: */
223: private Object readResolve() {
224: return getInstance(iLowerLimit, iUpperLimit);
225: }
226:
227: /**
228: * Compares this comparator to another.
229: *
230: * @param object the object to compare to
231: * @return true if equal
232: */
233: public boolean equals(Object object) {
234: if (object instanceof DateTimeComparator) {
235: DateTimeComparator other = (DateTimeComparator) object;
236: return (iLowerLimit == other.getLowerLimit() || (iLowerLimit != null && iLowerLimit
237: .equals(other.getLowerLimit())))
238: && (iUpperLimit == other.getUpperLimit() || (iUpperLimit != null && iUpperLimit
239: .equals(other.getUpperLimit())));
240: }
241: return false;
242: }
243:
244: /**
245: * Gets a suitable hashcode.
246: *
247: * @return the hashcode
248: */
249: public int hashCode() {
250: return (iLowerLimit == null ? 0 : iLowerLimit.hashCode())
251: + (123 * (iUpperLimit == null ? 0 : iUpperLimit
252: .hashCode()));
253: }
254:
255: /**
256: * Gets a debugging string.
257: *
258: * @return a debugging string
259: */
260: public String toString() {
261: if (iLowerLimit == iUpperLimit) {
262: return "DateTimeComparator["
263: + (iLowerLimit == null ? "" : iLowerLimit.getName())
264: + "]";
265: } else {
266: return "DateTimeComparator["
267: + (iLowerLimit == null ? "" : iLowerLimit.getName())
268: + "-"
269: + (iUpperLimit == null ? "" : iUpperLimit.getName())
270: + "]";
271: }
272: }
273:
274: }
|