001 package edu.nrao.sss.model.project.scan; 002 003 import java.util.Date; 004 005 import javax.xml.bind.annotation.XmlElement; 006 import javax.xml.bind.annotation.XmlTransient; 007 import javax.xml.bind.annotation.XmlType; 008 009 import edu.nrao.sss.measure.TimeDuration; 010 import edu.nrao.sss.model.resource.Resource; 011 import edu.nrao.sss.model.source.Source; 012 import edu.nrao.sss.model.source.SourceCatalogEntry; 013 import edu.nrao.sss.model.source.SourceLookupTable; 014 import edu.nrao.sss.util.Identifiable; 015 016 /** 017 * A switch setting used by a {@link SwitchingScan}. 018 * <p> 019 * <b>Version Info:</b> 020 * <table style="margin-left:2em"> 021 * <tr><td>$Revision: 2190 $</td></tr> 022 * <tr><td>$Date: 2009-04-13 15:15:03 -0600 (Mon, 13 Apr 2009) $</td></tr> 023 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 024 * </table></p> 025 * 026 * @author David M. Harland 027 * @since 2006-07-10 028 */ 029 @XmlType( 030 propOrder={ 031 "xmlSource", "xmlSourceLookupTable", "resource", 032 "timeAtSetting", "durationType", "useForAutophasing" 033 } 034 ) 035 public class SwitchSetting 036 implements Cloneable 037 { 038 private static final ScanTimeType DEFAULT_TYPE = 039 ScanTimeType.ON_SOURCE_SIDEREAL; 040 041 @SuppressWarnings("unused") 042 private Long id; //Late add'n due to Hibernate. See ScanLoopElement.hbm.xml. 043 044 private SourceCatalogEntry sourceCatalogEntry; 045 private Resource resource; 046 private TimeDuration timeAtSetting; 047 private ScanTimeType durationType; 048 private boolean useForAutophasing; 049 050 //These instance variables are not persisted 051 private SwitchingScan scan; 052 053 /** Creates a new instance. */ 054 public SwitchSetting() 055 { 056 id = Identifiable.UNIDENTIFIED; 057 058 sourceCatalogEntry = null; 059 resource = null; 060 timeAtSetting = new TimeDuration(); 061 durationType = DEFAULT_TYPE; 062 useForAutophasing = false; 063 064 scan = null; 065 } 066 067 //============================================================================ 068 // PERSISTED PROPERTIES 069 //============================================================================ 070 071 /** 072 * Sets either the {@code Source} or {@code SourceLookupTable} to use for this 073 * setting. 074 * 075 * @param sourceOrTable the {@code Source} or {@code SourceLookupTable} to 076 * use for this setting. 077 */ 078 public void setSourceCatalogEntry(SourceCatalogEntry sourceOrTable) 079 { 080 //No special coding for null parameter 081 sourceCatalogEntry = sourceOrTable; 082 } 083 084 /** 085 * Returns either the {@code Source} or {@code SourceLookupTable} that is the 086 * used for this setting. The returned value may be <i>null</i>. 087 * 088 * @return the {@code Source} or {@code SourceLookupTable} used for this scan, 089 * or <i>null</i> if this setting has neither. 090 */ 091 @XmlTransient 092 public SourceCatalogEntry getSourceCatalogEntry() 093 { 094 return sourceCatalogEntry; 095 } 096 097 /** 098 * Returns the source to use at the current time. If this setting has no such 099 * source, <i>null</i> is returned. 100 * 101 * @return the source to use at the current 102 * time, or <i>null</i> if there is no such source. 103 */ 104 public Source getSource() 105 { 106 return getSource(new Date()); 107 } 108 109 /** 110 * Returns the source to use at the given time. If this setting has no such 111 * source, <i>null</i> is returned. 112 * 113 * @param dateTime the time for which the source is needed. 114 * 115 * @return the source to use at the given 116 * time, or <i>null</i> if there is no such source. 117 */ 118 public Source getSource(Date dateTime) 119 { 120 Source source = null; 121 122 if (sourceCatalogEntry != null) 123 source = sourceCatalogEntry.get(dateTime); 124 125 return source; 126 } 127 128 /** 129 * Sets the resource to be used for this scan. 130 * This method will accept a value of <i>null</i>. 131 * 132 * @param newResource the resource to be used for this scan. 133 */ 134 public void setResource(Resource newResource) 135 { 136 //No special coding for null parameter 137 resource = newResource; 138 } 139 140 /** 141 * Returns the resource to use for this scan, or <i>null</i> if one cannot 142 * be found. 143 * @return the resource to use for this scan, or <i>null</i> if one cannot 144 * be found. 145 */ 146 public Resource getResource() 147 { 148 return resource; 149 } 150 151 /** 152 * Sets the amount of time that should be spent at this setting. 153 * <p> 154 * If {@code duration} is <i>null</i>, it will be treated as 155 * an non-null duration of size zero.</p> 156 * 157 * @param duration the amount of time that should be spent at this setting. 158 */ 159 public void setTimeAtSetting(TimeDuration duration) 160 { 161 timeAtSetting = (duration == null) ? new TimeDuration() : duration; 162 } 163 164 /** 165 * Returns the amount of time that should be spent at this setting. 166 * <p> 167 * The returned value is guaranteed to be non-null. It is also the 168 * duration that is held internally by this position, so any changes 169 * made to the returned duration will be reflected in this object.</p> 170 * 171 * @return the amount of time that should be spent at this setting. 172 */ 173 public TimeDuration getTimeAtSetting() 174 { 175 return timeAtSetting; 176 } 177 178 /** 179 * Sets a new duration type for this setting. 180 * 181 * @param newType 182 * the new duration type for this setting. 183 * If this value is <i>null</i>, the default duration type of 184 * <tt>ON_SOURCE_SIDEREAL</tt> will be used. 185 * This value must have its <tt>isDuration()</tt> method 186 * return <i>true</i>. If it does not, an 187 * <tt>IllegalArgumentException</tt> is thrown. 188 * 189 * @throws IllegalArgumentException 190 * if {@code newType} is not a duration type. 191 */ 192 public void setDurationType(ScanTimeType newType) 193 { 194 if (newType == null) 195 newType = DEFAULT_TYPE; 196 197 if (!newType.isDuration()) 198 throw new IllegalArgumentException("newType '" + newType + 199 "' must be a duration type."); 200 201 durationType = newType; 202 } 203 204 /** 205 * Returns the type of duration used by this setting. 206 * This method helps clients interpret the value returned by 207 * {@link #getTimeAtSetting()}. 208 * <p> 209 * The returned type will be non-null and its <tt>isDuration()</tt> method 210 * will always return <i>true</i>.</p> 211 * 212 * @return 213 * the type of duration used by this setting. 214 */ 215 public ScanTimeType getDurationType() 216 { 217 return durationType; 218 } 219 220 /** 221 * Determines whether or not the source in this switch setting should 222 * be used for autophasing. 223 * 224 * @param use <i>true</i> if the source in this setting should be used 225 * for autophasing. 226 */ 227 public void setUseForAutophasing(boolean use) 228 { 229 useForAutophasing = use; 230 } 231 232 /** 233 * Returns <i>true</i> if the source in this setting should be used 234 * for autophasing. 235 * 236 * @return <i>true</i> if the source in this setting should be used 237 * for autophasing. 238 */ 239 public boolean getUseForAutophasing() 240 { 241 return useForAutophasing; 242 } 243 244 //============================================================================ 245 // CONTAINER 246 //============================================================================ 247 248 /** 249 * Lets this setting know the scan to which it belongs. 250 * This setting will use its scan to retrieve its resource. 251 */ 252 void setScan(SwitchingScan container) 253 { 254 //Let previous container know we've been moved 255 if ((scan != container) && (scan != null)) 256 scan.getSwitchSettings().remove(this); 257 258 scan = container; 259 } 260 261 //============================================================================ 262 // CONVENIENCE METHODS 263 //============================================================================ 264 265 /** 266 * A convenience method for setting the time spent at this setting. 267 * This setting will <i>not</i> hold a reference to <tt>newSpec</tt>. 268 * Instead, this method will use its 269 * {@link ScanTimeSpecification#getTimeType() getTimeType()} and 270 * {@link ScanTimeSpecification#getDuration() getDuration()} methods 271 * to set the time at setting and duration type of this object. 272 * 273 * @param newSpec 274 * the provider of the time at setting and duration type values for 275 * this setting. A value of <i>null</i> will result in a 276 * {@code NullPointerException}. 277 * 278 * @throws IllegalArgumentException 279 * if the time type held by {@code newSpec} is not a duration type. 280 */ 281 @XmlTransient 282 public void setTimeSpec(ScanTimeSpecification newSpec) 283 { 284 setDurationType(newSpec.getTimeType()); 285 setTimeAtSetting(newSpec.getDuration()); 286 } 287 288 /** 289 * A convenience method for fetching the time spent at this setting. 290 * The returned object is guaranteed to be non-null. 291 * It is not referenced internally by this setting, so any changes made 292 * to it by clients will not affect this object. 293 * 294 * @return 295 * the time at setting and duration type of this offset. 296 */ 297 public ScanTimeSpecification getTimeSpec() 298 { 299 ScanTimeSpecification spec = new ScanTimeSpecification(); 300 301 spec.set(durationType, timeAtSetting); 302 303 return spec; 304 } 305 306 //============================================================================ 307 // XML Helpers 308 //============================================================================ 309 310 //These methods are here solely to help JAXB do its thing. Depending on what 311 //future releases of JAXB bring, we may be able to eliminate, or replace, 312 //these methods later on. --DMH Oct 2006 313 314 @SuppressWarnings("unused") 315 private void setXmlSource(Source newSource) 316 { 317 setSourceCatalogEntry(newSource); 318 } 319 320 @XmlElement(name="source") 321 @SuppressWarnings("unused") 322 private Source getXmlSource() 323 { 324 return (sourceCatalogEntry instanceof Source) ? (Source)sourceCatalogEntry 325 : null; 326 } 327 328 @SuppressWarnings("unused") 329 private void setXmlSourceLookupTable(SourceLookupTable newTable) 330 { 331 setSourceCatalogEntry(newTable); 332 } 333 334 @XmlElement(name="sourceLookupTable") 335 @SuppressWarnings("unused") 336 private SourceLookupTable getXmlSourceLookupTable() 337 { 338 return (sourceCatalogEntry instanceof SourceLookupTable) ? 339 (SourceLookupTable)sourceCatalogEntry : null; 340 } 341 342 //============================================================================ 343 // 344 //============================================================================ 345 346 /** 347 * Returns a text representation of this pointing position. 348 * The returned string is of the form <i>source.name,resource.name, 349 * timeAtSetting,useForAutophasing</i>. 350 */ 351 @Override 352 public String toString() 353 { 354 StringBuilder buff = new StringBuilder(); 355 356 buff.append(sourceCatalogEntry.getName()); 357 buff.append(',').append(getResource().getName()); 358 buff.append(',').append(timeAtSetting); 359 buff.append(',').append(durationType); 360 buff.append(',').append(useForAutophasing); 361 362 return buff.toString(); 363 } 364 365 /** 366 * Returns a setting that is a copy of this one. 367 * <p> 368 * If anything goes wrong during the cloning procedure, 369 * a {@code RuntimeException} will be thrown.</p> 370 */ 371 @Override 372 public SwitchSetting clone() 373 { 374 SwitchSetting clone = null; 375 376 try 377 { 378 //This line takes care of primitive & immutable fields properly 379 clone = (SwitchSetting)super.clone(); 380 381 //We do NOT want the clone to have the same ID as the original. 382 //The ID is here for the persistence layer; it is in charge of 383 //setting IDs. To help it, we put the clone's ID in the uninitialized 384 //state. 385 clone.id = Identifiable.UNIDENTIFIED; 386 387 clone.timeAtSetting = this.timeAtSetting.clone(); 388 389 if (this.sourceCatalogEntry != null) 390 clone.sourceCatalogEntry = this.sourceCatalogEntry.clone(); 391 392 if (this.resource != null) 393 clone.resource = this.resource.clone(); 394 395 //Do not automatically deem the clone to be part of the same scan 396 clone.scan = null; 397 } 398 catch (Exception ex) 399 { 400 throw new RuntimeException(ex); 401 } 402 403 return clone; 404 } 405 406 /** Returns <i>true</i> if {@code o} is equal to this setting. */ 407 @Override 408 public boolean equals(Object o) 409 { 410 //Quick exit if o is null 411 if (o == null) 412 return false; 413 414 //Quick exit if o is this object 415 if (o == this) 416 return true; 417 418 //Quick exit if classes are different 419 if (!o.getClass().equals(this.getClass())) 420 return false; 421 422 //A safe cast if we got this far 423 SwitchSetting other = (SwitchSetting)o; 424 425 //Attributes that we INTENTIONALLY DO NOT COMPARE: id 426 427 if (!this.durationType.equals(other.durationType)) 428 return false; 429 430 //Compare source & resource 431 if (!objectsAreEqual( this.sourceCatalogEntry, 432 other.sourceCatalogEntry)) 433 return false; 434 435 if (!objectsAreEqual(this.resource, other.resource)) 436 return false; 437 438 //Compare ohter attributes 439 return (this.timeAtSetting.equals(other.timeAtSetting) && 440 this.useForAutophasing == other.useForAutophasing); 441 } 442 443 private boolean objectsAreEqual(Object thisOne, Object thatOne) 444 { 445 return (thisOne == null) ? (thatOne == null) : thisOne.equals(thatOne); 446 } 447 448 /** Returns a hash code value for this setting. */ 449 @Override 450 public int hashCode() 451 { 452 //Taken from the Effective Java book by Joshua Bloch. 453 //The constants 17 & 37 are arbitrary & carry no meaning. 454 int result = 17; 455 456 //You MUST keep this method in sync w/ the equals method 457 458 if (this.sourceCatalogEntry != null) 459 result = 37 * result + sourceCatalogEntry.hashCode(); 460 461 if (this.resource != null) 462 result = 37 * result + resource.hashCode(); 463 464 result = 37 * result + durationType.hashCode(); 465 result = 37 * result + timeAtSetting.hashCode(); 466 result = 37 * result + new Boolean(useForAutophasing).hashCode(); 467 468 return result; 469 } 470 }