001 package edu.nrao.sss.measure; 002 003 import static java.math.BigDecimal.ONE; 004 005 import java.math.BigDecimal; 006 import java.util.ArrayList; 007 import java.util.List; 008 import java.util.SortedMap; 009 import java.util.TreeMap; 010 011 import static edu.nrao.sss.math.MathUtil.MC_FINAL_CALC; 012 013 import edu.nrao.sss.util.EnumerationUtility; 014 import edu.nrao.sss.util.Symbolic; 015 016 /** 017 * Units of angular velocity. 018 * <p> 019 * <b>Version Info:</b> 020 * <table style="margin-left:2em"> 021 * <tr><td>$Revision: 1586 $</td></tr> 022 * <tr><td>$Date: 2008-10-01 10:38:49 -0600 (Wed, 01 Oct 2008) $</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-05-30 028 */ 029 public class AngularVelocityUnits 030 implements Symbolic 031 { 032 private static final int PRECISION = MC_FINAL_CALC.getPrecision(); 033 034 private static final boolean IS_CASE_SENSITIVE; 035 static 036 { 037 IS_CASE_SENSITIVE = 038 ArcUnits.getDefault().symbolsAreCaseSensitive() || 039 TimeUnits.getDefault().symbolsAreCaseSensitive(); 040 } 041 042 private static final SortedMap<String, AngularVelocityUnits> UNITS = 043 new TreeMap<String, AngularVelocityUnits>(); 044 045 /** 046 * Returns angular velocity units for the given angle and time units. 047 * Successive calls to this method with the same parameters will return 048 * the same object, not new equal objects. 049 * 050 * @param angleUnits 051 * units of arc, the numerator for the returned velocity units. 052 * 053 * @param timeUnits 054 * units of time, the denominator for the returned velocity units. 055 * 056 * @return 057 * angular velocity units of <tt>angleUnits</tt> per <tt>timeUnits</tt>. 058 */ 059 public static AngularVelocityUnits from(ArcUnits angleUnits, 060 TimeUnits timeUnits) 061 { 062 String key = makeSymbol(angleUnits, timeUnits); 063 064 AngularVelocityUnits units = UNITS.get(key); 065 066 if (units == null) 067 { 068 units = new AngularVelocityUnits(angleUnits, timeUnits); 069 UNITS.put(key, units); 070 } 071 072 return units; 073 } 074 075 /** 076 * Returns default units for angular velocity. 077 * @return default units for angular velocity. 078 */ 079 public static AngularVelocityUnits getDefault() 080 { 081 return MILLI_ARC_SECONDS_PER_YEAR; 082 } 083 084 /** 085 * This non-public method is here only to support unit testing and should 086 * not be employed for other purposes. Calling this method erases this 087 * class's memory of which units have been created. If called after clients 088 * have made velocity units, it will be possible for one client to have 089 * one m/s instance, and new clients to have a different m/s instance. 090 * Not a crisis, but wasteful, and in violation of comments in 091 * "from" method. 092 */ 093 static void clearCache() { UNITS.clear(); } 094 095 //============================================================================ 096 // FREQUENTLY USED UNITS 097 //============================================================================ 098 099 /** Arcseconds per day. */ 100 public static final AngularVelocityUnits ARC_SECONDS_PER_DAY= 101 from(ArcUnits.ARC_SECOND, TimeUnits.DAY); 102 103 /** Milliarcseconds per year. */ 104 public static final AngularVelocityUnits MILLI_ARC_SECONDS_PER_YEAR = 105 from(ArcUnits.MILLI_ARC_SECOND, TimeUnits.YEAR); 106 107 /** 108 * Returns a list of frequently used velocity units. 109 * This is a small subset of the total set of units that could 110 * be produced by all combinations of {@link ArcUnits} 111 * and {@link TimeUnits}. 112 * 113 * @return a list of frequently used velocity units. 114 */ 115 public static List<AngularVelocityUnits> getFrequentlyUsedUnits() 116 { 117 List<AngularVelocityUnits> list = new ArrayList<AngularVelocityUnits>(); 118 119 list.add(ARC_SECONDS_PER_DAY); 120 list.add(MILLI_ARC_SECONDS_PER_YEAR); 121 122 return list; 123 } 124 125 //============================================================================ 126 // INSTANCE VARIABLES, CONSTRUCTORS, & SIMPLE GETTERS 127 //============================================================================ 128 129 private ArcUnits arcUnits; 130 private TimeUnits timeUnits; 131 132 //This no-arg constructor is here for mechanisms such as JAXB & Hibernate 133 private AngularVelocityUnits() 134 { 135 this(ArcUnits.getDefault(), TimeUnits.getDefault()); 136 } 137 138 private AngularVelocityUnits(ArcUnits au, TimeUnits tu) 139 { 140 arcUnits = au; 141 timeUnits = tu; 142 } 143 144 /** 145 * Returns <i>true</i> if either the {@link ArcUnits} or {@link TimeUnits} 146 * class has case-sensitive symbols. 147 */ 148 public boolean symbolsAreCaseSensitive() 149 { 150 return IS_CASE_SENSITIVE; 151 } 152 153 /** 154 * Returns the units of arc used by this unit of velocity. 155 * @return the units of arc used by this unit of velocity. 156 */ 157 public ArcUnits getArcUnits() 158 { 159 return arcUnits; 160 } 161 162 /** 163 * Returns the units of time used by this unit of velocity. 164 * @return the units of time used by this unit of velocity. 165 */ 166 public TimeUnits getTimeUnits() 167 { 168 return timeUnits; 169 } 170 171 //============================================================================ 172 // CONVERSIONS TO OTHER UNITS 173 //============================================================================ 174 175 /** 176 * Returns a factor for converting from this unit to {@code otherUnits}. 177 * 178 * @param otherUnits the unit to which conversion is desired. 179 * 180 * @return a factor for converting from this unit to {@code otherUnits}. 181 */ 182 public BigDecimal toUnits(AngularVelocityUnits otherUnits) 183 { 184 return convertTo(otherUnits, ONE); 185 } 186 187 /** 188 * Non-public method for use by this and other classes in pkg. 189 * Does the actual conversion; keeps answer in BigDecimal form. 190 */ 191 BigDecimal convertTo(AngularVelocityUnits otherUnits, BigDecimal value) 192 { 193 BigDecimal answer = value; 194 195 if (!otherUnits.equals(this)) 196 { 197 if (value.scale() < PRECISION) 198 value = value.setScale(PRECISION); 199 200 //Strategy: use conversion methods of ArcUnits and TimeUnits then divide 201 BigDecimal angle = this.arcUnits.convertTo(otherUnits.arcUnits, value); 202 BigDecimal time = this.timeUnits.convertTo(otherUnits.timeUnits, BigDecimal.ONE); 203 204 try //to use BigDecimal, for better accuracy, but... 205 { 206 answer = angle.divide(time, MC_FINAL_CALC); 207 } 208 catch (Exception ex) 209 { 210 //...if it fails, use java primitives 211 answer = BigDecimal.valueOf(angle.doubleValue() / time.doubleValue()); 212 } 213 } 214 215 return answer; 216 } 217 218 //============================================================================ 219 // TO / FROM TEXT 220 //============================================================================ 221 222 private static String makeSymbol(ArcUnits au, TimeUnits tu) 223 { 224 return au.getSymbol() + '/' + tu.getSymbol(); 225 } 226 227 /** 228 * Returns the symbol for this unit. 229 * @return the symbol for this unit. 230 */ 231 public String getSymbol() 232 { 233 return makeSymbol(arcUnits, timeUnits); 234 } 235 236 /** 237 * Returns the name of this unit. 238 * This method is here for historical reasons; it mimics the behavior 239 * this method had when this class had been an <tt>enum</tt> class. 240 * The {@link #toString()} is a better alternative. 241 * 242 * @return the name of this unit. 243 */ 244 public String name() 245 { 246 return arcUnits.name() + "_PER_" + timeUnits.name(); 247 } 248 249 /** 250 * Returns a text representation of this unit of angular velocity. 251 * @return a text representation of this unit of angular velocity. 252 */ 253 @Override 254 public String toString() 255 { 256 EnumerationUtility eu = EnumerationUtility.getSharedInstance(); 257 258 return eu.enumToString(arcUnits) + " Per " + eu.enumToString(timeUnits); 259 } 260 261 /** 262 * Returns the angular velocity units represented by {@code text}. 263 * <p> 264 * Two basic formats for <tt>text</tt> are employed, both based on the 265 * text representations of the component angle and time units. 266 * The first is of the form 267 * <tt><i>arcUnits.symbol</i> / <i>timeUnits.symbol</i></tt>. 268 * The amount of space surrounding the "/" separator is not material; 269 * zero or more spaces are allowed on either side of the separator. 270 * The second is of the form 271 * <tt><i>arcUnits.name</i> per <i>timeUnits.name</i></tt>. 272 * The case of the separator "per" is not material. The same comment about 273 * whitespace given above applies here as well. 274 * The rules governing the format of the symbol or name of the component 275 * units are given in 276 * {@link EnumerationUtility#enumFromString(Class, String)}.</p> 277 * 278 * @param text a text representation of a unit of linear velocity. 279 * 280 * @return the linear velocity units represented by {@code text}. 281 */ 282 public static AngularVelocityUnits fromString(String text) 283 { 284 text = text.trim(); 285 286 AngularVelocityUnits units = null; 287 288 //See if we already made this unit 289 if (UNITS.containsKey(text)) 290 { 291 units = UNITS.get(text); 292 } 293 //Parse text to create angle and time units 294 else 295 { 296 String separator = "/"; 297 298 if (!text.contains(separator)) //then try separator "per" 299 { 300 String upperText = text.toUpperCase(); 301 int sepIndex = upperText.indexOf(" PER "); 302 if (sepIndex > -1) 303 { 304 separator = text.substring(sepIndex, sepIndex+" PER ".length()); 305 } 306 else 307 { 308 sepIndex = upperText.indexOf("_PER_"); 309 if (sepIndex > -1) 310 { 311 separator = text.substring(sepIndex, sepIndex+" PER ".length()); 312 } 313 else 314 { 315 separator = null; 316 } 317 } 318 } 319 320 if (separator != null) 321 { 322 String[] tokens = text.split(separator); 323 324 EnumerationUtility eu = EnumerationUtility.getSharedInstance(); 325 326 TimeUnits tu = eu.enumFromString(TimeUnits.class, tokens[1]); 327 ArcUnits au = eu.enumFromString( ArcUnits.class, tokens[0]); 328 329 if (au == null) 330 { 331 //Arc units c/b plural; make singular 332 int indexLast = tokens[0].length() - 1; 333 if (tokens[0].toUpperCase().charAt(indexLast) == 'S') 334 { 335 tokens[0] = tokens[0].substring(0, indexLast); 336 au = eu.enumFromString(ArcUnits.class, tokens[0]); 337 } 338 } 339 340 units = from(au, tu); 341 } 342 else //no separator 343 { 344 throw new IllegalArgumentException("Text '" + text + 345 "' could not be recognized as a unit of velocity."); 346 } 347 } 348 349 return units; 350 } 351 352 //============================================================================ 353 // 354 //============================================================================ 355 356 /** Returns <i>true</i> if <tt>o</tt> is equal to this units. */ 357 @Override 358 public boolean equals(Object o) 359 { 360 //Quick exit if o is this 361 if (o == this) 362 return true; 363 364 //Quick exit if o is null 365 if (o == null) 366 return false; 367 368 //Quick exit if classes are different 369 if (!o.getClass().equals(this.getClass())) 370 return false; 371 372 AngularVelocityUnits other = (AngularVelocityUnits)o; 373 374 return other.getSymbol().equals(this.getSymbol()); 375 } 376 377 /** Returns a hash code for this units. */ 378 @Override 379 public int hashCode() 380 { 381 return getSymbol().hashCode(); 382 } 383 384 //============================================================================ 385 // 386 //============================================================================ 387 /* 388 public static void main(String... args) throws Exception 389 { 390 AngularVelocityUnits avu; 391 392 //Build from good symbols 393 for (ArcUnits au : ArcUnits.values()) 394 { 395 for (TimeUnits tu : TimeUnits.values()) 396 { 397 String symbol = makeSymbol(au, tu); 398 avu = fromString(symbol); 399 System.out.println(avu.getSymbol() + ", " + avu.toString()); 400 } 401 } 402 403 //Clear memory of constructed units 404 AngularVelocityUnits.UNITS.clear(); 405 System.out.println("--------------------------------------------------------"); 406 407 //Build from good names 408 for (ArcUnits au : ArcUnits.values()) 409 { 410 for (TimeUnits tu : TimeUnits.values()) 411 { 412 String name = au.toString() + " per " + tu.toString(); 413 avu = fromString(name); 414 System.out.println(avu.getSymbol() + ", " + avu.toString()); 415 } 416 } 417 } 418 */ 419 }