001/*
002 * Units of Measurement Enum Implementation
003 * Copyright © 2005-2021, Werner Keil and others.
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, Unit-API 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.uom.impl.enums;
031
032import tech.uom.lib.common.function.DoubleFactorSupplier;
033import tech.uom.lib.common.function.QuantityConverter;
034import tech.uom.impl.enums.format.UnitStyle;
035
036import java.util.Objects;
037
038import javax.measure.Quantity;
039import javax.measure.Unit;
040
041/**
042 * @author Werner Keil
043 * @version 2.1, $Date: 2020-12-06 $
044 * @since 1.0
045 */
046public abstract class AbstractQuantity<Q extends Quantity<Q>> implements 
047        Quantity<Q>, QuantityConverter<Q>, Comparable<Quantity<Q>> {
048
049    protected abstract Number getScalar();
050
051    protected abstract boolean eq(AbstractQuantity<Q> dq);
052
053    protected abstract boolean isZero();
054    
055    private final Scale scale;
056    
057    /**
058     * Constructor.
059     * @param scale a scale of measurement
060     */
061    protected AbstractQuantity(Scale scale) {
062        this.scale = scale;
063    }
064    
065    /**
066     * Constructor.
067     */
068    protected AbstractQuantity() {
069        this(Scale.ABSOLUTE);
070    }
071
072    @SuppressWarnings("unchecked")
073        @Override
074    public boolean equals(Object o) {
075        if (o instanceof AbstractQuantity) {
076            return eq((AbstractQuantity<Q>) o);
077        }
078        return false;
079    }
080    
081    @Override
082    public boolean isEquivalentTo(Quantity<Q> that) {
083        return this.compareTo(that) == 0;
084    }
085    
086    /**
087     * Returns the measurement level.
088     *
089     * @return the measurement level.
090     * @since 2.0
091     */
092    @Override
093    public Scale getScale() {
094        return scale;
095    }
096    
097    /**
098     * Returns the hash code for this measure.
099     *
100     * @return the hash code value.
101     */
102    @Override
103    public int hashCode() {
104      return Objects.hash(getValue(), getUnit(), getScale());
105    }
106
107    public abstract String toString(boolean withUnit, boolean withSpace, 
108            int precision);
109
110    public String toString(boolean withUnit, boolean withSpace) {
111        return toString(withUnit, withSpace, 0);
112    }
113
114    /**
115     * Casts this quantity to a parameterized quantity of specified nature or throw a
116     * <code>ClassCastException</code> if the dimension of the specified
117     * quantity and its unit's dimension do not match. For
118     * example:<br><code>
119     *     Quantity<Length> length = BaseQuantity.of("2 km").asType(Length.class);
120     * </code>
121     *
122     * @param type the quantity class identifying the nature of the measure.
123     * @return this measure parameterized with the specified type.
124     * @throws ClassCastException if the dimension of this unit is different
125     *         from the specified quantity dimension.
126     * @throws UnsupportedOperationException
127     *             if the specified quantity class does not have a public static
128     *             field named "UNIT" holding the SI unit for the quantity.
129     * @see Unit#asType(Class)
130     */
131    @SuppressWarnings("unchecked")
132    public final <T extends Quantity<T>> AbstractQuantity<T> asType(Class<T> type)
133            throws ClassCastException {
134        this.getUnit().asType(type); // Raises ClassCastException if dimension mismatches.
135        return (AbstractQuantity<T>) this;
136    }
137    
138    protected String toString(boolean withSpace) {
139        return toString(true, withSpace);
140    }
141
142    protected String toString(int precision) {
143        return toString(true, false, precision);
144    }
145
146    @Override
147    public String toString() {
148        return toString(true);
149    }
150
151    protected abstract String showInUnit(Unit<?> u, int precision, 
152                UnitStyle style);
153
154    protected String showInUnit(Unit<?> u, Number s, int precision, 
155                UnitStyle style, boolean withSpace) {
156        if (u == null) {
157            throw new IllegalArgumentException("Null unit not allowed!");  //$NON-NLS-1$
158        }
159        double result;
160        if (u instanceof DoubleFactorSupplier) {
161            result = s.doubleValue() / ((DoubleFactorSupplier)u).getFactor();
162        } else {
163            result = s.doubleValue();
164        }
165
166        final String str = getStr(Double.valueOf(result), precision);
167        StringBuilder sb;
168        switch (style) {
169            case NAME:
170                sb = new StringBuilder(str);
171                if (withSpace) sb.append(' ');
172                return sb.append(u.getName()).toString();
173                //return str;
174            default:
175                sb = new StringBuilder(str);
176                if (withSpace) sb.append(' ');
177                return sb.append(u.getSymbol()).toString();
178        }
179    }
180    
181    protected String showInUnit(Unit<?> u, Double s, int precision, 
182                UnitStyle style) {
183        return showInUnit(u, s, precision, style, true);
184    }
185
186    /**
187     *
188     * @param u
189     * @param precision number of decimal places
190     * @return
191     */
192    protected String showInUnit(Unit<?> u, int precision) {
193        return showInUnit(u, precision, UnitStyle.SYMBOL);
194    }
195
196    protected String getStr(Number val, int precision) {
197//      if (val instanceof BigDecimal) { //TODO for #JavaME disable that part
198//              BigDecimal num = ((BigDecimal)val).setScale(precision, RoundingMode.HALF_UP);
199//              String str = num.toString();
200//              return str;
201//      } 
202        return String.valueOf(val);
203    }
204}