001/* 002 * Units of Measurement Implementation for Java SE 003 * Copyright (c) 2005-2017, Jean-Marie Dautelle, Werner Keil, V2COM. 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-363 nor the names of its 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 tec.uom.se.quantity; 031 032import static tec.uom.se.unit.Units.*; 033 034import java.lang.reflect.InvocationHandler; 035import java.lang.reflect.Method; 036import java.lang.reflect.Proxy; 037import java.util.HashMap; 038import java.util.Map; 039import java.util.concurrent.ConcurrentHashMap; 040import java.util.logging.Level; 041import java.util.logging.Logger; 042 043import javax.measure.Quantity; 044import javax.measure.Unit; 045import javax.measure.quantity.*; 046import javax.measure.spi.QuantityFactory; 047 048import tec.uom.se.AbstractQuantity; 049import tec.uom.se.AbstractUnit; 050 051/** 052 * A factory producing simple quantities instances (tuples {@link Number}/{@link Unit}). 053 * 054 * For example:<br/> 055 * <code> 056 * Quantity<Mass> m = ProxyQuantityFactory.getInstance(Mass.class).create(23.0, KILOGRAM); // 23.0 kg<br/> 057 * Quantity<Time> t = ProxyQuantityFactory.getInstance(Time.class).create(124, MILLI(SECOND)); // 124 ms 058 * </code> 059 * 060 * @param <Q> 061 * The type of the quantity. 062 * 063 * @author <a href="mailto:martin.desruisseaux@geomatys.com">Martin Desruisseaux</a> 064 * @author <a href="mailto:units@catmedia.us">Werner Keil</a> 065 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 066 * @version 1.0.1, $Date: 2017-02-12 $ 067 */ 068public abstract class ProxyQuantityFactory<Q extends Quantity<Q>> implements QuantityFactory<Q> { 069 070 /** 071 * Holds the current instances. 072 */ 073 @SuppressWarnings("rawtypes") 074 private static final Map<Class, ProxyQuantityFactory> INSTANCES = new ConcurrentHashMap<>(); 075 076 private static final Logger logger = Logger.getLogger(ProxyQuantityFactory.class.getName()); 077 078 private static final Level LOG_LEVEL = Level.FINE; 079 080 /** 081 * Returns the default instance for the specified quantity type. 082 * 083 * @param <Q> 084 * The type of the quantity 085 * @param type 086 * the quantity type 087 * @return the quantity factory for the specified type 088 */ 089 @SuppressWarnings("unchecked") 090 public static <Q extends Quantity<Q>> ProxyQuantityFactory<Q> getInstance(final Class<Q> type) { 091 092 logger.log(LOG_LEVEL, "Type: " + type + ": " + type.isInterface()); 093 ProxyQuantityFactory<Q> factory; 094 if (!type.isInterface()) { 095 if (type != null && type.getInterfaces() != null & type.getInterfaces().length > 0) { 096 logger.log(LOG_LEVEL, "Type0: " + type.getInterfaces()[0]); 097 Class<?> type2 = type.getInterfaces()[0]; 098 099 factory = INSTANCES.get(type2); 100 if (factory != null) 101 return factory; 102 if (!AbstractQuantity.class.isAssignableFrom(type2)) 103 // This exception is not documented because it should never happen if the 104 // user don't try to trick the Java generic types system with unsafe cast. 105 throw new ClassCastException(); 106 factory = new Default<>((Class<Q>) type2); 107 INSTANCES.put(type2, factory); 108 } else { 109 factory = INSTANCES.get(type); 110 if (factory != null) 111 return factory; 112 if (!AbstractQuantity.class.isAssignableFrom(type)) 113 // This exception is not documented because it should never happen if the 114 // user don't try to trick the Java generic types system with unsafe cast. 115 throw new ClassCastException(); 116 factory = new Default<>(type); 117 INSTANCES.put(type, factory); 118 } 119 } else { 120 factory = INSTANCES.get(type); 121 if (factory != null) 122 return factory; 123 if (!Quantity.class.isAssignableFrom(type)) 124 // This exception is not documented because it should never happen if the 125 // user don't try to trick the Java generic types system with unsafe cast. 126 throw new ClassCastException(); 127 factory = new Default<>(type); 128 INSTANCES.put(type, factory); 129 } 130 return factory; 131 } 132 133 /** 134 * Overrides the default implementation of the factory for the specified quantity type. 135 * 136 * @param <Q> 137 * The type of the quantity 138 * @param type 139 * the quantity type 140 * @param factory 141 * the quantity factory 142 */ 143 protected static <Q extends Quantity<Q>> void setInstance(final Class<Q> type, ProxyQuantityFactory<Q> factory) { 144 if (!AbstractQuantity.class.isAssignableFrom(type)) 145 // This exception is not documented because it should never happen if the 146 // user don't try to trick the Java generic types system with unsafe cast. 147 throw new ClassCastException(); 148 INSTANCES.put(type, factory); 149 } 150 151 /** 152 * Returns the metric unit for quantities produced by this factory or <code>null</code> if unknown. 153 * 154 * @return the metric units for this factory quantities. 155 */ 156 public abstract Unit<Q> getSystemUnit(); 157 158 /** 159 * The default factory implementation. This factory uses reflection for providing a default implementation for every {@link AbstractMeasurement} 160 * sub-types. 161 * 162 * @param <Q> 163 * The type of the quantity 164 */ 165 private static final class Default<Q extends Quantity<Q>> extends ProxyQuantityFactory<Q> { 166 167 /** 168 * The type of the quantities created by this factory. 169 */ 170 private final Class<Q> type; 171 172 /** 173 * The metric unit for quantities created by this factory. 174 */ 175 private final Unit<Q> metricUnit; 176 177 /** 178 * Creates a new factory for quantities of the given type. 179 * 180 * @param type 181 * The type of the quantities created by this factory. 182 */ 183 @SuppressWarnings("unchecked") 184 Default(final Class<Q> type) { 185 this.type = type; 186 metricUnit = CLASS_TO_METRIC_UNIT.get(type); 187 } 188 189 @SuppressWarnings("rawtypes") 190 static final HashMap<Class, Unit> CLASS_TO_METRIC_UNIT = new HashMap<>(); 191 static { 192 CLASS_TO_METRIC_UNIT.put(Dimensionless.class, AbstractUnit.ONE); 193 CLASS_TO_METRIC_UNIT.put(ElectricCurrent.class, AMPERE); 194 CLASS_TO_METRIC_UNIT.put(LuminousIntensity.class, CANDELA); 195 CLASS_TO_METRIC_UNIT.put(Temperature.class, KELVIN); 196 CLASS_TO_METRIC_UNIT.put(Mass.class, KILOGRAM); 197 CLASS_TO_METRIC_UNIT.put(Length.class, METRE); 198 CLASS_TO_METRIC_UNIT.put(AmountOfSubstance.class, MOLE); 199 CLASS_TO_METRIC_UNIT.put(Time.class, SECOND); 200 // CLASS_TO_METRIC_UNIT.put(MagnetomotiveForce.class, AMPERE_TURN); 201 CLASS_TO_METRIC_UNIT.put(Angle.class, RADIAN); 202 CLASS_TO_METRIC_UNIT.put(SolidAngle.class, STERADIAN); 203 // CLASS_TO_METRIC_UNIT.put(Information.class, BIT); 204 CLASS_TO_METRIC_UNIT.put(Frequency.class, HERTZ); 205 CLASS_TO_METRIC_UNIT.put(Force.class, NEWTON); 206 CLASS_TO_METRIC_UNIT.put(Pressure.class, PASCAL); 207 CLASS_TO_METRIC_UNIT.put(Energy.class, JOULE); 208 CLASS_TO_METRIC_UNIT.put(Power.class, WATT); 209 CLASS_TO_METRIC_UNIT.put(ElectricCharge.class, COULOMB); 210 CLASS_TO_METRIC_UNIT.put(ElectricPotential.class, VOLT); 211 CLASS_TO_METRIC_UNIT.put(ElectricCapacitance.class, FARAD); 212 CLASS_TO_METRIC_UNIT.put(ElectricResistance.class, OHM); 213 CLASS_TO_METRIC_UNIT.put(ElectricConductance.class, SIEMENS); 214 CLASS_TO_METRIC_UNIT.put(MagneticFlux.class, WEBER); 215 CLASS_TO_METRIC_UNIT.put(MagneticFluxDensity.class, TESLA); 216 CLASS_TO_METRIC_UNIT.put(ElectricInductance.class, HENRY); 217 CLASS_TO_METRIC_UNIT.put(LuminousFlux.class, LUMEN); 218 CLASS_TO_METRIC_UNIT.put(Illuminance.class, LUX); 219 CLASS_TO_METRIC_UNIT.put(Radioactivity.class, BECQUEREL); 220 CLASS_TO_METRIC_UNIT.put(RadiationDoseAbsorbed.class, GRAY); 221 CLASS_TO_METRIC_UNIT.put(RadiationDoseEffective.class, SIEVERT); 222 CLASS_TO_METRIC_UNIT.put(CatalyticActivity.class, KATAL); 223 CLASS_TO_METRIC_UNIT.put(Speed.class, METRE_PER_SECOND); 224 CLASS_TO_METRIC_UNIT.put(Acceleration.class, METRE_PER_SQUARE_SECOND); 225 CLASS_TO_METRIC_UNIT.put(Area.class, SQUARE_METRE); 226 CLASS_TO_METRIC_UNIT.put(Volume.class, CUBIC_METRE); 227 } 228 229 @Override 230 public Unit<Q> getSystemUnit() { 231 return metricUnit; 232 } 233 234 @SuppressWarnings("unchecked") 235 @Override 236 public Quantity<Q> create(Number value, Unit<Q> unit) { 237 // System.out.println("Type: " + type); 238 return (Q) Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[] { type }, new GenericHandler<>(value, unit)); 239 } 240 } 241 242 /** 243 * The method invocation handler for implementation backed by any kind of {@link Number}. This is a fall back used when no specialized handler is 244 * available for the number type. 245 */ 246 private static final class GenericHandler<Q extends Quantity<Q>> implements InvocationHandler { 247 final Unit<Q> unit; 248 final Number value; 249 250 GenericHandler(final Number value, final Unit<Q> unit) { 251 this.unit = unit; 252 this.value = value; 253 } 254 255 @SuppressWarnings("unchecked") 256 @Override 257 public Object invoke(final Object proxy, final Method method, final Object[] args) { 258 final String name = method.getName(); 259 switch (name) { 260 case "doubleValue": { // Most frequent. 261 final Unit<Q> toUnit = (Unit<Q>) args[0]; 262 if ((toUnit == unit) || (toUnit.equals(unit))) 263 return value.doubleValue(); // Returns value directly. 264 return unit.getConverterTo(toUnit).convert(value.doubleValue()); 265 } 266 case "longValue": { 267 final Unit<Q> toUnit = (Unit<Q>) args[0]; 268 if ((toUnit == unit) || (toUnit.equals(unit))) 269 return value.longValue(); // Returns value directly. 270 double doubleValue = unit.getConverterTo(toUnit).convert(value.doubleValue()); 271 if ((doubleValue < Long.MIN_VALUE) || (doubleValue > Long.MAX_VALUE)) 272 throw new ArithmeticException("Overflow: " + doubleValue + " cannot be represented as a long"); 273 return (long) doubleValue; 274 } 275 case "getValue": 276 return value; 277 case "getUnit": 278 return unit; 279 case "toString": 280 return String.valueOf(value) + ' ' + unit; 281 case "hashCode": 282 return value.hashCode() * 31 + unit.hashCode(); 283 case "equals": { 284 final Object obj = args[0]; 285 if (!(obj instanceof AbstractQuantity)) 286 return false; 287 final AbstractQuantity<Q> that = (AbstractQuantity<Q>) obj; 288 return unit.isCompatible((AbstractUnit<?>) that.getUnit()) && value.doubleValue() == (that).doubleValue(unit); 289 } 290 case "compareTo": { 291 final AbstractQuantity<Q> that = (AbstractQuantity<Q>) args[0]; 292 return Double.compare(value.doubleValue(), that.doubleValue(unit)); 293 } 294 default: 295 throw new UnsupportedOperationException(name); 296 } 297 } 298 } 299}