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 }