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.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.calc.Calculator;
051import tech.units.indriya.quantity.Quantities;
052
053/**
054 * Class that represents {@link TimeUnit} in Unit-API
055 * 
056 * @author otaviojava
057 * @author keilw
058 * @author Andi Huber
059 * @version 1.0.4
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 the {@link TimeUnitQuantity} using {@link TimeUnit} and {@link Integer}
102   * 
103   * @param timeUnit
104   *          - time to be used
105   * @param value
106   *          - value to be used
107   * @since 1.0
108   * @deprecated use #of(Integer, TimeUnit)
109   */
110  @Deprecated
111  public static TimeUnitQuantity of(TimeUnit timeUnit, Integer number) {
112    return of(number, timeUnit);
113  }
114
115  /**
116   * Creates a {@link TimeUnitQuantity} based a {@link Quantity<Time>} converted to {@link SI#SECOND}.
117   * 
118   * @param quantity
119   *          - quantity to be used
120   * @return the {@link TimeUnitQuantity} converted be quantity in seconds.
121   * @since 1.0
122   */
123  public static TimeUnitQuantity of(Quantity<Time> quantity) {
124    Quantity<Time> seconds = Objects.requireNonNull(quantity).to(SECOND);
125    return new TimeUnitQuantity(TimeUnit.SECONDS, seconds.getValue());
126  }
127
128  /**
129   * get to {@link TimeUnit}
130   * 
131   * @return the TimeUnit
132   * @since 1.0
133   */
134  public TimeUnit getTimeUnit() {
135    return timeUnit;
136  }
137
138  /**
139   * get value expressed in {@link Number}
140   * 
141   * @return the value
142   * @since 1.0
143   */
144  public Number getValue() {
145    return value;
146  }
147
148  /**
149   * converts the {@link TimeUnit} to {@link Unit}
150   * 
151   * @return the {@link TimeUnitQuantity#getTimeUnit()} converted to Unit
152   * @since 1.0
153   */
154  public Unit<Time> toUnit() {
155    return toUnit(timeUnit);
156  }
157
158  /**
159   * Converts the {@link TimeUnitQuantity} to {@link Quantity<Time>}
160   * 
161   * @return this class converted to Quantity
162   * @since 1.0
163   */
164  public Quantity<Time> toQuantity() {
165    return Quantities.getQuantity(value, toUnit());
166  }
167
168  public TimeUnitQuantity to(TimeUnit aTimeUnit) {
169    Quantity<Time> time = toQuantity().to(toUnit(aTimeUnit));
170    return new TimeUnitQuantity(aTimeUnit, time.getValue().longValue());
171  }
172
173  private static Unit<Time> toUnit(TimeUnit timeUnit) {
174    switch (timeUnit) {
175      case MICROSECONDS:
176        return TimeQuantities.MICROSECOND;
177      case MILLISECONDS:
178        return TimeQuantities.MILLISECOND;
179      case NANOSECONDS:
180        return TimeQuantities.NANOSECOND;
181      case SECONDS:
182        return SECOND;
183      case MINUTES:
184        return MINUTE;
185      case HOURS:
186        return HOUR;
187      case DAYS:
188        return DAY;
189      default:
190        throw new IllegalStateException("In TimeUnitQuantity just supports DAYS, HOURS, MICROSECONDS, MILLISECONDS, MINUTES, NANOSECONDS, SECONDS ");
191    }
192  }
193
194  /**
195   * @since 1.0
196   */
197  @Override
198  public int hashCode() {
199    return Objects.hash(timeUnit, value);
200  }
201
202  /**
203   * @since 1.0
204   */
205  @Override
206  public boolean equals(Object obj) {
207    if (this == obj) {
208      return true;
209    }
210    if (TimeUnitQuantity.class.isInstance(obj)) {
211      TimeUnitQuantity other = TimeUnitQuantity.class.cast(obj);
212      return Objects.equals(timeUnit, other.timeUnit) && Objects.equals(value, other.value);
213    }
214    if (obj instanceof Quantity<?>) {
215      Quantity<?> that = (Quantity<?>) obj;
216      return Objects.equals(getUnit(), that.getUnit()) && 
217              Calculus.currentNumberSystem().compare(value, that.getValue()) == 0;
218              
219    }
220    return super.equals(obj);
221  }
222
223  @Override
224  public String toString() {
225    return "Time unit:" + timeUnit + " value: " + value;
226  }
227
228  /**
229   * @since 1.0.1
230   */
231  @Override
232  public ComparableQuantity<Time> add(Quantity<Time> that) {
233      final UnitConverter thisToThat = this.getUnit().getConverterTo(that.getUnit());
234      final boolean thatUnitIsSmaller = 
235              Calculus.currentNumberSystem().compare(thisToThat.convert(1.), 1.)>0;
236
237      final Unit<Time> preferedUnit = thatUnitIsSmaller ? that.getUnit() : this.getUnit();
238      
239      final Number thisValueInPreferedUnit = convertedQuantityValue(this, preferedUnit);
240      final Number thatValueInPreferedUnit = convertedQuantityValue(that, preferedUnit);
241      
242      final Number resultValueInPreferedUnit = Calculator.of(thisValueInPreferedUnit)
243              .add(thatValueInPreferedUnit)
244              .peek();
245      
246      return Quantities.getQuantity(resultValueInPreferedUnit, preferedUnit);
247  }
248
249  /**
250   * @since 1.0.1
251   */
252  @Override
253  public ComparableQuantity<Time> subtract(Quantity<Time> that) {
254    return add(that.negate());
255  }
256
257  /**
258   * @since 1.0.1
259   */
260  @Override
261  public ComparableQuantity<?> divide(Quantity<?> that) {
262      return applyMultiplicativeQuantityOperation(
263              that, (a, b)->Calculator.of(a).divide(b).peek(), Unit::divide);
264  }
265
266  /**
267   * @since 1.0.1
268   */
269  @Override
270  public ComparableQuantity<Time> divide(Number that) {
271      return applyMultiplicativeNumberOperation(
272              that, (a, b)->Calculator.of(a).divide(b).peek());
273  }
274
275  /**
276   * @since 1.0.1
277   */
278  @Override
279  public ComparableQuantity<?> multiply(Quantity<?> that) {
280      return applyMultiplicativeQuantityOperation(
281              that, (a, b)->Calculator.of(a).multiply(b).peek(), Unit::multiply);
282  }
283
284  /**
285   * @since 1.0.1
286   */
287  @Override
288  public ComparableQuantity<Time> multiply(Number that) {
289      return applyMultiplicativeNumberOperation(
290              that, (a, b)->Calculator.of(a).multiply(b).peek());
291  }
292  
293  /**
294   * @since 1.0.1
295   */
296  @Override
297  public ComparableQuantity<Frequency> inverse() {
298    return Quantities.getQuantity(
299            Calculator.of(value).reciprocal().peek(),
300            toUnit(timeUnit).inverse()).asType(Frequency.class);
301  }
302
303  /**
304   * @since 1.0.2
305   */
306  @Override
307  public Quantity<Time> negate() {
308    return of(
309            Calculator.of(value).negate().peek(), 
310            getTimeUnit());
311  }
312  
313  // -- HELPER
314  
315  private static <R extends Quantity<R>> Number quantityValue(Quantity<R> that) {
316      return convertedQuantityValue(that, that.getUnit());
317  }
318
319  private static <R extends Quantity<R>> Number convertedQuantityValue(Quantity<R> that, Unit<R> unit) {
320      return that.getUnit().getConverterTo(unit).convert(that.getValue());
321  }
322
323  private ComparableQuantity<?> applyMultiplicativeQuantityOperation(
324          Quantity<?> that,
325          BinaryOperator<Number> valueOperator,
326          BinaryOperator<Unit<?>> unitOperator) {
327
328      final Number thisValue = quantityValue(this);
329      final Number thatValue = quantityValue(that);
330      final Number result = valueOperator.apply(thisValue, thatValue);
331      final Unit<?> resultUnit = unitOperator.apply(getUnit(), that.getUnit());
332      return Quantities.getQuantity(result, resultUnit);
333  }
334
335  private ComparableQuantity<Time> applyMultiplicativeNumberOperation(Number that,
336          BinaryOperator<Number> valueOperator) {
337      final Number thisValue = this.getValue();
338      final Number thatValue = that;
339      final Number result = valueOperator.apply(thisValue, thatValue);
340      return Quantities.getQuantity(result, getUnit());
341  }
342}