001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2021, 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, 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.time;
031
032import static tech.units.indriya.unit.Units.DAY;
033import static tech.units.indriya.unit.Units.HOUR;
034import static tech.units.indriya.unit.Units.MINUTE;
035import static tech.units.indriya.unit.Units.SECOND;
036
037import java.util.Objects;
038import java.util.concurrent.TimeUnit;
039import java.util.function.BinaryOperator;
040
041import javax.measure.Quantity;
042import javax.measure.Unit;
043import javax.measure.UnitConverter;
044import javax.measure.quantity.Frequency;
045import javax.measure.quantity.Time;
046
047import tech.units.indriya.AbstractQuantity;
048import tech.units.indriya.ComparableQuantity;
049import tech.units.indriya.function.Calculus;
050import tech.units.indriya.internal.function.Calculator;
051import tech.units.indriya.quantity.Quantities;
052
053/**
054 * Class that represents {@link TimeUnit} in Unit-API
055 * 
056 * @author Otavio Santana
057 * @author Werner Keil
058 * @author Andi Huber
059 * @version 1.2
060 * @since 1.0
061 */
062public final class TimeUnitQuantity extends AbstractQuantity<Time> {
063
064  /**
065   * 
066   */
067  private static final long serialVersionUID = -5840251813363744230L;
068
069  private final TimeUnit timeUnit;
070
071  private final Number value;
072
073  /**
074   * creates the {@link TimeUnitQuantity} using {@link TimeUnit} and {@link Long}
075   * 
076   * @param timeUnit
077   *          - time to be used
078   * @param value
079   *          - value to be used
080   */
081  TimeUnitQuantity(TimeUnit timeUnit, Number value) {
082    super(toUnit(timeUnit));
083    this.timeUnit = timeUnit;
084    this.value = value;
085  }
086
087  /**
088   * creates the {@link TimeUnitQuantity} using {@link TimeUnit} and {@link Long}
089   * 
090   * @param timeUnit
091   *          - time to be used
092   * @param value
093   *          - value to be used
094   * @since 1.0.9
095   */
096  public static TimeUnitQuantity of(Number number, TimeUnit timeUnit) {
097    return new TimeUnitQuantity(Objects.requireNonNull(timeUnit), Objects.requireNonNull(number));
098  }
099
100  /**
101   * Creates a {@link TimeUnitQuantity} based a {@link Quantity<Time>} converted to {@link SI#SECOND}.
102   * 
103   * @param quantity
104   *          - quantity to be used
105   * @return the {@link TimeUnitQuantity} converted be quantity in seconds.
106   * @since 1.0
107   */
108  public static TimeUnitQuantity of(Quantity<Time> quantity) {
109    Quantity<Time> seconds = Objects.requireNonNull(quantity).to(SECOND);
110    return new TimeUnitQuantity(TimeUnit.SECONDS, seconds.getValue());
111  }
112
113  /**
114   * get to {@link TimeUnit}
115   * 
116   * @return the TimeUnit
117   * @since 1.0
118   */
119  public TimeUnit getTimeUnit() {
120    return timeUnit;
121  }
122
123  /**
124   * get value expressed in {@link Number}
125   * 
126   * @return the value
127   * @since 1.0
128   */
129  public Number getValue() {
130    return value;
131  }
132
133  /**
134   * converts the {@link TimeUnit} to {@link Unit}
135   * 
136   * @return the {@link TimeUnitQuantity#getTimeUnit()} converted to Unit
137   * @since 1.0
138   */
139  public Unit<Time> toUnit() {
140    return toUnit(timeUnit);
141  }
142
143  /**
144   * Converts the {@link TimeUnitQuantity} to {@link Quantity<Time>}
145   * 
146   * @return this class converted to Quantity
147   * @since 1.0
148   */
149  public Quantity<Time> toQuantity() {
150    return Quantities.getQuantity(value, toUnit());
151  }
152
153  public TimeUnitQuantity to(TimeUnit aTimeUnit) {
154    Quantity<Time> time = toQuantity().to(toUnit(aTimeUnit));
155    return new TimeUnitQuantity(aTimeUnit, time.getValue().longValue());
156  }
157
158  private static Unit<Time> toUnit(TimeUnit timeUnit) {
159    switch (timeUnit) {
160      case MICROSECONDS:
161        return TimeQuantities.MICROSECOND;
162      case MILLISECONDS:
163        return TimeQuantities.MILLISECOND;
164      case NANOSECONDS:
165        return TimeQuantities.NANOSECOND;
166      case SECONDS:
167        return SECOND;
168      case MINUTES:
169        return MINUTE;
170      case HOURS:
171        return HOUR;
172      case DAYS:
173        return DAY;
174      default:
175        throw new IllegalStateException("In TimeUnitQuantity just supports DAYS, HOURS, MICROSECONDS, MILLISECONDS, MINUTES, NANOSECONDS, SECONDS ");
176    }
177  }
178
179  /**
180   * @since 1.0
181   */
182  @Override
183  public int hashCode() {
184    return Objects.hash(timeUnit, value);
185  }
186
187  /**
188   * @since 1.0
189   */
190  @Override
191  public boolean equals(Object obj) {
192    if (this == obj) {
193      return true;
194    }
195    if (TimeUnitQuantity.class.isInstance(obj)) {
196      TimeUnitQuantity other = TimeUnitQuantity.class.cast(obj);
197      return Objects.equals(timeUnit, other.timeUnit) && Objects.equals(value, other.value);
198    }
199    if (obj instanceof Quantity<?>) {
200      Quantity<?> that = (Quantity<?>) obj;
201      return Objects.equals(getUnit(), that.getUnit()) && 
202              Calculus.currentNumberSystem().compare(value, that.getValue()) == 0;
203              
204    }
205    return super.equals(obj);
206  }
207
208  @Override
209  public String toString() {
210    return "Time unit:" + timeUnit + " value: " + value;
211  }
212
213  /**
214   * @since 1.0.1
215   */
216  @Override
217  public ComparableQuantity<Time> add(Quantity<Time> that) {
218      final UnitConverter thisToThat = this.getUnit().getConverterTo(that.getUnit());
219      final boolean thatUnitIsSmaller = 
220              Calculus.currentNumberSystem().compare(thisToThat.convert(1.), 1.)>0;
221
222      final Unit<Time> preferedUnit = thatUnitIsSmaller ? that.getUnit() : this.getUnit();
223      
224      final Number thisValueInPreferedUnit = convertedQuantityValue(this, preferedUnit);
225      final Number thatValueInPreferedUnit = convertedQuantityValue(that, preferedUnit);
226      
227      final Number resultValueInPreferedUnit = Calculator.of(thisValueInPreferedUnit)
228              .add(thatValueInPreferedUnit)
229              .peek();
230      
231      return Quantities.getQuantity(resultValueInPreferedUnit, preferedUnit);
232  }
233
234  /**
235   * @since 1.0.1
236   */
237  @Override
238  public ComparableQuantity<Time> subtract(Quantity<Time> that) {
239    return add(that.negate());
240  }
241
242  /**
243   * @since 1.0.1
244   */
245  @Override
246  public ComparableQuantity<?> divide(Quantity<?> that) {
247      return applyMultiplicativeQuantityOperation(
248              that, (a, b)->Calculator.of(a).divide(b).peek(), Unit::divide);
249  }
250
251  /**
252   * @since 1.0.1
253   */
254  @Override
255  public ComparableQuantity<Time> divide(Number that) {
256      return applyMultiplicativeNumberOperation(
257              that, (a, b)->Calculator.of(a).divide(b).peek());
258  }
259
260  /**
261   * @since 1.0.1
262   */
263  @Override
264  public ComparableQuantity<?> multiply(Quantity<?> that) {
265      return applyMultiplicativeQuantityOperation(
266              that, (a, b)->Calculator.of(a).multiply(b).peek(), Unit::multiply);
267  }
268
269  /**
270   * @since 1.0.1
271   */
272  @Override
273  public ComparableQuantity<Time> multiply(Number that) {
274      return applyMultiplicativeNumberOperation(
275              that, (a, b)->Calculator.of(a).multiply(b).peek());
276  }
277  
278  /**
279   * @since 1.0.1
280   */
281  @Override
282  public ComparableQuantity<Frequency> inverse() {
283    return Quantities.getQuantity(
284            Calculator.of(value).reciprocal().peek(),
285            toUnit(timeUnit).inverse()).asType(Frequency.class);
286  }
287
288  /**
289   * @since 1.0.2
290   */
291  @Override
292  public Quantity<Time> negate() {
293    return of(
294            Calculator.of(value).negate().peek(), 
295            getTimeUnit());
296  }
297  
298  // -- HELPER
299  
300  private static <R extends Quantity<R>> Number quantityValue(Quantity<R> that) {
301      return convertedQuantityValue(that, that.getUnit());
302  }
303
304  private static <R extends Quantity<R>> Number convertedQuantityValue(Quantity<R> that, Unit<R> unit) {
305      return that.getUnit().getConverterTo(unit).convert(that.getValue());
306  }
307
308  private ComparableQuantity<?> applyMultiplicativeQuantityOperation(
309          Quantity<?> that,
310          BinaryOperator<Number> valueOperator,
311          BinaryOperator<Unit<?>> unitOperator) {
312
313      final Number thisValue = quantityValue(this);
314      final Number thatValue = quantityValue(that);
315      final Number result = valueOperator.apply(thisValue, thatValue);
316      final Unit<?> resultUnit = unitOperator.apply(getUnit(), that.getUnit());
317      return Quantities.getQuantity(result, resultUnit);
318  }
319
320  private ComparableQuantity<Time> applyMultiplicativeNumberOperation(Number that,
321          BinaryOperator<Number> valueOperator) {
322      final Number thisValue = this.getValue();
323      final Number thatValue = that;
324      final Number result = valueOperator.apply(thisValue, thatValue);
325      return Quantities.getQuantity(result, getUnit());
326  }
327}