001/* Representation of a Unit 002 003 Copyright (c) 2003-2014 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_3 025 COPYRIGHTENDKEY 026 */ 027package ptolemy.moml.unit; 028 029import java.util.Vector; 030 031import ptolemy.data.unit.BaseUnit; 032import ptolemy.data.unit.UnitUtilities; 033 034/////////////////////////////////////////////////////////////////// 035//// Unit 036 037/** 038 Class that contains the internal representation of a Unit. 039 A Unit has the mathematical notation <b>S</b><E1, E2, ..., En> 040 where <b>S</b> is the 041 <i>scale</i> and <E1, E2, ..., En> is the <i>type</i> of the Unit. 042 <p> 043 This class also contains methods for operating on Units, such as multiply, 044 divide, etc. 045 @author Rowland R Johnson 046 @version $Id$ 047 @since Ptolemy II 8.0 048 @Pt.ProposedRating Red (rowland) 049 @Pt.AcceptedRating Red (rowland) 050 */ 051public class Unit implements UnitPresentation { 052 /** Create a Unit with no name and the unitless type. 053 * 054 */ 055 public Unit() { 056 _type = new int[UnitLibrary.getNumCategories()]; 057 058 for (int i = 0; i < UnitLibrary.getNumCategories(); i++) { 059 _type[i] = 0; 060 } 061 062 _labels.add("noLabel" + _noLabelCounter++); 063 } 064 065 /** Create a Unit from a BaseUnit. 066 * @param bu BaseUnit that provides the basis for this Unit. 067 */ 068 public Unit(BaseUnit bu) { 069 this(); 070 071 String name = bu.getName(); 072 setPrimaryLabel(name); 073 074 int index = UnitUtilities.getUnitCategoryIndex(name); 075 _type[index] = 1; 076 } 077 078 /** Create a Unit with a specified name, and the unitless type. 079 * @param name Name of the Unit. 080 */ 081 public Unit(String name) { 082 this(); 083 setPrimaryLabel(name); 084 } 085 086 /////////////////////////////////////////////////////////////////// 087 //// public methods //// 088 089 /** Make a copy of this Unit. 090 * @return A new Unit. 091 */ 092 public Unit copy() { 093 Unit retv = new Unit(); 094 retv._setLabels((Vector) getLabels().clone()); 095 096 int[] newExponents = retv.getType(); 097 098 for (int i = 0; i < UnitLibrary.getNumCategories(); i++) { 099 newExponents[i] = _type[i]; 100 } 101 102 retv.setScale(getScale()); 103 return retv; 104 } 105 106 /** The expression of the Unit that is commonly used by humans. 107 * For example, the unit 4.1868E7<2, 1, -3, 0, 0> will produce the 108 * common expression "calorie second^-1". 109 * @see ptolemy.moml.unit.UnitPresentation#descriptiveForm() 110 */ 111 @Override 112 public String descriptiveForm() { 113 StringBuffer retv = null; 114 Unit unit = UnitLibrary.getUnit(this); 115 116 if (unit != null) { 117 return unit.getPrimaryLabel(); 118 } 119 120 UnitExpr factorization = factor(); 121 122 if (factorization != null) { 123 Vector numerator = new Vector(); 124 Vector denominator = new Vector(); 125 Vector uTerms = factorization.getUTerms(); 126 127 for (int i = 0; i < uTerms.size(); i++) { 128 UnitTerm uterm = (UnitTerm) uTerms.elementAt(i); 129 130 if (uterm.getExponent() < 0) { 131 denominator.add(uterm.invert()); 132 } else if (uterm.getExponent() > 0) { 133 numerator.add(uterm); 134 } 135 } 136 137 if (numerator.size() == 0) { 138 retv = new StringBuffer("1"); 139 } else { 140 retv = new StringBuffer(((UnitTerm) numerator.elementAt(0)) 141 .getUnit().getPrimaryLabel()); 142 143 for (int i = 1; i < numerator.size(); i++) { 144 retv.append(" " + ((UnitTerm) numerator.elementAt(i)) 145 .getUnit().getPrimaryLabel()); 146 } 147 } 148 149 if (denominator.size() > 0) { 150 retv.append("/" + ((UnitTerm) denominator.elementAt(0)) 151 .getUnit().getPrimaryLabel()); 152 153 for (int i = 1; i < denominator.size(); i++) { 154 retv.append(" " + ((UnitTerm) denominator.elementAt(i)) 155 .getUnit().getPrimaryLabel()); 156 } 157 } 158 159 return retv.toString(); 160 } 161 162 if (_scale == 1.0) { 163 int numCats = _type.length; 164 StringBuffer desc = new StringBuffer(); 165 166 for (int i = 0; i < numCats; i++) { 167 if (_type[i] != 0) { 168 Unit baseUnit = UnitLibrary.getBaseUnit(i); 169 170 // Coverity: getBaseUnit() could return null; 171 if (baseUnit != null) { 172 if (_type[i] == 1) { 173 desc.append(" " + baseUnit.getPrimaryLabel()); 174 } else { 175 desc.append(" " + baseUnit.getPrimaryLabel() + "^" 176 + _type[i]); 177 } 178 } 179 } 180 } 181 182 return desc.toString().substring(1); 183 } 184 185 // End up here if nothing works, so just return the formal description 186 return toString(); 187 } 188 189 /** Divide this Unit by another Unit. 190 * @param divisor The divisor unit. 191 * @return This Unit divided by the divisor. 192 */ 193 public Unit divideBy(Unit divisor) { 194 Unit retv = copy(); 195 int[] otherExponents = divisor.getType(); 196 int[] thisExponents = retv.getType(); 197 198 for (int i = 0; i < UnitLibrary.getNumCategories(); i++) { 199 thisExponents[i] -= otherExponents[i]; 200 } 201 202 retv.setType(thisExponents); 203 retv.setScale(retv.getScale() / divisor.getScale()); 204 return retv; 205 } 206 207 /** Return True if this Unit equals another object 208 * @param object The object to be compared against. 209 * @return True if this Unit equals the other Unit. Return false 210 * if the other object is null or not an instance of Unit. 211 */ 212 @Override 213 public boolean equals(Object object) { 214 if (object == null) { 215 return false; 216 } 217 Unit otherUnit = null; 218 if (!(object instanceof Unit)) { 219 return false; 220 } else { 221 otherUnit = (Unit) object; 222 } 223 224 int[] otherExponents = otherUnit.getType(); 225 226 for (int i = 0; i < UnitLibrary.getNumCategories(); i++) { 227 if (_type[i] != otherExponents[i]) { 228 return false; 229 } 230 } 231 232 if (_scale != otherUnit.getScale()) { 233 return false; 234 } 235 236 if (!getLabelsString().equals(otherUnit.getLabelsString())) { 237 return false; 238 } 239 240 return true; 241 } 242 243 /** Factor a Unit into a UnitExpr that has UnitTerms that are in the 244 * Library. In general, factorization is a <i>lengthy</i> process, and this 245 * method is not a complete factorization algorithm. It only tries some of 246 * the more likely combinations. 247 * @return UnitExpr that is equivalent to to the Unit. 248 */ 249 public UnitExpr factor() { 250 // First see if it is simply an invert 251 Unit invert = UnitLibrary.getUnit(invert()); 252 253 if (invert != null) { 254 UnitExpr retv = new UnitExpr(); 255 UnitTerm uTerm = new UnitTerm(invert); 256 uTerm.setExponent(-1); 257 retv.addUnitTerm(uTerm); 258 return retv; 259 } 260 261 // Second see if this is of the form numerator/denominator 262 Vector libraryUnits = UnitLibrary.getLibrary(); 263 264 for (int i = 0; i < libraryUnits.size(); i++) { 265 Unit factor = (Unit) libraryUnits.elementAt(i); 266 Unit numerator = this.multiplyBy(factor); 267 Unit xx = UnitLibrary.getUnit(numerator); 268 269 if (xx != null) { 270 UnitExpr retv = new UnitExpr(); 271 272 if (xx != UnitLibrary.Identity) { 273 UnitTerm uTerm1 = new UnitTerm(xx); 274 retv.addUnitTerm(uTerm1); 275 } 276 277 UnitTerm uTerm2 = new UnitTerm(factor); 278 uTerm2.setExponent(-1); 279 retv.addUnitTerm(uTerm2); 280 return retv; 281 } 282 } 283 284 for (int i = 0; i < libraryUnits.size(); i++) { 285 Unit factor = (Unit) libraryUnits.elementAt(i); 286 Unit remainder = this.divideBy(factor); 287 Unit xx = UnitLibrary.getUnit(remainder); 288 289 if (xx != null && xx != UnitLibrary.Identity) { 290 UnitExpr retv = new UnitExpr(); 291 UnitTerm uTerm = new UnitTerm(factor); 292 retv.addUnitTerm(uTerm); 293 uTerm = new UnitTerm(xx); 294 retv.addUnitTerm(uTerm); 295 return retv; 296 } 297 } 298 299 return null; 300 } 301 302 /** Get the labels for a Unit. 303 * @see ptolemy.moml.unit.Unit#getPrimaryLabel() 304 * @return The labels. 305 */ 306 public Vector getLabels() { 307 return _labels; 308 } 309 310 /** Create a String that is the concatenation of all the labels. 311 * @return The concatenation of the labels. 312 */ 313 public String getLabelsString() { 314 StringBuffer retv = null; 315 316 if (_labels.size() > 0) { 317 retv = new StringBuffer((String) _labels.elementAt(0)); 318 } else { 319 return ""; 320 } 321 322 for (int i = 1; i < _labels.size(); i++) { 323 retv.append((String) _labels.elementAt(i) + ","); 324 } 325 326 return retv.toString(); 327 } 328 329 /** Get the primary label of a Unit. 330 * A Unit can have more than one label. For example, cm, and centimeter are 331 * labels for the same Unit. There always exists a label that is primary. 332 * @return The primary label. 333 */ 334 public String getPrimaryLabel() { 335 return (String) _labels.elementAt(0); 336 } 337 338 /** Get the scale. 339 * @return Scale. 340 */ 341 public double getScale() { 342 return _scale; 343 } 344 345 /** Get the type (represented as a int array) of this Unit. 346 * @return The type (represented as a int array) of this Unit. 347 */ 348 public int[] getType() { 349 return _type; 350 } 351 352 /** Return a hash code value for this Unit. This method returns 353 * the bitwise xor of the hashCode of the label String, the 354 * categories and the hashCode() of the scale. 355 * @return A hash code value for this Unit. 356 */ 357 @Override 358 public int hashCode() { 359 int hashCode = getLabelsString().hashCode(); 360 for (int i = 0; i < UnitLibrary.getNumCategories(); i++) { 361 hashCode >>>= _type[i]; 362 } 363 364 hashCode >>>= Double.valueOf(_scale).hashCode(); 365 366 return hashCode; 367 } 368 369 /** Return true if the Unit has the same type as another Unit. 370 * @param otherUnit 371 * @return True if the Unit has the same type as the argument. 372 */ 373 public boolean hasSameType(Unit otherUnit) { 374 int[] otherType = otherUnit.getType(); 375 376 for (int i = 0; i < UnitLibrary.getNumCategories(); i++) { 377 if (_type[i] != otherType[i]) { 378 return false; 379 } 380 } 381 382 return true; 383 } 384 385 /** Invert this Unit. 386 * @return The inverse of this Unit. 387 */ 388 public Unit invert() { 389 Unit retv = new Unit(); 390 int[] otherExponents = getType(); 391 int[] exponents = new int[UnitLibrary.getNumCategories()]; 392 393 for (int i = 0; i < UnitLibrary.getNumCategories(); i++) { 394 exponents[i] = -otherExponents[i]; 395 } 396 397 retv.setType(exponents); 398 retv.setScale(1.0 / getScale()); 399 return retv; 400 } 401 402 /** Multiply this Unit by another Unit. 403 * @param multiplicand 404 * @return The product of this Unit multiplied by the argument. 405 */ 406 public Unit multiplyBy(Unit multiplicand) { 407 Unit retv = copy(); 408 int[] otherExponents = multiplicand.getType(); 409 int[] thisExponents = retv.getType(); 410 411 for (int i = 0; i < UnitLibrary.getNumCategories(); i++) { 412 thisExponents[i] += otherExponents[i]; 413 } 414 415 retv.setType(thisExponents); 416 retv.setScale(retv.getScale() * multiplicand.getScale()); 417 return retv; 418 } 419 420 /** Returns of value of this Unit raised to the power of the argument. 421 * @param power The exponent. 422 * @return This Unit raised to the power of the argument. 423 */ 424 public Unit pow(double power) { 425 Unit unit = copy(); 426 int[] exponents = unit.getType(); 427 double scale = unit.getScale(); 428 429 for (int i = 0; i < UnitLibrary.getNumCategories(); i++) { 430 exponents[i] *= power; 431 } 432 433 scale = Math.pow(scale, power); 434 unit.setType(exponents); 435 unit.setScale(scale); 436 return unit; 437 } 438 439 /** Set the primary label. 440 * @param label The primary label. 441 */ 442 public void setPrimaryLabel(String label) { 443 _labels.setElementAt(label, 0); 444 } 445 446 /** Set the scale. 447 * @param d The scale. 448 */ 449 public void setScale(double d) { 450 _scale = d; 451 } 452 453 /** Set the type. 454 * @param type 455 */ 456 public void setType(int[] type) { 457 _type = type; 458 } 459 460 /* (non-Javadoc) 461 * @see java.lang.Object#toString() 462 */ 463 @Override 464 public String toString() { 465 StringBuffer retv = new StringBuffer( 466 "Unit:(" + getLabelsString() + ") " + _scale + "*<" + _type[0]); 467 468 for (int i = 1; i < UnitLibrary.getNumCategories(); i++) { 469 retv.append(", " + _type[i]); 470 } 471 472 retv.append(">"); 473 return retv.toString(); 474 } 475 476 /////////////////////////////////////////////////////////////////// 477 //// protected methods //// 478 479 /** Set the labels of the Unit. 480 * @param labels The labels. 481 */ 482 protected void _setLabels(Vector labels) { 483 _labels = labels; 484 } 485 486 /////////////////////////////////////////////////////////////////// 487 //// private variables //// 488 Vector _labels = new Vector(); 489 490 private static int _noLabelCounter = 0; 491 492 private double _scale = 1.0; 493 494 int[] _type; 495}