1 | /* |
2 | * Copyright 2001-2005 Stephen Colebourne |
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 org.joda.time; |
17 | |
18 | import java.io.Serializable; |
19 | import java.util.Comparator; |
20 | |
21 | import org.joda.time.convert.ConverterManager; |
22 | import org.joda.time.convert.InstantConverter; |
23 | |
24 | /** |
25 | * DateTimeComparator provides comparators to compare one date with another. |
26 | * <p> |
27 | * Dates may be specified using any object recognised by the |
28 | * {@link org.joda.time.convert.ConverterManager ConverterManager} class. |
29 | * <p> |
30 | * The default objects recognised by the comparator are: |
31 | * <ul> |
32 | * <li>ReadableInstant |
33 | * <li>String |
34 | * <li>Calendar |
35 | * <li>Date |
36 | * <li>Long (milliseconds) |
37 | * <li>null (now) |
38 | * </ul> |
39 | * |
40 | * <p> |
41 | * DateTimeComparator is thread-safe and immutable. |
42 | * |
43 | * @author Guy Allard |
44 | * @author Stephen Colebourne |
45 | * @author Brian S O'Neill |
46 | * @since 1.0 |
47 | */ |
48 | public class DateTimeComparator implements Comparator, Serializable { |
49 | |
50 | /** Serialization lock */ |
51 | private static final long serialVersionUID = -6097339773320178364L; |
52 | |
53 | /** Singleton instance */ |
54 | private static final DateTimeComparator ALL_INSTANCE = new DateTimeComparator(null, null); |
55 | /** Singleton instance */ |
56 | private static final DateTimeComparator DATE_INSTANCE = new DateTimeComparator(DateTimeFieldType.dayOfYear(), null); |
57 | /** Singleton instance */ |
58 | private static final DateTimeComparator TIME_INSTANCE = new DateTimeComparator(null, DateTimeFieldType.dayOfYear()); |
59 | |
60 | /** The lower limit of fields to compare, null if no limit */ |
61 | private final DateTimeFieldType iLowerLimit; |
62 | /** The upper limit of fields to compare, null if no limit */ |
63 | private final DateTimeFieldType iUpperLimit; |
64 | |
65 | //----------------------------------------------------------------------- |
66 | /** |
67 | * Returns a DateTimeComparator the compares the entire date time value. |
68 | * |
69 | * @return a comparator over all fields |
70 | */ |
71 | public static DateTimeComparator getInstance() { |
72 | return ALL_INSTANCE; |
73 | } |
74 | |
75 | /** |
76 | * Returns a DateTimeComparator with a lower limit only. Fields of a |
77 | * magnitude less than the lower limit are excluded from comparisons. |
78 | * |
79 | * @param lowerLimit inclusive lower limit for fields to be compared, null means no limit |
80 | * @return a comparator over all fields above the lower limit |
81 | */ |
82 | public static DateTimeComparator getInstance(DateTimeFieldType lowerLimit) { |
83 | return getInstance(lowerLimit, null); |
84 | } |
85 | |
86 | /** |
87 | * Returns a DateTimeComparator with a lower and upper limit. Fields of a |
88 | * magnitude less than the lower limit are excluded from comparisons. |
89 | * Fields of a magnitude greater than or equal to the upper limit are also |
90 | * excluded from comparisons. Either limit may be specified as null, which |
91 | * indicates an unbounded limit. |
92 | * |
93 | * @param lowerLimit inclusive lower limit for fields to be compared, null means no limit |
94 | * @param upperLimit exclusive upper limit for fields to be compared, null means no limit |
95 | * @return a comparator over all fields between the limits |
96 | */ |
97 | public static DateTimeComparator getInstance(DateTimeFieldType lowerLimit, DateTimeFieldType upperLimit) { |
98 | if (lowerLimit == null && upperLimit == null) { |
99 | 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 < comparison, null means now |
167 | * @param rhsObj the second object, |
168 | * logically on the right of a < comparison, null means now |
169 | * @return zero if order does not matter, |
170 | * negative value if lhsObj < 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 | } |