001 package edu.nrao.sss.model.project.scan; 002 003 import java.text.DateFormat; 004 import java.util.Date; 005 006 import javax.xml.bind.annotation.XmlElement; 007 import javax.xml.bind.annotation.XmlType; 008 009 import edu.nrao.sss.measure.LocalSiderealTime; 010 import edu.nrao.sss.measure.TimeDuration; 011 import edu.nrao.sss.measure.TimeOfDay; 012 import edu.nrao.sss.measure.TimeUnits; 013 import edu.nrao.sss.util.DateUtility; 014 015 /** 016 * Specification for the timing of a {@link Scan}. 017 * <p> 018 * <b>Version Info:</b> 019 * <table style="margin-left:2em"> 020 * <tr><td>$Revision: 1709 $</td></tr> 021 * <tr><td>$Date: 2008-11-14 11:22:37 -0700 (Fri, 14 Nov 2008) $</td></tr> 022 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 023 * </table></p> 024 * 025 * @author David M. Harland 026 * @since 2008-08-05 027 */ 028 @XmlType(propOrder={"timeType", "duration", "lst", "time"}) 029 public class ScanTimeSpecification 030 implements Cloneable 031 { 032 //Persisted properties 033 private ScanTimeType timeType; 034 private String timeText; 035 036 //Transient properties 037 private Date utDateTime; 038 private TimeOfDay lst; 039 private TimeDuration duration; 040 041 /** 042 * Creates a new time specification. 043 */ 044 public ScanTimeSpecification() 045 { 046 init(); 047 } 048 049 private void init() 050 { 051 timeType = ScanTimeType.ON_SOURCE_SIDEREAL; 052 duration = new TimeDuration("5.0", TimeUnits.MINUTE); 053 timeText = duration.toStringHms(); 054 lst = null; 055 utDateTime = null; 056 } 057 058 /** 059 * Returns this object to the same state as a newly created specification. 060 */ 061 public void reset() 062 { 063 init(); 064 } 065 066 //============================================================================ 067 // SETTING PROPERTIES 068 //============================================================================ 069 070 /** 071 * Specifies the timing for a scan. 072 * <p> 073 * The values of the two parameters must make sense together. If they 074 * do not, an {@link IllegalArgumentException} is thrown. These are the 075 * rules for the parameters:</p> 076 * <ul> 077 * <li>If {@code newType} is a duration, {@code newTimeText} must conform 078 * to the rules given by {@link TimeDuration#parse(String)}.</li> 079 * 080 * <li>If {@code newType} is an LST, {@code newTimeText} must conform 081 * to the rules given by {@link TimeOfDay#parse(String)}.</li> 082 * 083 * <li>If {@code newType} is a non-LST point in time, {@code newTimeText} 084 * must be in the ISO 8601 form yyyy-MM-ddTHH:mm:ss.SSS+####, 085 * where 'T' is the literal date/time separator. The parser is 086 * very strict: 087 * <ol type="a"> 088 * <li>There must be a decimal point, and the number of places 089 * after the point must be 1, 2, or 3.</li> 090 * <li>After the decimal seconds there must be either a 091 * '+' or '-' sign.</li> 092 * <li>After the plus or minus sign there must be at least four 093 * digits to represent the timezone offset from GMT. 094 * "-0700" means "seven hours earlier than GMT"; 095 * "+1230" means "twelve and one half hours later than GMT".</li> 096 * </ol> 097 * If you already have a <tt>Date</tt> object, you should use 098 * {@link #set(ScanTimeType, Date)} instead.</li> 099 * </ul> 100 * 101 * @param newType 102 * the kind of time that {@code newTimeText} represents. 103 * 104 * @param newTimeText 105 * a text representation of a duration or point in time, depending 106 * on the value of {@code newType}. 107 * 108 * @throws IllegalArgumentException 109 * if the parameters do not make sense as a pair, or if {@code newTimeText} 110 * cannot be parsed. 111 * 112 * @see #set(ScanTimeType, Date) 113 * @see #set(ScanTimeType, LocalSiderealTime) 114 * @see #set(ScanTimeType, TimeDuration) 115 * @see #set(ScanTimeType, TimeOfDay) 116 */ 117 public void set(ScanTimeType newType, String newTimeText) 118 { 119 if (newType.isDuration()) 120 { 121 setDuration(newType, newTimeText); 122 } 123 else //a point in time 124 { 125 if (newType.isSiderealTime()) //LST time of day 126 setLst(newType, newTimeText); 127 else //UT date/time 128 setUt(newType, newTimeText); 129 } 130 } 131 132 /** Helps public set(ScanTimeType, String) method. */ 133 private void setDuration(ScanTimeType newType, String newTimeText) 134 { 135 TimeDuration newDur; 136 137 try 138 { 139 newDur = TimeDuration.parse(newTimeText); 140 } 141 catch (Exception ex) 142 { 143 throw new IllegalArgumentException("Could not parse '" + newTimeText + 144 "' as a time duration.", ex); 145 } 146 147 update(newType, newTimeText, null, null, newDur); 148 } 149 150 /** Helps public set(ScanTimeType, String) method. */ 151 private void setLst(ScanTimeType newType, String newTimeText) 152 { 153 TimeOfDay newLst; 154 155 try 156 { 157 newLst = TimeOfDay.parse(newTimeText); 158 } 159 catch (Exception ex) 160 { 161 throw new IllegalArgumentException("Could not parse '" + newTimeText + 162 "' as a local sidereal time of day.", ex); 163 } 164 165 update(newType, newTimeText, null, newLst, null); 166 } 167 168 private static final DateFormat DATE_PARSER_8601 = 169 DateUtility.makeIso8601Formatter(); 170 171 /** Helps public set(ScanTimeType, String) method. */ 172 private void setUt(ScanTimeType newType, String newTimeText) 173 { 174 Date newUt; 175 176 try 177 { 178 newUt = DATE_PARSER_8601.parse(newTimeText); 179 } 180 catch (Exception ex) 181 { 182 throw new IllegalArgumentException("Could not parse '" + newTimeText + 183 "' as a UT date/time.", ex); 184 } 185 186 update(newType, DATE_PARSER_8601.format(newUt), newUt, null, null); 187 } 188 189 /** 190 * Specifies the duration for a scan. 191 * 192 * @param newType 193 * must be a {@link ScanTimeType#isDuration() duration} type. 194 * If it is not, an {@code IllegalArgumentException} will be thrown. 195 * 196 * @param newDuration 197 * the new duration for this specification. May not be <i>null</i>. 198 * 199 * @throws IllegalArgumentException 200 * if {@code newType} is not a duration or 201 * if {@code newDuration} is <i>null</i>. 202 */ 203 public void set(ScanTimeType newType, TimeDuration newDuration) 204 { 205 if (!newType.isDuration()) 206 throw new IllegalArgumentException("Type '"+newType+"' is not a duration."); 207 208 if (newDuration == null) 209 throw new IllegalArgumentException("NULL durations are not allowed."); 210 211 update(newType, newDuration.toStringHms(), null, null, newDuration.clone()); 212 } 213 214 /** 215 * Specifies the local sidereal time for a scan. 216 * 217 * @param newType 218 * must be both a {@link ScanTimeType#isPointInTime() point-in-time} and 219 * a {@link ScanTimeType#isSiderealTime() sidereal time}. 220 * If it is not, an {@code IllegalArgumentException} will be thrown. 221 * At this time this was written, the only legal values were: 222 * {@link ScanTimeType#START_LST} and {@link ScanTimeType#STOP_LST}. 223 * 224 * @param newLst 225 * the new LST for this specification. May not be <i>null</i>. 226 * 227 * @throws IllegalArgumentException 228 * if the specifications above are not met. 229 */ 230 public void set(ScanTimeType newType, TimeOfDay newLst) 231 { 232 if (!newType.isSiderealTime()) 233 throw new IllegalArgumentException("Type '"+newType+"' is not a sidereal time."); 234 235 if (!newType.isPointInTime()) 236 throw new IllegalArgumentException("Type '"+newType+"' is not a point in time."); 237 238 if (newLst == null) 239 throw new IllegalArgumentException("NULL times are not allowed."); 240 241 update(newType, newLst.toString(), null, newLst.clone(), null); 242 } 243 244 /** 245 * Specifies the local sidereal time for a scan. 246 * 247 * @param newType 248 * must be both a {@link ScanTimeType#isPointInTime() point-in-time} and 249 * a {@link ScanTimeType#isSiderealTime() sidereal time}. 250 * If it is not, an {@code IllegalArgumentException} will be thrown. 251 * At this time this was written, the only legal values were: 252 * {@link ScanTimeType#START_LST} and {@link ScanTimeType#STOP_LST}. 253 * 254 * @param newLst 255 * the new LST for this specification. May not be <i>null</i>. 256 * The {@link LocalSiderealTime} class is a date/time class, but 257 * only the time-of-day portion of this parameter will be used here. 258 * 259 * @throws IllegalArgumentException 260 * if the specifications above are not met. 261 */ 262 public void set(ScanTimeType newType, LocalSiderealTime newLst) 263 { 264 if (!newType.isSiderealTime()) 265 throw new IllegalArgumentException("Type '"+newType+"' is not a sidereal time."); 266 267 if (!newType.isPointInTime()) 268 throw new IllegalArgumentException("Type '"+newType+"' is not a point in time."); 269 270 if (newLst == null) 271 throw new IllegalArgumentException("NULL times are not allowed."); 272 273 TimeOfDay newToD = newLst.toTimeOfDay(); 274 275 update(newType, newToD.toString(), null, newToD, null); 276 } 277 278 /** 279 * Specifies the universal date and time for a scan. 280 * 281 * @param newType 282 * must be both a {@link ScanTimeType#isPointInTime() point-in-time} and 283 * a {@link ScanTimeType#isUniversalTime() universal time}. 284 * If it is not, an {@code IllegalArgumentException} will be thrown. 285 * At this time this was written, the only legal values were: 286 * {@link ScanTimeType#START_UT} and {@link ScanTimeType#STOP_UT}. 287 * 288 * @param newDateTime 289 * the new date and time for this specification. May not be <i>null</i>. 290 * 291 * @throws IllegalArgumentException 292 * if the specifications above are not met. 293 */ 294 public void set(ScanTimeType newType, Date newDateTime) 295 { 296 if (!newType.isUniversalTime()) 297 throw new IllegalArgumentException("Type '"+newType+"' is not a universal time."); 298 299 if (!newType.isPointInTime()) 300 throw new IllegalArgumentException("Type '"+newType+"' is not a point in time."); 301 302 if (newDateTime == null) 303 throw new IllegalArgumentException("NULL times are not allowed."); 304 305 update(newType, DATE_PARSER_8601.format(newDateTime), newDateTime, null, null); 306 } 307 308 /** Sets all the instance variables. */ 309 private void update(ScanTimeType newType, String newText, 310 Date newUt, TimeOfDay newLst, TimeDuration newDur) 311 { 312 timeType = newType; 313 timeText = newText; 314 315 utDateTime = newUt; 316 lst = newLst; 317 duration = newDur; 318 } 319 320 //============================================================================ 321 // SPECIAL CODE FOR JAXB & HIBERNATE 322 //============================================================================ 323 324 private ScanTimeType tempType = null; 325 private String tempText = null; 326 327 @XmlElement 328 @SuppressWarnings("unused") 329 private void setTimeType(ScanTimeType newType) 330 { 331 if (newType != null) 332 { 333 tempType = newType; 334 validate(); 335 } 336 } 337 338 @XmlElement 339 @SuppressWarnings("unused") 340 private void setDuration(TimeDuration newDur) 341 { 342 if (newDur != null) 343 { 344 tempText = newDur.toStringHms(); 345 validate(); 346 } 347 } 348 349 @XmlElement 350 @SuppressWarnings("unused") 351 private void setLst(TimeOfDay newLst) 352 { 353 if (newLst != null) 354 { 355 tempText = newLst.toString(); 356 validate(); 357 } 358 } 359 360 @XmlElement 361 @SuppressWarnings("unused") 362 private void setTime(Date newDate) 363 { 364 if (newDate != null) 365 { 366 tempText = DATE_PARSER_8601.format(newDate); 367 validate(); 368 } 369 } 370 371 @SuppressWarnings("unused") 372 private String getTimeValue() 373 { 374 return timeText; 375 } 376 377 @SuppressWarnings("unused") 378 private void setTimeValue(String newValue) 379 { 380 if (newValue != null) 381 { 382 tempText = newValue; 383 validate(); 384 } 385 } 386 387 private void validate() 388 { 389 //Wait until we've received both items 390 if (tempType != null && tempText != null) 391 { 392 set(tempType, tempText); 393 tempType = null; 394 tempText = null; 395 } 396 } 397 398 //============================================================================ 399 // FETCHTING PROPERTIES 400 //============================================================================ 401 402 /** 403 * Returns the type of time held by this specification. 404 * @return the type of time held by this specification. 405 */ 406 public ScanTimeType getTimeType() { return timeType; } 407 408 /** 409 * Returns a text representation of the duration or point in time held by 410 * this specification. 411 * 412 * @return 413 * a text representation of the time held by this specification. 414 */ 415 public String getTimeText() { return timeText; } 416 417 /** 418 * Returns the specified duration, provided the type of this specification 419 * is a duration. Otherwise returns <i>null</i>. 420 * <p> 421 * The type of duration returned can be determined by calling 422 * {@link #getTimeType()}, or by using the <tt>is<i>Xxx</i></tt> 423 * boolean methods.</p> 424 * <p> 425 * The object returned is <i>not</i> the one held internally by this 426 * specification, so any changes made to it will not affect this 427 * specification.</p> 428 * 429 * @return 430 * the specified duration, or <i>null</i> if this specification 431 * represents a point in time. 432 */ 433 public TimeDuration getDuration() 434 { 435 return duration == null ? null : duration.clone(); 436 } 437 438 /** 439 * Returns the specified local sidereal time of day, provided the type of this 440 * specification is an LST. Otherwise returns <i>null</i>. 441 * <p> 442 * Whether the returned time is a <i>start</i> or <i>stop</i> time can be 443 * determined by calling {@link #getTimeType()}, {@link #isStartTime()}, or 444 * {@link #isStopTime()}.</p> 445 * <p> 446 * The object returned is <i>not</i> the one held internally by this 447 * specification, so any changes made to it will not affect this 448 * specification.</p> 449 * 450 * @return 451 * the specified LST, or <i>null</i> if this specification represents 452 * a duration or UT point in time. 453 */ 454 public TimeOfDay getLst() 455 { 456 return lst == null ? null : lst.clone(); 457 } 458 459 /** 460 * Returns the specified UT date and time, provided the type of this 461 * specification is a UT point in time. Otherwise returns <i>null</i>. 462 * <p> 463 * Whether the returned time is a <i>start</i> or <i>stop</i> time can be 464 * determined by calling {@link #getTimeType()}, {@link #isStartTime()}, or 465 * {@link #isStopTime()}.</p> 466 * <p> 467 * The object returned is <i>not</i> the one held internally by this 468 * specification, so any changes made to it will not affect this 469 * specification.</p> 470 * 471 * @return 472 * the specified UT date and time, or <i>null</i> if this specification 473 * represents a duration or local sidereal time. 474 */ 475 public Date getTime() 476 { 477 return utDateTime == null ? null : (Date)utDateTime.clone(); 478 } 479 480 //============================================================================ 481 // PASS-THROUGHS TO ScanTimeType 482 //============================================================================ 483 484 /** 485 * Returns <i>true</i> if this specification is a duration 486 * (as opposed to a point in time). 487 * 488 * @return <i>true</i> if this specification is a duration. 489 */ 490 public boolean isLengthOfTime() { return timeType.isDuration(); } 491 492 /** 493 * Returns <i>true</i> if this specification is a point in time 494 * (as opposed to a duration). 495 * 496 * @return <i>true</i> if this specification is a point in time. 497 */ 498 public boolean isPointInTime() { return timeType.isPointInTime(); } 499 500 /** 501 * Returns <i>true</i> if this specification is a time-on-source duration. 502 * @return <i>true</i> if this specification is a time-on-source duration. 503 */ 504 public boolean isOnSourceDuration() { return timeType.isOnSourceDuration(); } 505 506 /** 507 * Returns <i>true</i> if this specification is a total duration. 508 * @return <i>true</i> if this specification is a total duration. 509 */ 510 public boolean isTotalDuration() { return timeType.isTotalDuration(); } 511 512 /** 513 * Returns <i>true</i> if this specification is a start time. 514 * @return <i>true</i> if this specification is a start time. 515 */ 516 public boolean isStartTime() { return timeType.isStartTime(); } 517 518 /** 519 * Returns <i>true</i> if this specification is a stop time. 520 * @return <i>true</i> if this specification is a stop time. 521 */ 522 public boolean isStopTime() { return timeType.isStopTime(); } 523 524 /** 525 * Returns <i>true</i> if this specification is expressed in SI units. 526 * @return <i>true</i> if this specification is expressed in SI units. 527 */ 528 public boolean isUniversalTime() { return timeType.isUniversalTime(); } 529 530 /** 531 * Returns <i>true</i> if this specification is expressed in sidereal units. 532 * @return <i>true</i> if this specification is expressed in sidereal units. 533 */ 534 public boolean isSiderealTime() { return timeType.isSiderealTime(); } 535 536 //============================================================================ 537 // 538 //============================================================================ 539 540 /** 541 * Returns a text representation of this specification. 542 */ 543 @Override 544 public String toString() 545 { 546 return timeText + " " + timeType.toString(); 547 } 548 549 /** 550 * Returns a copy of this specification. 551 * <p> 552 * If anything goes wrong during the cloning procedure, 553 * a {@code RuntimeException} will be thrown.</p> 554 */ 555 @Override 556 public ScanTimeSpecification clone() 557 { 558 ScanTimeSpecification clone = null; 559 560 try 561 { 562 //This line takes care of the primitive fields properly 563 clone = (ScanTimeSpecification)super.clone(); 564 565 clone.duration = (this.duration == null) ? null : this.duration.clone(); 566 clone.lst = (this.lst == null) ? null : this.lst.clone(); 567 clone.utDateTime = (this.utDateTime == null) ? null : (Date)this.utDateTime.clone(); 568 } 569 catch (Exception ex) 570 { 571 throw new RuntimeException(ex); 572 } 573 574 return clone; 575 } 576 577 /** Returns <i>true</i> if {@code o} is equal to this specification. */ 578 @Override 579 public boolean equals(Object o) 580 { 581 //Quick exit if o is this 582 if (o == this) 583 return true; 584 585 //Quick exit if o is null 586 if (o == null) 587 return false; 588 589 //Quick exit if classes are different 590 if (!o.getClass().equals(this.getClass())) 591 return false; 592 593 ScanTimeSpecification other = (ScanTimeSpecification)o; 594 595 return 596 this.timeText.equals(other.timeText) && 597 this.timeType.equals(other.timeType); 598 } 599 600 /** Returns a hash code value for this specification. */ 601 @Override 602 public int hashCode() 603 { 604 //Taken from the Effective Java book by Joshua Bloch. 605 //The constants 17 & 37 are arbitrary & carry no meaning. 606 int result = 17; 607 608 result = 37 * result + timeText.hashCode(); 609 result = 37 * result + timeType.hashCode(); 610 611 return result; 612 } 613 614 //============================================================================ 615 // 616 //============================================================================ 617 /* 618 public static void main(String... args) throws Exception 619 { 620 ScanTimeSpecification spec = new ScanTimeSpecification(); 621 622 spec.set(ScanTimeType.STOP_UT, new Date()); 623 624 System.out.println(spec.getTimeText()); 625 626 spec.set(ScanTimeType.STOP_UT, "2010-09-08T12:34:56.12+1234567"); 627 //spec.set(ScanTimeType.STOP_UT, "QUACK"); 628 629 System.out.println(spec.getTimeText()); 630 //System.out.println(spec.getTime()); 631 } 632 */ 633 }