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.DurationFieldType;
019    import org.joda.time.MutablePeriod;
020    import org.joda.time.Period;
021    import org.joda.time.ReadablePeriod;
022    import org.joda.time.format.ISOPeriodFormat;
023    import org.joda.time.format.PeriodFormatter;
024    
025    /**
026     * AbstractPeriod provides the common behaviour for period classes.
027     * <p>
028     * This class should generally not be used directly by API users. The 
029     * {@link ReadablePeriod} interface should be used when different 
030     * kinds of periods are to be referenced.
031     * <p>
032     * AbstractPeriod 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 AbstractPeriod implements ReadablePeriod {
039    
040        /**
041         * Constructor.
042         */
043        protected AbstractPeriod() {
044            super();
045        }
046    
047        //-----------------------------------------------------------------------
048        /**
049         * Gets an array of the field types that this period supports.
050         * <p>
051         * The fields are returned largest to smallest, for example Hours, Minutes, Seconds.
052         *
053         * @return the fields supported in an array that may be altered, largest to smallest
054         */
055        public DurationFieldType[] getFieldTypes() {
056            DurationFieldType[] result = new DurationFieldType[size()];
057            for (int i = 0; i < result.length; i++) {
058                result[i] = getFieldType(i);
059            }
060            return result;
061        }
062    
063        /**
064         * Gets an array of the value of each of the fields that this period supports.
065         * <p>
066         * The fields are returned largest to smallest, for example Hours, Minutes, Seconds.
067         * Each value corresponds to the same array index as <code>getFields()</code>
068         *
069         * @return the current values of each field in an array that may be altered, largest to smallest
070         */
071        public int[] getValues() {
072            int[] result = new int[size()];
073            for (int i = 0; i < result.length; i++) {
074                result[i] = getValue(i);
075            }
076            return result;
077        }
078    
079        //-----------------------------------------------------------------------
080        /**
081         * Gets the value of one of the fields.
082         * <p>
083         * If the field type specified is not supported by the period then zero
084         * is returned.
085         *
086         * @param type  the field type to query, null returns zero
087         * @return the value of that field, zero if field not supported
088         */
089        public int get(DurationFieldType type) {
090            int index = indexOf(type);
091            if (index == -1) {
092                return 0;
093            }
094            return getValue(index);
095        }
096    
097        /**
098         * Checks whether the field specified is supported by this period.
099         *
100         * @param type  the type to check, may be null which returns false
101         * @return true if the field is supported
102         */
103        public boolean isSupported(DurationFieldType type) {
104            return getPeriodType().isSupported(type);
105        }
106    
107        /**
108         * Gets the index of the field in this period.
109         *
110         * @param type  the type to check, may be null which returns -1
111         * @return the index of -1 if not supported
112         */
113        public int indexOf(DurationFieldType type) {
114            return getPeriodType().indexOf(type);
115        }
116    
117        //-----------------------------------------------------------------------
118        /**
119         * Get this period as an immutable <code>Period</code> object.
120         * 
121         * @return a Period using the same field set and values
122         */
123        public Period toPeriod() {
124            return new Period(this);
125        }
126    
127        /**
128         * Get this object as a <code>MutablePeriod</code>.
129         * <p>
130         * This will always return a new <code>MutablePeriod</code> with the same fields.
131         * 
132         * @return a MutablePeriod using the same field set and values
133         */
134        public MutablePeriod toMutablePeriod() {
135            return new MutablePeriod(this);
136        }
137    
138        //-----------------------------------------------------------------------
139        /**
140         * Compares this object with the specified object for equality based
141         * on the value of each field. All ReadablePeriod instances are accepted.
142         * <p>
143         * Note that a period of 1 day is not equal to a period of 24 hours,
144         * nor is 1 hour equal to 60 minutes. Only periods with the same amount
145         * in each field are equal.
146         * <p>
147         * This is because periods represent an abstracted definition of a time
148         * period (eg. a day may not actually be 24 hours, it might be 23 or 25
149         * at daylight savings boundary).
150         * <p>
151         * To compare the actual duration of two periods, convert both to
152         * {@link org.joda.time.Duration Duration}s, an operation that emphasises
153         * that the result may differ according to the date you choose.
154         *
155         * @param period  a readable period to check against
156         * @return true if all the field values are equal, false if
157         *  not or the period is null or of an incorrect type
158         */
159        public boolean equals(Object period) {
160            if (this == period) {
161                return true;
162            }
163            if (period instanceof ReadablePeriod == false) {
164                return false;
165            }
166            ReadablePeriod other = (ReadablePeriod) period;
167            if (size() != other.size()) {
168                return false;
169            }
170            for (int i = 0, isize = size(); i < isize; i++) {
171                if (getValue(i) != other.getValue(i) || getFieldType(i) != other.getFieldType(i)) {
172                    return false;
173                }
174            }
175            return true;
176        }
177    
178        /**
179         * Gets a hash code for the period as defined by ReadablePeriod.
180         *
181         * @return a hash code
182         */
183        public int hashCode() {
184            int total = 17;
185            for (int i = 0, isize = size(); i < isize; i++) {
186                total = 27 * total + getValue(i);
187                total = 27 * total + getFieldType(i).hashCode();
188            }
189            return total;
190        }
191    
192        //-----------------------------------------------------------------------
193        /**
194         * Gets the value as a String in the ISO8601 duration format.
195         * <p>
196         * For example, "P6H3M7S" represents 6 hours, 3 minutes, 7 seconds.
197         * <p>
198         * For more control over the output, see
199         * {@link org.joda.time.format.PeriodFormatterBuilder PeriodFormatterBuilder}.
200         *
201         * @return the value as an ISO8601 string
202         */
203        public String toString() {
204            return ISOPeriodFormat.standard().print(this);
205        }
206    
207        //-----------------------------------------------------------------------
208        /**
209         * Uses the specified formatter to convert this period to a String.
210         *
211         * @param formatter  the formatter to use, null means use <code>toString()</code>.
212         * @return the formatted string
213         * @since 1.5
214         */
215        public String toString(PeriodFormatter formatter) {
216            if (formatter == null) {
217                return toString();
218            }
219            return formatter.print(this);
220        }
221    
222    }