1 /* 2 * Copyright 2001-2010 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.convert.ToString; 19 import org.joda.time.DurationFieldType; 20 import org.joda.time.MutablePeriod; 21 import org.joda.time.Period; 22 import org.joda.time.ReadablePeriod; 23 import org.joda.time.format.ISOPeriodFormat; 24 import org.joda.time.format.PeriodFormatter; 25 26 /** 27 * AbstractPeriod provides the common behaviour for period classes. 28 * <p> 29 * This class should generally not be used directly by API users. The 30 * {@link ReadablePeriod} interface should be used when different 31 * kinds of periods are to be referenced. 32 * <p> 33 * AbstractPeriod subclasses may be mutable and not thread-safe. 34 * 35 * @author Brian S O'Neill 36 * @author Stephen Colebourne 37 * @since 1.0 38 */ 39 public abstract class AbstractPeriod implements ReadablePeriod { 40 41 /** 42 * Constructor. 43 */ 44 protected AbstractPeriod() { 45 super(); 46 } 47 48 //----------------------------------------------------------------------- 49 /** 50 * Gets the number of fields that this period supports. 51 * 52 * @return the number of fields supported 53 * @since 2.0 (previously on BasePeriod) 54 */ 55 public int size() { 56 return getPeriodType().size(); 57 } 58 59 /** 60 * Gets the field type at the specified index. 61 * 62 * @param index the index to retrieve 63 * @return the field at the specified index 64 * @throws IndexOutOfBoundsException if the index is invalid 65 * @since 2.0 (previously on BasePeriod) 66 */ 67 public DurationFieldType getFieldType(int index) { 68 return getPeriodType().getFieldType(index); 69 } 70 71 /** 72 * Gets an array of the field types that this period supports. 73 * <p> 74 * The fields are returned largest to smallest, for example Hours, Minutes, Seconds. 75 * 76 * @return the fields supported in an array that may be altered, largest to smallest 77 */ 78 public DurationFieldType[] getFieldTypes() { 79 DurationFieldType[] result = new DurationFieldType[size()]; 80 for (int i = 0; i < result.length; i++) { 81 result[i] = getFieldType(i); 82 } 83 return result; 84 } 85 86 /** 87 * Gets an array of the value of each of the fields that this period supports. 88 * <p> 89 * The fields are returned largest to smallest, for example Hours, Minutes, Seconds. 90 * Each value corresponds to the same array index as <code>getFields()</code> 91 * 92 * @return the current values of each field in an array that may be altered, largest to smallest 93 */ 94 public int[] getValues() { 95 int[] result = new int[size()]; 96 for (int i = 0; i < result.length; i++) { 97 result[i] = getValue(i); 98 } 99 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 }