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