001 /* 002 * Copyright 2001-2005 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.field; 017 018 import java.io.Serializable; 019 import java.util.Locale; 020 021 import org.joda.time.Chronology; 022 import org.joda.time.DateTimeField; 023 import org.joda.time.DateTimeFieldType; 024 import org.joda.time.DateTimeUtils; 025 import org.joda.time.DurationField; 026 import org.joda.time.Interval; 027 import org.joda.time.ReadableInstant; 028 import org.joda.time.ReadablePartial; 029 030 /** 031 * AbstractReadableInstantFieldProperty is a base class for binding a 032 * ReadableInstant to a DateTimeField. 033 * <p> 034 * It allows the date and time manipulation code to be field based yet 035 * still easy to use. 036 * <p> 037 * AbstractReadableInstantFieldProperty itself is thread-safe and immutable, 038 * but the ReadableInstant being operated on may be mutable and not 039 * thread-safe. 040 * 041 * @author Stephen Colebourne 042 * @author Brian S O'Neill 043 * @author Mike Schrag 044 * @since 1.0 045 */ 046 public abstract class AbstractReadableInstantFieldProperty implements Serializable { 047 048 /** Serialization version. */ 049 private static final long serialVersionUID = 1971226328211649661L; 050 051 /** 052 * Constructor. 053 */ 054 public AbstractReadableInstantFieldProperty() { 055 super(); 056 } 057 058 //----------------------------------------------------------------------- 059 /** 060 * Gets the field being used. 061 * 062 * @return the field 063 */ 064 public abstract DateTimeField getField(); 065 066 /** 067 * Gets the field type being used. 068 * 069 * @return the field type 070 */ 071 public DateTimeFieldType getFieldType() { 072 return getField().getType(); 073 } 074 075 /** 076 * Gets the name of the field. 077 * 078 * @return the field name 079 */ 080 public String getName() { 081 return getField().getName(); 082 } 083 084 /** 085 * Gets the milliseconds of the datetime that this property is linked to. 086 * 087 * @return the milliseconds 088 */ 089 protected abstract long getMillis(); 090 091 /** 092 * Gets the chronology of the datetime that this property is linked to. 093 * <p> 094 * This implementation throws UnsupportedOperationException, and must be 095 * implemented by subclasses to enable the equals() and hashCode() methods. 096 * 097 * @return the chronology 098 * @since 1.4 099 */ 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 }