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.field;
017
018 import java.io.Serializable;
019 import java.util.Locale;
020
021 import org.joda.time.Chronology;
022 import org.joda.time.DateTimeField;
023 import org.joda.time.DateTimeFieldType;
024 import org.joda.time.DateTimeUtils;
025 import org.joda.time.DurationField;
026 import org.joda.time.Interval;
027 import org.joda.time.ReadableInstant;
028 import org.joda.time.ReadablePartial;
029
030 /**
031 * AbstractReadableInstantFieldProperty is a base class for binding a
032 * ReadableInstant to a DateTimeField.
033 * <p>
034 * It allows the date and time manipulation code to be field based yet
035 * still easy to use.
036 * <p>
037 * AbstractReadableInstantFieldProperty itself is thread-safe and immutable,
038 * but the ReadableInstant being operated on may be mutable and not
039 * thread-safe.
040 *
041 * @author Stephen Colebourne
042 * @author Brian S O'Neill
043 * @author Mike Schrag
044 * @since 1.0
045 */
046 public abstract class AbstractReadableInstantFieldProperty implements Serializable {
047
048 /** Serialization version. */
049 private static final long serialVersionUID = 1971226328211649661L;
050
051 /**
052 * Constructor.
053 */
054 public AbstractReadableInstantFieldProperty() {
055 super();
056 }
057
058 //-----------------------------------------------------------------------
059 /**
060 * Gets the field being used.
061 *
062 * @return the field
063 */
064 public abstract DateTimeField getField();
065
066 /**
067 * Gets the field type being used.
068 *
069 * @return the field type
070 */
071 public DateTimeFieldType getFieldType() {
072 return getField().getType();
073 }
074
075 /**
076 * Gets the name of the field.
077 *
078 * @return the field name
079 */
080 public String getName() {
081 return getField().getName();
082 }
083
084 /**
085 * Gets the milliseconds of the datetime that this property is linked to.
086 *
087 * @return the milliseconds
088 */
089 protected abstract long getMillis();
090
091 /**
092 * Gets the chronology of the datetime that this property is linked to.
093 * <p>
094 * This implementation throws UnsupportedOperationException, and must be
095 * implemented by subclasses to enable the equals() and hashCode() methods.
096 *
097 * @return the chronology
098 * @since 1.4
099 */
100 protected Chronology getChronology() {
101 throw new UnsupportedOperationException(
102 "The method getChronology() was added in v1.4 and needs " +
103 "to be implemented by subclasses of AbstractReadableInstantFieldProperty");
104 }
105
106 //-----------------------------------------------------------------------
107 /**
108 * Gets the value of this property from the instant.
109 * <p>
110 * For example, the following two lines of code are equivalent:
111 * <pre>
112 * datetime.getDayOfMonth();
113 * datetime.dayOfMonth().get();
114 * </pre>
115 *
116 * @return the current value
117 * @see DateTimeField#get
118 */
119 public int get() {
120 return getField().get(getMillis());
121 }
122
123 /**
124 * Gets the value of this property from the instant as a string.
125 * <p>
126 * This method returns the value converted to a <code>String</code>
127 * using <code>Integer.toString</code>. This method does NOT return
128 * textual descriptions such as 'Monday' or 'January'.
129 * See {@link #getAsText()} and {@link #getAsShortText()} for those.
130 *
131 * @return the current value
132 * @see DateTimeField#get
133 * @since 1.1
134 */
135 public String getAsString() {
136 return Integer.toString(get());
137 }
138
139 /**
140 * Gets the textual value of this property from the instant as a
141 * string in the default locale.
142 * <p>
143 * This method returns the value converted to a <code>String</code>
144 * returning the appropriate textual description wherever possible.
145 * Thus, a day of week of 1 would return 'Monday' in English.
146 *
147 * @return the current text value
148 * @see DateTimeField#getAsText
149 */
150 public String getAsText() {
151 return getAsText(null);
152 }
153
154 /**
155 * Gets the textual value of this property from the instant as a
156 * string in the specified locale.
157 * <p>
158 * This method returns the value converted to a <code>String</code>
159 * returning the appropriate textual description wherever possible.
160 * Thus, a day of week of 1 would return 'Monday' in English.
161 *
162 * @param locale locale to use for selecting a text symbol, null means default
163 * @return the current text value
164 * @see DateTimeField#getAsText
165 */
166 public String getAsText(Locale locale) {
167 return getField().getAsText(getMillis(), locale);
168 }
169
170 /**
171 * Gets the short textual value of this property from the instant as a
172 * string in the default locale.
173 * <p>
174 * This method returns the value converted to a <code>String</code>
175 * returning the appropriate textual description wherever possible.
176 * Thus, a day of week of 1 would return 'Mon' in English.
177 *
178 * @return the current text value
179 * @see DateTimeField#getAsShortText
180 */
181 public String getAsShortText() {
182 return getAsShortText(null);
183 }
184
185 /**
186 * Gets the short textual value of this property from the instant as a
187 * string in the specified locale.
188 * <p>
189 * This method returns the value converted to a <code>String</code>
190 * returning the appropriate textual description wherever possible.
191 * Thus, a day of week of 1 would return 'Mon' in English.
192 *
193 * @param locale locale to use for selecting a text symbol, null means default
194 * @return the current text value
195 * @see DateTimeField#getAsShortText
196 */
197 public String getAsShortText(Locale locale) {
198 return getField().getAsShortText(getMillis(), locale);
199 }
200
201 //-----------------------------------------------------------------------
202 /**
203 * Returns the difference between this field property instant and the one
204 * passed in, in the units of this field. The sign of the difference
205 * matches that of compareTo. In other words, this field property's instant
206 * is the minuend.
207 *
208 * @param instant the subtrahend, null means now
209 * @return the difference in the units of this field
210 * @see DateTimeField#getDifference
211 */
212 public int getDifference(ReadableInstant instant) {
213 if (instant == null) {
214 return getField().getDifference(getMillis(), DateTimeUtils.currentTimeMillis());
215 }
216 return getField().getDifference(getMillis(), instant.getMillis());
217 }
218
219 /**
220 * Returns the difference between this field property instant and the one
221 * passed in, in the units of this field. The sign of the difference
222 * matches that of compareTo. In other words, this field property's instant
223 * is the minuend.
224 *
225 * @param instant the subtrahend, null means now
226 * @return the difference in the units of this field
227 * @see DateTimeField#getDifference
228 */
229 public long getDifferenceAsLong(ReadableInstant instant) {
230 if (instant == null) {
231 return getField().getDifferenceAsLong(getMillis(), DateTimeUtils.currentTimeMillis());
232 }
233 return getField().getDifferenceAsLong(getMillis(), instant.getMillis());
234 }
235
236 //-----------------------------------------------------------------------
237 /**
238 * Returns the duration per unit value of this field. For example, if this
239 * field represents "hour of day", then the duration is an hour.
240 *
241 * @return the duration of this field, or UnsupportedDurationField
242 */
243 public DurationField getDurationField() {
244 return getField().getDurationField();
245 }
246
247 /**
248 * Returns the range duration of this field. For example, if this field
249 * represents "hour of day", then the range duration is a day.
250 *
251 * @return the range duration of this field, or null if field has no range
252 */
253 public DurationField getRangeDurationField() {
254 return getField().getRangeDurationField();
255 }
256
257 /**
258 * Gets whether this field is leap.
259 *
260 * @return true if a leap field
261 * @see DateTimeField#isLeap
262 */
263 public boolean isLeap() {
264 return getField().isLeap(getMillis());
265 }
266
267 /**
268 * Gets the amount by which this field is leap.
269 *
270 * @return the amount by which the field is leap
271 * @see DateTimeField#getLeapAmount
272 */
273 public int getLeapAmount() {
274 return getField().getLeapAmount(getMillis());
275 }
276
277 /**
278 * If this field were to leap, then it would be in units described by the
279 * returned duration. If this field doesn't ever leap, null is returned.
280 */
281 public DurationField getLeapDurationField() {
282 return getField().getLeapDurationField();
283 }
284
285 //-----------------------------------------------------------------------
286 /**
287 * Gets the minimum value for the field ignoring the current time.
288 *
289 * @return the minimum value
290 * @see DateTimeField#getMinimumValue
291 */
292 public int getMinimumValueOverall() {
293 return getField().getMinimumValue();
294 }
295
296 /**
297 * Gets the minimum value for the field.
298 *
299 * @return the minimum value
300 * @see DateTimeField#getMinimumValue
301 */
302 public int getMinimumValue() {
303 return getField().getMinimumValue(getMillis());
304 }
305
306 /**
307 * Gets the maximum value for the field ignoring the current time.
308 *
309 * @return the maximum value
310 * @see DateTimeField#getMaximumValue
311 */
312 public int getMaximumValueOverall() {
313 return getField().getMaximumValue();
314 }
315
316 /**
317 * Gets the maximum value for the field.
318 *
319 * @return the maximum value
320 * @see DateTimeField#getMaximumValue
321 */
322 public int getMaximumValue() {
323 return getField().getMaximumValue(getMillis());
324 }
325
326 /**
327 * Gets the maximum text length for the field.
328 *
329 * @param locale optional locale to use for selecting a text symbol
330 * @return the maximum length
331 * @see DateTimeField#getMaximumTextLength
332 */
333 public int getMaximumTextLength(Locale locale) {
334 return getField().getMaximumTextLength(locale);
335 }
336
337 /**
338 * Gets the maximum short text length for the field.
339 *
340 * @param locale optional locale to use for selecting a text symbol
341 * @return the maximum length
342 * @see DateTimeField#getMaximumShortTextLength
343 */
344 public int getMaximumShortTextLength(Locale locale) {
345 return getField().getMaximumShortTextLength(locale);
346 }
347
348
349 /**
350 * Returns the fractional duration milliseconds of this field.
351 *
352 * @see DateTimeField#remainder
353 * @return remainder duration, in milliseconds
354 */
355 public long remainder() {
356 return getField().remainder(getMillis());
357 }
358
359 /**
360 * Returns the interval that represents the range of the minimum
361 * and maximum values of this field.
362 * <p>
363 * For example, <code>datetime.monthOfYear().toInterval()</code>
364 * will return an interval over the whole month.
365 *
366 * @return the interval of this field
367 * @since 1.2
368 */
369 public Interval toInterval() {
370 DateTimeField field = getField();
371 long start = field.roundFloor(getMillis());
372 long end = field.add(start, 1);
373 Interval interval = new Interval(start, end);
374 return interval;
375 }
376
377 //-----------------------------------------------------------------------
378 /**
379 * Compare this field to the same field on another instant.
380 * <p>
381 * The comparison is based on the value of the same field type, irrespective
382 * of any difference in chronology. Thus, if this property represents the
383 * hourOfDay field, then the hourOfDay field of the other instant will be queried
384 * whether in the same chronology or not.
385 *
386 * @param instant the instant to compare to
387 * @return negative value if this is less, 0 if equal, or positive value if greater
388 * @throws IllegalArgumentException if the instant is null
389 */
390 public int compareTo(ReadableInstant instant) {
391 if (instant == null) {
392 throw new IllegalArgumentException("The instant must not be null");
393 }
394 int thisValue = get();
395 int otherValue = instant.get(getFieldType());
396 if (thisValue < otherValue) {
397 return -1;
398 } else if (thisValue > otherValue) {
399 return 1;
400 } else {
401 return 0;
402 }
403 }
404
405 //-----------------------------------------------------------------------
406 /**
407 * Compare this field to the same field on another partial instant.
408 * <p>
409 * The comparison is based on the value of the same field type, irrespective
410 * of any difference in chronology. Thus, if this property represents the
411 * hourOfDay field, then the hourOfDay field of the other partial will be queried
412 * whether in the same chronology or not.
413 *
414 * @param partial the partial to compare to
415 * @return negative value if this is less, 0 if equal, or positive value if greater
416 * @throws IllegalArgumentException if the partial is null
417 * @throws IllegalArgumentException if the partial doesn't support this field
418 */
419 public int compareTo(ReadablePartial partial) {
420 if (partial == null) {
421 throw new IllegalArgumentException("The partial must not be null");
422 }
423 int thisValue = get();
424 int otherValue = partial.get(getFieldType());
425 if (thisValue < otherValue) {
426 return -1;
427 } else if (thisValue > otherValue) {
428 return 1;
429 } else {
430 return 0;
431 }
432 }
433
434 //-----------------------------------------------------------------------
435 /**
436 * Compares this property to another.
437 *
438 * @param object the object to compare to
439 * @return true if equal
440 */
441 public boolean equals(Object object) {
442 if (this == object) {
443 return true;
444 }
445 if (object instanceof AbstractReadableInstantFieldProperty == false) {
446 return false;
447 }
448 AbstractReadableInstantFieldProperty other = (AbstractReadableInstantFieldProperty) object;
449 return
450 get() == other.get() &&
451 getFieldType().equals(other.getFieldType()) &&
452 FieldUtils.equals(getChronology(), other.getChronology());
453 }
454
455 /**
456 * Returns a hashcode compatible with the equals method.
457 *
458 * @return the hashcode
459 */
460 public int hashCode() {
461 return get() * 17 + getFieldType().hashCode() + getChronology().hashCode();
462 }
463
464 //-----------------------------------------------------------------------
465 /**
466 * Output a debugging string.
467 *
468 * @return debugging string
469 */
470 public String toString() {
471 return "Property[" + getName() + "]";
472 }
473
474 }