001 package edu.nrao.sss.model.project.scan; 002 003 import javax.xml.bind.annotation.XmlTransient; 004 005 import edu.nrao.sss.measure.Distance; 006 import edu.nrao.sss.measure.DistanceUnits; 007 import edu.nrao.sss.measure.TimeDuration; 008 009 /** 010 * An offset used by a {@link FocusScan}. 011 * <p> 012 * <b>Version Info:</b> 013 * <table style="margin-left:2em"> 014 * <tr><td>$Revision: 1494 $</td></tr> 015 * <tr><td>$Date: 2008-08-14 13:51:17 -0600 (Thu, 14 Aug 2008) $</td></tr> 016 * <tr><td>$Author: dharland $</td></tr> 017 * </table></p> 018 * 019 * @author David M. Harland 020 * @since 2006-07-10 021 */ 022 public class FocusOffset 023 implements Cloneable, Comparable<FocusOffset> 024 { 025 private static final ScanTimeType DEFAULT_TYPE = 026 ScanTimeType.ON_SOURCE_SIDEREAL; 027 028 private Distance offsetLength; 029 private TimeDuration timeAtOffset; 030 private ScanTimeType durationType; 031 032 /** Creates a new instance. */ 033 public FocusOffset() 034 { 035 offsetLength = new Distance("0.0", DistanceUnits.MILLIMETER); 036 timeAtOffset = new TimeDuration(); 037 durationType = DEFAULT_TYPE; 038 } 039 040 //============================================================================ 041 // PERSISTED PROPERTIES 042 //============================================================================ 043 044 /** 045 * Sets the length of this offset. 046 * <p> 047 * If {@code offset} is <i>null</i>, it will be treated as 048 * a non-null distance of zero millimeters.</p> 049 * 050 * @param offset the length of this offset. 051 */ 052 public void setOffsetLength(Distance offset) 053 { 054 offsetLength = 055 (offset == null) ? new Distance("0.0", DistanceUnits.MILLIMETER) 056 : offset; 057 } 058 059 /** 060 * Returns the length of this offset. 061 * <p> 062 * The returned value is guaranteed to be non-null. It is also the 063 * offset that is held internally by this position, so any changes 064 * made to the returned offset will be reflected in this object.</p> 065 * 066 * @return the length of this offset. 067 */ 068 public Distance getOffsetLength() 069 { 070 return offsetLength; 071 } 072 073 /** 074 * Sets the amount of time that the containing scan should spend at this 075 * position. 076 * <p> 077 * If {@code duration} is <i>null</i>, it will be treated as 078 * a non-null duration of size zero.</p> 079 * 080 * @param duration the amount of time that should be spent at this position. 081 */ 082 public void setTimeAtOffset(TimeDuration duration) 083 { 084 timeAtOffset = (duration == null) ? new TimeDuration() : duration; 085 } 086 087 /** 088 * Returns the amount of time that the containing scan should spend at this 089 * position. 090 * <p> 091 * The returned value is guaranteed to be non-null. It is also the 092 * duration that is held internally by this position, so any changes 093 * made to the returned duration will be reflected in this object.</p> 094 * 095 * @return the amount of time that should be spent at this position. 096 */ 097 public TimeDuration getTimeAtOffset() 098 { 099 return timeAtOffset; 100 } 101 102 /** 103 * Sets a new duration type for this offset. 104 * 105 * @param newType 106 * the new duration type for this offset. 107 * If this value is <i>null</i>, the default duration type of 108 * <tt>ON_SOURCE_SIDEREAL</tt> will be used. 109 * This value must have its <tt>isDuration()</tt> method 110 * return <i>true</i>. If it does not, an 111 * <tt>IllegalArgumentException</tt> is thrown. 112 * 113 * @throws IllegalArgumentException 114 * if {@code newType} is not a duration type. 115 */ 116 public void setDurationType(ScanTimeType newType) 117 { 118 if (newType == null) 119 newType = DEFAULT_TYPE; 120 121 if (!newType.isDuration()) 122 throw new IllegalArgumentException("newType '" + newType + 123 "' must be a duration type."); 124 125 durationType = newType; 126 } 127 128 /** 129 * Returns the type of duration used by this offset. 130 * This method helps clients interpret the value returned by 131 * {@link #getTimeAtOffset()}. 132 * <p> 133 * The returned type will be non-null and its <tt>isDuration()</tt> method 134 * will always return <i>true</i>.</p> 135 * 136 * @return 137 * the type of duration used by this offset. 138 */ 139 public ScanTimeType getDurationType() 140 { 141 return durationType; 142 } 143 144 //============================================================================ 145 // CONVENIENCE METHODS 146 //============================================================================ 147 148 /** 149 * A convenience method for setting the time spent at this offset. 150 * This offset will <i>not</i> hold a reference to <tt>newSpec</tt>. 151 * Instead, this method will use its 152 * {@link ScanTimeSpecification#getTimeType() getTimeType()} and 153 * {@link ScanTimeSpecification#getDuration() getDuration()} methods 154 * to set the time at offset and duration type of this object. 155 * 156 * @param newSpec 157 * the provider of the time at offset and duration type values for 158 * this offset. A value of <i>null</i> will result in a 159 * {@code NullPointerException}. 160 * 161 * @throws IllegalArgumentException 162 * if the time type held by {@code newSpec} is not a duration type. 163 */ 164 @XmlTransient 165 public void setTimeSpec(ScanTimeSpecification newSpec) 166 { 167 setDurationType(newSpec.getTimeType()); 168 setTimeAtOffset(newSpec.getDuration()); 169 } 170 171 /** 172 * A convenience method for fetching the time spent at this offset. 173 * The returned object is guaranteed to be non-null. 174 * It is not referenced internally by this offset, so any changes made 175 * to it by clients will not affect this object. 176 * 177 * @return 178 * the time at offset and duration type of this offset. 179 */ 180 public ScanTimeSpecification getTimeSpec() 181 { 182 ScanTimeSpecification spec = new ScanTimeSpecification(); 183 184 spec.set(durationType, timeAtOffset); 185 186 return spec; 187 } 188 189 //============================================================================ 190 // 191 //============================================================================ 192 193 /** 194 * Returns a text representation of this focus offset. 195 * The returned string is of the form <i>offsetLength,timeAtOffset</i>. 196 */ 197 @Override 198 public String toString() 199 { 200 StringBuilder buff = new StringBuilder(); 201 202 buff.append(offsetLength); 203 buff.append(',').append(timeAtOffset); 204 buff.append(',').append(durationType); 205 206 return buff.toString(); 207 } 208 209 /** 210 * Compares this offset with {@code other} for order. 211 * <p> 212 * If this offset has a smaller offset length than the other's, a negative 213 * value is returned; if larger, a positive value is returned. If both 214 * offsets have the same length, a negative value is returned if this 215 * offset has a smaller time duration than the other. The duration type 216 * is not used for figuring out which time duration is larger or smaller. 217 * If the durations are equal in magnitude, though, a comparison of 218 * durations will be used to break the tie, but only in order to stay 219 * consistent with equals().</p> 220 */ 221 public int compareTo(FocusOffset other) 222 { 223 int result = this.offsetLength.compareTo(other.offsetLength); 224 if (result != 0) 225 return result; 226 227 result = this.timeAtOffset.compareTo(other.timeAtOffset); 228 if (result != 0) 229 return result; 230 231 return this.durationType.compareTo(other.durationType); 232 } 233 234 /** 235 * Returns an offset that is a copy of this one. 236 * <p> 237 * If anything goes wrong during the cloning procedure, 238 * a {@code RuntimeException} will be thrown.</p> 239 */ 240 @Override 241 public FocusOffset clone() 242 { 243 FocusOffset clone = null; 244 245 try 246 { 247 //This line takes care of primitive & immutable fields properly 248 clone = (FocusOffset)super.clone(); 249 250 clone.offsetLength = this.offsetLength.clone(); 251 clone.timeAtOffset = this.timeAtOffset.clone(); 252 } 253 catch (Exception ex) 254 { 255 throw new RuntimeException(ex); 256 } 257 258 return clone; 259 } 260 261 /** Returns <i>true</i> if {@code o} is equal to this offset. */ 262 @Override 263 public boolean equals(Object o) 264 { 265 //Quick exit if o is null 266 if (o == null) 267 return false; 268 269 //Quick exit if o is this object 270 if (o == this) 271 return true; 272 273 //Quick exit if classes are different 274 if (!o.getClass().equals(this.getClass())) 275 return false; 276 277 //A safe cast if we got this far 278 FocusOffset other = (FocusOffset)o; 279 280 //Compare attributes 281 return 282 this.durationType.equals(other.durationType) && 283 this.offsetLength.equals(other.offsetLength) && 284 this.timeAtOffset.equals(other.timeAtOffset); 285 } 286 287 /** Returns a hash code value for this offset. */ 288 @Override 289 public int hashCode() 290 { 291 //Taken from the Effective Java book by Joshua Bloch. 292 //The constants 17 & 37 are arbitrary & carry no meaning. 293 int result = 17; 294 295 result = 37 * result + offsetLength.hashCode(); 296 result = 37 * result + durationType.hashCode(); 297 result = 37 * result + timeAtOffset.hashCode(); 298 299 return result; 300 } 301 }