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}