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>&lt;E1, E2, ..., En&gt;
040 where <b>S</b> is the
041 <i>scale</i> and &lt;E1, E2, ..., En&gt; 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&lt;2, 1, -3, 0, 0&gt; 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}