1 /*
  2  * MeasurementFactory.js - Function to instantiate the appropriate subclasses of
  3  * the Measurement class.
  4  *
  5  * Copyright © 2015, 2018, 2022 JEDLSoft
  6  *
  7  * Licensed under the Apache License, Version 2.0 (the "License");
  8  * you may not use this file except in compliance with the License.
  9  * You may obtain a copy of the License at
 10  *
 11  *     http://www.apache.org/licenses/LICENSE-2.0
 12  *
 13  * Unless required by applicable law or agreed to in writing, software
 14  * distributed under the License is distributed on an "AS IS" BASIS,
 15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 16  *
 17  * See the License for the specific language governing permissions and
 18  * limitations under the License.
 19  */
 20 
 21 /*
 22 !depends
 23 UnknownUnit.js
 24 AreaUnit.js
 25 DigitalStorageUnit.js
 26 DigitalSpeedUnit.js
 27 EnergyUnit.js
 28 FuelConsumptionUnit.js
 29 LengthUnit.js
 30 MassUnit.js
 31 TemperatureUnit.js
 32 TimeUnit.js
 33 VelocityUnit.js
 34 VolumeUnit.js
 35 Measurement.js
 36 */
 37 
 38 // TODO: make these dependencies dynamic or at least generate them in the build
 39 // These will each add themselves to Measurement._constructors[]
 40 var ilib = require("../index.js");
 41 var UnknownUnit = require("./UnknownUnit.js");
 42 var AreaUnit = require("./AreaUnit.js");
 43 var DigitalStorageUnit = require("./DigitalStorageUnit.js");
 44 var DigitalSpeedUnit = require("./DigitalSpeedUnit.js");
 45 var EnergyUnit = require("./EnergyUnit.js");
 46 var ForceUnit = require("./ForceUnit.js");
 47 var PowerUnit = require("./PowerUnit.js");
 48 var PressureUnit = require("./PressureUnit.js");
 49 var FuelConsumptionUnit = require("./FuelConsumptionUnit.js");
 50 var LengthUnit = require("./LengthUnit.js");
 51 var MassUnit = require("./MassUnit.js");
 52 var TemperatureUnit = require("./TemperatureUnit.js");
 53 var TimeUnit = require("./TimeUnit.js");
 54 var VelocityUnit = require("./VelocityUnit.js");
 55 var VolumeUnit = require("./VolumeUnit.js");
 56 
 57 var Measurement = require("./Measurement.js");
 58 
 59 /**
 60  * Create a measurement subclass instance based on a particular measure
 61  * required. The measurement is immutable once
 62  * it is created, but it can be converted to other measurements later.<p>
 63  *
 64  * The options may contain any of the following properties:
 65  *
 66  * <ul>
 67  * <li><i>amount</i> - either a numeric amount for this measurement given
 68  * as a number of the specified units, or another Measurement instance
 69  * to convert to the requested units. If converting to new units, the type
 70  * of measure between the other instance's units and the current units
 71  * must be the same. That is, you can only convert one unit of mass to
 72  * another. You cannot convert a unit of mass into a unit of length.
 73  *
 74  * <li><i>unit</i> - units of this measurement. Use the
 75  * static call {@link MeasurementFactory.getAvailableUnits}
 76  * to find out what units this version of ilib supports. If the given unit
 77  * is not a base unit, the amount will be normalized to the number of base units
 78  * and stored as that number of base units.
 79  * For example, if an instance is constructed with 1 kg, this will be converted
 80  * automatically into 1000 g, as grams are the base unit and kg is merely a
 81  * commonly used scale of grams.
 82  * </ul>
 83  *
 84  * Here are some examples of converting a length into new units.
 85  * The first method is via this factory function by passing the old measurement
 86  * in as the "amount" property.<p>
 87  *
 88  * <pre>
 89  * var measurement1 = MeasurementFactory({
 90  *   amount: 5,
 91  *   units: "kilometers"
 92  * });
 93  * var measurement2 = MeasurementFactory({
 94  *   amount: measurement1,
 95  *   units: "miles"
 96  * });
 97  * </pre>
 98  *
 99  * The value in measurement2 will end up being about 3.125 miles.<p>
100  *
101  * The second method uses the convert method.<p>
102  *
103  * <pre>
104  * var measurement1 = MeasurementFactory({
105  *   amount: 5,
106  *   units: "kilometers"
107  * });
108  * var measurement2 = measurement1.convert("miles");
109  * });
110  * </pre>
111  *
112  * The value in measurement2 will again end up being about 3.125 miles.
113  *
114  * @static
115  * @param {Object=} options options that control the construction of this instance
116  */
117 var MeasurementFactory = function(options) {
118     if (!options || typeof(options.unit) === 'undefined') {
119         return undefined;
120     }
121 
122     var measurement, measure = undefined;
123     var c;
124     // first try in the existing case
125     for (c in Measurement._constructors) {
126         measurement = Measurement._constructors[c];
127         if (Measurement.getUnitId(measurement, options.unit)) {
128             measure = c;
129             break;
130         }
131     }
132 
133     if (!measure) {
134         // if it wasn't found before, try again in lower case -- this may recognize incorrectly because some
135         // units can differ only in their case like "mm" and "Mm"
136         for (c in Measurement._constructors) {
137             measurement = Measurement._constructors[c];
138             if (typeof(Measurement.getUnitIdCaseInsensitive(measurement, options.unit)) !== 'undefined') {
139                 measure = c;
140                 break;
141             }
142         }
143     }
144 
145     if (!measure || typeof(measure) === 'undefined') {
146         return new UnknownUnit({
147             unit: options.unit,
148             amount: options.amount
149         });
150     } else {
151         return new Measurement._constructors[measure](options);
152     }
153 };
154 
155 /**
156  * Return a list of all possible units that this version of ilib supports.
157  * Typically, the units are given as their full names in English. Unit names
158  * are case-insensitive.
159  *
160  * @static
161  * @return {Array.<string>} an array of strings containing names of measurement
162  * units available
163  */
164 MeasurementFactory.getAvailableUnits = function () {
165     var units = [];
166     for (var c in Measurement._constructors) {
167         var measure = Measurement._constructors[c];
168         units = units.concat(measure.getMeasures());
169     }
170     return units;
171 };
172 
173 module.exports = MeasurementFactory;
174