/**
* Copyright (C) 2019 Adrien Hopkins
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package org.unitConverter.unit;
import java.util.Objects;
import java.util.Optional;
/**
* A value expressed in a unit.
*
* Unless otherwise indicated, all methods in this class throw a
* {@code NullPointerException} when an argument is null.
*
* @author Adrien Hopkins
* @since 2020-07-26
*/
public final class UnitValue {
/**
* Creates a {@code UnitValue} from a unit and the associated value.
*
* @param unit unit to use
* @param value value to use
* @return {@code UnitValue} instance
*/
public static UnitValue of(Unit unit, double value) {
return new UnitValue(
Objects.requireNonNull(unit, "unit must not be null"), value);
}
private final Unit unit;
private final double value;
/**
* @param unit the unit being used
* @param value the value being represented
*/
private UnitValue(Unit unit, Double value) {
this.unit = unit;
this.value = value;
}
/**
* @return true if this value can be converted to {@code other}.
* @since 2020-10-01
*/
public final boolean canConvertTo(Unit other) {
return this.unit.canConvertTo(other);
}
/**
* @return true if this value can be converted to {@code other}.
* @since 2020-10-01
*/
public final boolean canConvertTo(Unitlike other) {
return this.unit.canConvertTo(other);
}
/**
* Returns a UnitlikeValue that represents the same value expressed in a
* different unitlike form.
*
* @param other new unit to express value in
* @return value expressed in {@code other}
*/
public final , W> UnitlikeValue convertTo(
U other) {
return UnitlikeValue.of(other,
this.unit.convertTo(other, this.getValue()));
}
/**
* Returns a UnitValue that represents the same value expressed in a
* different unit
*
* @param other new unit to express value in
* @return value expressed in {@code other}
*/
public final UnitValue convertTo(Unit other) {
return UnitValue.of(other,
this.getUnit().convertTo(other, this.getValue()));
}
/**
* Returns this unit value represented as a {@code LinearUnitValue} with this
* unit's base unit as the base.
*
* @param ns name and symbol for the base unit, use NameSymbol.EMPTY if not
* needed.
* @since 2020-09-29
*/
public final LinearUnitValue convertToBase(NameSymbol ns) {
final LinearUnit base = LinearUnit.getBase(this.unit).withName(ns);
return this.convertToLinear(base);
}
/**
* @return a {@code LinearUnitValue} that is equivalent to this value. It
* will have zero uncertainty.
* @since 2020-09-29
*/
public final LinearUnitValue convertToLinear(LinearUnit other) {
return LinearUnitValue.getExact(other,
this.getUnit().convertTo(other, this.getValue()));
}
/**
* Returns true if this and obj represent the same value, regardless of
* whether or not they are expressed in the same unit. So (1000 m).equals(1
* km) returns true.
*/
@Override
public boolean equals(Object obj) {
if (!(obj instanceof UnitValue))
return false;
final UnitValue other = (UnitValue) obj;
return Objects.equals(this.getUnit().getBase(), other.getUnit().getBase())
&& Double.doubleToLongBits(
this.getUnit().convertToBase(this.getValue())) == Double
.doubleToLongBits(
other.getUnit().convertToBase(other.getValue()));
}
/**
* @return the unit
* @since 2020-09-29
*/
public final Unit getUnit() {
return this.unit;
}
/**
* @return the value
* @since 2020-09-29
*/
public final double getValue() {
return this.value;
}
@Override
public int hashCode() {
return Objects.hash(this.getUnit().getBase(),
this.getUnit().convertFromBase(this.getValue()));
}
@Override
public String toString() {
final Optional primaryName = this.getUnit().getPrimaryName();
final Optional symbol = this.getUnit().getSymbol();
if (primaryName.isEmpty() && symbol.isEmpty()) {
final double baseValue = this.getUnit().convertToBase(this.getValue());
return String.format("%s unnamed unit (= %s %s)", this.getValue(),
baseValue, this.getUnit().getBase());
} else {
final String unitName = symbol.orElse(primaryName.get());
return this.getValue() + " " + unitName;
}
}
}