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