001/*
002 * International System of Units (SI)
003 * Copyright (c) 2005-2018, 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 si.uom;
031
032import javax.measure.Quantity;
033import javax.measure.Unit;
034import javax.measure.quantity.Acceleration;
035import javax.measure.quantity.Angle;
036import javax.measure.quantity.Area;
037import javax.measure.quantity.Energy;
038import javax.measure.quantity.Length;
039import javax.measure.quantity.Mass;
040
041import si.uom.quantity.Action;
042import si.uom.quantity.AngularAcceleration;
043import si.uom.quantity.AngularSpeed;
044import si.uom.quantity.DynamicViscosity;
045import si.uom.quantity.ElectricPermittivity;
046import si.uom.quantity.Intensity;
047import si.uom.quantity.IonizingRadiation;
048import si.uom.quantity.KinematicViscosity;
049import si.uom.quantity.Luminance;
050import si.uom.quantity.MagneticFieldStrength;
051import si.uom.quantity.MagneticPermeability;
052import si.uom.quantity.MagnetomotiveForce;
053import si.uom.quantity.Radiance;
054import si.uom.quantity.RadiantIntensity;
055import si.uom.quantity.WaveNumber;
056import tec.uom.se.AbstractUnit;
057import tec.uom.se.format.SimpleUnitFormat;
058import tec.uom.se.function.MultiplyConverter;
059import tec.uom.se.function.PiMultiplierConverter;
060import tec.uom.se.function.RationalConverter;
061import tec.uom.se.unit.AlternateUnit;
062import tec.uom.se.unit.ProductUnit;
063import tec.uom.se.unit.TransformedUnit;
064import tec.uom.se.unit.Units;
065
066/**
067 * <p>
068 * This class defines all SI (Système International d'Unités) base units and
069 * derived units as well as units that are accepted for use with the SI units.
070 * </p>
071 *
072 * @see <a href=
073 *      "http://en.wikipedia.org/wiki/International_System_of_Units">Wikipedia:
074 *      International System of Units</a>
075 * @see <a href="http://physics.nist.gov/cuu/Units/outside.html">Units outside
076 *      the SI that are accepted for use with the SI</a>
077 * @see <a href="http://www.bipm.org/utils/common/pdf/si_brochure_8.pdf">SI 2006
078 *      - Official Specification</a>
079 * @see tec.uom.se.unit.MetricPrefix
080 *
081 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
082 * @author <a href="mailto:units@catmedia.us">Werner Keil</a>
083 * @version 1.2, May 20, 2018
084 */
085public final class SI extends Units {
086    /**
087     * The singleton instance.
088     */
089    private static final SI INSTANCE = new SI();
090
091    ////////////////////////////////
092    // SI DERIVED ALTERNATE UNITS //
093    ////////////////////////////////
094
095    /**
096     * The SI unit for magnetomotive force (standard name <code>At</code>).
097     */
098    public static final Unit<MagnetomotiveForce> AMPERE_TURN = addUnit(
099            new AlternateUnit<MagnetomotiveForce>(Units.AMPERE, "At"), MagnetomotiveForce.class);
100
101    //////////////////////////////
102    // SI DERIVED PRODUCT UNITS //
103    //////////////////////////////
104
105    /**
106     * The SI unit for acceleration quantities (standard name
107     * <code>m/s²</code>).
108     */
109    public static final Unit<Acceleration> METRE_PER_SQUARE_SECOND = addUnit(
110            new ProductUnit<Acceleration>(METRE_PER_SECOND.divide(SECOND)), Acceleration.class);
111    /**
112     * Alias {@link #METRE_PER_SQUARE_SECOND}
113     * 
114     * @deprecated use METRE_PER_SQUARE_SECOND
115     */
116    public static final Unit<Acceleration> METRES_PER_SQUARE_SECOND = METRE_PER_SQUARE_SECOND;
117
118    /**
119     * The SI unit for action quantities (standard name <code>j.s</code>).
120     */
121    public static final Unit<Action> JOULE_SECOND = addUnit(new ProductUnit<Action>(JOULE.multiply(SECOND)),
122            Action.class);
123
124    /**
125     * The SI unit for electric permittivity (standard name <code>ε</code>,
126     * <code>F/m </code> or <code>F·m−1</code>). In electromagnetism, absolute
127     * permittivity is the measure of resistance that is encountered when
128     * forming an electric field in a medium.
129     */
130    public static final Unit<ElectricPermittivity> FARAD_PER_METRE = addUnit(
131            new AlternateUnit<ElectricPermittivity>(FARAD.divide(METRE), "ε"), ElectricPermittivity.class);
132  
133    /**
134     * The SI unit for magnetic permeability quantities (standard name
135     * <code>N/A2</code>).
136     */
137    public static final Unit<MagneticPermeability> NEWTON_PER_SQUARE_AMPERE = addUnit(
138            new ProductUnit<MagneticPermeability>(NEWTON.divide(AMPERE.pow(2))), MagneticPermeability.class);
139
140    /**
141     * The SI unit for wave number quantities (standard name <code>1/m</code>).
142     */
143    public static final Unit<WaveNumber> RECIPROCAL_METRE = addUnit(new ProductUnit<WaveNumber>(METRE.pow(-1)),
144            WaveNumber.class);
145
146    /**
147     * The SI unit for dynamic viscosity quantities (standard name
148     * <code>Pa.s</code>).
149     */
150    public static final Unit<DynamicViscosity> PASCAL_SECOND = addUnit(
151            new ProductUnit<DynamicViscosity>(PASCAL.multiply(SECOND)), DynamicViscosity.class);
152
153    /**
154     * Luminance is a photometric measure of the luminous intensity per unit
155     * area of light travelling in a given direction. It describes the amount of
156     * light that passes through, is emitted or reflected from a particular
157     * area, and falls within a given solid angle. The SI unit for luminance is
158     * candela per square metre (<code>cd/m2</code>).
159     * 
160     * @see <a href="https://en.wikipedia.org/wiki/Luminance"> Wikipedia:
161     *      Luminance</a>
162     */
163    public static final Unit<Luminance> CANDELA_PER_SQUARE_METRE = addUnit(
164            new ProductUnit<Luminance>(CANDELA.divide(SQUARE_METRE)), Luminance.class);
165
166    /**
167     * The SI unit for kinematic viscosity quantities (standard name
168     * <code>m2/s"</code>).
169     */
170    public static final Unit<KinematicViscosity> SQUARE_METRE_PER_SECOND = addUnit(
171            new ProductUnit<KinematicViscosity>(SQUARE_METRE.divide(SECOND)), KinematicViscosity.class);
172
173    /**
174     * Alias for {@link #SQUARE_METRE_PER_SECOND}
175     * 
176     * @deprecated use SQUARE_METRE_PER_SECOND
177     */
178    public static final Unit<KinematicViscosity> SQUARE_METRES_PER_SECOND = SQUARE_METRE_PER_SECOND;
179
180    /**
181     * A magnetic field is the magnetic effect of electric currents and magnetic
182     * materials. The magnetic field at any given point is specified by both a
183     * direction and a magnitude (or strength); as such it is a vector field.
184     * The H-field is measured in amperes per metre (<code>A/m</code>) in SI
185     * units.
186     * 
187     * @see <a href="https://en.wikipedia.org/wiki/Magnetic_field#The_H-field">
188     *      Wikipedia: Magnetic Field - The H Field</a>
189     */
190    public static final Unit<MagneticFieldStrength> AMPERE_PER_METRE = addUnit(
191            new ProductUnit<MagneticFieldStrength>(AMPERE.divide(METRE)), MagneticFieldStrength.class);
192
193
194    /**
195     * The SI unit for ionizing radiation quantities (standard name
196     * <code>C/kg"</code>).
197     */
198    public static final Unit<IonizingRadiation> COULOMB_PER_KILOGRAM = addUnit(
199            new ProductUnit<IonizingRadiation>(COULOMB.divide(KILOGRAM)), IonizingRadiation.class);
200 
201    /**
202     * The SI unit for radiant intensity (standard name <code>W/sr</code>).
203     */
204    public static final Unit<RadiantIntensity> WATT_PER_STERADIAN = addUnit(
205            WATT.divide(STERADIAN).asType(RadiantIntensity.class));
206
207    /**
208     * The SI unit for radiance (standard name <code>W⋅sr−1⋅m−2</code>).
209     */
210    public static final Unit<Radiance> WATT_PER_STERADIAN_PER_SQUARE_METRE = addUnit(
211            WATT_PER_STERADIAN.divide(SQUARE_METRE).asType(Radiance.class));
212
213    /**
214     * The SI unit for intensity (standard name <code>W/m<sup>2</sup></code>).
215     */
216    public static final Unit<Intensity> WATT_PER_SQUARE_METRE = addUnit(
217            WATT.divide(SQUARE_METRE).asType(Intensity.class));
218    
219    /**
220     *  The SI unit of angular speed (standard name
221     * <code>rad/s</code>).
222     * @see AngularSpeed
223     */
224    public static final Unit<AngularSpeed> RADIAN_PER_SECOND = addUnit(
225        new ProductUnit<AngularSpeed>(RADIAN.divide(SECOND)), "Radian per second", AngularSpeed.class);
226    
227    /**
228     *  The SI unit of angular acceleration (standard name
229     * <code>rad/s²</code>).
230     * @see AngularAcceleration
231     */
232    public static final Unit<AngularAcceleration> RADIAN_PER_SQUARE_SECOND = addUnit(
233        new ProductUnit<AngularAcceleration>(RADIAN_PER_SECOND.divide(SECOND)), "Radian per square second", AngularAcceleration.class);
234
235    
236    /////////////////////////////////////////////////////////////////
237    // Units outside the SI that are accepted for use with the SI. //
238    /////////////////////////////////////////////////////////////////
239
240    /**
241     * An energy unit accepted for use with SI units (standard name
242     * <code>eV</code>). The electronvolt is the kinetic energy acquired by an
243     * electron passing through a potential difference of 1 V in vacuum. The
244     * value must be obtained by experiment, and is therefore not known exactly.
245     */
246    public static final Unit<Energy> ELECTRON_VOLT = new TransformedUnit<Energy>(JOULE,
247            new MultiplyConverter(1.602176487E-19));
248    // CODATA 2006 - http://physics.nist.gov/cuu/Constants/codata.pdf
249
250    /**
251     * A mass unit accepted for use with SI units (standard name
252     * <code>u</code>). The unified atomic mass unit is equal to 1/12 of the
253     * mass of an unbound atom of the nuclide 12C, at rest and in its ground
254     * state. The value must be obtained by experiment, and is therefore not
255     * known exactly.
256     */
257    public static final Unit<Mass> UNIFIED_ATOMIC_MASS = addUnit(
258            new TransformedUnit<Mass>(KILOGRAM, new MultiplyConverter(1.660538782E-27)), "Unified atomic mass", "u",
259            true);
260    // CODATA 2006 - http://physics.nist.gov/cuu/Constants/codata.pdf
261
262    /**
263     * A length unit accepted for use with SI units (standard name
264     * <code>UA</code>). The astronomical unit is a unit of length. Its value is
265     * such that, when used to describe the motion of bodies in the solar
266     * system, the heliocentric gravitation constant is (0.017 202 098 95)2
267     * ua3·d-2. The value must be obtained by experiment, and is therefore not
268     * known exactly.
269     */
270    public static final Unit<Length> ASTRONOMICAL_UNIT = addUnit(
271            new TransformedUnit<Length>(METRE, new MultiplyConverter(149597871000.0)));
272    // Best estimate source: http://maia.usno.navy.mil/NSFA/CBE.html
273
274    /**
275     * An angle unit accepted for use with SI units (standard name
276     * <code>rev</code>).
277     */
278    public static final Unit<Angle> REVOLUTION = addUnit(
279            new TransformedUnit<Angle>(RADIAN, new PiMultiplierConverter().concatenate(new RationalConverter(2, 1))));
280
281    /**
282     * An angle unit accepted for use with SI units (standard name
283     * <code>ha</code>).
284     */
285    public static final Unit<Area> HECTARE = new TransformedUnit<Area>(SQUARE_METRE, new RationalConverter(10000, 1));
286
287    /////////////////////
288    // Collection View //
289    /////////////////////
290    
291    /**
292     * Default constructor (prevents this class from being instantiated).
293     */
294    private SI() { // Singleton
295    }
296
297    @Override
298    public String getName() {
299        return SI.class.getSimpleName(); // for Java SE this works
300    }
301    
302    /**
303     * Returns the singleton instance of this class.
304     *
305     * @return the metric system instance.
306     */
307    public static SI getInstance() {
308        return INSTANCE;
309    }
310    
311    /**
312     * Adds a new unit not mapped to any specified quantity type and puts a text
313     * as symbol or label.
314     *
315     * @param unit
316     *            the unit being added.
317     * @param name
318     *            the string to use as name
319     * @param text
320     *            the string to use as label or symbol
321     * @param isLabel
322     *            if the string should be used as a label or not
323     * @return <code>unit</code>.
324     */
325    private static <U extends Unit<?>> U addUnit(U unit, String name, String text, boolean isLabel) {
326        if (isLabel) {
327            SimpleUnitFormat.getInstance().label(unit, text);
328        }
329        if (name != null && unit instanceof AbstractUnit) {
330            return Helper.addUnit(INSTANCE.units, unit, name);
331        } else {
332            INSTANCE.units.add(unit);
333        }
334        return unit;
335    }
336
337    /**
338     * Adds a new unit not mapped to any specified quantity type and puts a text
339     * as symbol or label.
340     *
341     * @param unit
342     *            the unit being added.
343     * @param text
344     *            the string to use as label or symbol
345     * @param isLabel
346     *            if the string should be used as a label or not
347     * @return <code>unit</code>.
348     */
349    @SuppressWarnings("unused")
350    private static <U extends Unit<?>> U addUnit(U unit, String text, boolean isLabel) {
351        return addUnit(unit, null, text, isLabel);
352    }
353
354    /**
355     * Adds a new unit not mapped to any specified quantity type.
356     *
357     * @param unit
358     *            the unit being added.
359     * @return <code>unit</code>.
360     */
361    private static <U extends Unit<?>> U addUnit(U unit) {
362        INSTANCE.units.add(unit);
363        return unit;
364    }
365    
366    /**
367     * Adds a new unit with name and label and maps it to the specified quantity type.
368     *
369     * @param unit
370     *            the unit being added.
371     * @param name
372     *            the string to use as name
373     * @param label
374     *            the string to use as label
375     * @param type
376     *            the quantity type.
377     * @return <code>unit</code>.
378     */
379//    private static <U extends AbstractUnit<?>> U addUnit(U unit, String name, String label, Class<? extends Quantity<?>> type) {
380//        INSTANCE.quantityToUnit.put(type, unit);
381//        return addUnit(unit, name, label, true);
382//    }
383    
384    /**
385     * Adds a new unit with a name and maps it to the specified quantity type.
386     *
387     * @param unit
388     *            the unit being added.
389     * @param name
390     *            the string to use as name
391     * @param type
392     *            the quantity type.
393     * @return <code>unit</code>.
394     */
395    private static <U extends AbstractUnit<?>> U addUnit(U unit, String name, Class<? extends Quantity<?>> type) {
396        INSTANCE.quantityToUnit.put(type, unit);
397        return addUnit(unit, name, null, false);
398    }
399
400    /**
401     * Adds a new unit and maps it to the specified quantity type.
402     *
403     * @param unit
404     *            the unit being added.
405     * @param type
406     *            the quantity type.
407     * @return <code>unit</code>.
408     */
409    private static <U extends AbstractUnit<?>> U addUnit(U unit, Class<? extends Quantity<?>> type) {
410        INSTANCE.units.add(unit);
411        INSTANCE.quantityToUnit.put(type, unit);
412        return unit;
413    }
414}