001/* 002 * Units of Measurement API 003 * Copyright (c) 2014-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 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 javax.measure.test; 031 032import java.math.BigDecimal; 033import java.math.RoundingMode; 034import java.util.Map; 035import java.util.Objects; 036 037import javax.measure.Dimension; 038import javax.measure.Quantity; 039import javax.measure.Unit; 040import javax.measure.IncommensurableException; 041import javax.measure.Prefix; 042import javax.measure.UnconvertibleException; 043import javax.measure.UnitConverter; 044import javax.measure.quantity.Dimensionless; 045import javax.measure.test.function.MultiplyConverter; 046import javax.measure.test.unit.BaseUnit; 047 048/** 049 * @author Werner Keil 050 */ 051public abstract class TestUnit<Q extends Quantity<Q>> implements Unit<Q> { 052 public static final Unit<Dimensionless> ONE = new BaseUnit<Dimensionless>("one"); 053 054 protected String symbol; // e.g. "A" 055 protected final String name; // e.g. "Angstrom" 056 protected BigDecimal multFactor; // e.g. 1E-10 057 // private double addFactor = 0.0; // used for temperatures 058 private final Dimension dimension = TestDimension.getInstance(); 059 060 protected TestUnit() { 061 name = ""; 062 } 063 064 protected TestUnit(String name, double factor) { 065 this.name = name; 066 this.multFactor = BigDecimal.valueOf(factor); 067 } 068 069 protected TestUnit(String name) { 070 this(name, 0); 071 } 072 073 public Unit<Q> shift(double offset) { 074 return this; 075 } 076 077 @SuppressWarnings({ "unchecked", "rawtypes" }) 078 public Unit<Q> alternate(String symbol) { 079 return new BaseUnit(symbol); 080 } 081 082 public <T extends Quantity<T>> Unit<T> asType(Class<T> type) throws ClassCastException { 083 // Unit<T> metricUnit = 084 // QuantityFactory.getInstance(type).getMetricUnit(); 085 // if ((metricUnit == null) || metricUnit.isCompatible(this)) 086 // return (Unit<T>) this; 087 // throw new ClassCastException("The unit: " + this //$NON-NLS-1$ 088 // + " is not of parameterized type " + type); //$NON-NLS-1$ 089 return null; 090 } 091 092 public Unit<Q> divide(double divisor) { 093 return null; 094 } 095 096 public Unit<?> divide(Unit<?> that) { 097 return null; 098 } 099 100 public UnitConverter getConverterTo(Unit<Q> that) throws UnconvertibleException { 101 if ((this == that) || this.equals(that)) 102 return TestConverter.IDENTITY; // Shortcut. 103 Unit<Q> thisSystemUnit = this.getSystemUnit(); 104 Unit<Q> thatSystemUnit = that.getSystemUnit(); 105 if (!thisSystemUnit.equals(thatSystemUnit)) 106 try { 107 return getConverterToAny(that); 108 } catch (IncommensurableException e) { 109 throw new UnconvertibleException(e); 110 } 111 UnitConverter thisToSI = this.getSystemConverter(); 112 UnitConverter thatToSI = that.getConverterTo(thatSystemUnit); 113 return thatToSI.inverse().concatenate(thisToSI); 114 } 115 116 public UnitConverter getConverterToAny(Unit<?> that) throws IncommensurableException, UnconvertibleException { 117 if (!isCompatible(that)) 118 throw new IncommensurableException(this + " is not compatible with " + that); 119 TestUnit<?> thatAbstr = (TestUnit<?>) that; // Since both units are 120 // compatible they must 121 // be both test 122 // units. 123 UnitConverter thisToDimension = this.getSystemConverter(); 124 UnitConverter thatToDimension = thatAbstr.getSystemConverter(); 125 return thatToDimension.inverse().concatenate(thisToDimension); 126 } 127 128 public Dimension getDimension() { 129 return dimension; 130 } 131 132 public String getName() { 133 return name; 134 } 135 136 public Map<Unit<?>, Integer> getBaseUnits() { 137 return null; 138 } 139 140 public String getSymbol() { 141 return symbol; 142 } 143 144 public Unit<?> inverse() { 145 return null; 146 } 147 148 public boolean isCompatible(Unit<?> that) { 149 return false; 150 } 151 152 @Override 153 public boolean isEquivalentTo(Unit<Q> that) { 154 return this.getConverterTo(that).isIdentity(); 155 } 156 157 public Unit<Q> multiply(double factor) { 158 return new BaseUnit<Q>(symbol, multFactor.doubleValue() * factor); 159 } 160 161 public Unit<?> multiply(Unit<?> that) { 162 return null; 163 } 164 165 public Unit<?> pow(int n) { 166 return null; 167 } 168 169 public Unit<?> root(int n) { 170 return null; 171 } 172 173 public abstract Unit<Q> getSystemUnit(); 174 175 /** 176 * Returns the converter from this unit to its unscaled {@link #getSystemUnit System Unit} unit. 177 * 178 * @return <code>getConverterTo(this.toSystemUnit())</code> 179 * @see #getSystemUnit 180 */ 181 public UnitConverter getSystemConverter() throws UnsupportedOperationException { 182 return TestConverter.IDENTITY; 183 } 184 185 public Unit<Q> transform(UnitConverter operation) { 186 if (operation instanceof MultiplyConverter) { 187 MultiplyConverter mult = (MultiplyConverter) operation; 188 return this.multiply(mult.getFactor()); 189 } else { 190 return this; 191 } 192 } 193 194 @Override 195 public Unit<Q> prefix(Prefix prefix) { 196 final MultiplyConverter converter = new MultiplyConverter( 197 Math.pow(prefix.getValue().doubleValue(), prefix.getExponent())); 198 return this.transform(converter); 199 } 200 201 public double getMultFactor() { 202 return multFactor.doubleValue(); 203 } 204 205 @Override 206 public String toString() { 207 final StringBuilder sb = new StringBuilder(); 208 if (name != null) { 209 sb.append(name); 210 } 211 if (symbol != null) { 212 if (sb.length() > 0) 213 sb.append(' '); 214 sb.append(symbol); 215 } 216 if (multFactor != null && 217 !BigDecimal.ONE.equals(multFactor) && multFactor.doubleValue()!=1d && 218 !BigDecimal.ONE.equals(multFactor) && multFactor.doubleValue()!=0d) { 219 if (sb.length() > 0) 220 sb.append(" * "); 221 sb.append(printFactor(multFactor)); 222 } 223 return sb.toString(); 224 } 225 226 @Override 227 public Unit<Q> shift(Number offset) { 228 Objects.requireNonNull(offset); 229 return shift(offset.doubleValue()); 230 } 231 232 @Override 233 public Unit<Q> multiply(Number multiplier) { 234 Objects.requireNonNull(multiplier); 235 return multiply(multiplier.doubleValue()); 236 } 237 238 @Override 239 public Unit<Q> divide(Number divisor) { 240 Objects.requireNonNull(divisor); 241 return divide(divisor.doubleValue()); 242 } 243 244 private String printFactor(final BigDecimal x) { 245 final int s = x.scale(); 246 if (s > 40) { 247 BigDecimal y = x.setScale(27, RoundingMode.HALF_UP); 248 final String result = y.toPlainString(); 249 return result; 250 } else { 251 final String result = x.toPlainString(); 252 if (result.contains(".")) { 253 return result.length() < 24 ? result : result.substring(0, 23); 254 } else { 255 return result; 256 } 257 } 258 } 259}