001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2020, Units of Measurement project.
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, Indriya nor the names of their contributors may be used to endorse or promote products
017 *    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 tech.units.indriya.quantity;
031
032import static tech.units.indriya.unit.Units.AMPERE;
033import static tech.units.indriya.unit.Units.BECQUEREL;
034import static tech.units.indriya.unit.Units.CANDELA;
035import static tech.units.indriya.unit.Units.COULOMB;
036import static tech.units.indriya.unit.Units.CUBIC_METRE;
037import static tech.units.indriya.unit.Units.FARAD;
038import static tech.units.indriya.unit.Units.GRAY;
039import static tech.units.indriya.unit.Units.HENRY;
040import static tech.units.indriya.unit.Units.HERTZ;
041import static tech.units.indriya.unit.Units.JOULE;
042import static tech.units.indriya.unit.Units.KATAL;
043import static tech.units.indriya.unit.Units.KELVIN;
044import static tech.units.indriya.unit.Units.KILOGRAM;
045import static tech.units.indriya.unit.Units.LUMEN;
046import static tech.units.indriya.unit.Units.LUX;
047import static tech.units.indriya.unit.Units.METRE;
048import static tech.units.indriya.unit.Units.METRE_PER_SECOND;
049import static tech.units.indriya.unit.Units.METRE_PER_SQUARE_SECOND;
050import static tech.units.indriya.unit.Units.MOLE;
051import static tech.units.indriya.unit.Units.NEWTON;
052import static tech.units.indriya.unit.Units.OHM;
053import static tech.units.indriya.unit.Units.PASCAL;
054import static tech.units.indriya.unit.Units.RADIAN;
055import static tech.units.indriya.unit.Units.SECOND;
056import static tech.units.indriya.unit.Units.SIEMENS;
057import static tech.units.indriya.unit.Units.SIEVERT;
058import static tech.units.indriya.unit.Units.SQUARE_METRE;
059import static tech.units.indriya.unit.Units.STERADIAN;
060import static tech.units.indriya.unit.Units.TESLA;
061import static tech.units.indriya.unit.Units.VOLT;
062import static tech.units.indriya.unit.Units.WATT;
063import static tech.units.indriya.unit.Units.WEBER;
064
065import java.util.HashMap;
066import java.util.Map;
067import java.util.Objects;
068import java.util.concurrent.ConcurrentHashMap;
069import java.util.logging.Level;
070import java.util.logging.Logger;
071
072import javax.measure.Quantity;
073import javax.measure.Quantity.Scale;
074import javax.measure.Unit;
075import javax.measure.quantity.*;
076import javax.measure.spi.QuantityFactory;
077
078import tech.units.indriya.AbstractUnit;
079
080/**
081 * A factory producing simple quantities instances (tuples {@link Number}/ {@link Unit}).<br>
082 *
083 * For example:<br>
084 * <code>
085 *      Mass m = DefaultQuantityFactory.getInstance(Mass.class).create(23.0, KILOGRAM); // 23.0 kg<br>
086 *      Time m = DefaultQuantityFactory.getInstance(Time.class).create(124, MILLI(SECOND)); // 124 ms
087 * </code>
088 * 
089 * @param <Q>
090 *            The type of the quantity.
091 *
092 * @author <a href="mailto:martin.desruisseaux@geomatys.com">Martin Desruisseaux</a>
093 * @author <a href="mailto:werner@units.tech">Werner Keil</a>
094 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
095 * @author <a href="mailto:otaviojava@java.net">Otavio Santana</a>
096 * @version 1.3, $Date: 2018-11-02 $
097 * @since 1.0
098 */
099public class DefaultQuantityFactory<Q extends Quantity<Q>> implements QuantityFactory<Q> {
100    @SuppressWarnings("rawtypes")
101    static final Map<Class, QuantityFactory> INSTANCES = new HashMap<>();
102
103    static final Logger logger = Logger.getLogger(DefaultQuantityFactory.class.getName());
104
105    static final Level LOG_LEVEL = Level.FINE;
106
107    /**
108     * The type of the quantities created by this factory.
109     */
110    private final Class<Q> type;
111
112    /**
113     * The system unit for quantities created by this factory.
114     */
115    private final Unit<Q> systemUnit;
116
117    @SuppressWarnings("rawtypes")
118    private static final Map<Class, Unit> CLASS_TO_SYSTEM_UNIT = new ConcurrentHashMap<>();
119
120    static {
121        CLASS_TO_SYSTEM_UNIT.put(Dimensionless.class, AbstractUnit.ONE);
122        CLASS_TO_SYSTEM_UNIT.put(ElectricCurrent.class, AMPERE);
123        CLASS_TO_SYSTEM_UNIT.put(LuminousIntensity.class, CANDELA);
124        CLASS_TO_SYSTEM_UNIT.put(Temperature.class, KELVIN);
125        CLASS_TO_SYSTEM_UNIT.put(Mass.class, KILOGRAM);
126        CLASS_TO_SYSTEM_UNIT.put(Length.class, METRE);
127        CLASS_TO_SYSTEM_UNIT.put(AmountOfSubstance.class, MOLE);
128        CLASS_TO_SYSTEM_UNIT.put(Time.class, SECOND);
129        CLASS_TO_SYSTEM_UNIT.put(Angle.class, RADIAN);
130        CLASS_TO_SYSTEM_UNIT.put(SolidAngle.class, STERADIAN);
131        CLASS_TO_SYSTEM_UNIT.put(Frequency.class, HERTZ);
132        CLASS_TO_SYSTEM_UNIT.put(Force.class, NEWTON);
133        CLASS_TO_SYSTEM_UNIT.put(Pressure.class, PASCAL);
134        CLASS_TO_SYSTEM_UNIT.put(Energy.class, JOULE);
135        CLASS_TO_SYSTEM_UNIT.put(Power.class, WATT);
136        CLASS_TO_SYSTEM_UNIT.put(ElectricCharge.class, COULOMB);
137        CLASS_TO_SYSTEM_UNIT.put(ElectricPotential.class, VOLT);
138        CLASS_TO_SYSTEM_UNIT.put(ElectricCapacitance.class, FARAD);
139        CLASS_TO_SYSTEM_UNIT.put(ElectricResistance.class, OHM);
140        CLASS_TO_SYSTEM_UNIT.put(ElectricConductance.class, SIEMENS);
141        CLASS_TO_SYSTEM_UNIT.put(MagneticFlux.class, WEBER);
142        CLASS_TO_SYSTEM_UNIT.put(MagneticFluxDensity.class, TESLA);
143        CLASS_TO_SYSTEM_UNIT.put(ElectricInductance.class, HENRY);
144        CLASS_TO_SYSTEM_UNIT.put(LuminousFlux.class, LUMEN);
145        CLASS_TO_SYSTEM_UNIT.put(Illuminance.class, LUX);
146        CLASS_TO_SYSTEM_UNIT.put(Radioactivity.class, BECQUEREL);
147        CLASS_TO_SYSTEM_UNIT.put(RadiationDoseAbsorbed.class, GRAY);
148        CLASS_TO_SYSTEM_UNIT.put(RadiationDoseEffective.class, SIEVERT);
149        CLASS_TO_SYSTEM_UNIT.put(CatalyticActivity.class, KATAL);
150        CLASS_TO_SYSTEM_UNIT.put(Speed.class, METRE_PER_SECOND);
151        CLASS_TO_SYSTEM_UNIT.put(Acceleration.class, METRE_PER_SQUARE_SECOND);
152        CLASS_TO_SYSTEM_UNIT.put(Area.class, SQUARE_METRE);
153        CLASS_TO_SYSTEM_UNIT.put(Volume.class, CUBIC_METRE);
154    }
155
156    @SuppressWarnings("unchecked")
157    private DefaultQuantityFactory(Class<Q> quantity) {
158        type = quantity;
159        systemUnit = CLASS_TO_SYSTEM_UNIT.get(type);
160    }
161
162    /**
163     * Returns the default instance for the specified quantity type.
164     *
165     * @param <Q>
166     *            The type of the quantity
167     * @param type
168     *            the quantity type
169     * @return the quantity factory for the specified type
170     */
171    @SuppressWarnings("unchecked")
172    public static <Q extends Quantity<Q>> QuantityFactory<Q> getInstance(final Class<Q> type) {
173        logger.log(LOG_LEVEL, "Type: " + type + ": " + type.isInterface());
174        QuantityFactory<Q> factory;
175        factory = INSTANCES.get(type);
176        if (factory != null) {
177            return factory;
178        }
179        if (!Quantity.class.isAssignableFrom(type)) {
180            // This exception is not documented because it should never
181            // happen if the
182            // user don't try to trick the Java generic types system with
183            // unsafe cast.
184            throw new ClassCastException();
185        }
186        factory = new DefaultQuantityFactory<Q>(type);
187        INSTANCES.put(type, factory);
188        return factory;
189    }
190
191    public String toString() {
192        return getClass().getName() + " <" + type.getName() + '>';
193    }
194
195    public boolean equals(Object obj) {
196        if (DefaultQuantityFactory.class.isInstance(obj)) {
197            @SuppressWarnings("rawtypes")
198            DefaultQuantityFactory other = DefaultQuantityFactory.class.cast(obj);
199            return Objects.equals(type, other.type);
200        }
201        return false;
202    }
203
204    public int hashCode() {
205        return type.hashCode();
206    }
207
208    public Quantity<Q> create(Number value, Unit<Q> unit) {
209        return Quantities.getQuantity(value, unit);
210    }
211
212    @Override
213    public Quantity<Q> create(Number value, Unit<Q> unit, Scale sc) {
214        return Quantities.getQuantity(value, unit, sc);
215    }
216
217    public Unit<Q> getSystemUnit() {
218        return systemUnit;
219    }
220}