001/*
002 * Units of Measurement Systems
003 * Copyright (c) 2005-2019, Jean-Marie Dautelle, Werner Keil and others.
004 *
005 * All rights reserved.
006 *
007 * Redistribution and use in source and binary forms, with or without modification,
008 * are permitted provided that the following conditions are met:
009 *
010 * 1. Redistributions of source code must retain the above copyright notice,
011 *    this list of conditions and the following disclaimer.
012 *
013 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
014 *    and the following disclaimer in the documentation and/or other materials provided with the distribution.
015 *
016 * 3. Neither the name of JSR-363, Units of Measurement nor the names of their contributors may be used to
017 *    endorse or promote products derived from this software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
021 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
023 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
026 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package systems.uom.common;
031
032import static tech.units.indriya.unit.MetricPrefix.MICRO;
033import static tech.units.indriya.unit.Units.*;
034
035import java.util.Objects;
036
037import javax.measure.Unit;
038import javax.measure.quantity.Area;
039import javax.measure.quantity.Force;
040import javax.measure.quantity.Length;
041import javax.measure.quantity.Mass;
042import javax.measure.quantity.Temperature;
043import javax.measure.quantity.Time;
044import javax.measure.quantity.Volume;
045import javax.measure.spi.SystemOfUnits;
046
047import tech.units.indriya.AbstractSystemOfUnits;
048import tech.units.indriya.AbstractUnit;
049import tech.units.indriya.format.SimpleUnitFormat;
050import tech.units.indriya.unit.ProductUnit;
051
052/**
053 * <p>
054 * This class contains units from the Imperial system.
055 * </p>
056 * <p>
057 * 
058 * @noextend This class is not intended to be extended by clients.
059 * 
060 * @author <a href="mailto:units@catmedia.us">Werner Keil</a>
061 * @version 1.1, $Date: 2019-01-21 $
062 * @see <a href="http://en.wikipedia.org/wiki/Imperial_unit">Wikipedia: Imperial
063 *      Units</a>
064 * @see <a href=
065 *      "https://en.wikipedia.org/wiki/Imperial_and_US_customary_measurement_systems">
066 * @since 0.2
067 */
068public final class Imperial extends AbstractSystemOfUnits {
069    private static final String SYSTEM_NAME = "Imperial";
070    
071    /**
072     * Holds the avoirdupois pound: 0.45359237 kg exact
073     */
074    static final int AVOIRDUPOIS_POUND_DIVIDEND = 45359237;
075
076    static final int AVOIRDUPOIS_POUND_DIVISOR = 100000000;
077    
078    /**
079     * Holds the standard gravity constant: 9.80665 m/s² exact.
080     */
081    private static final int STANDARD_GRAVITY_DIVIDEND = 980665;
082
083    private static final int STANDARD_GRAVITY_DIVISOR = 100000;
084
085    /**
086     * Default constructor (prevents this class from being instantiated).
087     */
088    private Imperial() {
089    }
090
091    /**
092     * Returns the unique instance of this class.
093     * 
094     * @return the Imperial instance.
095     */
096    public static SystemOfUnits getInstance() {
097        return INSTANCE;
098    }
099
100    private static final Imperial INSTANCE = new Imperial();
101
102    // //////////
103    // Length //
104    // //////////
105
106    /**
107     * A unit of length equal to <code>0.0254 m</code> (standard name
108     * <code>in</code>).
109     */
110    public static final Unit<Length> INCH = addUnit(USCustomary.INCH, "Inch", "in");
111
112    // ////////
113    // Mass //
114    // ////////
115
116    /**
117     * A unit of mass equal to <code>453.59237 grams</code> (avoirdupois pound,
118     * standard name <code>lb</code>).
119     */
120    public static final Unit<Mass> POUND = addUnit(
121            KILOGRAM.multiply(AVOIRDUPOIS_POUND_DIVIDEND).divide(AVOIRDUPOIS_POUND_DIVISOR), "Pound", "lb", true);
122    // LABEL);
123    /**
124     * An English and imperial unit of weight or mass now equal to 14
125     * avoirdupois pounds or 6.35029318 kg (<code>st</code>).
126     */
127    public static final Unit<Mass> STONE = addUnit(KILOGRAM.multiply(6.35029318), "st", true);
128
129    /**
130     * A unit of mass equal to <code>1 / 16 {@link #POUND}</code> (standard name
131     * <code>oz</code>).
132     */
133    public static final Unit<Mass> OUNCE = addUnit(POUND.divide(16), "oz");
134
135    /**
136     * A unit of mass equal to <code>2240 {@link #POUND}</code> (long ton,
137     * standard name <code>ton_uk</code>).
138     */
139    public static final Unit<Mass> TON_UK = addUnit(POUND.multiply(2240), "ton_uk");
140
141    /**
142     * A unit of mass equal to <code>1000 kg</code> (metric ton, standard name
143     * <code>t</code>).
144     */
145    public static final Unit<Mass> METRIC_TON = addUnit(KILOGRAM.multiply(1000), "t");
146
147    // ///////////////
148    // Temperature //
149    // ///////////////
150
151    /**
152     * A unit of temperature equal to <code>5/9 °K</code> (standard name
153     * <code>°R</code>).
154     */
155    static final Unit<Temperature> RANKINE = addUnit(KELVIN.multiply(5).divide(9), "°R", true);
156
157    /**
158     * A unit of temperature equal to degree Rankine minus
159     * <code>459.67 °R</code> (standard name <code>°F</code>).
160     * 
161     * @see #RANKINE
162     */
163    static final Unit<Temperature> FAHRENHEIT = addUnit(RANKINE.shift(459.67), "°F", true);
164
165    //////////////
166    // Time     //
167    //////////////
168    /**
169     * A unit of time equal to <code>60 s</code> (standard name <code>min</code>
170     * ).
171     */
172    static final Unit<Time> MINUTE = addUnit(SECOND.multiply(60));
173
174    /**
175     * A unit of duration equal to <code>60 {@link #MINUTE}</code> (standard
176     * name <code>h</code>).
177     */
178    static final Unit<Time> HOUR = addUnit(MINUTE.multiply(60));
179    
180    //////////
181    // Area //
182    //////////
183
184    /**
185     * A unit of area (standard name <code>sft</code> ).
186     */
187    public static final Unit<Area> SQUARE_FOOT = addUnit(USCustomary.SQUARE_FOOT, "sft", true);
188
189    /**
190     * One acre is 43,560 <code>square feet</code> (standard name
191     * <code>ac</code> ).
192     */
193    public static final Unit<Area> ACRE = addUnit(USCustomary.SQUARE_FOOT.multiply(43560), "Acre", "ac", true);
194
195    ////////////
196    // Volume //
197    ////////////
198    /**
199     * A unit of volume equal to one cubic decimeter (default label
200     * <code>L</code>, also recognized <code>µL, mL, cL, dL</code>).
201     */
202    public static final Unit<Volume> LITRE = addUnit(CUBIC_METRE.divide(1000), "L", true);
203
204    /**
205     * A unit of volume equal to one cubic inch (<code>in³</code>).
206     */
207    public static final Unit<Volume> CUBIC_INCH = addUnit(new ProductUnit<Volume>(USCustomary.INCH.pow(3)),
208            "Cubic Inch", "in³");
209
210    /**
211     * A unit of volume equal to <code>4.546 09 {@link #LITRE}</code> (standard
212     * name <code>gal_uk</code>).
213     */
214    public static final Unit<Volume> GALLON_UK = addUnit(LITRE.multiply(454609).divide(100000), "gal_uk");
215
216    /**
217     * A unit of volume equal to one UK gallon, Liquid Unit.
218     */
219    // public static final Unit<Volume> GALLON_LIQUID =
220    // addUnit(CUBIC_INCH.multiply(277.42));
221
222    /**
223     * A unit of volume equal to <code>1 / 160 {@link #GALLON_UK}</code>
224     * (standard name <code>fl_oz_uk</code>).
225     */
226    static final Unit<Volume> FLUID_OUNCE_UK = GALLON_UK.divide(160);
227
228    /**
229     * A unit of volume equal to <code>1 / 160 {@link #GALLON_LIQUID}</code>
230     * (standard name <code>fl_oz</code>).
231     */
232    public static final Unit<Volume> FLUID_OUNCE = addUnit(FLUID_OUNCE_UK, "fl_oz", true);
233
234    /**
235     * A unit of volume equal to <code>1 / 160 {@link #GALLON_LIQUID}</code>
236     * (standard name <code>fl_oz</code>).
237     * @deprecated use FLUID_OUNCE
238     */
239    public static final Unit<Volume> OUNCE_LIQUID = FLUID_OUNCE_UK;
240
241    /**
242     * A unit of volume equal to <code>5 {@link #FLUID_OUNCE}</code> (standard
243     * name <code>gi</code>).
244     */
245    public static final Unit<Volume> GILL = addUnit(FLUID_OUNCE.multiply(5), "Gill", "gi");
246
247    /**
248     * A unit of volume equal to <code>20 {@link #FLUID_OUNCE}</code> (standard
249     * name <code>pt</code>).
250     */
251    public static final Unit<Volume> PINT = addUnit(FLUID_OUNCE.multiply(20), "Pint", "pt", true);
252
253    /**
254     * A unit of volume equal to <code>40 {@link #FLUID_OUNCE}</code> (standard
255     * name <code>qt</code>).
256     */
257    public static final Unit<Volume> QUART = addUnit(FLUID_OUNCE.multiply(40), "Quart", "qt");
258
259    /**
260     * A unit of volume <code>~ 1 drop or 0.95 grain of water </code> (standard
261     * name <code>min</code>).
262     */
263    public static final Unit<Volume> MINIM = addUnit(MICRO(LITRE).multiply(59.1938802d), "Minim", "min_br");
264
265    /**
266     * A unit of volume equal to <code>20 {@link #MINIM}</code> (standard name
267     * <code>fl scr</code>).
268     */
269    public static final Unit<Volume> FLUID_SCRUPLE = addUnit(MINIM.multiply(60), "fl scr", true);
270
271    /**
272     * A unit of volume equal to <code>3 {@link #FLUID_SCRUPLE}</code> (standard
273     * name <code>fl drc</code>).
274     */
275    public static final Unit<Volume> FLUID_DRACHM = addUnit(FLUID_SCRUPLE.multiply(3), "fl drc", true);
276    
277    /**
278     * A unit of force equal to <code>{@link #POUND}·{@link #G}</code>
279     * (standard name <code>lbf</code>).
280     */
281    public static final Unit<Force> POUND_FORCE = addUnit(
282            NEWTON.multiply(1L * AVOIRDUPOIS_POUND_DIVIDEND * STANDARD_GRAVITY_DIVIDEND)
283                    .divide(1L * AVOIRDUPOIS_POUND_DIVISOR * STANDARD_GRAVITY_DIVISOR), "lbf");
284    /**
285     * A unit of force equal to <code>9.80665 N</code> (standard name
286     * <code>kgf</code>).
287     */
288    static final Unit<Force> KILOGRAM_FORCE = addUnit(
289            NEWTON.multiply(STANDARD_GRAVITY_DIVIDEND).divide(STANDARD_GRAVITY_DIVISOR));
290
291    
292    /**
293     * Adds a new unit not mapped to any specified quantity type.
294     *
295     * @param unit
296     *            the unit being added.
297     * @return <code>unit</code>.
298     */
299    private static <U extends Unit<?>> U addUnit(U unit) {
300        INSTANCE.units.add(unit);
301        return unit;
302    }
303
304    /**
305     * Adds a new unit not mapped to any specified quantity type and puts a text
306     * as symbol or label.
307     *
308     * @param unit
309     *            the unit being added.
310     * @param name
311     *            the string to use as name
312     * @param text
313     *            the string to use as label or symbol
314     * @param isLabel
315     *            if the string should be used as a label or not
316     * @return <code>unit</code>.
317     */
318    private static <U extends Unit<?>> U addUnit(U unit, String name, String text, boolean isLabel) {
319        if (isLabel) {
320            SimpleUnitFormat.getInstance().label(unit, text);
321        }
322        if (name != null && unit instanceof AbstractUnit) {
323            return Helper.addUnit(INSTANCE.units, unit, name);
324        } else {
325            INSTANCE.units.add(unit);
326        }
327        return unit;
328    }
329
330    /**
331     * Adds a new unit not mapped to any specified quantity type and puts a text
332     * as symbol or label.
333     *
334     * @param unit
335     *            the unit being added.
336     * @param name
337     *            the string to use as name
338     * @param label
339     *            the string to use as label
340     * @return <code>unit</code>.
341     */
342    private static <U extends Unit<?>> U addUnit(U unit, String name, String label) {
343        return addUnit(unit, name, label, true);
344    }
345
346    /**
347     * Adds a new unit not mapped to any specified quantity type and puts a text
348     * as symbol or label.
349     *
350     * @param unit
351     *            the unit being added.
352     * @param text
353     *            the string to use as label or symbol
354     * @param isLabel
355     *            if the string should be used as a label or not
356     * @return <code>unit</code>.
357     */
358    private static <U extends Unit<?>> U addUnit(U unit, String text, boolean isLabel) {
359        return addUnit(unit, null, text, isLabel);
360    }
361
362    /**
363     * Adds a new unit not mapped to any specified quantity type and puts a text
364     * as label.
365     *
366     * @param unit
367     *            the unit being added.
368     * @param text
369     *            the string to use as label or symbol
370     * @return <code>unit</code>.
371     */
372    private static <U extends Unit<?>> U addUnit(U unit, String text) {
373        return addUnit(unit, null, text, true);
374    }
375
376    // ///////////////////
377    // Collection View //
378    // ///////////////////
379
380    @Override
381    public String getName() {
382        return SYSTEM_NAME;
383    }
384    
385    @Override
386    public Unit<?> getUnit(String string) {
387        Objects.requireNonNull(string);
388        return this.getUnits().stream()
389                  .filter((u) -> string.equals(u.toString()))
390                  .findAny()
391                  .orElse(null);
392    }
393}