1 | /* |
2 | * Copyright 2001-2005 Stephen Colebourne |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | package org.joda.time.base; |
17 | |
18 | import org.joda.time.DurationFieldType; |
19 | import org.joda.time.MutablePeriod; |
20 | import org.joda.time.Period; |
21 | import org.joda.time.ReadablePeriod; |
22 | import org.joda.time.format.ISOPeriodFormat; |
23 | import org.joda.time.format.PeriodFormatter; |
24 | |
25 | /** |
26 | * AbstractPeriod provides the common behaviour for period classes. |
27 | * <p> |
28 | * This class should generally not be used directly by API users. The |
29 | * {@link ReadablePeriod} interface should be used when different |
30 | * kinds of periods are to be referenced. |
31 | * <p> |
32 | * AbstractPeriod subclasses may be mutable and not thread-safe. |
33 | * |
34 | * @author Brian S O'Neill |
35 | * @author Stephen Colebourne |
36 | * @since 1.0 |
37 | */ |
38 | public abstract class AbstractPeriod implements ReadablePeriod { |
39 | |
40 | /** |
41 | * Constructor. |
42 | */ |
43 | protected AbstractPeriod() { |
44 | super(); |
45 | } |
46 | |
47 | //----------------------------------------------------------------------- |
48 | /** |
49 | * Gets an array of the field types that this period supports. |
50 | * <p> |
51 | * The fields are returned largest to smallest, for example Hours, Minutes, Seconds. |
52 | * |
53 | * @return the fields supported in an array that may be altered, largest to smallest |
54 | */ |
55 | public DurationFieldType[] getFieldTypes() { |
56 | DurationFieldType[] result = new DurationFieldType[size()]; |
57 | for (int i = 0; i < result.length; i++) { |
58 | result[i] = getFieldType(i); |
59 | } |
60 | return result; |
61 | } |
62 | |
63 | /** |
64 | * Gets an array of the value of each of the fields that this period supports. |
65 | * <p> |
66 | * The fields are returned largest to smallest, for example Hours, Minutes, Seconds. |
67 | * Each value corresponds to the same array index as <code>getFields()</code> |
68 | * |
69 | * @return the current values of each field in an array that may be altered, largest to smallest |
70 | */ |
71 | public int[] getValues() { |
72 | int[] result = new int[size()]; |
73 | for (int i = 0; i < result.length; i++) { |
74 | result[i] = getValue(i); |
75 | } |
76 | return result; |
77 | } |
78 | |
79 | //----------------------------------------------------------------------- |
80 | /** |
81 | * Gets the value of one of the fields. |
82 | * <p> |
83 | * If the field type specified is not supported by the period then zero |
84 | * is returned. |
85 | * |
86 | * @param type the field type to query, null returns zero |
87 | * @return the value of that field, zero if field not supported |
88 | */ |
89 | public int get(DurationFieldType type) { |
90 | int index = indexOf(type); |
91 | if (index == -1) { |
92 | return 0; |
93 | } |
94 | return getValue(index); |
95 | } |
96 | |
97 | /** |
98 | * Checks whether the field specified is supported by this period. |
99 | * |
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 | } |