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.field; 17 18 import java.io.Serializable; 19 import java.util.Locale; 20 21 import org.joda.time.Chronology; 22 import org.joda.time.DateTimeField; 23 import org.joda.time.DateTimeFieldType; 24 import org.joda.time.DateTimeUtils; 25 import org.joda.time.DurationField; 26 import org.joda.time.Interval; 27 import org.joda.time.ReadableInstant; 28 import org.joda.time.ReadablePartial; 29 30 /** 31 * AbstractReadableInstantFieldProperty is a base class for binding a 32 * ReadableInstant to a DateTimeField. 33 * <p> 34 * It allows the date and time manipulation code to be field based yet 35 * still easy to use. 36 * <p> 37 * AbstractReadableInstantFieldProperty itself is thread-safe and immutable, 38 * but the ReadableInstant being operated on may be mutable and not 39 * thread-safe. 40 * 41 * @author Stephen Colebourne 42 * @author Brian S O'Neill 43 * @author Mike Schrag 44 * @since 1.0 45 */ 46 public abstract class AbstractReadableInstantFieldProperty implements Serializable { 47 48 /** Serialization version. */ 49 private static final long serialVersionUID = 1971226328211649661L; 50 51 /** 52 * Constructor. 53 */ 54 public AbstractReadableInstantFieldProperty() { 55 super(); 56 } 57 58 //----------------------------------------------------------------------- 59 /** 60 * Gets the field being used. 61 * 62 * @return the field 63 */ 64 public abstract DateTimeField getField(); 65 66 /** 67 * Gets the field type being used. 68 * 69 * @return the field type 70 */ 71 public DateTimeFieldType getFieldType() { 72 return getField().getType(); 73 } 74 75 /** 76 * Gets the name of the field. 77 * 78 * @return the field name 79 */ 80 public String getName() { 81 return getField().getName(); 82 } 83 84 /** 85 * Gets the milliseconds of the datetime that this property is linked to. 86 * 87 * @return the milliseconds 88 */ 89 protected abstract long getMillis(); 90 91 /** 92 * Gets the chronology of the datetime that this property is linked to. 93 * <p> 94 * This implementation throws UnsupportedOperationException, and must be 95 * implemented by subclasses to enable the equals() and hashCode() methods. 96 * 97 * @return the chronology 98 * @since 1.4 99 */ 100 protected Chronology getChronology() { 101 throw new UnsupportedOperationException( 102 "The method getChronology() was added in v1.4 and needs " + 103 "to be implemented by subclasses of AbstractReadableInstantFieldProperty"); 104 } 105 106 //----------------------------------------------------------------------- 107 /** 108 * Gets the value of this property from the instant. 109 * <p> 110 * For example, the following two lines of code are equivalent: 111 * <pre> 112 * datetime.getDayOfMonth(); 113 * datetime.dayOfMonth().get(); 114 * </pre> 115 * 116 * @return the current value 117 * @see DateTimeField#get 118 */ 119 public int get() { 120 return getField().get(getMillis()); 121 } 122 123 /** 124 * Gets the value of this property from the instant as a string. 125 * <p> 126 * This method returns the value converted to a <code>String</code> 127 * using <code>Integer.toString</code>. This method does NOT return 128 * textual descriptions such as 'Monday' or 'January'. 129 * See {@link #getAsText()} and {@link #getAsShortText()} for those. 130 * 131 * @return the current value 132 * @see DateTimeField#get 133 * @since 1.1 134 */ 135 public String getAsString() { 136 return Integer.toString(get()); 137 } 138 139 /** 140 * Gets the textual value of this property from the instant as a 141 * string in the default locale. 142 * <p> 143 * This method returns the value converted to a <code>String</code> 144 * returning the appropriate textual description wherever possible. 145 * Thus, a day of week of 1 would return 'Monday' in English. 146 * 147 * @return the current text value 148 * @see DateTimeField#getAsText 149 */ 150 public String getAsText() { 151 return getAsText(null); 152 } 153 154 /** 155 * Gets the textual value of this property from the instant as a 156 * string in the specified locale. 157 * <p> 158 * This method returns the value converted to a <code>String</code> 159 * returning the appropriate textual description wherever possible. 160 * Thus, a day of week of 1 would return 'Monday' in English. 161 * 162 * @param locale locale to use for selecting a text symbol, null means default 163 * @return the current text value 164 * @see DateTimeField#getAsText 165 */ 166 public String getAsText(Locale locale) { 167 return getField().getAsText(getMillis(), locale); 168 } 169 170 /** 171 * Gets the short textual value of this property from the instant as a 172 * string in the default locale. 173 * <p> 174 * This method returns the value converted to a <code>String</code> 175 * returning the appropriate textual description wherever possible. 176 * Thus, a day of week of 1 would return 'Mon' in English. 177 * 178 * @return the current text value 179 * @see DateTimeField#getAsShortText 180 */ 181 public String getAsShortText() { 182 return getAsShortText(null); 183 } 184 185 /** 186 * Gets the short textual value of this property from the instant as a 187 * string in the specified locale. 188 * <p> 189 * This method returns the value converted to a <code>String</code> 190 * returning the appropriate textual description wherever possible. 191 * Thus, a day of week of 1 would return 'Mon' in English. 192 * 193 * @param locale locale to use for selecting a text symbol, null means default 194 * @return the current text value 195 * @see DateTimeField#getAsShortText 196 */ 197 public String getAsShortText(Locale locale) { 198 return getField().getAsShortText(getMillis(), locale); 199 } 200 201 //----------------------------------------------------------------------- 202 /** 203 * Returns the difference between this field property instant and the one 204 * passed in, in the units of this field. The sign of the difference 205 * matches that of compareTo. In other words, this field property's instant 206 * is the minuend. 207 * 208 * @param instant the subtrahend, null means now 209 * @return the difference in the units of this field 210 * @see DateTimeField#getDifference 211 */ 212 public int getDifference(ReadableInstant instant) { 213 if (instant == null) { 214 return getField().getDifference(getMillis(), DateTimeUtils.currentTimeMillis()); 215 } 216 return getField().getDifference(getMillis(), instant.getMillis()); 217 } 218 219 /** 220 * Returns the difference between this field property instant and the one 221 * passed in, in the units of this field. The sign of the difference 222 * matches that of compareTo. In other words, this field property's instant 223 * is the minuend. 224 * 225 * @param instant the subtrahend, null means now 226 * @return the difference in the units of this field 227 * @see DateTimeField#getDifference 228 */ 229 public long getDifferenceAsLong(ReadableInstant instant) { 230 if (instant == null) { 231 return getField().getDifferenceAsLong(getMillis(), DateTimeUtils.currentTimeMillis()); 232 } 233 return getField().getDifferenceAsLong(getMillis(), instant.getMillis()); 234 } 235 236 //----------------------------------------------------------------------- 237 /** 238 * Returns the duration per unit value of this field. For example, if this 239 * field represents "hour of day", then the duration is an hour. 240 * 241 * @return the duration of this field, or UnsupportedDurationField 242 */ 243 public DurationField getDurationField() { 244 return getField().getDurationField(); 245 } 246 247 /** 248 * Returns the range duration of this field. For example, if this field 249 * represents "hour of day", then the range duration is a day. 250 * 251 * @return the range duration of this field, or null if field has no range 252 */ 253 public DurationField getRangeDurationField() { 254 return getField().getRangeDurationField(); 255 } 256 257 /** 258 * Gets whether this field is leap. 259 * 260 * @return true if a leap field 261 * @see DateTimeField#isLeap 262 */ 263 public boolean isLeap() { 264 return getField().isLeap(getMillis()); 265 } 266 267 /** 268 * Gets the amount by which this field is leap. 269 * 270 * @return the amount by which the field is leap 271 * @see DateTimeField#getLeapAmount 272 */ 273 public int getLeapAmount() { 274 return getField().getLeapAmount(getMillis()); 275 } 276 277 /** 278 * If this field were to leap, then it would be in units described by the 279 * returned duration. If this field doesn't ever leap, null is returned. 280 */ 281 public DurationField getLeapDurationField() { 282 return getField().getLeapDurationField(); 283 } 284 285 //----------------------------------------------------------------------- 286 /** 287 * Gets the minimum value for the field ignoring the current time. 288 * 289 * @return the minimum value 290 * @see DateTimeField#getMinimumValue 291 */ 292 public int getMinimumValueOverall() { 293 return getField().getMinimumValue(); 294 } 295 296 /** 297 * Gets the minimum value for the field. 298 * 299 * @return the minimum value 300 * @see DateTimeField#getMinimumValue 301 */ 302 public int getMinimumValue() { 303 return getField().getMinimumValue(getMillis()); 304 } 305 306 /** 307 * Gets the maximum value for the field ignoring the current time. 308 * 309 * @return the maximum value 310 * @see DateTimeField#getMaximumValue 311 */ 312 public int getMaximumValueOverall() { 313 return getField().getMaximumValue(); 314 } 315 316 /** 317 * Gets the maximum value for the field. 318 * 319 * @return the maximum value 320 * @see DateTimeField#getMaximumValue 321 */ 322 public int getMaximumValue() { 323 return getField().getMaximumValue(getMillis()); 324 } 325 326 /** 327 * Gets the maximum text length for the field. 328 * 329 * @param locale optional locale to use for selecting a text symbol 330 * @return the maximum length 331 * @see DateTimeField#getMaximumTextLength 332 */ 333 public int getMaximumTextLength(Locale locale) { 334 return getField().getMaximumTextLength(locale); 335 } 336 337 /** 338 * Gets the maximum short text length for the field. 339 * 340 * @param locale optional locale to use for selecting a text symbol 341 * @return the maximum length 342 * @see DateTimeField#getMaximumShortTextLength 343 */ 344 public int getMaximumShortTextLength(Locale locale) { 345 return getField().getMaximumShortTextLength(locale); 346 } 347 348 349 /** 350 * Returns the fractional duration milliseconds of this field. 351 * 352 * @see DateTimeField#remainder 353 * @return remainder duration, in milliseconds 354 */ 355 public long remainder() { 356 return getField().remainder(getMillis()); 357 } 358 359 /** 360 * Returns the interval that represents the range of the minimum 361 * and maximum values of this field. 362 * <p> 363 * For example, <code>datetime.monthOfYear().toInterval()</code> 364 * will return an interval over the whole month. 365 * 366 * @return the interval of this field 367 * @since 1.2 368 */ 369 public Interval toInterval() { 370 DateTimeField field = getField(); 371 long start = field.roundFloor(getMillis()); 372 long end = field.add(start, 1); 373 Interval interval = new Interval(start, end); 374 return interval; 375 } 376 377 //----------------------------------------------------------------------- 378 /** 379 * Compare this field to the same field on another instant. 380 * <p> 381 * The comparison is based on the value of the same field type, irrespective 382 * of any difference in chronology. Thus, if this property represents the 383 * hourOfDay field, then the hourOfDay field of the other instant will be queried 384 * whether in the same chronology or not. 385 * 386 * @param instant the instant to compare to 387 * @return negative value if this is less, 0 if equal, or positive value if greater 388 * @throws IllegalArgumentException if the instant is null 389 */ 390 public int compareTo(ReadableInstant instant) { 391 if (instant == null) { 392 throw new IllegalArgumentException("The instant must not be null"); 393 } 394 int thisValue = get(); 395 int otherValue = instant.get(getFieldType()); 396 if (thisValue < otherValue) { 397 return -1; 398 } else if (thisValue > otherValue) { 399 return 1; 400 } else { 401 return 0; 402 } 403 } 404 405 //----------------------------------------------------------------------- 406 /** 407 * Compare this field to the same field on another partial instant. 408 * <p> 409 * The comparison is based on the value of the same field type, irrespective 410 * of any difference in chronology. Thus, if this property represents the 411 * hourOfDay field, then the hourOfDay field of the other partial will be queried 412 * whether in the same chronology or not. 413 * 414 * @param partial the partial to compare to 415 * @return negative value if this is less, 0 if equal, or positive value if greater 416 * @throws IllegalArgumentException if the partial is null 417 * @throws IllegalArgumentException if the partial doesn't support this field 418 */ 419 public int compareTo(ReadablePartial partial) { 420 if (partial == null) { 421 throw new IllegalArgumentException("The partial must not be null"); 422 } 423 int thisValue = get(); 424 int otherValue = partial.get(getFieldType()); 425 if (thisValue < otherValue) { 426 return -1; 427 } else if (thisValue > otherValue) { 428 return 1; 429 } else { 430 return 0; 431 } 432 } 433 434 //----------------------------------------------------------------------- 435 /** 436 * Compares this property to another. 437 * 438 * @param object the object to compare to 439 * @return true if equal 440 */ 441 public boolean equals(Object object) { 442 if (this == object) { 443 return true; 444 } 445 if (object instanceof AbstractReadableInstantFieldProperty == false) { 446 return false; 447 } 448 AbstractReadableInstantFieldProperty other = (AbstractReadableInstantFieldProperty) object; 449 return 450 get() == other.get() && 451 getFieldType().equals(other.getFieldType()) && 452 FieldUtils.equals(getChronology(), other.getChronology()); 453 } 454 455 /** 456 * Returns a hashcode compatible with the equals method. 457 * 458 * @return the hashcode 459 */ 460 public int hashCode() { 461 return get() * 17 + getFieldType().hashCode() + getChronology().hashCode(); 462 } 463 464 //----------------------------------------------------------------------- 465 /** 466 * Output a debugging string. 467 * 468 * @return debugging string 469 */ 470 public String toString() { 471 return "Property[" + getName() + "]"; 472 } 473 474 }