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