001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2020, Units of Measurement project.
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 javax.measure.Quantity.Scale.ABSOLUTE;
033import static javax.measure.Quantity.Scale.RELATIVE;
034
035import java.util.function.BinaryOperator;
036
037import javax.measure.Quantity;
038import javax.measure.Unit;
039import javax.measure.UnitConverter;
040
041import tech.units.indriya.AbstractQuantity;
042import tech.units.indriya.ComparableQuantity;
043import tech.units.indriya.function.AddConverter;
044import tech.units.indriya.internal.function.calc.Calculator;
045
046/**
047 * Implementation of {@link ComparableQuantity} that holds a Java {@link Number}, 
048 * which represented this quantity's amount.
049 * <p> 
050 * This object is immutable. 
051 * <p>
052 *
053 * @see AbstractQuantity
054 * @see Quantity
055 * @see ComparableQuantity
056 * @param <Q>
057 *          The type of the quantity.
058 * @author Andi Huber
059 * @author Werner Keil
060 * @version 1.2
061 * @since 1.0
062 * 
063 */
064public class NumberQuantity<Q extends Quantity<Q>> extends AbstractQuantity<Q> {
065
066    private static final long serialVersionUID = -6494337491031528402L;
067    
068    private final Number value;
069
070    /**
071     * @since 2.0
072     */
073    protected NumberQuantity(Number number, Unit<Q> unit, Scale sc) {
074      super(unit, sc);
075      value = number;
076    }
077    
078    protected NumberQuantity(Number number, Unit<Q> unit) {
079        this(number, unit, ABSOLUTE); 
080    }
081
082    @Override
083    public ComparableQuantity<Q> add(Quantity<Q> that) {
084        return addition(that, (thisValueInSystemUnit, thatValueInSystemUnit) ->
085        Calculator
086            .of(thisValueInSystemUnit)
087            .add(thatValueInSystemUnit)
088            .peek());
089    }
090
091    @Override
092    public ComparableQuantity<Q> subtract(Quantity<Q> that) {
093        return addition(that, (thisValueInSystemUnit, thatValueInSystemUnit) -> 
094        Calculator
095            .of(thisValueInSystemUnit)
096            .subtract(thatValueInSystemUnit)
097            .peek());
098    }
099
100    @Override
101    public ComparableQuantity<?> divide(Quantity<?> that) {
102        final Number resultValueInThisUnit = Calculator
103                .of(getValue())
104                .divide(that.getValue())
105                .peek();
106        return Quantities.getQuantity(resultValueInThisUnit, getUnit().divide(that.getUnit()));
107    }
108
109    @Override
110    public ComparableQuantity<Q> divide(Number divisor) {
111        final Number resultValueInThisUnit = Calculator
112                .of(getValue())
113                .divide(divisor)
114                .peek();
115        return Quantities.getQuantity(resultValueInThisUnit, getUnit());
116    }
117
118    @Override
119    public ComparableQuantity<?> multiply(Quantity<?> that) {
120        final Number resultValueInThisUnit = Calculator
121                .of(getValue())
122                .multiply(that.getValue())
123                .peek();
124        return Quantities.getQuantity(resultValueInThisUnit, getUnit().multiply(that.getUnit()));
125    }
126
127    @Override
128    public ComparableQuantity<Q> multiply(Number multiplier) {
129        final Number resultValueInThisUnit = Calculator
130                .of(getValue())
131                .multiply(multiplier)
132                .peek();
133        return Quantities.getQuantity(resultValueInThisUnit, getUnit());
134    }
135
136    @Override
137    public ComparableQuantity<?> inverse() {
138        final Number resultValueInThisUnit = Calculator
139                .of(getValue())
140                .reciprocal()
141                .peek();
142        return Quantities.getQuantity(resultValueInThisUnit, getUnit().inverse());
143    }
144
145    @Override
146    public Quantity<Q> negate() {
147        final Number resultValueInThisUnit = Calculator
148                .of(getValue())
149                .negate()
150                .peek();
151        return Quantities.getQuantity(resultValueInThisUnit, getUnit());
152    }
153
154    @Override
155    public Number getValue() {
156        return value;
157    }
158    
159    // -- HELPER
160
161        private ComparableQuantity<Q> addition(Quantity<Q> that, BinaryOperator<Number> operator) {
162
163                final Unit<Q> systemUnit = getUnit().getSystemUnit();
164                final UnitConverter c1 = this.getUnit().getConverterTo(systemUnit);
165                final UnitConverter c2 = that.getUnit().getConverterTo(systemUnit);
166
167                boolean shouldConvertThis = shouldConvertQuantityForAddition(c1, getScale());
168                boolean shouldConvertThat = shouldConvertQuantityForAddition(c2, that.getScale());
169                final Number thisValueInSystemUnit = shouldConvertThis ? c1.convert(this.getValue()) : this.getValue();
170                final Number thatValueInSystemUnit = shouldConvertThat ? c2.convert(that.getValue()) : this.getValue();
171
172                final Number resultValueInSystemUnit =
173                        operator.apply(thisValueInSystemUnit, thatValueInSystemUnit);
174
175                final Number resultValueInThisUnit =
176                        shouldConvertThis || shouldConvertThat ? c1.inverse().convert(resultValueInSystemUnit) : resultValueInSystemUnit;
177                //TODO[220] scale not handled at all !!!
178                if (getScale().equals(that.getScale())) {
179                        return Quantities.getQuantity(resultValueInThisUnit, getUnit(), getScale());
180                } else {
181                        return Quantities.getQuantity(resultValueInThisUnit, getUnit()); // becomes ABSOLUTE TODO, should it be ABSOLUTE?
182                }
183        }
184
185        private boolean shouldConvertQuantityForAddition(UnitConverter c1, Scale scale) {
186                return !(c1 instanceof AddConverter && scale.equals(RELATIVE));
187        }
188
189}