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.base;
017    
018    import org.joda.time.Duration;
019    import org.joda.time.Period;
020    import org.joda.time.ReadableDuration;
021    import org.joda.time.format.FormatUtils;
022    
023    /**
024     * AbstractDuration provides the common behaviour for duration classes.
025     * <p>
026     * This class should generally not be used directly by API users. The 
027     * {@link ReadableDuration} interface should be used when different 
028     * kinds of durations are to be referenced.
029     * <p>
030     * AbstractDuration subclasses may be mutable and not thread-safe.
031     *
032     * @author Brian S O'Neill
033     * @author Stephen Colebourne
034     * @since 1.0
035     */
036    public abstract class AbstractDuration implements ReadableDuration {
037    
038        /**
039         * Constructor.
040         */
041        protected AbstractDuration() {
042            super();
043        }
044    
045        //-----------------------------------------------------------------------
046        /**
047         * Get this duration as an immutable <code>Duration</code> object.
048         * 
049         * @return a Duration created using the millisecond duration from this instance
050         */
051        public Duration toDuration() {
052            return new Duration(getMillis());
053        }
054    
055        //-----------------------------------------------------------------------
056        /**
057         * Converts this duration to a Period instance using the standard period type
058         * and the ISO chronology.
059         * <p>
060         * Only precise fields in the period type will be used. Thus, only the hour,
061         * minute, second and millisecond fields on the period will be used.
062         * The year, month, week and day fields will not be populated.
063         * <p>
064         * If the duration is small, less than one day, then this method will perform
065         * as you might expect and split the fields evenly.
066         * If the duration is larger than one day then all the remaining duration will
067         * be stored in the largest available field, hours in this case.
068         * <p>
069         * For example, a duration effectively equal to (365 + 60 + 5) days will be
070         * converted to ((365 + 60 + 5) * 24) hours by this constructor.
071         * <p>
072         * For more control over the conversion process, you must pair the duration with
073         * an instant, see {@link Period#Period(ReadableInstant,ReadableDuration)}.
074         * 
075         * @return a Period created using the millisecond duration from this instance
076         */
077        public Period toPeriod() {
078            return new Period(getMillis());
079        }
080    
081        //-----------------------------------------------------------------------
082        /**
083         * Compares this duration with the specified duration based on length.
084         *
085         * @param obj  a duration to check against
086         * @return negative value if this is less, 0 if equal, or positive value if greater
087         * @throws NullPointerException if the object is null
088         * @throws ClassCastException if the given object is not supported
089         */
090        public int compareTo(Object obj) {
091            // Comparable contract means we cannot handle null or other types gracefully
092            ReadableDuration thisDuration = (ReadableDuration) this;
093            ReadableDuration otherDuration = (ReadableDuration) obj;
094            
095            long thisMillis = thisDuration.getMillis();
096            long otherMillis = otherDuration.getMillis();
097            
098            // cannot do (thisMillis - otherMillis) as it can overflow
099            if (thisMillis < otherMillis) {
100                return -1;
101            }
102            if (thisMillis > otherMillis) {
103                return 1;
104            }
105            return 0;
106        }
107    
108        /**
109         * Is the length of this duration equal to the duration passed in.
110         *
111         * @param duration  another duration to compare to, null means zero milliseconds
112         * @return true if this duration is equal to than the duration passed in
113         */
114        public boolean isEqual(ReadableDuration duration) {
115            if (duration == null) {
116                duration = Duration.ZERO;
117            }
118            return compareTo(duration) == 0;
119        }
120    
121        /**
122         * Is the length of this duration longer than the duration passed in.
123         *
124         * @param duration  another duration to compare to, null means zero milliseconds
125         * @return true if this duration is equal to than the duration passed in
126         */
127        public boolean isLongerThan(ReadableDuration duration) {
128            if (duration == null) {
129                duration = Duration.ZERO;
130            }
131            return compareTo(duration) > 0;
132        }
133    
134        /**
135         * Is the length of this duration shorter than the duration passed in.
136         *
137         * @param duration  another duration to compare to, null means zero milliseconds
138         * @return true if this duration is equal to than the duration passed in
139         */
140        public boolean isShorterThan(ReadableDuration duration) {
141            if (duration == null) {
142                duration = Duration.ZERO;
143            }
144            return compareTo(duration) < 0;
145        }
146    
147        //-----------------------------------------------------------------------
148        /**
149         * Compares this object with the specified object for equality based
150         * on the millisecond length. All ReadableDuration instances are accepted.
151         *
152         * @param duration  a readable duration to check against
153         * @return true if the length of the duration is equal
154         */
155        public boolean equals(Object duration) {
156            if (this == duration) {
157                return true;
158            }
159            if (duration instanceof ReadableDuration == false) {
160                return false;
161            }
162            ReadableDuration other = (ReadableDuration) duration;
163            return (getMillis() == other.getMillis());
164        }
165    
166        /**
167         * Gets a hash code for the duration that is compatible with the 
168         * equals method.
169         *
170         * @return a hash code
171         */
172        public int hashCode() {
173            long len = getMillis();
174            return (int) (len ^ (len >>> 32));
175        }
176    
177        //-----------------------------------------------------------------------
178        /**
179         * Gets the value as a String in the ISO8601 duration format including
180         * only seconds and milliseconds.
181         * <p>
182         * For example, "PT72.345S" represents 1 minute, 12 seconds and 345 milliseconds.
183         * <p>
184         * For more control over the output, see
185         * {@link org.joda.time.format.PeriodFormatterBuilder PeriodFormatterBuilder}.
186         *
187         * @return the value as an ISO8601 string
188         */
189        public String toString() {
190            long millis = getMillis();
191            StringBuffer buf = new StringBuffer();
192            buf.append("PT");
193            FormatUtils.appendUnpaddedInteger(buf, millis / 1000);
194            long part = Math.abs(millis % 1000);
195            if (part > 0) {
196                buf.append('.');
197                FormatUtils.appendPaddedInteger(buf, part, 3);
198            }
199            buf.append('S');
200            return buf.toString();
201        }
202    
203    }