001/*
002 * Units of Measurement Systems
003 * Copyright (c) 2005-2021, 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-385, 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 javax.measure.MetricPrefix.MICRO;
033import static tech.units.indriya.unit.Units.*;
034
035import javax.measure.Unit;
036import javax.measure.quantity.Area;
037import javax.measure.quantity.Force;
038import javax.measure.quantity.Length;
039import javax.measure.quantity.Mass;
040import javax.measure.quantity.Temperature;
041import javax.measure.quantity.Time;
042import javax.measure.quantity.Volume;
043import javax.measure.spi.SystemOfUnits;
044
045import tech.units.indriya.AbstractSystemOfUnits;
046import tech.units.indriya.AbstractUnit;
047import tech.units.indriya.format.SimpleUnitFormat;
048import tech.units.indriya.unit.ProductUnit;
049
050/**
051 * <p>
052 * This class contains units from the Imperial system.
053 * </p>
054 * <p>
055 * 
056 * @noextend This class is not intended to be extended by clients.
057 * 
058 * @author <a href="mailto:werner@uom.systems">Werner Keil</a>
059 * @version 1.4, $Date: 2021-03-28 $
060 * @see <a href="http://en.wikipedia.org/wiki/Imperial_units">Wikipedia: Imperial
061 *      Units</a>
062 * @see <a href=
063 *      "https://en.wikipedia.org/wiki/Imperial_and_US_customary_measurement_systems">
064 * @since 0.2
065 */
066public final class Imperial extends AbstractSystemOfUnits {
067    private static final String SYSTEM_NAME = "Imperial Units";
068    
069    /**
070     * Holds the avoirdupois pound: 0.45359237 kg exact
071     */
072    private static final int AVOIRDUPOIS_POUND_DIVIDEND = 45359237;
073
074    private static final int AVOIRDUPOIS_POUND_DIVISOR = 100000000;
075    
076    /**
077     * Holds the standard gravity constant: 9.80665 m/s² exact.
078     */
079    private static final int STANDARD_GRAVITY_DIVIDEND = 980665;
080
081    private static final int STANDARD_GRAVITY_DIVISOR = 100000;
082
083    private static final Imperial INSTANCE = new Imperial();
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    ////////////
101    // Length //
102    ////////////
103    /**
104     * A unit of length equal to <code>0.0254 m</code> (standard name
105     * <code>in</code>).
106     */
107    public static final Unit<Length> INCH = addUnit(USCustomary.INCH, "Inch", "in");
108
109    //////////
110    // Mass //
111    //////////
112    /**
113     * A unit of mass equal to <code>453.59237 grams</code> (avoirdupois pound,
114     * standard name <code>lb</code>).
115     */
116    public static final Unit<Mass> POUND = addUnit(
117            KILOGRAM.multiply(AVOIRDUPOIS_POUND_DIVIDEND).divide(AVOIRDUPOIS_POUND_DIVISOR), "Pound", "lb", true);
118    // LABEL);
119    /**
120     * An English and imperial unit of weight or mass now equal to 14
121     * avoirdupois pounds or 6.35029318 kg (<code>st</code>).
122     */
123    public static final Unit<Mass> STONE = addUnit(KILOGRAM.multiply(6.35029318), "st", true);
124
125    /**
126     * A unit of mass equal to <code>1 / 16 {@link #POUND}</code> (standard name
127     * <code>oz</code>).
128     */
129    public static final Unit<Mass> OUNCE = addUnit(POUND.divide(16), "oz");
130
131    /**
132     * A unit of mass equal to <code>2240 {@link #POUND}</code> (long ton,
133     * standard name <code>ton_uk</code>).
134     */
135    public static final Unit<Mass> TON_UK = addUnit(POUND.multiply(2240), "ton_uk");
136
137    /**
138     * A unit of mass equal to <code>1000 kg</code> (metric ton, standard name
139     * <code>t</code>).
140     */
141    public static final Unit<Mass> METRIC_TON = addUnit(KILOGRAM.multiply(1000), "t");
142
143    /////////////////
144    // Temperature //
145    /////////////////
146    /**
147     * A unit of temperature equal to <code>5/9 °K</code> (standard name
148     * <code>°R</code>).
149     */
150    static final Unit<Temperature> RANKINE = addUnit(KELVIN.multiply(5).divide(9), "°R", true);
151
152    /**
153     * A unit of temperature equal to degree Rankine minus
154     * <code>459.67 °R</code> (standard name <code>°F</code>).
155     * 
156     * @see #RANKINE
157     */
158    static final Unit<Temperature> FAHRENHEIT = addUnit(RANKINE.shift(459.67), "°F", true);
159
160    //////////////
161    // Time     //
162    //////////////
163    /**
164     * A unit of time equal to <code>60 s</code> (standard name <code>min</code>
165     * ).
166     */
167    static final Unit<Time> MINUTE = addUnit(SECOND.multiply(60));
168
169    /**
170     * A unit of duration equal to <code>60 {@link #MINUTE}</code> (standard
171     * name <code>h</code>).
172     */
173    static final Unit<Time> HOUR = addUnit(MINUTE.multiply(60));
174    
175    //////////
176    // Area //
177    //////////
178    /**
179     * A unit of area (standard name <code>sft</code> ).
180     */
181    public static final Unit<Area> SQUARE_FOOT = addUnit(USCustomary.SQUARE_FOOT, "sft", true);
182
183    /**
184     * One acre is 43,560 <code>square feet</code> (standard name
185     * <code>ac</code> ).
186     */
187    public static final Unit<Area> ACRE = addUnit(USCustomary.SQUARE_FOOT.multiply(43560), "Acre", "ac", true);
188
189    ////////////
190    // Volume //
191    ////////////
192    /**
193     * A unit of volume equal to one cubic decimeter (default label
194     * <code>L</code>, also recognized <code>µL, mL, cL, dL</code>).
195     */
196    public static final Unit<Volume> LITRE = addUnit(CUBIC_METRE.divide(1000), "L", true);
197
198    /**
199     * A unit of volume equal to one cubic inch (<code>in³</code>).
200     */
201    public static final Unit<Volume> CUBIC_INCH = addUnit(new ProductUnit<Volume>(USCustomary.INCH.pow(3)),
202            "Cubic Inch", "in³");
203
204    /**
205     * A unit of volume equal to <code>4.546 09 {@link #LITRE}</code> (standard
206     * name <code>gal_uk</code>).
207     */
208    public static final Unit<Volume> GALLON_UK = addUnit(LITRE.multiply(454609).divide(100000), "gal_uk");
209
210    /**
211     * A unit of volume equal to <code>1 / 160 {@link #GALLON_UK}</code>
212     * (standard name <code>fl_oz_uk</code>).
213     */
214    static final Unit<Volume> FLUID_OUNCE_UK = GALLON_UK.divide(160);
215
216    /**
217     * A unit of volume equal to <code>1 / 160 {@link #GALLON_LIQUID}</code>
218     * (standard name <code>fl_oz</code>).
219     */
220    public static final Unit<Volume> FLUID_OUNCE = addUnit(FLUID_OUNCE_UK, "fl_oz", true);
221
222    /**
223     * A unit of volume equal to <code>5 {@link #FLUID_OUNCE}</code> (standard
224     * name <code>gi</code>).
225     */
226    public static final Unit<Volume> GILL = addUnit(FLUID_OUNCE.multiply(5), "Gill", "gi");
227
228    /**
229     * A unit of volume equal to <code>20 {@link #FLUID_OUNCE}</code> (standard
230     * name <code>pt</code>).
231     */
232    public static final Unit<Volume> PINT = addUnit(FLUID_OUNCE.multiply(20), "Pint", "pt", true);
233
234    /**
235     * A unit of volume equal to <code>40 {@link #FLUID_OUNCE}</code> (standard
236     * name <code>qt</code>).
237     */
238    public static final Unit<Volume> QUART = addUnit(FLUID_OUNCE.multiply(40), "Quart", "qt");
239
240    /**
241     * A unit of volume <code>~ 1 drop or 0.95 grain of water </code> (standard
242     * name <code>min</code>).
243     */
244    public static final Unit<Volume> MINIM = addUnit(MICRO(LITRE).multiply(59.1938802d), "Minim", "min_br");
245
246    /**
247     * A unit of volume equal to <code>20 {@link #MINIM}</code> (standard name
248     * <code>fl scr</code>).
249     */
250    public static final Unit<Volume> FLUID_SCRUPLE = addUnit(MINIM.multiply(60), "fl scr", true);
251
252    /**
253     * A unit of volume equal to <code>3 {@link #FLUID_SCRUPLE}</code> (standard
254     * name <code>fl drc</code>).
255     */
256    public static final Unit<Volume> FLUID_DRACHM = addUnit(FLUID_SCRUPLE.multiply(3), "fl drc", true);
257    
258    /**
259     * A unit of force equal to <code>{@link #POUND}·{@link #G}</code>
260     * (standard name <code>lbf</code>).
261     */
262    public static final Unit<Force> POUND_FORCE = addUnit(
263            NEWTON.multiply(1L * AVOIRDUPOIS_POUND_DIVIDEND * STANDARD_GRAVITY_DIVIDEND)
264                    .divide(1L * AVOIRDUPOIS_POUND_DIVISOR * STANDARD_GRAVITY_DIVISOR), "lbf");
265    /**
266     * A unit of force equal to <code>9.80665 N</code> (standard name
267     * <code>kgf</code>).
268     */
269    static final Unit<Force> KILOGRAM_FORCE = addUnit(
270            NEWTON.multiply(STANDARD_GRAVITY_DIVIDEND).divide(STANDARD_GRAVITY_DIVISOR));
271    
272    /**
273     * Adds a new unit not mapped to any specified quantity type.
274     *
275     * @param unit
276     *            the unit being added.
277     * @return <code>unit</code>.
278     */
279    private static <U extends Unit<?>> U addUnit(U unit) {
280        INSTANCE.units.add(unit);
281        return unit;
282    }
283
284    /**
285     * Adds a new unit not mapped to any specified quantity type and puts a text
286     * as symbol or label.
287     *
288     * @param unit
289     *            the unit being added.
290     * @param name
291     *            the string to use as name
292     * @param text
293     *            the string to use as label or symbol
294     * @param isLabel
295     *            if the string should be used as a label or not
296     * @return <code>unit</code>.
297     */
298    private static <U extends Unit<?>> U addUnit(U unit, String name, String text, boolean isLabel) {
299        if (isLabel) {
300            SimpleUnitFormat.getInstance().label(unit, text);
301        }
302        if (name != null && unit instanceof AbstractUnit) {
303            return Helper.addUnit(INSTANCE.units, unit, name);
304        } else {
305            INSTANCE.units.add(unit);
306        }
307        return unit;
308    }
309
310    /**
311     * Adds a new unit not mapped to any specified quantity type and puts a text
312     * as symbol or label.
313     *
314     * @param unit
315     *            the unit being added.
316     * @param name
317     *            the string to use as name
318     * @param label
319     *            the string to use as label
320     * @return <code>unit</code>.
321     */
322    private static <U extends Unit<?>> U addUnit(U unit, String name, String label) {
323        return addUnit(unit, name, label, true);
324    }
325
326    /**
327     * Adds a new unit not mapped to any specified quantity type and puts a text
328     * as symbol or label.
329     *
330     * @param unit
331     *            the unit being added.
332     * @param text
333     *            the string to use as label or symbol
334     * @param isLabel
335     *            if the string should be used as a label or not
336     * @return <code>unit</code>.
337     */
338    private static <U extends Unit<?>> U addUnit(U unit, String text, boolean isLabel) {
339        return addUnit(unit, null, text, isLabel);
340    }
341
342    /**
343     * Adds a new unit not mapped to any specified quantity type and puts a text
344     * as label.
345     *
346     * @param unit
347     *            the unit being added.
348     * @param text
349     *            the string to use as label or symbol
350     * @return <code>unit</code>.
351     */
352    private static <U extends Unit<?>> U addUnit(U unit, String text) {
353        return addUnit(unit, null, text, true);
354    }
355
356    /////////////////////
357    // Collection View //
358    /////////////////////
359
360    @Override
361    public String getName() {
362        return SYSTEM_NAME;
363    }
364}