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