001 /* 002 * Copyright 2001-2011 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.Chronology; 019 import org.joda.time.DateTime; 020 import org.joda.time.DateTimeField; 021 import org.joda.time.DateTimeFieldType; 022 import org.joda.time.DateTimeUtils; 023 import org.joda.time.DurationFieldType; 024 import org.joda.time.ReadableInstant; 025 import org.joda.time.ReadablePartial; 026 import org.joda.time.field.FieldUtils; 027 import org.joda.time.format.DateTimeFormatter; 028 029 /** 030 * AbstractPartial provides a standard base implementation of most methods 031 * in the ReadablePartial interface. 032 * <p> 033 * Calculations on are performed using a {@link Chronology}. 034 * This chronology is set to be in the UTC time zone for all calculations. 035 * <p> 036 * The methods on this class use {@link ReadablePartial#size()}, 037 * {@link AbstractPartial#getField(int, Chronology)} and 038 * {@link ReadablePartial#getValue(int)} to calculate their results. 039 * Subclasses may have a better implementation. 040 * <p> 041 * AbstractPartial allows subclasses may be mutable and not thread-safe. 042 * 043 * @author Stephen Colebourne 044 * @since 1.0 045 */ 046 public abstract class AbstractPartial 047 implements ReadablePartial, Comparable<ReadablePartial> { 048 049 //----------------------------------------------------------------------- 050 /** 051 * Constructor. 052 */ 053 protected AbstractPartial() { 054 super(); 055 } 056 057 //----------------------------------------------------------------------- 058 /** 059 * Gets the field for a specific index in the chronology specified. 060 * <p> 061 * This method must not use any instance variables. 062 * 063 * @param index the index to retrieve 064 * @param chrono the chronology to use 065 * @return the field 066 * @throws IndexOutOfBoundsException if the index is invalid 067 */ 068 protected abstract DateTimeField getField(int index, Chronology chrono); 069 070 //----------------------------------------------------------------------- 071 /** 072 * Gets the field type at the specifed index. 073 * 074 * @param index the index 075 * @return the field type 076 * @throws IndexOutOfBoundsException if the index is invalid 077 */ 078 public DateTimeFieldType getFieldType(int index) { 079 return getField(index, getChronology()).getType(); 080 } 081 082 /** 083 * Gets an array of the field types that this partial supports. 084 * <p> 085 * The fields are returned largest to smallest, for example Hour, Minute, Second. 086 * 087 * @return the fields supported in an array that may be altered, largest to smallest 088 */ 089 public DateTimeFieldType[] getFieldTypes() { 090 DateTimeFieldType[] result = new DateTimeFieldType[size()]; 091 for (int i = 0; i < result.length; i++) { 092 result[i] = getFieldType(i); 093 } 094 return result; 095 } 096 097 /** 098 * Gets the field at the specifed index. 099 * 100 * @param index the index 101 * @return the field 102 * @throws IndexOutOfBoundsException if the index is invalid 103 */ 104 public DateTimeField getField(int index) { 105 return getField(index, getChronology()); 106 } 107 108 /** 109 * Gets an array of the fields that this partial supports. 110 * <p> 111 * The fields are returned largest to smallest, for example Hour, Minute, Second. 112 * 113 * @return the fields supported in an array that may be altered, largest to smallest 114 */ 115 public DateTimeField[] getFields() { 116 DateTimeField[] result = new DateTimeField[size()]; 117 for (int i = 0; i < result.length; i++) { 118 result[i] = getField(i); 119 } 120 return result; 121 } 122 123 /** 124 * Gets an array of the value of each of the fields that this partial supports. 125 * <p> 126 * The fields are returned largest to smallest, for example Hour, Minute, Second. 127 * Each value corresponds to the same array index as <code>getFields()</code> 128 * 129 * @return the current values of each field in an array that may be altered, largest to smallest 130 */ 131 public int[] getValues() { 132 int[] result = new int[size()]; 133 for (int i = 0; i < result.length; i++) { 134 result[i] = getValue(i); 135 } 136 return result; 137 } 138 139 //----------------------------------------------------------------------- 140 /** 141 * Get the value of one of the fields of a datetime. 142 * <p> 143 * The field specified must be one of those that is supported by the partial. 144 * 145 * @param type a DateTimeFieldType instance that is supported by this partial 146 * @return the value of that field 147 * @throws IllegalArgumentException if the field is null or not supported 148 */ 149 public int get(DateTimeFieldType type) { 150 return getValue(indexOfSupported(type)); 151 } 152 153 /** 154 * Checks whether the field specified is supported by this partial. 155 * 156 * @param type the type to check, may be null which returns false 157 * @return true if the field is supported 158 */ 159 public boolean isSupported(DateTimeFieldType type) { 160 return (indexOf(type) != -1); 161 } 162 163 /** 164 * Gets the index of the specified field, or -1 if the field is unsupported. 165 * 166 * @param type the type to check, may be null which returns -1 167 * @return the index of the field, -1 if unsupported 168 */ 169 public int indexOf(DateTimeFieldType type) { 170 for (int i = 0, isize = size(); i < isize; i++) { 171 if (getFieldType(i) == type) { 172 return i; 173 } 174 } 175 return -1; 176 } 177 178 /** 179 * Gets the index of the specified field, throwing an exception if the 180 * field is unsupported. 181 * 182 * @param type the type to check, not null 183 * @return the index of the field 184 * @throws IllegalArgumentException if the field is null or not supported 185 */ 186 protected int indexOfSupported(DateTimeFieldType type) { 187 int index = indexOf(type); 188 if (index == -1) { 189 throw new IllegalArgumentException("Field '" + type + "' is not supported"); 190 } 191 return index; 192 } 193 194 /** 195 * Gets the index of the first fields to have the specified duration, 196 * or -1 if the field is unsupported. 197 * 198 * @param type the type to check, may be null which returns -1 199 * @return the index of the field, -1 if unsupported 200 */ 201 protected int indexOf(DurationFieldType type) { 202 for (int i = 0, isize = size(); i < isize; i++) { 203 if (getFieldType(i).getDurationType() == type) { 204 return i; 205 } 206 } 207 return -1; 208 } 209 210 /** 211 * Gets the index of the first fields to have the specified duration, 212 * throwing an exception if the field is unsupported. 213 * 214 * @param type the type to check, not null 215 * @return the index of the field 216 * @throws IllegalArgumentException if the field is null or not supported 217 */ 218 protected int indexOfSupported(DurationFieldType type) { 219 int index = indexOf(type); 220 if (index == -1) { 221 throw new IllegalArgumentException("Field '" + type + "' is not supported"); 222 } 223 return index; 224 } 225 226 //----------------------------------------------------------------------- 227 /** 228 * Resolves this partial against another complete instant to create a new 229 * full instant. The combination is performed using the chronology of the 230 * specified instant. 231 * <p> 232 * For example, if this partial represents a time, then the result of this 233 * method will be the datetime from the specified base instant plus the 234 * time from this partial. 235 * 236 * @param baseInstant the instant that provides the missing fields, null means now 237 * @return the combined datetime 238 */ 239 public DateTime toDateTime(ReadableInstant baseInstant) { 240 Chronology chrono = DateTimeUtils.getInstantChronology(baseInstant); 241 long instantMillis = DateTimeUtils.getInstantMillis(baseInstant); 242 long resolved = chrono.set(this, instantMillis); 243 return new DateTime(resolved, chrono); 244 } 245 246 //----------------------------------------------------------------------- 247 /** 248 * Compares this ReadablePartial with another returning true if the chronology, 249 * field types and values are equal. 250 * 251 * @param partial an object to check against 252 * @return true if fields and values are equal 253 */ 254 public boolean equals(Object partial) { 255 if (this == partial) { 256 return true; 257 } 258 if (partial instanceof ReadablePartial == false) { 259 return false; 260 } 261 ReadablePartial other = (ReadablePartial) partial; 262 if (size() != other.size()) { 263 return false; 264 } 265 for (int i = 0, isize = size(); i < isize; i++) { 266 if (getValue(i) != other.getValue(i) || getFieldType(i) != other.getFieldType(i)) { 267 return false; 268 } 269 } 270 return FieldUtils.equals(getChronology(), other.getChronology()); 271 } 272 273 /** 274 * Gets a hash code for the ReadablePartial that is compatible with the 275 * equals method. 276 * 277 * @return a suitable hash code 278 */ 279 public int hashCode() { 280 int total = 157; 281 for (int i = 0, isize = size(); i < isize; i++) { 282 total = 23 * total + getValue(i); 283 total = 23 * total + getFieldType(i).hashCode(); 284 } 285 total += getChronology().hashCode(); 286 return total; 287 } 288 289 //----------------------------------------------------------------------- 290 /** 291 * Compares this partial with another returning an integer 292 * indicating the order. 293 * <p> 294 * The fields are compared in order, from largest to smallest. 295 * The first field that is non-equal is used to determine the result. 296 * <p> 297 * The specified object must be a partial instance whose field types 298 * match those of this partial. 299 * <p> 300 * NOTE: Prior to v2.0, the {@code Comparable} interface was only implemented 301 * in this class and not in the {@code ReadablePartial} interface. 302 * 303 * @param other an object to check against 304 * @return negative if this is less, zero if equal, positive if greater 305 * @throws ClassCastException if the partial is the wrong class 306 * or if it has field types that don't match 307 * @throws NullPointerException if the partial is null 308 * @since 1.1 309 */ 310 public int compareTo(ReadablePartial other) { 311 if (this == other) { 312 return 0; 313 } 314 if (size() != other.size()) { 315 throw new ClassCastException("ReadablePartial objects must have matching field types"); 316 } 317 for (int i = 0, isize = size(); i < isize; i++) { 318 if (getFieldType(i) != other.getFieldType(i)) { 319 throw new ClassCastException("ReadablePartial objects must have matching field types"); 320 } 321 } 322 // fields are ordered largest first 323 for (int i = 0, isize = size(); i < isize; i++) { 324 if (getValue(i) > other.getValue(i)) { 325 return 1; 326 } 327 if (getValue(i) < other.getValue(i)) { 328 return -1; 329 } 330 } 331 return 0; 332 } 333 334 /** 335 * Is this partial later than the specified partial. 336 * <p> 337 * The fields are compared in order, from largest to smallest. 338 * The first field that is non-equal is used to determine the result. 339 * <p> 340 * You may not pass null into this method. This is because you need 341 * a time zone to accurately determine the current date. 342 * 343 * @param partial a partial to check against, must not be null 344 * @return true if this date is after the date passed in 345 * @throws IllegalArgumentException if the specified partial is null 346 * @throws ClassCastException if the partial has field types that don't match 347 * @since 1.1 348 */ 349 public boolean isAfter(ReadablePartial partial) { 350 if (partial == null) { 351 throw new IllegalArgumentException("Partial cannot be null"); 352 } 353 return compareTo(partial) > 0; 354 } 355 356 /** 357 * Is this partial earlier than the specified partial. 358 * <p> 359 * The fields are compared in order, from largest to smallest. 360 * The first field that is non-equal is used to determine the result. 361 * <p> 362 * You may not pass null into this method. This is because you need 363 * a time zone to accurately determine the current date. 364 * 365 * @param partial a partial to check against, must not be null 366 * @return true if this date is before the date passed in 367 * @throws IllegalArgumentException if the specified partial is null 368 * @throws ClassCastException if the partial has field types that don't match 369 * @since 1.1 370 */ 371 public boolean isBefore(ReadablePartial partial) { 372 if (partial == null) { 373 throw new IllegalArgumentException("Partial cannot be null"); 374 } 375 return compareTo(partial) < 0; 376 } 377 378 /** 379 * Is this partial the same as the specified partial. 380 * <p> 381 * The fields are compared in order, from largest to smallest. 382 * If all fields are equal, the result is true. 383 * <p> 384 * You may not pass null into this method. This is because you need 385 * a time zone to accurately determine the current date. 386 * 387 * @param partial a partial to check against, must not be null 388 * @return true if this date is the same as the date passed in 389 * @throws IllegalArgumentException if the specified partial is null 390 * @throws ClassCastException if the partial has field types that don't match 391 * @since 1.1 392 */ 393 public boolean isEqual(ReadablePartial partial) { 394 if (partial == null) { 395 throw new IllegalArgumentException("Partial cannot be null"); 396 } 397 return compareTo(partial) == 0; 398 } 399 400 //----------------------------------------------------------------------- 401 /** 402 * Uses the specified formatter to convert this partial to a String. 403 * 404 * @param formatter the formatter to use, null means use <code>toString()</code>. 405 * @return the formatted string 406 * @since 1.1 407 */ 408 public String toString(DateTimeFormatter formatter) { 409 if (formatter == null) { 410 return toString(); 411 } 412 return formatter.print(this); 413 } 414 415 }