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