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    }