001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-2023, 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.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.5, $Date: 2022-05-21 $ 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 ConcurrentHashMap<>(); 102 103 static final Logger LOGGER = Logger.getLogger(DefaultQuantityFactory.class.getName()); 104 105 static final Level DEFAULT_LOG_LEVEL = Level.FINER; 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 if (LOGGER.isLoggable(Level.CONFIG)) { 156 LOGGER.config(String.format("Registered quantity types: %s", 157 CLASS_TO_SYSTEM_UNIT.keySet())); 158 } 159 } 160 161 @SuppressWarnings("unchecked") 162 private DefaultQuantityFactory(Class<Q> quantity) { 163 type = quantity; 164 systemUnit = CLASS_TO_SYSTEM_UNIT.get(type); 165 } 166 167 /** 168 * Returns the default instance for the specified quantity type. 169 * 170 * @param <Q> 171 * The type of the quantity 172 * @param type 173 * the quantity type 174 * @return the quantity factory for the specified type 175 */ 176 @SuppressWarnings("unchecked") 177 public static <Q extends Quantity<Q>> QuantityFactory<Q> getInstance(final Class<Q> type) { 178 LOGGER.log(DEFAULT_LOG_LEVEL, "Type: " + type + ": " + type.isInterface()); 179 return INSTANCES 180 .computeIfAbsent(type, DefaultQuantityFactory::createNewFactoryInstance); 181 } 182 183 private static <Q extends Quantity<Q>> QuantityFactory<Q> createNewFactoryInstance(final Class<Q> type) { 184 if (!Quantity.class.isAssignableFrom(type)) { 185 // This exception is not documented because it should never 186 // happen if the 187 // user don't try to trick the Java generic types system with 188 // unsafe cast. 189 throw new ClassCastException(String.format("%s is not a Quantity type", type)); 190 } 191 return new DefaultQuantityFactory<Q>(type); 192 } 193 194 public String toString() { 195 return getClass().getName() + " <" + type.getName() + '>'; 196 } 197 198 public boolean equals(Object obj) { 199 if (DefaultQuantityFactory.class.isInstance(obj)) { 200 @SuppressWarnings("rawtypes") 201 DefaultQuantityFactory other = DefaultQuantityFactory.class.cast(obj); 202 return Objects.equals(type, other.type); 203 } 204 return false; 205 } 206 207 public int hashCode() { 208 return type.hashCode(); 209 } 210 211 public Quantity<Q> create(Number value, Unit<Q> unit) { 212 return Quantities.getQuantity(value, unit); 213 } 214 215 @Override 216 public Quantity<Q> create(Number value, Unit<Q> unit, Scale sc) { 217 return Quantities.getQuantity(value, unit, sc); 218 } 219 220 public Unit<Q> getSystemUnit() { 221 return systemUnit; 222 } 223}