001/* The Unit Library
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.net.URL;
030import java.util.Iterator;
031import java.util.Vector;
032
033import ptolemy.data.expr.Parameter;
034import ptolemy.data.unit.BaseUnit;
035import ptolemy.data.unit.UnitSystem;
036import ptolemy.data.unit.UnitUtilities;
037import ptolemy.kernel.util.InternalErrorException;
038import ptolemy.kernel.util.NamedObj;
039import ptolemy.moml.MoMLParser;
040import ptolemy.util.FileUtilities;
041
042///////////////////////////////////////////////////////////////////
043//// UnitLibrary
044
045/** A Library containing definitions of commonly used units.
046
047 Currently, the Unit library is static in the sense that it is
048 loaded when the system starts and is hard-wired to a particular
049 Unit System (the System International Unit System). However, it
050 should be easy to change the architecture so that multiple Unit
051 Systems can be accommodated, and loaded on-the-fly.
052
053 @author Rowland R Johnson
054 @version $Id$
055 @since Ptolemy II 8.0
056 @Pt.ProposedRating Red (rowland)
057 @Pt.AcceptedRating Red (rowland)
058 */
059public class UnitLibrary {
060    public UnitLibrary() {
061    }
062
063    ///////////////////////////////////////////////////////////////////
064    ////                         public variables                  ////
065
066    /** The Identity Unit, i.e. 1.0*<0, 0, ..., 0> */
067    public static final Unit Identity;
068
069    ///////////////////////////////////////////////////////////////////
070    ////                         public methods                    ////
071
072    /** Add a unit to the Library.
073     * @param unit Unit to add to the library.
074     */
075    public static void addToLibrary(Unit unit) {
076        _unitsLibrary.add(unit);
077    }
078
079    /** Find the Unit in the library that is basic (scale equal to 1),
080     * singular (all but one dimensions equal to 0).
081     * @param catNum The category number.
082     * @return The basic, singular unit.
083     */
084    public static Unit getBaseUnit(int catNum) {
085        Vector library = UnitLibrary.getLibrary();
086
087        for (int i = 0; i < library.size(); i++) {
088            Unit lUnit = (Unit) library.elementAt(i);
089
090            if (lUnit.getScale() != 1.0) {
091                continue;
092            }
093
094            int[] ltype = lUnit.getType();
095
096            if (ltype[catNum] != 1.0) {
097                continue;
098            }
099
100            // Findbugs says this is useless code.
101            //for (int j = 0; j < UnitLibrary.getNumCategories(); j++) {
102            //    if ((j != catNum) && (ltype[j] != 0)) {
103            //        continue;
104            //    }
105            //}
106
107            return lUnit;
108        }
109
110        return null;
111    }
112
113    /** Search Library to find Unit that has the same type and is the
114     *  closest to a unit in terms of the scalars.
115     *  @param unit The unit for which to search.
116     * @return The Unit closest to this the argument. Null, if there are no
117     * Units in the Library with the same type.
118     */
119    public static Unit getClosestUnit(Unit unit) {
120        Vector possibles = getUnitsByType(unit);
121
122        if (possibles.isEmpty()) {
123            return null;
124        }
125
126        double scalarDistance = Double.MAX_VALUE;
127        Unit retv = null;
128
129        for (int i = 0; i < possibles.size(); i++) {
130            Unit possible = (Unit) possibles.elementAt(i);
131            double distance = Math.abs(possible.getScale() - unit.getScale());
132
133            if (distance < scalarDistance) {
134                scalarDistance = distance;
135                retv = possible;
136            }
137        }
138
139        return retv;
140    }
141
142    /** Return the Library.
143     * @return The Library
144     */
145    public static Vector getLibrary() {
146        return _unitsLibrary;
147    }
148
149    /** Return the number of categories.
150     * @return Number of categories.
151     */
152    public static int getNumCategories() {
153        return _numCats;
154    }
155
156    /** Return the Parser.
157     * @return The Parser.
158     */
159    public static UParser getParser() {
160        return _parser;
161    }
162
163    /** Search Library for Unit equal to a particular unit. That is, both the
164     *  type and scalar must be equal to the argument.
165     *  @param unit The unit to search for.
166     *  @return Unit in Library equal to this one. Null if none found.
167     */
168    public static Unit getUnit(Unit unit) {
169        Unit retv = getClosestUnit(unit);
170
171        if (retv == null) {
172            return null;
173        }
174
175        if (Math.abs(retv.getScale() - unit.getScale()) < 1.0E-8) {
176            return retv;
177        }
178
179        return null;
180    }
181
182    /** Search the Library for a unit with a particular name.
183     * @param name The name of the desired unit.
184     * @return The unit with name equal to the argument. Null, if the Library
185     * doesn't have a unit with that name.
186     */
187    public static Unit getUnitByName(String name) {
188        Vector library = getLibrary();
189
190        for (int i = 0; i < library.size(); i++) {
191            Unit lUnit = (Unit) library.elementAt(i);
192            Vector names = lUnit.getLabels();
193
194            for (int j = 0; j < names.size(); j++) {
195                if (((String) names.elementAt(j)).equals(name)) {
196                    return lUnit;
197                }
198            }
199        }
200
201        return null;
202    }
203
204    /** Search Library for all Units with type equal to this one.
205     * @param unit The unit to search for.
206     * @return Vector of Units with type equal to the argument.
207     */
208    public static Vector getUnitsByType(Unit unit) {
209        Vector retv = new Vector();
210        Vector library = UnitLibrary.getLibrary();
211
212        for (int i = 0; i < library.size(); i++) {
213            Unit lUnit = (Unit) library.elementAt(i);
214
215            if (lUnit.hasSameType(unit)) {
216                retv.add(lUnit);
217            }
218        }
219
220        return retv;
221    }
222
223    ///////////////////////////////////////////////////////////////////
224    ////                         private variables                 ////
225    private static boolean _debug = false;
226
227    private static int _numCats;
228
229    private static UParser _parser;
230
231    private static Vector _unitsLibrary;
232
233    static {
234        _parser = new UParser();
235
236        MoMLParser momlParser = new MoMLParser();
237        UnitSystem us = null;
238
239        try {
240            NamedObj container = new NamedObj();
241            momlParser.setContext(container);
242
243            URL inURL = FileUtilities.nameToURL(
244                    "$CLASSPATH/ptolemy/data/unit/SI.xml", null, null);
245
246            // Strangely, the XmlParser does not want as base the
247            // directory containing the file, but rather the
248            // file itself.
249            URL base = inURL;
250
251            momlParser.parse(base, inURL);
252
253            us = (UnitSystem) container.getAttribute("SI");
254        } catch (Throwable throwable) {
255            throw new InternalErrorException(null, throwable,
256                    "Failed to initialize statics in UnitLibrary");
257        }
258
259        // Initialize the Library.
260        _unitsLibrary = new Vector();
261        _numCats = UnitUtilities.getNumCategories();
262        Identity = new Unit("Identity");
263        _unitsLibrary.add(Identity);
264
265        Iterator oldStyleUnits = us.attributeList().iterator();
266        //UnitLibrary enclosingObject = new UnitLibrary();
267        Vector pairs = new Vector();
268
269        while (oldStyleUnits.hasNext()) {
270            Object oldStyleUnit = oldStyleUnits.next();
271
272            if (oldStyleUnit instanceof BaseUnit) {
273                BaseUnit baseUnit = (BaseUnit) oldStyleUnit;
274                Unit basicUnit = new Unit(baseUnit);
275                addToLibrary(basicUnit);
276            } else if (oldStyleUnit instanceof Parameter) {
277                String name = ((Parameter) oldStyleUnit).getName();
278                String expr = ((Parameter) oldStyleUnit).getExpression();
279                UnitNameExprPair pair = new UnitNameExprPair(name, expr);
280                pairs.add(pair);
281            }
282        }
283
284        boolean madeChange = true;
285
286        while (!pairs.isEmpty() && madeChange) {
287            madeChange = false;
288
289            Iterator iter = pairs.iterator();
290
291            while (iter.hasNext()) {
292                UnitNameExprPair pair = (UnitNameExprPair) iter.next();
293                String expr = pair.getUExpr();
294
295                try {
296                    UnitExpr uExpr = _parser.parseUnitExpr(expr);
297                    Unit unit = uExpr.reduce().getSingleUnit();
298
299                    if (unit != null) {
300                        unit.setPrimaryLabel(pair.getName());
301                        iter.remove();
302                        madeChange = true;
303                        addToLibrary(unit);
304                    }
305                } catch (ParseException e1) {
306                    // OK here.
307                }
308            }
309        }
310
311        if (_debug) {
312            Vector units = getLibrary();
313
314            for (int i = 0; i < units.size(); i++) {
315                System.out.println(((Unit) units.elementAt(i)).toString());
316            }
317        }
318    }
319
320    /** UnitNameExprPair
321     * @author Rowland R Johnson
322     * @version $Id$
323     * @since Ptolemy II 8.0
324     * @Pt.ProposedRating Red (cxh)
325     * @Pt.AcceptedRating Red (cxh)
326     *
327     */
328    private static class UnitNameExprPair {
329        // FindBugs suggests making this class static so as to decrease
330        // the size of instances and avoid dangling references.
331        public UnitNameExprPair(String n, String ue) {
332            _name = n;
333            _uExpr = ue;
334        }
335
336        public String getName() {
337            return _name;
338        }
339
340        public String getUExpr() {
341            return _uExpr;
342        }
343
344        private String _name;
345
346        private String _uExpr;
347    }
348}