diff options
Diffstat (limited to 'src/org/unitConverter/unit')
22 files changed, 0 insertions, 6213 deletions
diff --git a/src/org/unitConverter/unit/BaseDimension.java b/src/org/unitConverter/unit/BaseDimension.java deleted file mode 100644 index 8e63a17..0000000 --- a/src/org/unitConverter/unit/BaseDimension.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -import java.util.Objects; - -/** - * A dimension that defines a {@code BaseUnit} - * - * @author Adrien Hopkins - * @since 2019-10-16 - */ -public final class BaseDimension { - /** - * Gets a {@code BaseDimension} with the provided name and symbol. - * - * @param name - * name of dimension - * @param symbol - * symbol used for dimension - * @return dimension - * @since 2019-10-16 - */ - public static BaseDimension valueOf(final String name, final String symbol) { - return new BaseDimension(name, symbol); - } - - /** - * The name of the dimension. - */ - private final String name; - /** - * The symbol used by the dimension. Symbols should be short, generally one or two characters. - */ - private final String symbol; - - /** - * Creates the {@code BaseDimension}. - * - * @param name - * name of unit - * @param symbol - * symbol of unit - * @throws NullPointerException - * if any argument is null - * @since 2019-10-16 - */ - private BaseDimension(final String name, final String symbol) { - this.name = Objects.requireNonNull(name, "name must not be null."); - this.symbol = Objects.requireNonNull(symbol, "symbol must not be null."); - } - - /** - * @return name - * @since 2019-10-16 - */ - public final String getName() { - return this.name; - } - - /** - * @return symbol - * @since 2019-10-16 - */ - public final String getSymbol() { - return this.symbol; - } - - @Override - public String toString() { - return String.format("%s (%s)", this.getName(), this.getSymbol()); - } -} diff --git a/src/org/unitConverter/unit/BaseUnit.java b/src/org/unitConverter/unit/BaseUnit.java deleted file mode 100644 index 6757bd0..0000000 --- a/src/org/unitConverter/unit/BaseUnit.java +++ /dev/null @@ -1,133 +0,0 @@ -/** - * 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -/** - * A unit that other units are defined by. - * <p> - * Note that BaseUnits <b>must</b> have names and symbols. This is because they - * are used for toString code. Therefore, the Optionals provided by - * {@link #getPrimaryName} and {@link #getSymbol} will always contain a value. - * - * @author Adrien Hopkins - * @since 2019-10-16 - */ -public final class BaseUnit extends Unit { - /** - * Gets a base unit from the dimension it measures, its name and its symbol. - * - * @param dimension dimension measured by this unit - * @param name name of unit - * @param symbol symbol of unit - * @return base unit - * @since 2019-10-16 - */ - public static BaseUnit valueOf(final BaseDimension dimension, - final String name, final String symbol) { - return new BaseUnit(dimension, name, symbol, new HashSet<>()); - } - - /** - * Gets a base unit from the dimension it measures, its name and its symbol. - * - * @param dimension dimension measured by this unit - * @param name name of unit - * @param symbol symbol of unit - * @return base unit - * @since 2019-10-21 - */ - public static BaseUnit valueOf(final BaseDimension dimension, - final String name, final String symbol, final Set<String> otherNames) { - return new BaseUnit(dimension, name, symbol, otherNames); - } - - /** - * The dimension measured by this base unit. - */ - private final BaseDimension dimension; - - /** - * Creates the {@code BaseUnit}. - * - * @param dimension dimension of unit - * @param primaryName name of unit - * @param symbol symbol of unit - * @throws NullPointerException if any argument is null - * @since 2019-10-16 - */ - private BaseUnit(final BaseDimension dimension, final String primaryName, - final String symbol, final Set<String> otherNames) { - super(primaryName, symbol, otherNames); - this.dimension = Objects.requireNonNull(dimension, - "dimension must not be null."); - } - - /** - * Returns a {@code LinearUnit} with this unit as a base and a conversion - * factor of 1. This operation must be done in order to allow units to be - * created with operations. - * - * @return this unit as a {@code LinearUnit} - * @since 2019-10-16 - */ - public LinearUnit asLinearUnit() { - return LinearUnit.valueOf(this.getBase(), 1); - } - - @Override - protected double convertFromBase(final double value) { - return value; - } - - @Override - protected double convertToBase(final double value) { - return value; - } - - /** - * @return dimension - * @since 2019-10-16 - */ - public final BaseDimension getBaseDimension() { - return this.dimension; - } - - @Override - public String toString() { - return this.getPrimaryName().orElse("Unnamed unit") - + (this.getSymbol().isPresent() - ? String.format(" (%s)", this.getSymbol().get()) - : ""); - } - - @Override - public BaseUnit withName(final NameSymbol ns) { - Objects.requireNonNull(ns, "ns must not be null."); - if (!ns.getPrimaryName().isPresent()) - throw new IllegalArgumentException( - "BaseUnits must have primary names."); - if (!ns.getSymbol().isPresent()) - throw new IllegalArgumentException("BaseUnits must have symbols."); - return BaseUnit.valueOf(this.getBaseDimension(), - ns.getPrimaryName().get(), ns.getSymbol().get(), - ns.getOtherNames()); - } -} diff --git a/src/org/unitConverter/unit/BritishImperial.java b/src/org/unitConverter/unit/BritishImperial.java deleted file mode 100644 index ea23cd1..0000000 --- a/src/org/unitConverter/unit/BritishImperial.java +++ /dev/null @@ -1,116 +0,0 @@ -/** - * 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -/** - * A static utility class that contains units in the British Imperial system. - * - * @author Adrien Hopkins - * @since 2019-10-21 - */ -public final class BritishImperial { - /** - * Imperial units that measure area - * - * @author Adrien Hopkins - * @since 2019-11-08 - */ - public static final class Area { - public static final LinearUnit SQUARE_FOOT = Length.FOOT.toExponent(2); - public static final LinearUnit SQUARE_YARD = Length.YARD.toExponent(2); - public static final LinearUnit SQUARE_MILE = Length.MILE.toExponent(2); - public static final LinearUnit PERCH = Length.ROD.times(Length.ROD); - public static final LinearUnit ROOD = Length.ROD.times(Length.FURLONG); - public static final LinearUnit ACRE = Length.FURLONG.times(Length.CHAIN); - } - - /** - * Imperial units that measure length - * - * @author Adrien Hopkins - * @since 2019-10-28 - */ - public static final class Length { - /** - * According to the International Yard and Pound of 1959, a yard is defined as exactly 0.9144 metres. - */ - public static final LinearUnit YARD = SI.METRE.times(0.9144); - public static final LinearUnit FOOT = YARD.dividedBy(3); - public static final LinearUnit INCH = FOOT.dividedBy(12); - public static final LinearUnit THOU = INCH.dividedBy(1000); - public static final LinearUnit CHAIN = YARD.times(22); - public static final LinearUnit FURLONG = CHAIN.times(10); - public static final LinearUnit MILE = FURLONG.times(8); - public static final LinearUnit LEAGUE = MILE.times(3); - - public static final LinearUnit NAUTICAL_MILE = SI.METRE.times(1852); - public static final LinearUnit CABLE = NAUTICAL_MILE.dividedBy(10); - public static final LinearUnit FATHOM = CABLE.dividedBy(100); - - public static final LinearUnit ROD = YARD.times(5.5); - public static final LinearUnit LINK = ROD.dividedBy(25); - } - - /** - * British Imperial units that measure mass. - * - * @author Adrien Hopkins - * @since 2019-11-08 - */ - public static final class Mass { - public static final LinearUnit POUND = SI.GRAM.times(453.59237); - public static final LinearUnit OUNCE = POUND.dividedBy(16); - public static final LinearUnit DRACHM = POUND.dividedBy(256); - public static final LinearUnit GRAIN = POUND.dividedBy(7000); - public static final LinearUnit STONE = POUND.times(14); - public static final LinearUnit QUARTER = STONE.times(2); - public static final LinearUnit HUNDREDWEIGHT = QUARTER.times(4); - public static final LinearUnit LONG_TON = HUNDREDWEIGHT.times(20); - public static final LinearUnit SLUG = SI.KILOGRAM.times(14.59390294); - } - - /** - * British Imperial units that measure volume - * - * @author Adrien Hopkins - * @since 2019-11-08 - */ - public static final class Volume { - public static final LinearUnit FLUID_OUNCE = SI.LITRE.withPrefix(SI.MILLI).times(28.4130625); - public static final LinearUnit GILL = FLUID_OUNCE.times(5); - public static final LinearUnit PINT = FLUID_OUNCE.times(20); - public static final LinearUnit QUART = PINT.times(2); - public static final LinearUnit GALLON = QUART.times(4); - public static final LinearUnit PECK = GALLON.times(2); - public static final LinearUnit BUSHEL = PECK.times(4); - - public static final LinearUnit CUBIC_INCH = Length.INCH.toExponent(3); - public static final LinearUnit CUBIC_FOOT = Length.FOOT.toExponent(3); - public static final LinearUnit CUBIC_YARD = Length.YARD.toExponent(3); - public static final LinearUnit ACRE_FOOT = Area.ACRE.times(Length.FOOT); - } - - public static final LinearUnit OUNCE_FORCE = Mass.OUNCE.times(SI.Constants.EARTH_GRAVITY); - public static final LinearUnit POUND_FORCE = Mass.POUND.times(SI.Constants.EARTH_GRAVITY); - - public static final LinearUnit BRITISH_THERMAL_UNIT = SI.JOULE.times(1055.06); - public static final LinearUnit CALORIE = SI.JOULE.times(4.184); - public static final LinearUnit KILOCALORIE = SI.JOULE.times(4184); - - public static final Unit FAHRENHEIT = Unit.fromConversionFunctions(SI.KELVIN.getBase(), - tempK -> tempK * 1.8 - 459.67, tempF -> (tempF + 459.67) / 1.8); -} diff --git a/src/org/unitConverter/unit/FunctionalUnit.java b/src/org/unitConverter/unit/FunctionalUnit.java deleted file mode 100644 index 586e0d7..0000000 --- a/src/org/unitConverter/unit/FunctionalUnit.java +++ /dev/null @@ -1,109 +0,0 @@ -/** - * 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -import java.util.Objects; -import java.util.function.DoubleUnaryOperator; - -import org.unitConverter.math.ObjectProduct; - -/** - * A unit that uses functional objects to convert to and from its base. - * - * @author Adrien Hopkins - * @since 2019-05-22 - */ -final class FunctionalUnit extends Unit { - /** - * A function that accepts a value expressed in the unit's base and returns that value expressed in this unit. - * - * @since 2019-05-22 - */ - private final DoubleUnaryOperator converterFrom; - - /** - * A function that accepts a value expressed in the unit and returns that value expressed in the unit's base. - * - * @since 2019-05-22 - */ - private final DoubleUnaryOperator converterTo; - - /** - * Creates the {@code FunctionalUnit}. - * - * @param base - * unit's base - * @param converterFrom - * function that accepts a value expressed in the unit's base and returns that value expressed in this - * unit. - * @param converterTo - * function that accepts a value expressed in the unit and returns that value expressed in the unit's - * base. - * @throws NullPointerException - * if any argument is null - * @since 2019-05-22 - */ - public FunctionalUnit(final ObjectProduct<BaseUnit> base, final DoubleUnaryOperator converterFrom, - final DoubleUnaryOperator converterTo) { - super(base, NameSymbol.EMPTY); - this.converterFrom = Objects.requireNonNull(converterFrom, "converterFrom must not be null."); - this.converterTo = Objects.requireNonNull(converterTo, "converterTo must not be null."); - } - - /** - * Creates the {@code FunctionalUnit}. - * - * @param base - * unit's base - * @param converterFrom - * function that accepts a value expressed in the unit's base and returns that value expressed in this - * unit. - * @param converterTo - * function that accepts a value expressed in the unit and returns that value expressed in the unit's - * base. - * @throws NullPointerException - * if any argument is null - * @since 2019-05-22 - */ - public FunctionalUnit(final ObjectProduct<BaseUnit> base, final DoubleUnaryOperator converterFrom, - final DoubleUnaryOperator converterTo, final NameSymbol ns) { - super(base, ns); - this.converterFrom = Objects.requireNonNull(converterFrom, "converterFrom must not be null."); - this.converterTo = Objects.requireNonNull(converterTo, "converterTo must not be null."); - } - - /** - * {@inheritDoc} - * - * Uses {@code converterFrom} to convert. - */ - @Override - public double convertFromBase(final double value) { - return this.converterFrom.applyAsDouble(value); - } - - /** - * {@inheritDoc} - * - * Uses {@code converterTo} to convert. - */ - @Override - public double convertToBase(final double value) { - return this.converterTo.applyAsDouble(value); - } - -} diff --git a/src/org/unitConverter/unit/FunctionalUnitlike.java b/src/org/unitConverter/unit/FunctionalUnitlike.java deleted file mode 100644 index 21c1fca..0000000 --- a/src/org/unitConverter/unit/FunctionalUnitlike.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (C) 2020 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -import java.util.function.DoubleFunction; -import java.util.function.ToDoubleFunction; - -import org.unitConverter.math.ObjectProduct; - -/** - * A unitlike form that converts using two conversion functions. - * - * @since 2020-09-07 - */ -final class FunctionalUnitlike<V> extends Unitlike<V> { - /** - * A function that accepts a value in the unitlike form's base and returns a - * value in the unitlike form. - * - * @since 2020-09-07 - */ - private final DoubleFunction<V> converterFrom; - - /** - * A function that accepts a value in the unitlike form and returns a value - * in the unitlike form's base. - */ - private final ToDoubleFunction<V> converterTo; - - /** - * Creates the {@code FunctionalUnitlike}. - * - * @param base unitlike form's base - * @param converterFrom function that accepts a value in the unitlike form's - * base and returns a value in the unitlike form. - * @param converterTo function that accepts a value in the unitlike form - * and returns a value in the unitlike form's base. - * @throws NullPointerException if any argument is null - * @since 2019-05-22 - */ - protected FunctionalUnitlike(ObjectProduct<BaseUnit> unitBase, NameSymbol ns, - DoubleFunction<V> converterFrom, ToDoubleFunction<V> converterTo) { - super(unitBase, ns); - this.converterFrom = converterFrom; - this.converterTo = converterTo; - } - - @Override - protected V convertFromBase(double value) { - return this.converterFrom.apply(value); - } - - @Override - protected double convertToBase(V value) { - return this.converterTo.applyAsDouble(value); - } - -} diff --git a/src/org/unitConverter/unit/LinearUnit.java b/src/org/unitConverter/unit/LinearUnit.java deleted file mode 100644 index b7f33d5..0000000 --- a/src/org/unitConverter/unit/LinearUnit.java +++ /dev/null @@ -1,441 +0,0 @@ -/** - * 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -import java.util.Objects; - -import org.unitConverter.math.DecimalComparison; -import org.unitConverter.math.ObjectProduct; -import org.unitConverter.math.UncertainDouble; - -/** - * A unit that can be expressed as a product of its base and a number. For - * example, kilometres, inches and pounds. - * - * @author Adrien Hopkins - * @since 2019-10-16 - */ -public final class LinearUnit extends Unit { - /** - * Gets a {@code LinearUnit} from a unit and a value. For example, converts - * '59 °F' to a linear unit with the value of '288.15 K' - * - * @param unit unit to convert - * @param value value to convert - * @return value expressed as a {@code LinearUnit} - * @since 2019-10-16 - * @throws NullPointerException if unit is null - */ - public static LinearUnit fromUnitValue(final Unit unit, final double value) { - return new LinearUnit( - Objects.requireNonNull(unit, "unit must not be null.").getBase(), - unit.convertToBase(value), NameSymbol.EMPTY); - } - - /** - * Gets a {@code LinearUnit} from a unit and a value. For example, converts - * '59 °F' to a linear unit with the value of '288.15 K' - * - * @param unit unit to convert - * @param value value to convert - * @param ns name(s) and symbol of unit - * @return value expressed as a {@code LinearUnit} - * @since 2019-10-21 - * @throws NullPointerException if unit or ns is null - */ - public static LinearUnit fromUnitValue(final Unit unit, final double value, - final NameSymbol ns) { - return new LinearUnit( - Objects.requireNonNull(unit, "unit must not be null.").getBase(), - unit.convertToBase(value), ns); - } - - /** - * @return the base unit associated with {@code unit}, as a - * {@code LinearUnit}. - * @since 2020-10-02 - */ - public static LinearUnit getBase(final Unit unit) { - return new LinearUnit(unit.getBase(), 1, NameSymbol.EMPTY); - } - - /** - * @return the base unit associated with {@code unitlike}, as a - * {@code LinearUnit}. - * @since 2020-10-02 - */ - public static LinearUnit getBase(final Unitlike<?> unit) { - return new LinearUnit(unit.getBase(), 1, NameSymbol.EMPTY); - } - - /** - * Gets a {@code LinearUnit} from a unit base and a conversion factor. In - * other words, gets the product of {@code unitBase} and - * {@code conversionFactor}, expressed as a {@code LinearUnit}. - * - * @param unitBase unit base to multiply by - * @param conversionFactor number to multiply base by - * @return product of base and conversion factor - * @since 2019-10-16 - * @throws NullPointerException if unitBase is null - */ - public static LinearUnit valueOf(final ObjectProduct<BaseUnit> unitBase, - final double conversionFactor) { - return new LinearUnit(unitBase, conversionFactor, NameSymbol.EMPTY); - } - - /** - * Gets a {@code LinearUnit} from a unit base and a conversion factor. In - * other words, gets the product of {@code unitBase} and - * {@code conversionFactor}, expressed as a {@code LinearUnit}. - * - * @param unitBase unit base to multiply by - * @param conversionFactor number to multiply base by - * @param ns name(s) and symbol of unit - * @return product of base and conversion factor - * @since 2019-10-21 - * @throws NullPointerException if unitBase is null - */ - public static LinearUnit valueOf(final ObjectProduct<BaseUnit> unitBase, - final double conversionFactor, final NameSymbol ns) { - return new LinearUnit(unitBase, conversionFactor, ns); - } - - /** - * The value of this unit as represented in its base form. Mathematically, - * - * <pre> - * this = conversionFactor * getBase() - * </pre> - * - * @since 2019-10-16 - */ - private final double conversionFactor; - - /** - * Creates the {@code LinearUnit}. - * - * @param unitBase base of linear unit - * @param conversionFactor conversion factor between base and unit - * @since 2019-10-16 - */ - private LinearUnit(final ObjectProduct<BaseUnit> unitBase, - final double conversionFactor, final NameSymbol ns) { - super(unitBase, ns); - this.conversionFactor = conversionFactor; - } - - /** - * {@inheritDoc} - * - * Converts by dividing by {@code conversionFactor} - */ - @Override - protected double convertFromBase(final double value) { - return value / this.getConversionFactor(); - } - - /** - * Converts an {@code UncertainDouble} value expressed in this unit to an - * {@code UncertainValue} value expressed in {@code other}. - * - * @param other unit to convert to - * @param value value to convert - * @return converted value - * @since 2019-09-07 - * @throws IllegalArgumentException if {@code other} is incompatible for - * conversion with this unit (as tested by - * {@link Unit#canConvertTo}). - * @throws NullPointerException if value or other is null - */ - public UncertainDouble convertTo(LinearUnit other, UncertainDouble value) { - Objects.requireNonNull(other, "other must not be null."); - Objects.requireNonNull(value, "value may not be null."); - if (this.canConvertTo(other)) - return value.timesExact( - this.getConversionFactor() / other.getConversionFactor()); - else - throw new IllegalArgumentException( - String.format("Cannot convert from %s to %s.", this, other)); - - } - - /** - * {@inheritDoc} - * - * Converts by multiplying by {@code conversionFactor} - */ - @Override - protected double convertToBase(final double value) { - return value * this.getConversionFactor(); - } - - /** - * Converts an {@code UncertainDouble} to the base unit. - * - * @since 2020-09-07 - */ - UncertainDouble convertToBase(final UncertainDouble value) { - return value.timesExact(this.getConversionFactor()); - } - - /** - * Divides this unit by a scalar. - * - * @param divisor scalar to divide by - * @return quotient - * @since 2018-12-23 - * @since v0.1.0 - */ - public LinearUnit dividedBy(final double divisor) { - return valueOf(this.getBase(), this.getConversionFactor() / divisor); - } - - /** - * Returns the quotient of this unit and another. - * - * @param divisor unit to divide by - * @return quotient of two units - * @throws NullPointerException if {@code divisor} is null - * @since 2018-12-22 - * @since v0.1.0 - */ - public LinearUnit dividedBy(final LinearUnit divisor) { - Objects.requireNonNull(divisor, "other must not be null"); - - // divide the units - final ObjectProduct<BaseUnit> base = this.getBase() - .dividedBy(divisor.getBase()); - return valueOf(base, - this.getConversionFactor() / divisor.getConversionFactor()); - } - - /** - * {@inheritDoc} - * - * Uses the base and conversion factor of units to test for equality. - */ - @Override - public boolean equals(final Object obj) { - if (!(obj instanceof LinearUnit)) - return false; - final LinearUnit other = (LinearUnit) obj; - return Objects.equals(this.getBase(), other.getBase()) - && DecimalComparison.equals(this.getConversionFactor(), - other.getConversionFactor()); - } - - /** - * @return conversion factor - * @since 2019-10-16 - */ - public double getConversionFactor() { - return this.conversionFactor; - } - - /** - * {@inheritDoc} - * - * Uses the base and conversion factor to compute a hash code. - */ - @Override - public int hashCode() { - return 31 * this.getBase().hashCode() - + DecimalComparison.hash(this.getConversionFactor()); - } - - /** - * @return whether this unit is equivalent to a {@code BaseUnit} (i.e. there - * is a {@code BaseUnit b} where - * {@code b.asLinearUnit().equals(this)} returns {@code true}.) - * @since 2019-10-16 - */ - public boolean isBase() { - return this.isCoherent() && this.getBase().isSingleObject(); - } - - /** - * @return whether this unit is coherent (i.e. has conversion factor 1) - * @since 2019-10-16 - */ - public boolean isCoherent() { - return this.getConversionFactor() == 1; - } - - /** - * Returns the difference of this unit and another. - * <p> - * Two units can be subtracted if they have the same base. Note that - * {@link #canConvertTo} can be used to determine this. If {@code subtrahend} - * does not meet this condition, an {@code IllegalArgumentException} will be - * thrown. - * </p> - * - * @param subtrahend unit to subtract - * @return difference of units - * @throws IllegalArgumentException if {@code subtrahend} is not compatible - * for subtraction as described above - * @throws NullPointerException if {@code subtrahend} is null - * @since 2019-03-17 - * @since v0.2.0 - */ - public LinearUnit minus(final LinearUnit subtrahend) { - Objects.requireNonNull(subtrahend, "addend must not be null."); - - // reject subtrahends that cannot be added to this unit - if (!this.getBase().equals(subtrahend.getBase())) - throw new IllegalArgumentException(String.format( - "Incompatible units for subtraction \"%s\" and \"%s\".", this, - subtrahend)); - - // subtract the units - return valueOf(this.getBase(), - this.getConversionFactor() - subtrahend.getConversionFactor()); - } - - /** - * Returns the sum of this unit and another. - * <p> - * Two units can be added if they have the same base. Note that - * {@link #canConvertTo} can be used to determine this. If {@code addend} - * does not meet this condition, an {@code IllegalArgumentException} will be - * thrown. - * </p> - * - * @param addend unit to add - * @return sum of units - * @throws IllegalArgumentException if {@code addend} is not compatible for - * addition as described above - * @throws NullPointerException if {@code addend} is null - * @since 2019-03-17 - * @since v0.2.0 - */ - public LinearUnit plus(final LinearUnit addend) { - Objects.requireNonNull(addend, "addend must not be null."); - - // reject addends that cannot be added to this unit - if (!this.getBase().equals(addend.getBase())) - throw new IllegalArgumentException(String.format( - "Incompatible units for addition \"%s\" and \"%s\".", this, - addend)); - - // add the units - return valueOf(this.getBase(), - this.getConversionFactor() + addend.getConversionFactor()); - } - - /** - * Multiplies this unit by a scalar. - * - * @param multiplier scalar to multiply by - * @return product - * @since 2018-12-23 - * @since v0.1.0 - */ - public LinearUnit times(final double multiplier) { - return valueOf(this.getBase(), this.getConversionFactor() * multiplier); - } - - /** - * Returns the product of this unit and another. - * - * @param multiplier unit to multiply by - * @return product of two units - * @throws NullPointerException if {@code multiplier} is null - * @since 2018-12-22 - * @since v0.1.0 - */ - public LinearUnit times(final LinearUnit multiplier) { - Objects.requireNonNull(multiplier, "other must not be null"); - - // multiply the units - final ObjectProduct<BaseUnit> base = this.getBase() - .times(multiplier.getBase()); - return valueOf(base, - this.getConversionFactor() * multiplier.getConversionFactor()); - } - - /** - * Returns this unit but to an exponent. - * - * @param exponent exponent to exponentiate unit to - * @return exponentiated unit - * @since 2019-01-15 - * @since v0.1.0 - */ - public LinearUnit toExponent(final int exponent) { - return valueOf(this.getBase().toExponent(exponent), - Math.pow(this.conversionFactor, exponent)); - } - - /** - * @return a string providing a definition of this unit - * @since 2019-10-21 - */ - @Override - public String toString() { - return this.getPrimaryName().orElse("Unnamed unit") - + (this.getSymbol().isPresent() - ? String.format(" (%s)", this.getSymbol().get()) - : "") - + ", " + Double.toString(this.conversionFactor) + " * " - + this.getBase().toString(u -> u.getSymbol().get()); - } - - @Override - public LinearUnit withName(final NameSymbol ns) { - return valueOf(this.getBase(), this.getConversionFactor(), ns); - } - - /** - * Returns the result of applying {@code prefix} to this unit. - * <p> - * If this unit and the provided prefix have a primary name, the returned - * unit will have a primary name (prefix's name + unit's name). <br> - * If this unit and the provided prefix have a symbol, the returned unit will - * have a symbol. <br> - * This method ignores alternate names of both this unit and the provided - * prefix. - * - * @param prefix prefix to apply - * @return unit with prefix - * @since 2019-03-18 - * @since v0.2.0 - * @throws NullPointerException if prefix is null - */ - public LinearUnit withPrefix(final UnitPrefix prefix) { - final LinearUnit unit = this.times(prefix.getMultiplier()); - - // create new name and symbol, if possible - final String name; - if (this.getPrimaryName().isPresent() - && prefix.getPrimaryName().isPresent()) { - name = prefix.getPrimaryName().get() + this.getPrimaryName().get(); - } else { - name = null; - } - - final String symbol; - if (this.getSymbol().isPresent() && prefix.getSymbol().isPresent()) { - symbol = prefix.getSymbol().get() + this.getSymbol().get(); - } else { - symbol = null; - } - - return unit.withName(NameSymbol.ofNullable(name, symbol)); - } -} diff --git a/src/org/unitConverter/unit/LinearUnitValue.java b/src/org/unitConverter/unit/LinearUnitValue.java deleted file mode 100644 index 8de734e..0000000 --- a/src/org/unitConverter/unit/LinearUnitValue.java +++ /dev/null @@ -1,341 +0,0 @@ -/** - * 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -import java.util.Objects; -import java.util.Optional; - -import org.unitConverter.math.DecimalComparison; -import org.unitConverter.math.UncertainDouble; - -/** - * A possibly uncertain value expressed in a linear 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 LinearUnitValue { - public static final LinearUnitValue ONE = getExact(SI.ONE, 1); - - /** - * Gets an exact {@code LinearUnitValue} - * - * @param unit unit to express with - * @param value value to express - * @return exact {@code LinearUnitValue} instance - * @since 2020-07-26 - */ - public static final LinearUnitValue getExact(final LinearUnit unit, - final double value) { - return new LinearUnitValue( - Objects.requireNonNull(unit, "unit must not be null"), - UncertainDouble.of(value, 0)); - } - - /** - * Gets an uncertain {@code LinearUnitValue} - * - * @param unit unit to express with - * @param value value to express - * @param uncertainty absolute uncertainty of value - * @return uncertain {@code LinearUnitValue} instance - * @since 2020-07-26 - */ - public static final LinearUnitValue of(final LinearUnit unit, - final UncertainDouble value) { - return new LinearUnitValue( - Objects.requireNonNull(unit, "unit must not be null"), - Objects.requireNonNull(value, "value may not be null")); - } - - private final LinearUnit unit; - - private final UncertainDouble value; - - /** - * @param unit unit to express as - * @param value value to express - * @since 2020-07-26 - */ - private LinearUnitValue(final LinearUnit unit, final UncertainDouble value) { - this.unit = unit; - this.value = value; - } - - /** - * @return this value as a {@code UnitValue}. All uncertainty information is - * removed from the returned value. - * @since 2020-08-04 - */ - public final UnitValue asUnitValue() { - return UnitValue.of(this.unit, this.value.value()); - } - - /** - * @param other a {@code LinearUnit} - * @return true iff this value can be represented with {@code other}. - * @since 2020-07-26 - */ - public final boolean canConvertTo(final LinearUnit other) { - return this.unit.canConvertTo(other); - } - - /** - * Returns a LinearUnitValue that represents the same value expressed in a - * different unit - * - * @param other new unit to express value in - * @return value expressed in {@code other} - * @since 2020-07-26 - */ - public final LinearUnitValue convertTo(final LinearUnit other) { - return LinearUnitValue.of(other, this.unit.convertTo(other, this.value)); - } - - /** - * Divides this value by a scalar - * - * @param divisor value to divide by - * @return multiplied value - * @since 2020-07-28 - */ - public LinearUnitValue dividedBy(final double divisor) { - return LinearUnitValue.of(this.unit, this.value.dividedByExact(divisor)); - } - - /** - * Divides this value by another value - * - * @param divisor value to multiply by - * @return quotient - * @since 2020-07-28 - */ - public LinearUnitValue dividedBy(final LinearUnitValue divisor) { - return LinearUnitValue.of(this.unit.dividedBy(divisor.unit), - this.value.dividedBy(divisor.value)); - } - - /** - * 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. - * - * @since 2020-07-26 - * @see #equals(Object, boolean) - */ - @Override - public boolean equals(final Object obj) { - if (!(obj instanceof LinearUnitValue)) - return false; - final LinearUnitValue other = (LinearUnitValue) obj; - return Objects.equals(this.unit.getBase(), other.unit.getBase()) - && this.unit.convertToBase(this.value) - .equals(other.unit.convertToBase(other.value)); - } - - /** - * 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. - * <p> - * If avoidFPErrors is true, this method will attempt to avoid floating-point - * errors, at the cost of not always being transitive. - * - * @since 2020-07-28 - */ - public boolean equals(final Object obj, final boolean avoidFPErrors) { - if (!avoidFPErrors) - return this.equals(obj); - if (!(obj instanceof LinearUnitValue)) - return false; - final LinearUnitValue other = (LinearUnitValue) obj; - return Objects.equals(this.unit.getBase(), other.unit.getBase()) - && DecimalComparison.equals(this.unit.convertToBase(this.value), - other.unit.convertToBase(other.value)); - } - - /** - * @param other another {@code LinearUnitValue} - * @return true iff this and other are within each other's uncertainty range - * - * @since 2020-07-26 - */ - public boolean equivalent(final LinearUnitValue other) { - if (other == null - || !Objects.equals(this.unit.getBase(), other.unit.getBase())) - return false; - final LinearUnit base = LinearUnit.valueOf(this.unit.getBase(), 1); - final LinearUnitValue thisBase = this.convertTo(base); - final LinearUnitValue otherBase = other.convertTo(base); - - return thisBase.value.equivalent(otherBase.value); - } - - /** - * @return the unit - * @since 2020-09-29 - */ - public final LinearUnit getUnit() { - return this.unit; - } - - /** - * @return the value - * @since 2020-09-29 - */ - public final UncertainDouble getValue() { - return this.value; - } - - /** - * @return the exact value - * @since 2020-09-07 - */ - public final double getValueExact() { - return this.value.value(); - } - - @Override - public int hashCode() { - return Objects.hash(this.unit.getBase(), - this.unit.convertToBase(this.getValue())); - } - - /** - * Returns the difference of this value and another, expressed in this - * value's unit - * - * @param subtrahend value to subtract - * @return difference of values - * @throws IllegalArgumentException if {@code subtrahend} has a unit that is - * not compatible for addition - * @since 2020-07-26 - */ - public LinearUnitValue minus(final LinearUnitValue subtrahend) { - Objects.requireNonNull(subtrahend, "subtrahend may not be null"); - - if (!this.canConvertTo(subtrahend.unit)) - throw new IllegalArgumentException(String.format( - "Incompatible units for subtraction \"%s\" and \"%s\".", - this.unit, subtrahend.unit)); - - final LinearUnitValue otherConverted = subtrahend.convertTo(this.unit); - return LinearUnitValue.of(this.unit, - this.value.minus(otherConverted.value)); - } - - /** - * Returns the sum of this value and another, expressed in this value's unit - * - * @param addend value to add - * @return sum of values - * @throws IllegalArgumentException if {@code addend} has a unit that is not - * compatible for addition - * @since 2020-07-26 - */ - public LinearUnitValue plus(final LinearUnitValue addend) { - Objects.requireNonNull(addend, "addend may not be null"); - - if (!this.canConvertTo(addend.unit)) - throw new IllegalArgumentException(String.format( - "Incompatible units for addition \"%s\" and \"%s\".", this.unit, - addend.unit)); - - final LinearUnitValue otherConverted = addend.convertTo(this.unit); - return LinearUnitValue.of(this.unit, - this.value.plus(otherConverted.value)); - } - - /** - * Multiplies this value by a scalar - * - * @param multiplier value to multiply by - * @return multiplied value - * @since 2020-07-28 - */ - public LinearUnitValue times(final double multiplier) { - return LinearUnitValue.of(this.unit, this.value.timesExact(multiplier)); - } - - /** - * Multiplies this value by another value - * - * @param multiplier value to multiply by - * @return product - * @since 2020-07-28 - */ - public LinearUnitValue times(final LinearUnitValue multiplier) { - return LinearUnitValue.of(this.unit.times(multiplier.unit), - this.value.times(multiplier.value)); - } - - /** - * Raises a value to an exponent - * - * @param exponent exponent to raise to - * @return result of exponentiation - * @since 2020-07-28 - */ - public LinearUnitValue toExponent(final int exponent) { - return LinearUnitValue.of(this.unit.toExponent(exponent), - this.value.toExponentExact(exponent)); - } - - @Override - public String toString() { - return this.toString(!this.value.isExact()); - } - - /** - * Returns a string representing the object. <br> - * If the attached unit has a name or symbol, the string looks like "12 km". - * Otherwise, it looks like "13 unnamed unit (= 2 m/s)". - * <p> - * If showUncertainty is true, strings like "35 ± 8" are shown instead of - * single numbers. - * <p> - * Non-exact values are rounded intelligently based on their uncertainty. - * - * @since 2020-07-26 - */ - public String toString(final boolean showUncertainty) { - final Optional<String> primaryName = this.unit.getPrimaryName(); - final Optional<String> symbol = this.unit.getSymbol(); - final String chosenName = symbol.orElse(primaryName.orElse(null)); - - final UncertainDouble baseValue = this.unit.convertToBase(this.value); - - // get rounded strings - // if showUncertainty is true, add brackets around the string - final String valueString = showUncertainty ? "(" - : "" + this.value.toString(showUncertainty) - + (showUncertainty ? ")" : ""); - final String baseValueString = showUncertainty ? "(" - : "" + baseValue.toString(showUncertainty) - + (showUncertainty ? ")" : ""); - - // create string - if (primaryName.isEmpty() && symbol.isEmpty()) - return String.format("%s unnamed unit (= %s %s)", valueString, - baseValueString, this.unit.getBase()); - else - return String.format("%s %s", valueString, chosenName); - } -} diff --git a/src/org/unitConverter/unit/MultiUnit.java b/src/org/unitConverter/unit/MultiUnit.java deleted file mode 100644 index a1623f8..0000000 --- a/src/org/unitConverter/unit/MultiUnit.java +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Copyright (C) 2020 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.unitConverter.math.ObjectProduct; - -/** - * A combination of units, like "5 foot + 7 inch". All but the last units should - * have a whole number value associated with them. - * - * @since 2020-10-02 - */ -public final class MultiUnit extends Unitlike<List<Double>> { - /** - * Creates a {@code MultiUnit} from its units. It will not have a name or - * symbol. - * - * @since 2020-10-03 - */ - public static final MultiUnit of(LinearUnit... units) { - return of(Arrays.asList(units)); - } - - /** - * Creates a {@code MultiUnit} from its units. It will not have a name or - * symbol. - * - * @since 2020-10-03 - */ - public static final MultiUnit of(List<LinearUnit> units) { - if (units.size() < 1) - throw new IllegalArgumentException("Must have at least one unit"); - final ObjectProduct<BaseUnit> unitBase = units.get(0).getBase(); - for (final LinearUnit unit : units) { - if (!unitBase.equals(unit.getBase())) - throw new IllegalArgumentException( - "All units must have the same base."); - } - return new MultiUnit(new ArrayList<>(units), unitBase, NameSymbol.EMPTY); - } - - /** - * The units that make up this value. - */ - private final List<LinearUnit> units; - - /** - * Creates a {@code MultiUnit}. - * - * @since 2020-10-03 - */ - private MultiUnit(List<LinearUnit> units, ObjectProduct<BaseUnit> unitBase, - NameSymbol ns) { - super(unitBase, ns); - this.units = units; - } - - @Override - protected List<Double> convertFromBase(double value) { - final List<Double> values = new ArrayList<>(this.units.size()); - double temp = value; - - for (final LinearUnit unit : this.units.subList(0, - this.units.size() - 1)) { - values.add(Math.floor(temp / unit.getConversionFactor())); - temp %= unit.getConversionFactor(); - } - - values.add(this.units.size() - 1, - this.units.get(this.units.size() - 1).convertFromBase(temp)); - - return values; - } - - /** - * Converts a value expressed in this unitlike form to a value expressed in - * {@code other}. - * - * @implSpec If conversion is possible, this implementation returns - * {@code other.convertFromBase(this.convertToBase(value))}. - * Therefore, overriding either of those methods will change the - * output of this method. - * - * @param other unit to convert to - * @param value value to convert - * @return converted value - * @since 2020-10-03 - * @throws IllegalArgumentException if {@code other} is incompatible for - * conversion with this unitlike form (as - * tested by {@link Unit#canConvertTo}). - * @throws NullPointerException if other is null - */ - public final <U extends Unitlike<V>, V> V convertTo(U other, - double... values) { - final List<Double> valueList = new ArrayList<>(values.length); - for (final double d : values) { - valueList.add(d); - } - - return this.convertTo(other, valueList); - } - - /** - * Converts a value expressed in this unitlike form to a value expressed in - * {@code other}. - * - * @implSpec If conversion is possible, this implementation returns - * {@code other.convertFromBase(this.convertToBase(value))}. - * Therefore, overriding either of those methods will change the - * output of this method. - * - * @param other unit to convert to - * @param value value to convert - * @return converted value - * @since 2020-10-03 - * @throws IllegalArgumentException if {@code other} is incompatible for - * conversion with this unitlike form (as - * tested by {@link Unit#canConvertTo}). - * @throws NullPointerException if other is null - */ - public final double convertTo(Unit other, double... values) { - final List<Double> valueList = new ArrayList<>(values.length); - for (final double d : values) { - valueList.add(d); - } - - return this.convertTo(other, valueList); - } - - @Override - protected double convertToBase(List<Double> value) { - if (value.size() != this.units.size()) - throw new IllegalArgumentException("Wrong number of values for " - + this.units.size() + "-unit MultiUnit."); - - double baseValue = 0; - for (int i = 0; i < this.units.size(); i++) { - baseValue += value.get(i) * this.units.get(i).getConversionFactor(); - } - return baseValue; - } -} diff --git a/src/org/unitConverter/unit/MultiUnitTest.java b/src/org/unitConverter/unit/MultiUnitTest.java deleted file mode 100644 index 5ea9d07..0000000 --- a/src/org/unitConverter/unit/MultiUnitTest.java +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright (C) 2020 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.util.Arrays; -import java.util.List; -import java.util.Random; -import java.util.concurrent.ThreadLocalRandom; - -import org.junit.jupiter.api.Test; - -/** - * Tests related to the {@code MultiUnit}. - * - * @since 2020-10-03 - */ -class MultiUnitTest { - - @Test - final void testConvert() { - final Random rng = ThreadLocalRandom.current(); - final MultiUnit footInch = MultiUnit.of(BritishImperial.Length.FOOT, - BritishImperial.Length.INCH); - - assertEquals(1702.0, footInch.convertTo(SI.METRE.withPrefix(SI.MILLI), - Arrays.asList(5.0, 7.0)), 1.0); - - for (int i = 0; i < 1000; i++) { - final double feet = rng.nextInt(1000); - final double inches = rng.nextDouble() * 12; - final double millimetres = feet * 304.8 + inches * 25.4; - - final List<Double> feetAndInches = SI.METRE.withPrefix(SI.MILLI) - .convertTo(footInch, millimetres); - assertEquals(feet, feetAndInches.get(0), 1e-10); - assertEquals(inches, feetAndInches.get(1), 1e-10); - } - } - - /** - * Test method for - * {@link org.unitConverter.unit.MultiUnit#convertFromBase(double)}. - */ - @Test - final void testConvertFromBase() { - final Random rng = ThreadLocalRandom.current(); - final MultiUnit footInch = MultiUnit.of(BritishImperial.Length.FOOT, - BritishImperial.Length.INCH); - - // 1.7 m =~ 5' + 7" - final List<Double> values = footInch.convertFromBase(1.7018); - - assertEquals(5, values.get(0)); - assertEquals(7, values.get(1), 1e-12); - - for (int i = 0; i < 1000; i++) { - final double feet = rng.nextInt(1000); - final double inches = rng.nextDouble() * 12; - final double metres = feet * 0.3048 + inches * 0.0254; - - final List<Double> feetAndInches = footInch.convertFromBase(metres); - assertEquals(feet, feetAndInches.get(0), 1e-10); - assertEquals(inches, feetAndInches.get(1), 1e-10); - } - } - - /** - * Test method for - * {@link org.unitConverter.unit.MultiUnit#convertToBase(java.util.List)}. - */ - @Test - final void testConvertToBase() { - final Random rng = ThreadLocalRandom.current(); - final MultiUnit footInch = MultiUnit.of(BritishImperial.Length.FOOT, - BritishImperial.Length.INCH); - - // 1.7 m =~ 5' + 7" - assertEquals(1.7018, footInch.convertToBase(Arrays.asList(5.0, 7.0)), - 1e-12); - - for (int i = 0; i < 1000; i++) { - final double feet = rng.nextInt(1000); - final double inches = rng.nextDouble() * 12; - final double metres = feet * 0.3048 + inches * 0.0254; - - assertEquals(metres, - footInch.convertToBase(Arrays.asList(feet, inches)), 1e-12); - } - } -} diff --git a/src/org/unitConverter/unit/NameSymbol.java b/src/org/unitConverter/unit/NameSymbol.java deleted file mode 100644 index 8d8302a..0000000 --- a/src/org/unitConverter/unit/NameSymbol.java +++ /dev/null @@ -1,280 +0,0 @@ -/** - * 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; - -/** - * A class that can be used to specify names and a symbol for a unit. - * - * @author Adrien Hopkins - * @since 2019-10-21 - */ -public final class NameSymbol { - public static final NameSymbol EMPTY = new NameSymbol(Optional.empty(), - Optional.empty(), new HashSet<>()); - - /** - * Creates a {@code NameSymbol}, ensuring that if primaryName is null and - * otherNames is not empty, one name is moved from otherNames to primaryName - * - * Ensure that otherNames is a copy of the inputted argument. - */ - private static final NameSymbol create(final String name, - final String symbol, final Set<String> otherNames) { - final Optional<String> primaryName; - - if (name == null && !otherNames.isEmpty()) { - // get primary name and remove it from savedNames - final Iterator<String> it = otherNames.iterator(); - assert it.hasNext(); - primaryName = Optional.of(it.next()); - otherNames.remove(primaryName.get()); - } else { - primaryName = Optional.ofNullable(name); - } - - return new NameSymbol(primaryName, Optional.ofNullable(symbol), - otherNames); - } - - /** - * Gets a {@code NameSymbol} with a primary name, a symbol and no other - * names. - * - * @param name name to use - * @param symbol symbol to use - * @return NameSymbol instance - * @since 2019-10-21 - * @throws NullPointerException if name or symbol is null - */ - public static final NameSymbol of(final String name, final String symbol) { - return new NameSymbol(Optional.of(name), Optional.of(symbol), - new HashSet<>()); - } - - /** - * Gets a {@code NameSymbol} with a primary name, a symbol and additional - * names. - * - * @param name name to use - * @param symbol symbol to use - * @param otherNames other names to use - * @return NameSymbol instance - * @since 2019-10-21 - * @throws NullPointerException if any argument is null - */ - public static final NameSymbol of(final String name, final String symbol, - final Set<String> otherNames) { - return new NameSymbol(Optional.of(name), Optional.of(symbol), - new HashSet<>(Objects.requireNonNull(otherNames, - "otherNames must not be null."))); - } - - /** - * h * Gets a {@code NameSymbol} with a primary name, a symbol and additional - * names. - * - * @param name name to use - * @param symbol symbol to use - * @param otherNames other names to use - * @return NameSymbol instance - * @since 2019-10-21 - * @throws NullPointerException if any argument is null - */ - public static final NameSymbol of(final String name, final String symbol, - final String... otherNames) { - return new NameSymbol(Optional.of(name), Optional.of(symbol), - new HashSet<>(Arrays.asList(Objects.requireNonNull(otherNames, - "otherNames must not be null.")))); - } - - /** - * Gets a {@code NameSymbol} with a primary name, no symbol, and no other - * names. - * - * @param name name to use - * @return NameSymbol instance - * @since 2019-10-21 - * @throws NullPointerException if name is null - */ - public static final NameSymbol ofName(final String name) { - return new NameSymbol(Optional.of(name), Optional.empty(), - new HashSet<>()); - } - - /** - * Gets a {@code NameSymbol} with a primary name, a symbol and additional - * names. - * <p> - * If any argument is null, this static factory replaces it with an empty - * Optional or empty Set. - * <p> - * If {@code name} is null and {@code otherNames} is not empty, a primary - * name will be picked from {@code otherNames}. This name will not appear in - * getOtherNames(). - * - * @param name name to use - * @param symbol symbol to use - * @param otherNames other names to use - * @return NameSymbol instance - * @since 2019-11-26 - */ - public static final NameSymbol ofNullable(final String name, - final String symbol, final Set<String> otherNames) { - return NameSymbol.create(name, symbol, - otherNames == null ? new HashSet<>() : new HashSet<>(otherNames)); - } - - /** - * h * Gets a {@code NameSymbol} with a primary name, a symbol and additional - * names. - * <p> - * If any argument is null, this static factory replaces it with an empty - * Optional or empty Set. - * <p> - * If {@code name} is null and {@code otherNames} is not empty, a primary - * name will be picked from {@code otherNames}. This name will not appear in - * getOtherNames(). - * - * @param name name to use - * @param symbol symbol to use - * @param otherNames other names to use - * @return NameSymbol instance - * @since 2019-11-26 - */ - public static final NameSymbol ofNullable(final String name, - final String symbol, final String... otherNames) { - return create(name, symbol, otherNames == null ? new HashSet<>() - : new HashSet<>(Arrays.asList(otherNames))); - } - - /** - * Gets a {@code NameSymbol} with a symbol and no names. - * - * @param symbol symbol to use - * @return NameSymbol instance - * @since 2019-10-21 - * @throws NullPointerException if symbol is null - */ - public static final NameSymbol ofSymbol(final String symbol) { - return new NameSymbol(Optional.empty(), Optional.of(symbol), - new HashSet<>()); - } - - private final Optional<String> primaryName; - private final Optional<String> symbol; - - private final Set<String> otherNames; - - /** - * Creates the {@code NameSymbol}. - * - * @param primaryName primary name of unit - * @param symbol symbol used to represent unit - * @param otherNames other names and/or spellings, should be a mutable copy - * of the argument - * @since 2019-10-21 - */ - private NameSymbol(final Optional<String> primaryName, - final Optional<String> symbol, final Set<String> otherNames) { - this.primaryName = primaryName; - this.symbol = symbol; - otherNames.remove(null); - this.otherNames = Collections.unmodifiableSet(otherNames); - - if (this.primaryName.isEmpty()) { - assert this.otherNames.isEmpty(); - } - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!(obj instanceof NameSymbol)) - return false; - final NameSymbol other = (NameSymbol) obj; - if (this.otherNames == null) { - if (other.otherNames != null) - return false; - } else if (!this.otherNames.equals(other.otherNames)) - return false; - if (this.primaryName == null) { - if (other.primaryName != null) - return false; - } else if (!this.primaryName.equals(other.primaryName)) - return false; - if (this.symbol == null) { - if (other.symbol != null) - return false; - } else if (!this.symbol.equals(other.symbol)) - return false; - return true; - } - - /** - * @return otherNames - * @since 2019-10-21 - */ - public final Set<String> getOtherNames() { - return this.otherNames; - } - - /** - * @return primaryName - * @since 2019-10-21 - */ - public final Optional<String> getPrimaryName() { - return this.primaryName; - } - - /** - * @return symbol - * @since 2019-10-21 - */ - public final Optional<String> getSymbol() { - return this.symbol; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + (this.otherNames == null ? 0 : this.otherNames.hashCode()); - result = prime * result - + (this.primaryName == null ? 0 : this.primaryName.hashCode()); - result = prime * result - + (this.symbol == null ? 0 : this.symbol.hashCode()); - return result; - } - - /** - * @return true iff this {@code NameSymbol} contains no names or symbols. - */ - public final boolean isEmpty() { - // if primaryName is empty, otherNames must also be empty - return this.primaryName.isEmpty() && this.symbol.isEmpty(); - } -}
\ No newline at end of file diff --git a/src/org/unitConverter/unit/Nameable.java b/src/org/unitConverter/unit/Nameable.java deleted file mode 100644 index 36740ab..0000000 --- a/src/org/unitConverter/unit/Nameable.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (C) 2020 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -import java.util.Optional; -import java.util.Set; - -/** - * An object that can hold one or more names, and possibly a symbol. The name - * and symbol data should be immutable. - * - * @since 2020-09-07 - */ -public interface Nameable { - /** - * @return a {@code NameSymbol} that contains this object's primary name, - * symbol and other names - * @since 2020-09-07 - */ - NameSymbol getNameSymbol(); - - /** - * @return set of alternate names - * @since 2020-09-07 - */ - default Set<String> getOtherNames() { - return this.getNameSymbol().getOtherNames(); - } - - /** - * @return preferred name of object - * @since 2020-09-07 - */ - default Optional<String> getPrimaryName() { - return this.getNameSymbol().getPrimaryName(); - } - - /** - * @return short symbol representing object - * @since 2020-09-07 - */ - default Optional<String> getSymbol() { - return this.getNameSymbol().getSymbol(); - } -} diff --git a/src/org/unitConverter/unit/SI.java b/src/org/unitConverter/unit/SI.java deleted file mode 100644 index c88d2bc..0000000 --- a/src/org/unitConverter/unit/SI.java +++ /dev/null @@ -1,479 +0,0 @@ -/**
- * Copyright (C) 2018 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 <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import java.util.Set;
-
-import org.unitConverter.math.ObjectProduct;
-
-/**
- * All of the units, prefixes and dimensions that are used by the SI, as well as
- * some outside the SI.
- *
- * <p>
- * This class does not include prefixed units. To obtain prefixed units, use
- * {@link LinearUnit#withPrefix}:
- *
- * <pre>
- * LinearUnit KILOMETRE = SI.METRE.withPrefix(SI.KILO);
- * </pre>
- *
- *
- * @author Adrien Hopkins
- * @since 2019-10-16
- */
-public final class SI {
- /// dimensions used by SI units
- // base dimensions, as BaseDimensions
- public static final class BaseDimensions {
- public static final BaseDimension LENGTH = BaseDimension.valueOf("Length",
- "L");
- public static final BaseDimension MASS = BaseDimension.valueOf("Mass",
- "M");
- public static final BaseDimension TIME = BaseDimension.valueOf("Time",
- "T");
- public static final BaseDimension ELECTRIC_CURRENT = BaseDimension
- .valueOf("Electric Current", "I");
- public static final BaseDimension TEMPERATURE = BaseDimension
- .valueOf("Temperature", "\u0398"); // theta symbol
- public static final BaseDimension QUANTITY = BaseDimension
- .valueOf("Quantity", "N");
- public static final BaseDimension LUMINOUS_INTENSITY = BaseDimension
- .valueOf("Luminous Intensity", "J");
- public static final BaseDimension INFORMATION = BaseDimension
- .valueOf("Information", "Info"); // non-SI
- public static final BaseDimension CURRENCY = BaseDimension
- .valueOf("Currency", "$$"); // non-SI
-
- // You may NOT get SI.BaseDimensions instances!
- private BaseDimensions() {
- throw new AssertionError();
- }
- }
-
- /// base units of the SI
- // suppressing warnings since these are the same object, but in a different
- /// form (class)
- @SuppressWarnings("hiding")
- public static final class BaseUnits {
- public static final BaseUnit METRE = BaseUnit
- .valueOf(BaseDimensions.LENGTH, "metre", "m");
- public static final BaseUnit KILOGRAM = BaseUnit
- .valueOf(BaseDimensions.MASS, "kilogram", "kg");
- public static final BaseUnit SECOND = BaseUnit
- .valueOf(BaseDimensions.TIME, "second", "s");
- public static final BaseUnit AMPERE = BaseUnit
- .valueOf(BaseDimensions.ELECTRIC_CURRENT, "ampere", "A");
- public static final BaseUnit KELVIN = BaseUnit
- .valueOf(BaseDimensions.TEMPERATURE, "kelvin", "K");
- public static final BaseUnit MOLE = BaseUnit
- .valueOf(BaseDimensions.QUANTITY, "mole", "mol");
- public static final BaseUnit CANDELA = BaseUnit
- .valueOf(BaseDimensions.LUMINOUS_INTENSITY, "candela", "cd");
- public static final BaseUnit BIT = BaseUnit
- .valueOf(BaseDimensions.INFORMATION, "bit", "b");
- public static final BaseUnit DOLLAR = BaseUnit
- .valueOf(BaseDimensions.CURRENCY, "dollar", "$");
-
- public static final Set<BaseUnit> BASE_UNITS = Set.of(METRE, KILOGRAM,
- SECOND, AMPERE, KELVIN, MOLE, CANDELA, BIT);
-
- // You may NOT get SI.BaseUnits instances!
- private BaseUnits() {
- throw new AssertionError();
- }
- }
-
- /**
- * Constants that relate to the SI or other systems.
- *
- * @author Adrien Hopkins
- * @since 2019-11-08
- */
- public static final class Constants {
- public static final LinearUnit EARTH_GRAVITY = METRE.dividedBy(SECOND)
- .dividedBy(SECOND).times(9.80665);
- }
-
- // dimensions used in the SI, as ObjectProducts
- public static final class Dimensions {
- public static final ObjectProduct<BaseDimension> EMPTY = ObjectProduct
- .empty();
- public static final ObjectProduct<BaseDimension> LENGTH = ObjectProduct
- .oneOf(BaseDimensions.LENGTH);
- public static final ObjectProduct<BaseDimension> MASS = ObjectProduct
- .oneOf(BaseDimensions.MASS);
- public static final ObjectProduct<BaseDimension> TIME = ObjectProduct
- .oneOf(BaseDimensions.TIME);
- public static final ObjectProduct<BaseDimension> ELECTRIC_CURRENT = ObjectProduct
- .oneOf(BaseDimensions.ELECTRIC_CURRENT);
- public static final ObjectProduct<BaseDimension> TEMPERATURE = ObjectProduct
- .oneOf(BaseDimensions.TEMPERATURE);
- public static final ObjectProduct<BaseDimension> QUANTITY = ObjectProduct
- .oneOf(BaseDimensions.QUANTITY);
- public static final ObjectProduct<BaseDimension> LUMINOUS_INTENSITY = ObjectProduct
- .oneOf(BaseDimensions.LUMINOUS_INTENSITY);
- public static final ObjectProduct<BaseDimension> INFORMATION = ObjectProduct
- .oneOf(BaseDimensions.INFORMATION);
- public static final ObjectProduct<BaseDimension> CURRENCY = ObjectProduct
- .oneOf(BaseDimensions.CURRENCY);
-
- // derived dimensions without named SI units
- public static final ObjectProduct<BaseDimension> AREA = LENGTH
- .times(LENGTH);
- public static final ObjectProduct<BaseDimension> VOLUME = AREA
- .times(LENGTH);
- public static final ObjectProduct<BaseDimension> VELOCITY = LENGTH
- .dividedBy(TIME);
- public static final ObjectProduct<BaseDimension> ACCELERATION = VELOCITY
- .dividedBy(TIME);
- public static final ObjectProduct<BaseDimension> WAVENUMBER = EMPTY
- .dividedBy(LENGTH);
- public static final ObjectProduct<BaseDimension> MASS_DENSITY = MASS
- .dividedBy(VOLUME);
- public static final ObjectProduct<BaseDimension> SURFACE_DENSITY = MASS
- .dividedBy(AREA);
- public static final ObjectProduct<BaseDimension> SPECIFIC_VOLUME = VOLUME
- .dividedBy(MASS);
- public static final ObjectProduct<BaseDimension> CURRENT_DENSITY = ELECTRIC_CURRENT
- .dividedBy(AREA);
- public static final ObjectProduct<BaseDimension> MAGNETIC_FIELD_STRENGTH = ELECTRIC_CURRENT
- .dividedBy(LENGTH);
- public static final ObjectProduct<BaseDimension> CONCENTRATION = QUANTITY
- .dividedBy(VOLUME);
- public static final ObjectProduct<BaseDimension> MASS_CONCENTRATION = CONCENTRATION
- .times(MASS);
- public static final ObjectProduct<BaseDimension> LUMINANCE = LUMINOUS_INTENSITY
- .dividedBy(AREA);
- public static final ObjectProduct<BaseDimension> REFRACTIVE_INDEX = VELOCITY
- .dividedBy(VELOCITY);
- public static final ObjectProduct<BaseDimension> REFRACTIVE_PERMEABILITY = EMPTY
- .times(EMPTY);
- public static final ObjectProduct<BaseDimension> ANGLE = LENGTH
- .dividedBy(LENGTH);
- public static final ObjectProduct<BaseDimension> SOLID_ANGLE = AREA
- .dividedBy(AREA);
-
- // derived dimensions with named SI units
- public static final ObjectProduct<BaseDimension> FREQUENCY = EMPTY
- .dividedBy(TIME);
- public static final ObjectProduct<BaseDimension> FORCE = MASS
- .times(ACCELERATION);
- public static final ObjectProduct<BaseDimension> ENERGY = FORCE
- .times(LENGTH);
- public static final ObjectProduct<BaseDimension> POWER = ENERGY
- .dividedBy(TIME);
- public static final ObjectProduct<BaseDimension> ELECTRIC_CHARGE = ELECTRIC_CURRENT
- .times(TIME);
- public static final ObjectProduct<BaseDimension> VOLTAGE = ENERGY
- .dividedBy(ELECTRIC_CHARGE);
- public static final ObjectProduct<BaseDimension> CAPACITANCE = ELECTRIC_CHARGE
- .dividedBy(VOLTAGE);
- public static final ObjectProduct<BaseDimension> ELECTRIC_RESISTANCE = VOLTAGE
- .dividedBy(ELECTRIC_CURRENT);
- public static final ObjectProduct<BaseDimension> ELECTRIC_CONDUCTANCE = ELECTRIC_CURRENT
- .dividedBy(VOLTAGE);
- public static final ObjectProduct<BaseDimension> MAGNETIC_FLUX = VOLTAGE
- .times(TIME);
- public static final ObjectProduct<BaseDimension> MAGNETIC_FLUX_DENSITY = MAGNETIC_FLUX
- .dividedBy(AREA);
- public static final ObjectProduct<BaseDimension> INDUCTANCE = MAGNETIC_FLUX
- .dividedBy(ELECTRIC_CURRENT);
- public static final ObjectProduct<BaseDimension> LUMINOUS_FLUX = LUMINOUS_INTENSITY
- .times(SOLID_ANGLE);
- public static final ObjectProduct<BaseDimension> ILLUMINANCE = LUMINOUS_FLUX
- .dividedBy(AREA);
- public static final ObjectProduct<BaseDimension> SPECIFIC_ENERGY = ENERGY
- .dividedBy(MASS);
- public static final ObjectProduct<BaseDimension> CATALYTIC_ACTIVITY = QUANTITY
- .dividedBy(TIME);
-
- // You may NOT get SI.Dimension instances!
- private Dimensions() {
- throw new AssertionError();
- }
- }
-
- /// The units of the SI
- public static final LinearUnit ONE = LinearUnit
- .valueOf(ObjectProduct.empty(), 1);
-
- public static final LinearUnit METRE = BaseUnits.METRE.asLinearUnit()
- .withName(NameSymbol.of("metre", "m", "meter"));
- public static final LinearUnit KILOGRAM = BaseUnits.KILOGRAM.asLinearUnit()
- .withName(NameSymbol.of("kilogram", "kg"));
- public static final LinearUnit SECOND = BaseUnits.SECOND.asLinearUnit()
- .withName(NameSymbol.of("second", "s", "sec"));
- public static final LinearUnit AMPERE = BaseUnits.AMPERE.asLinearUnit()
- .withName(NameSymbol.of("ampere", "A"));
- public static final LinearUnit KELVIN = BaseUnits.KELVIN.asLinearUnit()
- .withName(NameSymbol.of("kelvin", "K"));
- public static final LinearUnit MOLE = BaseUnits.MOLE.asLinearUnit()
- .withName(NameSymbol.of("mole", "mol"));
- public static final LinearUnit CANDELA = BaseUnits.CANDELA.asLinearUnit()
- .withName(NameSymbol.of("candela", "cd"));
- public static final LinearUnit BIT = BaseUnits.BIT.asLinearUnit()
- .withName(NameSymbol.of("bit", "b"));
- public static final LinearUnit DOLLAR = BaseUnits.DOLLAR.asLinearUnit()
- .withName(NameSymbol.of("dollar", "$"));
- // Non-base units
- public static final LinearUnit RADIAN = METRE.dividedBy(METRE)
- .withName(NameSymbol.of("radian", "rad"));
-
- public static final LinearUnit STERADIAN = RADIAN.times(RADIAN)
- .withName(NameSymbol.of("steradian", "sr"));
- public static final LinearUnit HERTZ = ONE.dividedBy(SECOND)
- .withName(NameSymbol.of("hertz", "Hz"));
- // for periodic phenomena
- public static final LinearUnit NEWTON = KILOGRAM.times(METRE)
- .dividedBy(SECOND.times(SECOND))
- .withName(NameSymbol.of("newton", "N"));
- public static final LinearUnit PASCAL = NEWTON.dividedBy(METRE.times(METRE))
- .withName(NameSymbol.of("pascal", "Pa"));
- public static final LinearUnit JOULE = NEWTON.times(METRE)
- .withName(NameSymbol.of("joule", "J"));
- public static final LinearUnit WATT = JOULE.dividedBy(SECOND)
- .withName(NameSymbol.of("watt", "W"));
- public static final LinearUnit COULOMB = AMPERE.times(SECOND)
- .withName(NameSymbol.of("coulomb", "C"));
- public static final LinearUnit VOLT = JOULE.dividedBy(COULOMB)
- .withName(NameSymbol.of("volt", "V"));
- public static final LinearUnit FARAD = COULOMB.dividedBy(VOLT)
- .withName(NameSymbol.of("farad", "F"));
- public static final LinearUnit OHM = VOLT.dividedBy(AMPERE)
- .withName(NameSymbol.of("ohm", "\u03A9")); // omega
- public static final LinearUnit SIEMENS = ONE.dividedBy(OHM)
- .withName(NameSymbol.of("siemens", "S"));
- public static final LinearUnit WEBER = VOLT.times(SECOND)
- .withName(NameSymbol.of("weber", "Wb"));
- public static final LinearUnit TESLA = WEBER.dividedBy(METRE.times(METRE))
- .withName(NameSymbol.of("tesla", "T"));
- public static final LinearUnit HENRY = WEBER.dividedBy(AMPERE)
- .withName(NameSymbol.of("henry", "H"));
- public static final LinearUnit LUMEN = CANDELA.times(STERADIAN)
- .withName(NameSymbol.of("lumen", "lm"));
- public static final LinearUnit LUX = LUMEN.dividedBy(METRE.times(METRE))
- .withName(NameSymbol.of("lux", "lx"));
- public static final LinearUnit BEQUEREL = ONE.dividedBy(SECOND)
- .withName(NameSymbol.of("bequerel", "Bq"));
- // for activity referred to a nucleotide
- public static final LinearUnit GRAY = JOULE.dividedBy(KILOGRAM)
- .withName(NameSymbol.of("grey", "Gy"));
- // for absorbed dose
- public static final LinearUnit SIEVERT = JOULE.dividedBy(KILOGRAM)
- .withName(NameSymbol.of("sievert", "Sv"));
- // for dose equivalent
- public static final LinearUnit KATAL = MOLE.dividedBy(SECOND)
- .withName(NameSymbol.of("katal", "kat"));
- // common derived units included for convenience
- public static final LinearUnit GRAM = KILOGRAM.dividedBy(1000)
- .withName(NameSymbol.of("gram", "g"));
-
- public static final LinearUnit SQUARE_METRE = METRE.toExponent(2)
- .withName(NameSymbol.of("square metre", "m^2", "square meter",
- "metre squared", "meter squared"));
- public static final LinearUnit CUBIC_METRE = METRE.toExponent(3)
- .withName(NameSymbol.of("cubic metre", "m^3", "cubic meter",
- "metre cubed", "meter cubed"));
- public static final LinearUnit METRE_PER_SECOND = METRE.dividedBy(SECOND)
- .withName(
- NameSymbol.of("metre per second", "m/s", "meter per second"));
- // Non-SI units included for convenience
- public static final Unit CELSIUS = Unit
- .fromConversionFunctions(KELVIN.getBase(), tempK -> tempK - 273.15,
- tempC -> tempC + 273.15)
- .withName(NameSymbol.of("degree Celsius", "\u00B0C"));
-
- public static final LinearUnit MINUTE = SECOND.times(60)
- .withName(NameSymbol.of("minute", "min"));
- public static final LinearUnit HOUR = MINUTE.times(60)
- .withName(NameSymbol.of("hour", "h", "hr"));
- public static final LinearUnit DAY = HOUR.times(60)
- .withName(NameSymbol.of("day", "d"));
- public static final LinearUnit KILOMETRE_PER_HOUR = METRE.times(1000)
- .dividedBy(HOUR).withName(NameSymbol.of("kilometre per hour", "km/h",
- "kilometer per hour"));
- public static final LinearUnit DEGREE = RADIAN.times(360 / (2 * Math.PI))
- .withName(NameSymbol.of("degree", "\u00B0", "deg"));
- public static final LinearUnit ARCMINUTE = DEGREE.dividedBy(60)
- .withName(NameSymbol.of("arcminute", "arcmin"));
- public static final LinearUnit ARCSECOND = ARCMINUTE.dividedBy(60)
- .withName(NameSymbol.of("arcsecond", "arcsec"));
- public static final LinearUnit ASTRONOMICAL_UNIT = METRE
- .times(149597870700.0)
- .withName(NameSymbol.of("astronomical unit", "au"));
- public static final LinearUnit PARSEC = ASTRONOMICAL_UNIT
- .dividedBy(ARCSECOND).withName(NameSymbol.of("parsec", "pc"));
- public static final LinearUnit HECTARE = METRE.times(METRE).times(10000.0)
- .withName(NameSymbol.of("hectare", "ha"));
- public static final LinearUnit LITRE = METRE.times(METRE).times(METRE)
- .dividedBy(1000.0).withName(NameSymbol.of("litre", "L", "l", "liter"));
- public static final LinearUnit TONNE = KILOGRAM.times(1000.0)
- .withName(NameSymbol.of("tonne", "t", "metric ton"));
- public static final LinearUnit DALTON = KILOGRAM.times(1.660539040e-27)
- .withName(NameSymbol.of("dalton", "Da", "atomic unit", "u")); // approximate
- // value
- public static final LinearUnit ELECTRONVOLT = JOULE.times(1.602176634e-19)
- .withName(NameSymbol.of("electron volt", "eV"));
- public static final LinearUnit BYTE = BIT.times(8)
- .withName(NameSymbol.of("byte", "B"));
- public static final Unit NEPER = Unit.fromConversionFunctions(ONE.getBase(),
- pr -> 0.5 * Math.log(pr), Np -> Math.exp(2 * Np))
- .withName(NameSymbol.of("neper", "Np"));
- public static final Unit BEL = Unit.fromConversionFunctions(ONE.getBase(),
- pr -> Math.log10(pr), dB -> Math.pow(10, dB))
- .withName(NameSymbol.of("bel", "B"));
- public static final Unit DECIBEL = Unit
- .fromConversionFunctions(ONE.getBase(), pr -> 10 * Math.log10(pr),
- dB -> Math.pow(10, dB / 10))
- .withName(NameSymbol.of("decibel", "dB"));
-
- /// The prefixes of the SI
- // expanding decimal prefixes
- public static final UnitPrefix KILO = UnitPrefix.valueOf(1e3)
- .withName(NameSymbol.of("kilo", "k", "K"));
- public static final UnitPrefix MEGA = UnitPrefix.valueOf(1e6)
- .withName(NameSymbol.of("mega", "M"));
- public static final UnitPrefix GIGA = UnitPrefix.valueOf(1e9)
- .withName(NameSymbol.of("giga", "G"));
- public static final UnitPrefix TERA = UnitPrefix.valueOf(1e12)
- .withName(NameSymbol.of("tera", "T"));
- public static final UnitPrefix PETA = UnitPrefix.valueOf(1e15)
- .withName(NameSymbol.of("peta", "P"));
- public static final UnitPrefix EXA = UnitPrefix.valueOf(1e18)
- .withName(NameSymbol.of("exa", "E"));
- public static final UnitPrefix ZETTA = UnitPrefix.valueOf(1e21)
- .withName(NameSymbol.of("zetta", "Z"));
- public static final UnitPrefix YOTTA = UnitPrefix.valueOf(1e24)
- .withName(NameSymbol.of("yotta", "Y"));
-
- // contracting decimal prefixes
- public static final UnitPrefix MILLI = UnitPrefix.valueOf(1e-3)
- .withName(NameSymbol.of("milli", "m"));
- public static final UnitPrefix MICRO = UnitPrefix.valueOf(1e-6)
- .withName(NameSymbol.of("micro", "\u03BC", "u")); // mu
- public static final UnitPrefix NANO = UnitPrefix.valueOf(1e-9)
- .withName(NameSymbol.of("nano", "n"));
- public static final UnitPrefix PICO = UnitPrefix.valueOf(1e-12)
- .withName(NameSymbol.of("pico", "p"));
- public static final UnitPrefix FEMTO = UnitPrefix.valueOf(1e-15)
- .withName(NameSymbol.of("femto", "f"));
- public static final UnitPrefix ATTO = UnitPrefix.valueOf(1e-18)
- .withName(NameSymbol.of("atto", "a"));
- public static final UnitPrefix ZEPTO = UnitPrefix.valueOf(1e-21)
- .withName(NameSymbol.of("zepto", "z"));
- public static final UnitPrefix YOCTO = UnitPrefix.valueOf(1e-24)
- .withName(NameSymbol.of("yocto", "y"));
-
- // prefixes that don't match the pattern of thousands
- public static final UnitPrefix DEKA = UnitPrefix.valueOf(1e1)
- .withName(NameSymbol.of("deka", "da", "deca", "D"));
- public static final UnitPrefix HECTO = UnitPrefix.valueOf(1e2)
- .withName(NameSymbol.of("hecto", "h", "H", "hekto"));
- public static final UnitPrefix DECI = UnitPrefix.valueOf(1e-1)
- .withName(NameSymbol.of("deci", "d"));
- public static final UnitPrefix CENTI = UnitPrefix.valueOf(1e-2)
- .withName(NameSymbol.of("centi", "c"));
- public static final UnitPrefix KIBI = UnitPrefix.valueOf(1024)
- .withName(NameSymbol.of("kibi", "Ki"));
- public static final UnitPrefix MEBI = KIBI.times(1024)
- .withName(NameSymbol.of("mebi", "Mi"));
- public static final UnitPrefix GIBI = MEBI.times(1024)
- .withName(NameSymbol.of("gibi", "Gi"));
- public static final UnitPrefix TEBI = GIBI.times(1024)
- .withName(NameSymbol.of("tebi", "Ti"));
- public static final UnitPrefix PEBI = TEBI.times(1024)
- .withName(NameSymbol.of("pebi", "Pi"));
- public static final UnitPrefix EXBI = PEBI.times(1024)
- .withName(NameSymbol.of("exbi", "Ei"));
-
- // a few prefixed units
- public static final LinearUnit MICROMETRE = SI.METRE.withPrefix(SI.MICRO);
- public static final LinearUnit MILLIMETRE = SI.METRE.withPrefix(SI.MILLI);
- public static final LinearUnit KILOMETRE = SI.METRE.withPrefix(SI.KILO);
- public static final LinearUnit MEGAMETRE = SI.METRE.withPrefix(SI.MEGA);
-
- public static final LinearUnit MICROLITRE = SI.LITRE.withPrefix(SI.MICRO);
- public static final LinearUnit MILLILITRE = SI.LITRE.withPrefix(SI.MILLI);
- public static final LinearUnit KILOLITRE = SI.LITRE.withPrefix(SI.KILO);
- public static final LinearUnit MEGALITRE = SI.LITRE.withPrefix(SI.MEGA);
-
- public static final LinearUnit MICROSECOND = SI.SECOND.withPrefix(SI.MICRO);
- public static final LinearUnit MILLISECOND = SI.SECOND.withPrefix(SI.MILLI);
- public static final LinearUnit KILOSECOND = SI.SECOND.withPrefix(SI.KILO);
- public static final LinearUnit MEGASECOND = SI.SECOND.withPrefix(SI.MEGA);
-
- public static final LinearUnit MICROGRAM = SI.GRAM.withPrefix(SI.MICRO);
- public static final LinearUnit MILLIGRAM = SI.GRAM.withPrefix(SI.MILLI);
- public static final LinearUnit MEGAGRAM = SI.GRAM.withPrefix(SI.MEGA);
-
- public static final LinearUnit MICRONEWTON = SI.NEWTON.withPrefix(SI.MICRO);
- public static final LinearUnit MILLINEWTON = SI.NEWTON.withPrefix(SI.MILLI);
- public static final LinearUnit KILONEWTON = SI.NEWTON.withPrefix(SI.KILO);
- public static final LinearUnit MEGANEWTON = SI.NEWTON.withPrefix(SI.MEGA);
-
- public static final LinearUnit MICROJOULE = SI.JOULE.withPrefix(SI.MICRO);
- public static final LinearUnit MILLIJOULE = SI.JOULE.withPrefix(SI.MILLI);
- public static final LinearUnit KILOJOULE = SI.JOULE.withPrefix(SI.KILO);
- public static final LinearUnit MEGAJOULE = SI.JOULE.withPrefix(SI.MEGA);
-
- public static final LinearUnit MICROWATT = SI.WATT.withPrefix(SI.MICRO);
- public static final LinearUnit MILLIWATT = SI.WATT.withPrefix(SI.MILLI);
- public static final LinearUnit KILOWATT = SI.WATT.withPrefix(SI.KILO);
- public static final LinearUnit MEGAWATT = SI.WATT.withPrefix(SI.MEGA);
-
- public static final LinearUnit MICROCOULOMB = SI.COULOMB
- .withPrefix(SI.MICRO);
- public static final LinearUnit MILLICOULOMB = SI.COULOMB
- .withPrefix(SI.MILLI);
- public static final LinearUnit KILOCOULOMB = SI.COULOMB.withPrefix(SI.KILO);
- public static final LinearUnit MEGACOULOMB = SI.COULOMB.withPrefix(SI.MEGA);
-
- public static final LinearUnit MICROAMPERE = SI.AMPERE.withPrefix(SI.MICRO);
- public static final LinearUnit MILLIAMPERE = SI.AMPERE.withPrefix(SI.MILLI);
-
- public static final LinearUnit MICROVOLT = SI.VOLT.withPrefix(SI.MICRO);
- public static final LinearUnit MILLIVOLT = SI.VOLT.withPrefix(SI.MILLI);
- public static final LinearUnit KILOVOLT = SI.VOLT.withPrefix(SI.KILO);
- public static final LinearUnit MEGAVOLT = SI.VOLT.withPrefix(SI.MEGA);
-
- public static final LinearUnit KILOOHM = SI.OHM.withPrefix(SI.KILO);
- public static final LinearUnit MEGAOHM = SI.OHM.withPrefix(SI.MEGA);
-
- // sets of prefixes
- public static final Set<UnitPrefix> ALL_PREFIXES = Set.of(DEKA, HECTO, KILO,
- MEGA, GIGA, TERA, PETA, EXA, ZETTA, YOTTA, DECI, CENTI, MILLI, MICRO,
- NANO, PICO, FEMTO, ATTO, ZEPTO, YOCTO, KIBI, MEBI, GIBI, TEBI, PEBI,
- EXBI);
-
- public static final Set<UnitPrefix> DECIMAL_PREFIXES = Set.of(DEKA, HECTO,
- KILO, MEGA, GIGA, TERA, PETA, EXA, ZETTA, YOTTA, DECI, CENTI, MILLI,
- MICRO, NANO, PICO, FEMTO, ATTO, ZEPTO, YOCTO);
- public static final Set<UnitPrefix> THOUSAND_PREFIXES = Set.of(KILO, MEGA,
- GIGA, TERA, PETA, EXA, ZETTA, YOTTA, MILLI, MICRO, NANO, PICO, FEMTO,
- ATTO, ZEPTO, YOCTO);
- public static final Set<UnitPrefix> MAGNIFYING_PREFIXES = Set.of(DEKA, HECTO,
- KILO, MEGA, GIGA, TERA, PETA, EXA, ZETTA, YOTTA, KIBI, MEBI, GIBI,
- TEBI, PEBI, EXBI);
- public static final Set<UnitPrefix> REDUCING_PREFIXES = Set.of(DECI, CENTI,
- MILLI, MICRO, NANO, PICO, FEMTO, ATTO, ZEPTO, YOCTO);
-
- // You may NOT get SI instances!
- private SI() {
- throw new AssertionError();
- }
-}
diff --git a/src/org/unitConverter/unit/USCustomary.java b/src/org/unitConverter/unit/USCustomary.java deleted file mode 100644 index 1c4bcfe..0000000 --- a/src/org/unitConverter/unit/USCustomary.java +++ /dev/null @@ -1,135 +0,0 @@ -/** - * 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -/** - * A static utility class that contains units in the US Customary system. - * - * @author Adrien Hopkins - * @since 2019-10-21 - */ -public final class USCustomary { - /** - * US Customary units that measure area - * - * @author Adrien Hopkins - * @since 2019-11-08 - */ - public static final class Area { - public static final LinearUnit SQUARE_SURVEY_FOOT = Length.SURVEY_FOOT.times(Length.SURVEY_FOOT); - public static final LinearUnit SQUARE_CHAIN = Length.SURVEY_CHAIN.times(Length.SURVEY_CHAIN); - public static final LinearUnit ACRE = Length.SURVEY_CHAIN.times(Length.SURVEY_FURLONG); - public static final LinearUnit SECTION = Length.SURVEY_MILE.times(Length.SURVEY_MILE); - public static final LinearUnit SURVEY_TOWNSHIP = SECTION.times(36); - } - - /** - * US Customary units that measure length - * - * @author Adrien Hopkins - * @since 2019-10-28 - */ - public static final class Length { - public static final LinearUnit FOOT = BritishImperial.Length.FOOT; - public static final LinearUnit INCH = BritishImperial.Length.INCH; - public static final LinearUnit HAND = INCH.times(4); - public static final LinearUnit PICA = INCH.dividedBy(6); - public static final LinearUnit POINT = PICA.dividedBy(12); - public static final LinearUnit YARD = BritishImperial.Length.YARD; - public static final LinearUnit MILE = BritishImperial.Length.MILE; - - public static final LinearUnit SURVEY_FOOT = SI.METRE.times(1200.0 / 3937.0); - public static final LinearUnit SURVEY_LINK = SURVEY_FOOT.times(33.0 / 50.0); - public static final LinearUnit SURVEY_ROD = SURVEY_FOOT.times(16.5); - public static final LinearUnit SURVEY_CHAIN = SURVEY_ROD.times(4); - public static final LinearUnit SURVEY_FURLONG = SURVEY_CHAIN.times(10); - public static final LinearUnit SURVEY_MILE = SURVEY_FURLONG.times(8); - public static final LinearUnit SURVEY_LEAGUE = SURVEY_MILE.times(3); - - public static final LinearUnit NAUTICAL_MILE = BritishImperial.Length.NAUTICAL_MILE; - public static final LinearUnit FATHOM = YARD.times(2); - public static final LinearUnit CABLE = FATHOM.times(120); - } - - /** - * mass units - * - * @author Adrien Hopkins - * @since 2019-11-08 - */ - public static final class Mass { - public static final LinearUnit GRAIN = BritishImperial.Mass.GRAIN; - public static final LinearUnit DRAM = BritishImperial.Mass.DRACHM; - public static final LinearUnit OUNCE = BritishImperial.Mass.OUNCE; - public static final LinearUnit POUND = BritishImperial.Mass.POUND; - public static final LinearUnit HUNDREDWEIGHT = POUND.times(100); - public static final LinearUnit SHORT_TON = HUNDREDWEIGHT.times(20); - - // troy system for precious metals - public static final LinearUnit PENNYWEIGHT = GRAIN.times(24); - public static final LinearUnit TROY_OUNCE = PENNYWEIGHT.times(20); - public static final LinearUnit TROY_POUND = TROY_OUNCE.times(12); - } - - /** - * Volume units - * - * @author Adrien Hopkins - * @since 2019-11-08 - */ - public static final class Volume { - public static final LinearUnit CUBIC_INCH = Length.INCH.toExponent(3); - public static final LinearUnit CUBIC_FOOT = Length.FOOT.toExponent(3); - public static final LinearUnit CUBIC_YARD = Length.YARD.toExponent(3); - public static final LinearUnit ACRE_FOOT = Area.ACRE.times(Length.FOOT); - - public static final LinearUnit MINIM = SI.LITRE.withPrefix(SI.MICRO).times(61.611519921875); - public static final LinearUnit FLUID_DRAM = MINIM.times(60); - public static final LinearUnit TEASPOON = MINIM.times(80); - public static final LinearUnit TABLESPOON = TEASPOON.times(3); - public static final LinearUnit FLUID_OUNCE = TABLESPOON.times(2); - public static final LinearUnit SHOT = TABLESPOON.times(3); - public static final LinearUnit GILL = FLUID_OUNCE.times(4); - public static final LinearUnit CUP = GILL.times(2); - public static final LinearUnit PINT = CUP.times(2); - public static final LinearUnit QUART = PINT.times(2); - public static final LinearUnit GALLON = QUART.times(4); - public static final LinearUnit BARREL = GALLON.times(31.5); - public static final LinearUnit OIL_BARREL = GALLON.times(42); - public static final LinearUnit HOGSHEAD = GALLON.times(63); - - public static final LinearUnit DRY_PINT = SI.LITRE.times(0.5506104713575); - public static final LinearUnit DRY_QUART = DRY_PINT.times(2); - public static final LinearUnit DRY_GALLON = DRY_QUART.times(4); - public static final LinearUnit PECK = DRY_GALLON.times(2); - public static final LinearUnit BUSHEL = PECK.times(4); - public static final LinearUnit DRY_BARREL = CUBIC_INCH.times(7056); - } - - public static final LinearUnit OUNCE_FORCE = BritishImperial.OUNCE_FORCE; - public static final LinearUnit POUND_FORCE = BritishImperial.POUND_FORCE; - - public static final LinearUnit BRITISH_THERMAL_UNIT = BritishImperial.BRITISH_THERMAL_UNIT; - public static final LinearUnit CALORIE = BritishImperial.CALORIE; - public static final LinearUnit KILOCALORIE = BritishImperial.KILOCALORIE; - public static final LinearUnit FOOT_POUND = POUND_FORCE.times(Length.FOOT); - - public static final LinearUnit HORSEPOWER = Length.FOOT.times(POUND_FORCE).dividedBy(SI.MINUTE).times(33000); - public static final LinearUnit POUND_PER_SQUARE_INCH = POUND_FORCE.dividedBy(Length.INCH.toExponent(2)); - - public static final Unit FAHRENHEIT = BritishImperial.FAHRENHEIT; -} diff --git a/src/org/unitConverter/unit/Unit.java b/src/org/unitConverter/unit/Unit.java deleted file mode 100644 index 0a3298f..0000000 --- a/src/org/unitConverter/unit/Unit.java +++ /dev/null @@ -1,377 +0,0 @@ -/** - * 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.function.DoubleUnaryOperator; - -import org.unitConverter.math.DecimalComparison; -import org.unitConverter.math.ObjectProduct; - -/** - * A unit that is composed of base units. - * - * @author Adrien Hopkins - * @since 2019-10-16 - */ -public abstract class Unit implements Nameable { - /** - * Returns a unit from its base and the functions it uses to convert to and - * from its base. - * - * <p> - * For example, to get a unit representing the degree Celsius, the following - * code can be used: - * - * {@code Unit.fromConversionFunctions(SI.KELVIN, tempK -> tempK - 273.15, tempC -> tempC + 273.15);} - * </p> - * - * @param base unit's base - * @param converterFrom function that accepts a value expressed in the unit's - * base and returns that value expressed in this unit. - * @param converterTo function that accepts a value expressed in the unit - * and returns that value expressed in the unit's base. - * @return a unit that uses the provided functions to convert. - * @since 2019-05-22 - * @throws NullPointerException if any argument is null - */ - public static final Unit fromConversionFunctions( - final ObjectProduct<BaseUnit> base, - final DoubleUnaryOperator converterFrom, - final DoubleUnaryOperator converterTo) { - return new FunctionalUnit(base, converterFrom, converterTo); - } - - /** - * Returns a unit from its base and the functions it uses to convert to and - * from its base. - * - * <p> - * For example, to get a unit representing the degree Celsius, the following - * code can be used: - * - * {@code Unit.fromConversionFunctions(SI.KELVIN, tempK -> tempK - 273.15, tempC -> tempC + 273.15);} - * </p> - * - * @param base unit's base - * @param converterFrom function that accepts a value expressed in the unit's - * base and returns that value expressed in this unit. - * @param converterTo function that accepts a value expressed in the unit - * and returns that value expressed in the unit's base. - * @param ns names and symbol of unit - * @return a unit that uses the provided functions to convert. - * @since 2019-05-22 - * @throws NullPointerException if any argument is null - */ - public static final Unit fromConversionFunctions( - final ObjectProduct<BaseUnit> base, - final DoubleUnaryOperator converterFrom, - final DoubleUnaryOperator converterTo, final NameSymbol ns) { - return new FunctionalUnit(base, converterFrom, converterTo, ns); - } - - /** - * The combination of units that this unit is based on. - * - * @since 2019-10-16 - */ - private final ObjectProduct<BaseUnit> unitBase; - - /** - * This unit's name(s) and symbol - * - * @since 2020-09-07 - */ - private final NameSymbol nameSymbol; - - /** - * Cache storing the result of getDimension() - * - * @since 2019-10-16 - */ - private transient ObjectProduct<BaseDimension> dimension = null; - - /** - * Creates the {@code Unit}. - * - * @param unitBase base of unit - * @param ns names and symbol of unit - * @since 2019-10-16 - * @throws NullPointerException if unitBase or ns is null - */ - Unit(ObjectProduct<BaseUnit> unitBase, NameSymbol ns) { - this.unitBase = Objects.requireNonNull(unitBase, - "unitBase may not be null"); - this.nameSymbol = Objects.requireNonNull(ns, "ns may not be null"); - } - - /** - * A constructor that constructs {@code BaseUnit} instances. - * - * @since 2019-10-16 - */ - Unit(final String primaryName, final String symbol, - final Set<String> otherNames) { - if (this instanceof BaseUnit) { - this.unitBase = ObjectProduct.oneOf((BaseUnit) this); - } else - throw new AssertionError(); - this.nameSymbol = NameSymbol.of(primaryName, symbol, - new HashSet<>(otherNames)); - } - - /** - * @return this unit as a {@link Unitlike} - * @since 2020-09-07 - */ - public final Unitlike<Double> asUnitlike() { - return Unitlike.fromConversionFunctions(this.getBase(), - this::convertFromBase, this::convertToBase, this.getNameSymbol()); - } - - /** - * Checks if a value expressed in this unit can be converted to a value - * expressed in {@code other} - * - * @param other unit or unitlike form to test with - * @return true if they are compatible - * @since 2019-01-13 - * @since v0.1.0 - * @throws NullPointerException if other is null - */ - public final boolean canConvertTo(final Unit other) { - Objects.requireNonNull(other, "other must not be null."); - return Objects.equals(this.getBase(), other.getBase()); - } - - /** - * Checks if a value expressed in this unit can be converted to a value - * expressed in {@code other} - * - * @param other unit or unitlike form to test with - * @return true if they are compatible - * @since 2019-01-13 - * @since v0.1.0 - * @throws NullPointerException if other is null - */ - public final <W> boolean canConvertTo(final Unitlike<W> other) { - Objects.requireNonNull(other, "other must not be null."); - return Objects.equals(this.getBase(), other.getBase()); - } - - /** - * Converts from a value expressed in this unit's base unit to a value - * expressed in this unit. - * <p> - * This must be the inverse of {@code convertToBase}, so - * {@code convertFromBase(convertToBase(value))} must be equal to - * {@code value} for any value, ignoring precision loss by roundoff error. - * </p> - * <p> - * If this unit <i>is</i> a base unit, this method should return - * {@code value}. - * </p> - * - * @implSpec This method is used by {@link #convertTo}, and its behaviour - * affects the behaviour of {@code convertTo}. - * - * @param value value expressed in <b>base</b> unit - * @return value expressed in <b>this</b> unit - * @since 2018-12-22 - * @since v0.1.0 - */ - protected abstract double convertFromBase(double value); - - /** - * Converts a value expressed in this unit to a value expressed in - * {@code other}. - * - * @implSpec If unit conversion is possible, this implementation returns - * {@code other.convertFromBase(this.convertToBase(value))}. - * Therefore, overriding either of those methods will change the - * output of this method. - * - * @param other unit to convert to - * @param value value to convert - * @return converted value - * @since 2019-05-22 - * @throws IllegalArgumentException if {@code other} is incompatible for - * conversion with this unit (as tested by - * {@link Unit#canConvertTo}). - * @throws NullPointerException if other is null - */ - public final double convertTo(final Unit other, final double value) { - Objects.requireNonNull(other, "other must not be null."); - if (this.canConvertTo(other)) - return other.convertFromBase(this.convertToBase(value)); - else - throw new IllegalArgumentException( - String.format("Cannot convert from %s to %s.", this, other)); - } - - /** - * Converts a value expressed in this unit to a value expressed in - * {@code other}. - * - * @implSpec If conversion is possible, this implementation returns - * {@code other.convertFromBase(this.convertToBase(value))}. - * Therefore, overriding either of those methods will change the - * output of this method. - * - * @param other unitlike form to convert to - * @param value value to convert - * @param <W> type of value to convert to - * @return converted value - * @since 2020-09-07 - * @throws IllegalArgumentException if {@code other} is incompatible for - * conversion with this unit (as tested by - * {@link Unit#canConvertTo}). - * @throws NullPointerException if other is null - */ - public final <W> W convertTo(final Unitlike<W> other, final double value) { - Objects.requireNonNull(other, "other must not be null."); - if (this.canConvertTo(other)) - return other.convertFromBase(this.convertToBase(value)); - else - throw new IllegalArgumentException( - String.format("Cannot convert from %s to %s.", this, other)); - } - - /** - * Converts from a value expressed in this unit to a value expressed in this - * unit's base unit. - * <p> - * This must be the inverse of {@code convertFromBase}, so - * {@code convertToBase(convertFromBase(value))} must be equal to - * {@code value} for any value, ignoring precision loss by roundoff error. - * </p> - * <p> - * If this unit <i>is</i> a base unit, this method should return - * {@code value}. - * </p> - * - * @implSpec This method is used by {@link #convertTo}, and its behaviour - * affects the behaviour of {@code convertTo}. - * - * @param value value expressed in <b>this</b> unit - * @return value expressed in <b>base</b> unit - * @since 2018-12-22 - * @since v0.1.0 - */ - protected abstract double convertToBase(double value); - - /** - * @return combination of units that this unit is based on - * @since 2018-12-22 - * @since v0.1.0 - */ - public final ObjectProduct<BaseUnit> getBase() { - return this.unitBase; - } - - /** - * @return dimension measured by this unit - * @since 2018-12-22 - * @since v0.1.0 - */ - public final ObjectProduct<BaseDimension> getDimension() { - if (this.dimension == null) { - final Map<BaseUnit, Integer> mapping = this.unitBase.exponentMap(); - final Map<BaseDimension, Integer> dimensionMap = new HashMap<>(); - - for (final BaseUnit key : mapping.keySet()) { - dimensionMap.put(key.getBaseDimension(), mapping.get(key)); - } - - this.dimension = ObjectProduct.fromExponentMapping(dimensionMap); - } - return this.dimension; - } - - /** - * @return the nameSymbol - * @since 2020-09-07 - */ - @Override - public final NameSymbol getNameSymbol() { - return this.nameSymbol; - } - - /** - * Returns true iff this unit is metric. - * <p> - * "Metric" is defined by three conditions: - * <ul> - * <li>Must be an instance of {@link LinearUnit}.</li> - * <li>Must be based on the SI base units (as determined by getBase())</li> - * <li>The conversion factor must be a power of 10.</li> - * </ul> - * <p> - * Note that this definition excludes some units that many would consider - * "metric", such as the degree Celsius (fails the first condition), - * calories, minutes and hours (fail the third condition). - * <p> - * All SI units (as designated by the BIPM) except the degree Celsius are - * considered "metric" by this definition. - * - * @since 2020-08-27 - */ - public final boolean isMetric() { - // first condition - check that it is a linear unit - if (!(this instanceof LinearUnit)) - return false; - final LinearUnit linear = (LinearUnit) this; - - // second condition - check that - for (final BaseUnit b : linear.getBase().getBaseSet()) { - if (!SI.BaseUnits.BASE_UNITS.contains(b)) - return false; - } - - // third condition - check that conversion factor is a power of 10 - return DecimalComparison - .equals(Math.log10(linear.getConversionFactor()) % 1.0, 0); - } - - @Override - public String toString() { - return this.getPrimaryName().orElse("Unnamed unit") - + (this.getSymbol().isPresent() - ? String.format(" (%s)", this.getSymbol().get()) - : "") - + ", derived from " - + this.getBase().toString(u -> u.getSymbol().get()) - + (this.getOtherNames().isEmpty() ? "" - : ", also called " + String.join(", ", this.getOtherNames())); - } - - /** - * @param ns name(s) and symbol to use - * @return a copy of this unit with provided name(s) and symbol - * @since 2019-10-21 - * @throws NullPointerException if ns is null - */ - public Unit withName(final NameSymbol ns) { - return fromConversionFunctions(this.getBase(), this::convertFromBase, - this::convertToBase, - Objects.requireNonNull(ns, "ns must not be null.")); - } -} diff --git a/src/org/unitConverter/unit/UnitDatabase.java b/src/org/unitConverter/unit/UnitDatabase.java deleted file mode 100644 index 000acf5..0000000 --- a/src/org/unitConverter/unit/UnitDatabase.java +++ /dev/null @@ -1,1991 +0,0 @@ -/** - * Copyright (C) 2018 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.math.BigDecimal; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.AbstractSet; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.Set; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.unitConverter.math.ConditionalExistenceCollections; -import org.unitConverter.math.DecimalComparison; -import org.unitConverter.math.ExpressionParser; -import org.unitConverter.math.ObjectProduct; -import org.unitConverter.math.UncertainDouble; - -/** - * A database of units, prefixes and dimensions, and their names. - * - * @author Adrien Hopkins - * @since 2019-01-07 - * @since v0.1.0 - */ -public final class UnitDatabase { - /** - * A map for units that allows the use of prefixes. - * <p> - * As this map implementation is intended to be used as a sort of "augmented - * view" of a unit and prefix map, it is unmodifiable but instead reflects - * the changes to the maps passed into it. Do not edit this map, instead edit - * the maps that were passed in during construction. - * </p> - * <p> - * The rules for applying prefixes onto units are the following: - * <ul> - * <li>Prefixes can only be applied to linear units.</li> - * <li>Before attempting to search for prefixes in a unit name, this map will - * first search for a unit name. So, if there are two units, "B" and "AB", - * and a prefix "A", this map will favour the unit "AB" over the unit "B" - * with the prefix "A", even though they have the same string.</li> - * <li>Longer prefixes are preferred to shorter prefixes. So, if you have - * units "BC" and "C", and prefixes "AB" and "A", inputting "ABC" will return - * the unit "C" with the prefix "AB", not "BC" with the prefix "A".</li> - * </ul> - * </p> - * <p> - * This map is infinite in size if there is at least one unit and at least - * one prefix. If it is infinite, some operations that only work with finite - * collections, like converting name/entry sets to arrays, will throw an - * {@code IllegalStateException}. - * </p> - * <p> - * Because of ambiguities between prefixes (i.e. kilokilo = mega), - * {@link #containsValue} and {@link #values()} currently ignore prefixes. - * </p> - * - * @author Adrien Hopkins - * @since 2019-04-13 - * @since v0.2.0 - */ - private static final class PrefixedUnitMap implements Map<String, Unit> { - /** - * The class used for entry sets. - * - * <p> - * If the map that created this set is infinite in size (has at least one - * unit and at least one prefix), this set is infinite as well. If this - * set is infinite in size, {@link #toArray} will fail with a - * {@code IllegalStateException} instead of creating an infinite-sized - * array. - * </p> - * - * @author Adrien Hopkins - * @since 2019-04-13 - * @since v0.2.0 - */ - private static final class PrefixedUnitEntrySet - extends AbstractSet<Map.Entry<String, Unit>> { - /** - * The entry for this set. - * - * @author Adrien Hopkins - * @since 2019-04-14 - * @since v0.2.0 - */ - private static final class PrefixedUnitEntry - implements Entry<String, Unit> { - private final String key; - private final Unit value; - - /** - * Creates the {@code PrefixedUnitEntry}. - * - * @param key key - * @param value value - * @since 2019-04-14 - * @since v0.2.0 - */ - public PrefixedUnitEntry(final String key, final Unit value) { - this.key = key; - this.value = value; - } - - /** - * @since 2019-05-03 - */ - @Override - public boolean equals(final Object o) { - if (!(o instanceof Map.Entry)) - return false; - final Map.Entry<?, ?> other = (Map.Entry<?, ?>) o; - return Objects.equals(this.getKey(), other.getKey()) - && Objects.equals(this.getValue(), other.getValue()); - } - - @Override - public String getKey() { - return this.key; - } - - @Override - public Unit getValue() { - return this.value; - } - - /** - * @since 2019-05-03 - */ - @Override - public int hashCode() { - return (this.getKey() == null ? 0 : this.getKey().hashCode()) - ^ (this.getValue() == null ? 0 - : this.getValue().hashCode()); - } - - @Override - public Unit setValue(final Unit value) { - throw new UnsupportedOperationException( - "Cannot set value in an immutable entry"); - } - - /** - * Returns a string representation of the entry. The format of the - * string is the string representation of the key, then the equals - * ({@code =}) character, then the string representation of the - * value. - * - * @since 2019-05-03 - */ - @Override - public String toString() { - return this.getKey() + "=" + this.getValue(); - } - } - - /** - * An iterator that iterates over the units of a - * {@code PrefixedUnitNameSet}. - * - * @author Adrien Hopkins - * @since 2019-04-14 - * @since v0.2.0 - */ - private static final class PrefixedUnitEntryIterator - implements Iterator<Entry<String, Unit>> { - // position in the unit list - private int unitNamePosition = 0; - // the indices of the prefixes attached to the current unit - private final List<Integer> prefixCoordinates = new ArrayList<>(); - - // values from the unit entry set - private final Map<String, Unit> map; - private transient final List<String> unitNames; - private transient final List<String> prefixNames; - - /** - * Creates the - * {@code UnitsDatabase.PrefixedUnitMap.PrefixedUnitNameSet.PrefixedUnitNameIterator}. - * - * @since 2019-04-14 - * @since v0.2.0 - */ - public PrefixedUnitEntryIterator(final PrefixedUnitMap map) { - this.map = map; - this.unitNames = new ArrayList<>(map.units.keySet()); - this.prefixNames = new ArrayList<>(map.prefixes.keySet()); - } - - /** - * @return current unit name - * @since 2019-04-14 - * @since v0.2.0 - */ - private String getCurrentUnitName() { - final StringBuilder unitName = new StringBuilder(); - for (final int i : this.prefixCoordinates) { - unitName.append(this.prefixNames.get(i)); - } - unitName.append(this.unitNames.get(this.unitNamePosition)); - - return unitName.toString(); - } - - @Override - public boolean hasNext() { - if (this.unitNames.isEmpty()) - return false; - else { - if (this.prefixNames.isEmpty()) - return this.unitNamePosition >= this.unitNames.size() - 1; - else - return true; - } - } - - /** - * Changes this iterator's position to the next available one. - * - * @since 2019-04-14 - * @since v0.2.0 - */ - private void incrementPosition() { - this.unitNamePosition++; - - if (this.unitNamePosition >= this.unitNames.size()) { - // we have used all of our units, go to a different prefix - this.unitNamePosition = 0; - - // if the prefix coordinates are empty, then set it to [0] - if (this.prefixCoordinates.isEmpty()) { - this.prefixCoordinates.add(0, 0); - } else { - // get the prefix coordinate to increment, then increment - int i = this.prefixCoordinates.size() - 1; - this.prefixCoordinates.set(i, - this.prefixCoordinates.get(i) + 1); - - // fix any carrying errors - while (i >= 0 && this.prefixCoordinates - .get(i) >= this.prefixNames.size()) { - // carry over - this.prefixCoordinates.set(i--, 0); // null and - // decrement at the - // same time - - if (i < 0) { // we need to add a new coordinate - this.prefixCoordinates.add(0, 0); - } else { // increment an existing one - this.prefixCoordinates.set(i, - this.prefixCoordinates.get(i) + 1); - } - } - } - } - } - - @Override - public Entry<String, Unit> next() { - // get next element - final Entry<String, Unit> nextEntry = this.peek(); - - // iterate to next position - this.incrementPosition(); - - return nextEntry; - } - - /** - * @return the next element in the iterator, without iterating over - * it - * @since 2019-05-03 - */ - private Entry<String, Unit> peek() { - if (!this.hasNext()) - throw new NoSuchElementException("No units left!"); - - // if I have prefixes, ensure I'm not using a nonlinear unit - // since all of the unprefixed stuff is done, just remove - // nonlinear units - if (!this.prefixCoordinates.isEmpty()) { - while (this.unitNamePosition < this.unitNames.size() - && !(this.map.get(this.unitNames.get( - this.unitNamePosition)) instanceof LinearUnit)) { - this.unitNames.remove(this.unitNamePosition); - } - } - - final String nextName = this.getCurrentUnitName(); - - return new PrefixedUnitEntry(nextName, this.map.get(nextName)); - } - - /** - * Returns a string representation of the object. The exact details - * of the representation are unspecified and subject to change. - * - * @since 2019-05-03 - */ - @Override - public String toString() { - return String.format( - "Iterator iterating over name-unit entries; next value is \"%s\"", - this.peek()); - } - } - - // the map that created this set - private final PrefixedUnitMap map; - - /** - * Creates the {@code PrefixedUnitNameSet}. - * - * @param map map that created this set - * @since 2019-04-13 - * @since v0.2.0 - */ - public PrefixedUnitEntrySet(final PrefixedUnitMap map) { - this.map = map; - } - - @Override - public boolean add(final Map.Entry<String, Unit> e) { - throw new UnsupportedOperationException( - "Cannot add to an immutable set"); - } - - @Override - public boolean addAll( - final Collection<? extends Map.Entry<String, Unit>> c) { - throw new UnsupportedOperationException( - "Cannot add to an immutable set"); - } - - @Override - public void clear() { - throw new UnsupportedOperationException( - "Cannot clear an immutable set"); - } - - @Override - public boolean contains(final Object o) { - // get the entry - final Entry<String, Unit> entry; - - try { - // This is OK because I'm in a try-catch block, catching the - // exact exception that would be thrown. - @SuppressWarnings("unchecked") - final Entry<String, Unit> tempEntry = (Entry<String, Unit>) o; - entry = tempEntry; - } catch (final ClassCastException e) { - throw new IllegalArgumentException( - "Attempted to test for an entry using a non-entry."); - } - - return this.map.containsKey(entry.getKey()) - && this.map.get(entry.getKey()).equals(entry.getValue()); - } - - @Override - public boolean containsAll(final Collection<?> c) { - for (final Object o : c) - if (!this.contains(o)) - return false; - return true; - } - - @Override - public boolean isEmpty() { - return this.map.isEmpty(); - } - - @Override - public Iterator<Entry<String, Unit>> iterator() { - return new PrefixedUnitEntryIterator(this.map); - } - - @Override - public boolean remove(final Object o) { - throw new UnsupportedOperationException( - "Cannot remove from an immutable set"); - } - - @Override - public boolean removeAll(final Collection<?> c) { - throw new UnsupportedOperationException( - "Cannot remove from an immutable set"); - } - - @Override - public boolean removeIf( - final Predicate<? super Entry<String, Unit>> filter) { - throw new UnsupportedOperationException( - "Cannot remove from an immutable set"); - } - - @Override - public boolean retainAll(final Collection<?> c) { - throw new UnsupportedOperationException( - "Cannot remove from an immutable set"); - } - - @Override - public int size() { - if (this.map.units.isEmpty()) - return 0; - else { - if (this.map.prefixes.isEmpty()) - return this.map.units.size(); - else - // infinite set - return Integer.MAX_VALUE; - } - } - - /** - * @throws IllegalStateException if the set is infinite in size - */ - @Override - public Object[] toArray() { - if (this.map.units.isEmpty() || this.map.prefixes.isEmpty()) - return super.toArray(); - else - // infinite set - throw new IllegalStateException( - "Cannot make an infinite set into an array."); - } - - /** - * @throws IllegalStateException if the set is infinite in size - */ - @Override - public <T> T[] toArray(final T[] a) { - if (this.map.units.isEmpty() || this.map.prefixes.isEmpty()) - return super.toArray(a); - else - // infinite set - throw new IllegalStateException( - "Cannot make an infinite set into an array."); - } - - @Override - public String toString() { - if (this.map.units.isEmpty() || this.map.prefixes.isEmpty()) - return super.toString(); - else - return String.format( - "Infinite set of name-unit entries created from units %s and prefixes %s", - this.map.units, this.map.prefixes); - } - } - - /** - * The class used for unit name sets. - * - * <p> - * If the map that created this set is infinite in size (has at least one - * unit and at least one prefix), this set is infinite as well. If this - * set is infinite in size, {@link #toArray} will fail with a - * {@code IllegalStateException} instead of creating an infinite-sized - * array. - * </p> - * - * @author Adrien Hopkins - * @since 2019-04-13 - * @since v0.2.0 - */ - private static final class PrefixedUnitNameSet - extends AbstractSet<String> { - /** - * An iterator that iterates over the units of a - * {@code PrefixedUnitNameSet}. - * - * @author Adrien Hopkins - * @since 2019-04-14 - * @since v0.2.0 - */ - private static final class PrefixedUnitNameIterator - implements Iterator<String> { - // position in the unit list - private int unitNamePosition = 0; - // the indices of the prefixes attached to the current unit - private final List<Integer> prefixCoordinates = new ArrayList<>(); - - // values from the unit name set - private final Map<String, Unit> map; - private transient final List<String> unitNames; - private transient final List<String> prefixNames; - - /** - * Creates the - * {@code UnitsDatabase.PrefixedUnitMap.PrefixedUnitNameSet.PrefixedUnitNameIterator}. - * - * @since 2019-04-14 - * @since v0.2.0 - */ - public PrefixedUnitNameIterator(final PrefixedUnitMap map) { - this.map = map; - this.unitNames = new ArrayList<>(map.units.keySet()); - this.prefixNames = new ArrayList<>(map.prefixes.keySet()); - } - - /** - * @return current unit name - * @since 2019-04-14 - * @since v0.2.0 - */ - private String getCurrentUnitName() { - final StringBuilder unitName = new StringBuilder(); - for (final int i : this.prefixCoordinates) { - unitName.append(this.prefixNames.get(i)); - } - unitName.append(this.unitNames.get(this.unitNamePosition)); - - return unitName.toString(); - } - - @Override - public boolean hasNext() { - if (this.unitNames.isEmpty()) - return false; - else { - if (this.prefixNames.isEmpty()) - return this.unitNamePosition >= this.unitNames.size() - 1; - else - return true; - } - } - - /** - * Changes this iterator's position to the next available one. - * - * @since 2019-04-14 - * @since v0.2.0 - */ - private void incrementPosition() { - this.unitNamePosition++; - - if (this.unitNamePosition >= this.unitNames.size()) { - // we have used all of our units, go to a different prefix - this.unitNamePosition = 0; - - // if the prefix coordinates are empty, then set it to [0] - if (this.prefixCoordinates.isEmpty()) { - this.prefixCoordinates.add(0, 0); - } else { - // get the prefix coordinate to increment, then increment - int i = this.prefixCoordinates.size() - 1; - this.prefixCoordinates.set(i, - this.prefixCoordinates.get(i) + 1); - - // fix any carrying errors - while (i >= 0 && this.prefixCoordinates - .get(i) >= this.prefixNames.size()) { - // carry over - this.prefixCoordinates.set(i--, 0); // null and - // decrement at the - // same time - - if (i < 0) { // we need to add a new coordinate - this.prefixCoordinates.add(0, 0); - } else { // increment an existing one - this.prefixCoordinates.set(i, - this.prefixCoordinates.get(i) + 1); - } - } - } - } - } - - @Override - public String next() { - final String nextName = this.peek(); - - this.incrementPosition(); - - return nextName; - } - - /** - * @return the next element in the iterator, without iterating over - * it - * @since 2019-05-03 - */ - private String peek() { - if (!this.hasNext()) - throw new NoSuchElementException("No units left!"); - // if I have prefixes, ensure I'm not using a nonlinear unit - // since all of the unprefixed stuff is done, just remove - // nonlinear units - if (!this.prefixCoordinates.isEmpty()) { - while (this.unitNamePosition < this.unitNames.size() - && !(this.map.get(this.unitNames.get( - this.unitNamePosition)) instanceof LinearUnit)) { - this.unitNames.remove(this.unitNamePosition); - } - } - - return this.getCurrentUnitName(); - } - - /** - * Returns a string representation of the object. The exact details - * of the representation are unspecified and subject to change. - * - * @since 2019-05-03 - */ - @Override - public String toString() { - return String.format( - "Iterator iterating over unit names; next value is \"%s\"", - this.peek()); - } - } - - // the map that created this set - private final PrefixedUnitMap map; - - /** - * Creates the {@code PrefixedUnitNameSet}. - * - * @param map map that created this set - * @since 2019-04-13 - * @since v0.2.0 - */ - public PrefixedUnitNameSet(final PrefixedUnitMap map) { - this.map = map; - } - - @Override - public boolean add(final String e) { - throw new UnsupportedOperationException( - "Cannot add to an immutable set"); - } - - @Override - public boolean addAll(final Collection<? extends String> c) { - throw new UnsupportedOperationException( - "Cannot add to an immutable set"); - } - - @Override - public void clear() { - throw new UnsupportedOperationException( - "Cannot clear an immutable set"); - } - - @Override - public boolean contains(final Object o) { - return this.map.containsKey(o); - } - - @Override - public boolean containsAll(final Collection<?> c) { - for (final Object o : c) - if (!this.contains(o)) - return false; - return true; - } - - @Override - public boolean isEmpty() { - return this.map.isEmpty(); - } - - @Override - public Iterator<String> iterator() { - return new PrefixedUnitNameIterator(this.map); - } - - @Override - public boolean remove(final Object o) { - throw new UnsupportedOperationException( - "Cannot remove from an immutable set"); - } - - @Override - public boolean removeAll(final Collection<?> c) { - throw new UnsupportedOperationException( - "Cannot remove from an immutable set"); - } - - @Override - public boolean removeIf(final Predicate<? super String> filter) { - throw new UnsupportedOperationException( - "Cannot remove from an immutable set"); - } - - @Override - public boolean retainAll(final Collection<?> c) { - throw new UnsupportedOperationException( - "Cannot remove from an immutable set"); - } - - @Override - public int size() { - if (this.map.units.isEmpty()) - return 0; - else { - if (this.map.prefixes.isEmpty()) - return this.map.units.size(); - else - // infinite set - return Integer.MAX_VALUE; - } - } - - /** - * @throws IllegalStateException if the set is infinite in size - */ - @Override - public Object[] toArray() { - if (this.map.units.isEmpty() || this.map.prefixes.isEmpty()) - return super.toArray(); - else - // infinite set - throw new IllegalStateException( - "Cannot make an infinite set into an array."); - - } - - /** - * @throws IllegalStateException if the set is infinite in size - */ - @Override - public <T> T[] toArray(final T[] a) { - if (this.map.units.isEmpty() || this.map.prefixes.isEmpty()) - return super.toArray(a); - else - // infinite set - throw new IllegalStateException( - "Cannot make an infinite set into an array."); - } - - @Override - public String toString() { - if (this.map.units.isEmpty() || this.map.prefixes.isEmpty()) - return super.toString(); - else - return String.format( - "Infinite set of name-unit entries created from units %s and prefixes %s", - this.map.units, this.map.prefixes); - } - } - - /** - * The units stored in this collection, without prefixes. - * - * @since 2019-04-13 - * @since v0.2.0 - */ - private final Map<String, Unit> units; - - /** - * The available prefixes for use. - * - * @since 2019-04-13 - * @since v0.2.0 - */ - private final Map<String, UnitPrefix> prefixes; - - // caches - private transient Collection<Unit> values = null; - private transient Set<String> keySet = null; - private transient Set<Entry<String, Unit>> entrySet = null; - - /** - * Creates the {@code PrefixedUnitMap}. - * - * @param units map mapping unit names to units - * @param prefixes map mapping prefix names to prefixes - * @since 2019-04-13 - * @since v0.2.0 - */ - public PrefixedUnitMap(final Map<String, Unit> units, - final Map<String, UnitPrefix> prefixes) { - // I am making unmodifiable maps to ensure I don't accidentally make - // changes. - this.units = Collections.unmodifiableMap(units); - this.prefixes = Collections.unmodifiableMap(prefixes); - } - - @Override - public void clear() { - throw new UnsupportedOperationException( - "Cannot clear an immutable map"); - } - - @Override - public Unit compute(final String key, - final BiFunction<? super String, ? super Unit, ? extends Unit> remappingFunction) { - throw new UnsupportedOperationException( - "Cannot edit an immutable map"); - } - - @Override - public Unit computeIfAbsent(final String key, - final Function<? super String, ? extends Unit> mappingFunction) { - throw new UnsupportedOperationException( - "Cannot edit an immutable map"); - } - - @Override - public Unit computeIfPresent(final String key, - final BiFunction<? super String, ? super Unit, ? extends Unit> remappingFunction) { - throw new UnsupportedOperationException( - "Cannot edit an immutable map"); - } - - @Override - public boolean containsKey(final Object key) { - // First, test if there is a unit with the key - if (this.units.containsKey(key)) - return true; - - // Next, try to cast it to String - if (!(key instanceof String)) - throw new IllegalArgumentException( - "Attempted to test for a unit using a non-string name."); - final String unitName = (String) key; - - // Then, look for the longest prefix that is attached to a valid unit - String longestPrefix = null; - int longestLength = 0; - - for (final String prefixName : this.prefixes.keySet()) { - // a prefix name is valid if: - // - it is prefixed (i.e. the unit name starts with it) - // - it is longer than the existing largest prefix (since I am - // looking for the longest valid prefix) - // - the part after the prefix is a valid unit name - // - the unit described that name is a linear unit (since only - // linear units can have prefixes) - if (unitName.startsWith(prefixName) - && prefixName.length() > longestLength) { - final String rest = unitName.substring(prefixName.length()); - if (this.containsKey(rest) - && this.get(rest) instanceof LinearUnit) { - longestPrefix = prefixName; - longestLength = prefixName.length(); - } - } - } - - return longestPrefix != null; - } - - /** - * {@inheritDoc} - * - * <p> - * Because of ambiguities between prefixes (i.e. kilokilo = mega), this - * method only tests for prefixless units. - * </p> - */ - @Override - public boolean containsValue(final Object value) { - return this.units.containsValue(value); - } - - @Override - public Set<Entry<String, Unit>> entrySet() { - if (this.entrySet == null) { - this.entrySet = new PrefixedUnitEntrySet(this); - } - return this.entrySet; - } - - @Override - public Unit get(final Object key) { - // First, test if there is a unit with the key - if (this.units.containsKey(key)) - return this.units.get(key); - - // Next, try to cast it to String - if (!(key instanceof String)) - throw new IllegalArgumentException( - "Attempted to obtain a unit using a non-string name."); - final String unitName = (String) key; - - // Then, look for the longest prefix that is attached to a valid unit - String longestPrefix = null; - int longestLength = 0; - - for (final String prefixName : this.prefixes.keySet()) { - // a prefix name is valid if: - // - it is prefixed (i.e. the unit name starts with it) - // - it is longer than the existing largest prefix (since I am - // looking for the longest valid prefix) - // - the part after the prefix is a valid unit name - // - the unit described that name is a linear unit (since only - // linear units can have prefixes) - if (unitName.startsWith(prefixName) - && prefixName.length() > longestLength) { - final String rest = unitName.substring(prefixName.length()); - if (this.containsKey(rest) - && this.get(rest) instanceof LinearUnit) { - longestPrefix = prefixName; - longestLength = prefixName.length(); - } - } - } - - // if none found, returns null - if (longestPrefix == null) - return null; - else { - // get necessary data - final String rest = unitName.substring(longestLength); - // this cast will not fail because I verified that it would work - // before selecting this prefix - final LinearUnit unit = (LinearUnit) this.get(rest); - final UnitPrefix prefix = this.prefixes.get(longestPrefix); - - return unit.withPrefix(prefix); - } - } - - @Override - public boolean isEmpty() { - return this.units.isEmpty(); - } - - @Override - public Set<String> keySet() { - if (this.keySet == null) { - this.keySet = new PrefixedUnitNameSet(this); - } - return this.keySet; - } - - @Override - public Unit merge(final String key, final Unit value, - final BiFunction<? super Unit, ? super Unit, ? extends Unit> remappingFunction) { - throw new UnsupportedOperationException( - "Cannot merge into an immutable map"); - } - - @Override - public Unit put(final String key, final Unit value) { - throw new UnsupportedOperationException( - "Cannot add entries to an immutable map"); - } - - @Override - public void putAll(final Map<? extends String, ? extends Unit> m) { - throw new UnsupportedOperationException( - "Cannot add entries to an immutable map"); - } - - @Override - public Unit putIfAbsent(final String key, final Unit value) { - throw new UnsupportedOperationException( - "Cannot add entries to an immutable map"); - } - - @Override - public Unit remove(final Object key) { - throw new UnsupportedOperationException( - "Cannot remove entries from an immutable map"); - } - - @Override - public boolean remove(final Object key, final Object value) { - throw new UnsupportedOperationException( - "Cannot remove entries from an immutable map"); - } - - @Override - public Unit replace(final String key, final Unit value) { - throw new UnsupportedOperationException( - "Cannot replace entries in an immutable map"); - } - - @Override - public boolean replace(final String key, final Unit oldValue, - final Unit newValue) { - throw new UnsupportedOperationException( - "Cannot replace entries in an immutable map"); - } - - @Override - public void replaceAll( - final BiFunction<? super String, ? super Unit, ? extends Unit> function) { - throw new UnsupportedOperationException( - "Cannot replace entries in an immutable map"); - } - - @Override - public int size() { - if (this.units.isEmpty()) - return 0; - else { - if (this.prefixes.isEmpty()) - return this.units.size(); - else - // infinite set - return Integer.MAX_VALUE; - } - } - - @Override - public String toString() { - if (this.units.isEmpty() || this.prefixes.isEmpty()) - return super.toString(); - else - return String.format( - "Infinite map of name-unit entries created from units %s and prefixes %s", - this.units, this.prefixes); - } - - /** - * {@inheritDoc} - * - * <p> - * Because of ambiguities between prefixes (i.e. kilokilo = mega), this - * method ignores prefixes. - * </p> - */ - @Override - public Collection<Unit> values() { - if (this.values == null) { - this.values = Collections - .unmodifiableCollection(this.units.values()); - } - return this.values; - } - } - - /** - * Replacements done to *all* expression types - */ - private static final Map<Pattern, String> EXPRESSION_REPLACEMENTS = new HashMap<>(); - - // add data to expression replacements - static { - // add spaces around operators - for (final String operator : Arrays.asList("\\*", "/", "\\^")) { - EXPRESSION_REPLACEMENTS.put(Pattern.compile(operator), - " " + operator + " "); - } - - // replace multiple spaces with a single space - EXPRESSION_REPLACEMENTS.put(Pattern.compile(" +"), " "); - // place brackets around any expression of the form "number unit", with or - // without the space - EXPRESSION_REPLACEMENTS.put(Pattern.compile("((?:-?[1-9]\\d*|0)" // integer - + "(?:\\.\\d+(?:[eE]\\d+))?)" // optional decimal point with numbers - // after it - + "\\s*" // optional space(s) - + "([a-zA-Z]+(?:\\^\\d+)?" // any string of letters - + "(?:\\s+[a-zA-Z]+(?:\\^\\d+)?))" // optional other letters - + "(?!-?\\d)" // no number directly afterwards (avoids matching - // "1e3") - ), "\\($1 $2\\)"); - } - - /** - * A regular expression that separates names and expressions in unit files. - */ - private static final Pattern NAME_EXPRESSION = Pattern - .compile("(\\S+)\\s+(\\S.*)"); - - /** - * The exponent operator - * - * @param base base of exponentiation - * @param exponentUnit exponent - * @return result - * @since 2019-04-10 - * @since v0.2.0 - */ - private static final LinearUnit exponentiateUnits(final LinearUnit base, - final LinearUnit exponentUnit) { - // exponent function - first check if o2 is a number, - if (exponentUnit.getBase().equals(SI.ONE.getBase())) { - // then check if it is an integer, - final double exponent = exponentUnit.getConversionFactor(); - if (DecimalComparison.equals(exponent % 1, 0)) - // then exponentiate - return base.toExponent((int) (exponent + 0.5)); - else - // not an integer - throw new UnsupportedOperationException( - "Decimal exponents are currently not supported."); - } else - // not a number - throw new IllegalArgumentException("Exponents must be numbers."); - } - - /** - * The exponent operator - * - * @param base base of exponentiation - * @param exponentUnit exponent - * @return result - * @since 2020-08-04 - */ - private static final LinearUnitValue exponentiateUnitValues( - final LinearUnitValue base, final LinearUnitValue exponentValue) { - // exponent function - first check if o2 is a number, - if (exponentValue.canConvertTo(SI.ONE)) { - // then check if it is an integer, - final double exponent = exponentValue.getValueExact(); - if (DecimalComparison.equals(exponent % 1, 0)) - // then exponentiate - return base.toExponent((int) (exponent + 0.5)); - else - // not an integer - throw new UnsupportedOperationException( - "Decimal exponents are currently not supported."); - } else - // not a number - throw new IllegalArgumentException("Exponents must be numbers."); - } - - /** - * The units in this system, excluding prefixes. - * - * @since 2019-01-07 - * @since v0.1.0 - */ - private final Map<String, Unit> prefixlessUnits; - - /** - * The unit prefixes in this system. - * - * @since 2019-01-14 - * @since v0.1.0 - */ - private final Map<String, UnitPrefix> prefixes; - - /** - * The dimensions in this system. - * - * @since 2019-03-14 - * @since v0.2.0 - */ - private final Map<String, ObjectProduct<BaseDimension>> dimensions; - - /** - * A map mapping strings to units (including prefixes) - * - * @since 2019-04-13 - * @since v0.2.0 - */ - private final Map<String, Unit> units; - - /** - * The rule that specifies when prefix repetition is allowed. It takes in one - * argument: a list of the prefixes being applied to the unit - * <p> - * The prefixes are inputted in <em>application order</em>. This means that - * testing whether "kilomegagigametre" is a valid unit is equivalent to - * running the following code (assuming all variables are defined correctly): - * <br> - * {@code prefixRepetitionRule.test(Arrays.asList(giga, mega, kilo))} - */ - private Predicate<List<UnitPrefix>> prefixRepetitionRule; - - /** - * A parser that can parse unit expressions. - * - * @since 2019-03-22 - * @since v0.2.0 - */ - private final ExpressionParser<LinearUnit> unitExpressionParser = new ExpressionParser.Builder<>( - this::getLinearUnit).addBinaryOperator("+", (o1, o2) -> o1.plus(o2), 0) - .addBinaryOperator("-", (o1, o2) -> o1.minus(o2), 0) - .addBinaryOperator("*", (o1, o2) -> o1.times(o2), 1) - .addSpaceFunction("*") - .addBinaryOperator("/", (o1, o2) -> o1.dividedBy(o2), 1) - .addBinaryOperator("^", UnitDatabase::exponentiateUnits, 2) - .build(); - - /** - * A parser that can parse unit value expressions. - * - * @since 2020-08-04 - */ - private final ExpressionParser<LinearUnitValue> unitValueExpressionParser = new ExpressionParser.Builder<>( - this::getLinearUnitValue) - .addBinaryOperator("+", (o1, o2) -> o1.plus(o2), 0) - .addBinaryOperator("-", (o1, o2) -> o1.minus(o2), 0) - .addBinaryOperator("*", (o1, o2) -> o1.times(o2), 1) - .addSpaceFunction("*") - .addBinaryOperator("/", (o1, o2) -> o1.dividedBy(o2), 1) - .addBinaryOperator("^", UnitDatabase::exponentiateUnitValues, 2) - .build(); - - /** - * A parser that can parse unit prefix expressions - * - * @since 2019-04-13 - * @since v0.2.0 - */ - private final ExpressionParser<UnitPrefix> prefixExpressionParser = new ExpressionParser.Builder<>( - this::getPrefix).addBinaryOperator("*", (o1, o2) -> o1.times(o2), 0) - .addSpaceFunction("*") - .addBinaryOperator("/", (o1, o2) -> o1.dividedBy(o2), 0) - .addBinaryOperator("^", - (o1, o2) -> o1.toExponent(o2.getMultiplier()), 1) - .build(); - - /** - * A parser that can parse unit dimension expressions. - * - * @since 2019-04-13 - * @since v0.2.0 - */ - private final ExpressionParser<ObjectProduct<BaseDimension>> unitDimensionParser = new ExpressionParser.Builder<>( - this::getDimension).addBinaryOperator("*", (o1, o2) -> o1.times(o2), 0) - .addSpaceFunction("*") - .addBinaryOperator("/", (o1, o2) -> o1.dividedBy(o2), 0).build(); - - /** - * Creates the {@code UnitsDatabase}. - * - * @since 2019-01-10 - * @since v0.1.0 - */ - public UnitDatabase() { - this(prefixes -> true); - } - - /** - * Creates the {@code UnitsDatabase} - * - * @param prefixRepetitionRule the rule that determines when prefix - * repetition is allowed - * @since 2020-08-26 - */ - public UnitDatabase(Predicate<List<UnitPrefix>> prefixRepetitionRule) { - this.prefixlessUnits = new HashMap<>(); - this.prefixes = new HashMap<>(); - this.dimensions = new HashMap<>(); - this.prefixRepetitionRule = prefixRepetitionRule; - this.units = ConditionalExistenceCollections.conditionalExistenceMap( - new PrefixedUnitMap(this.prefixlessUnits, this.prefixes), - entry -> this.prefixRepetitionRule - .test(this.getPrefixesFromName(entry.getKey()))); - } - - /** - * Adds a unit dimension to the database. - * - * @param name dimension's name - * @param dimension dimension to add - * @throws NullPointerException if name or dimension is null - * @since 2019-03-14 - * @since v0.2.0 - */ - public void addDimension(final String name, - final ObjectProduct<BaseDimension> dimension) { - this.dimensions.put( - Objects.requireNonNull(name, "name must not be null."), - Objects.requireNonNull(dimension, "dimension must not be null.")); - } - - /** - * Adds to the list from a line in a unit dimension file. - * - * @param line line to look at - * @param lineCounter number of line, for error messages - * @since 2019-04-10 - * @since v0.2.0 - */ - private void addDimensionFromLine(final String line, - final long lineCounter) { - // ignore lines that start with a # sign - they're comments - if (line.isEmpty()) - return; - if (line.contains("#")) { - this.addDimensionFromLine(line.substring(0, line.indexOf("#")), - lineCounter); - return; - } - - // divide line into name and expression - final Matcher lineMatcher = NAME_EXPRESSION.matcher(line); - if (!lineMatcher.matches()) - throw new IllegalArgumentException(String.format( - "Error at line %d: Lines of a dimension file must consist of a dimension name, then spaces or tabs, then a dimension expression.", - lineCounter)); - final String name = lineMatcher.group(1); - final String expression = lineMatcher.group(2); - - if (name.endsWith(" ")) { - System.err.printf("Warning - line %d's dimension name ends in a space", - lineCounter); - } - - // if expression is "!", search for an existing dimension - // if no unit found, throw an error - if (expression.equals("!")) { - if (!this.containsDimensionName(name)) - throw new IllegalArgumentException(String.format( - "! used but no dimension found (line %d).", lineCounter)); - } else { - // it's a unit, get the unit - final ObjectProduct<BaseDimension> dimension; - try { - dimension = this.getDimensionFromExpression(expression); - } catch (final IllegalArgumentException e) { - System.err.printf("Parsing error on line %d:%n", lineCounter); - throw e; - } - - this.addDimension(name, dimension); - } - } - - /** - * Adds a unit prefix to the database. - * - * @param name prefix's name - * @param prefix prefix to add - * @throws NullPointerException if name or prefix is null - * @since 2019-01-14 - * @since v0.1.0 - */ - public void addPrefix(final String name, final UnitPrefix prefix) { - this.prefixes.put(Objects.requireNonNull(name, "name must not be null."), - Objects.requireNonNull(prefix, "prefix must not be null.")); - } - - /** - * Adds a unit to the database. - * - * @param name unit's name - * @param unit unit to add - * @throws NullPointerException if unit is null - * @since 2019-01-10 - * @since v0.1.0 - */ - public void addUnit(final String name, final Unit unit) { - this.prefixlessUnits.put( - Objects.requireNonNull(name, "name must not be null."), - Objects.requireNonNull(unit, "unit must not be null.")); - } - - /** - * Adds to the list from a line in a unit file. - * - * @param line line to look at - * @param lineCounter number of line, for error messages - * @since 2019-04-10 - * @since v0.2.0 - */ - private void addUnitOrPrefixFromLine(final String line, - final long lineCounter) { - // ignore lines that start with a # sign - they're comments - if (line.isEmpty()) - return; - if (line.contains("#")) { - this.addUnitOrPrefixFromLine(line.substring(0, line.indexOf("#")), - lineCounter); - return; - } - - // divide line into name and expression - final Matcher lineMatcher = NAME_EXPRESSION.matcher(line); - if (!lineMatcher.matches()) - throw new IllegalArgumentException(String.format( - "Error at line %d: Lines of a unit file must consist of a unit name, then spaces or tabs, then a unit expression.", - lineCounter)); - final String name = lineMatcher.group(1); - - final String expression = lineMatcher.group(2); - - if (name.endsWith(" ")) { - System.err.printf("Warning - line %d's unit name ends in a space", - lineCounter); - } - - // if expression is "!", search for an existing unit - // if no unit found, throw an error - if (expression.equals("!")) { - if (!this.containsUnitName(name)) - throw new IllegalArgumentException(String - .format("! used but no unit found (line %d).", lineCounter)); - } else { - if (name.endsWith("-")) { - final UnitPrefix prefix; - try { - prefix = this.getPrefixFromExpression(expression); - } catch (final IllegalArgumentException e) { - System.err.printf("Parsing error on line %d:%n", lineCounter); - throw e; - } - this.addPrefix(name.substring(0, name.length() - 1), prefix); - } else { - // it's a unit, get the unit - final Unit unit; - try { - unit = this.getUnitFromExpression(expression); - } catch (final IllegalArgumentException e) { - System.err.printf("Parsing error on line %d:%n", lineCounter); - throw e; - } - - this.addUnit(name, unit); - } - } - } - - /** - * Tests if the database has a unit dimension with this name. - * - * @param name name to test - * @return if database contains name - * @since 2019-03-14 - * @since v0.2.0 - */ - public boolean containsDimensionName(final String name) { - return this.dimensions.containsKey(name); - } - - /** - * Tests if the database has a unit prefix with this name. - * - * @param name name to test - * @return if database contains name - * @since 2019-01-13 - * @since v0.1.0 - */ - public boolean containsPrefixName(final String name) { - return this.prefixes.containsKey(name); - } - - /** - * Tests if the database has a unit with this name, taking prefixes into - * consideration - * - * @param name name to test - * @return if database contains name - * @since 2019-01-13 - * @since v0.1.0 - */ - public boolean containsUnitName(final String name) { - return this.units.containsKey(name); - } - - /** - * @return a map mapping dimension names to dimensions - * @since 2019-04-13 - * @since v0.2.0 - */ - public Map<String, ObjectProduct<BaseDimension>> dimensionMap() { - return Collections.unmodifiableMap(this.dimensions); - } - - /** - * Evaluates a unit expression, following the same rules as - * {@link #getUnitFromExpression}. - * - * @param expression expression to parse - * @return {@code LinearUnitValue} representing value of expression - * @since 2020-08-04 - */ - public LinearUnitValue evaluateUnitExpression(final String expression) { - Objects.requireNonNull(expression, "expression must not be null."); - - // attempt to get a unit as an alias, or a number with precision first - if (this.containsUnitName(expression)) - return this.getLinearUnitValue(expression); - - // force operators to have spaces - String modifiedExpression = expression; - modifiedExpression = modifiedExpression.replaceAll("\\+", " \\+ "); - modifiedExpression = modifiedExpression.replaceAll("-", " - "); - - // format expression - for (final Entry<Pattern, String> replacement : EXPRESSION_REPLACEMENTS - .entrySet()) { - modifiedExpression = replacement.getKey().matcher(modifiedExpression) - .replaceAll(replacement.getValue()); - } - - // the previous operation breaks negative numbers, fix them! - // (i.e. -2 becomes - 2) - // FIXME the previous operaton also breaks stuff like "1e-5" - for (int i = 0; i < modifiedExpression.length(); i++) { - if (modifiedExpression.charAt(i) == '-' - && (i < 2 || Arrays.asList('+', '-', '*', '/', '^') - .contains(modifiedExpression.charAt(i - 2)))) { - // found a broken negative number - modifiedExpression = modifiedExpression.substring(0, i + 1) - + modifiedExpression.substring(i + 2); - } - } - - return this.unitValueExpressionParser.parseExpression(modifiedExpression); - } - - /** - * Gets a unit dimension from the database using its name. - * - * <p> - * This method accepts exponents, like "L^3" - * </p> - * - * @param name dimension's name - * @return dimension - * @since 2019-03-14 - * @since v0.2.0 - */ - public ObjectProduct<BaseDimension> getDimension(final String name) { - Objects.requireNonNull(name, "name must not be null."); - if (name.contains("^")) { - final String[] baseAndExponent = name.split("\\^"); - - final ObjectProduct<BaseDimension> base = this - .getDimension(baseAndExponent[0]); - - final int exponent; - try { - exponent = Integer - .parseInt(baseAndExponent[baseAndExponent.length - 1]); - } catch (final NumberFormatException e2) { - throw new IllegalArgumentException("Exponent must be an integer."); - } - - return base.toExponent(exponent); - } - return this.dimensions.get(name); - } - - /** - * Uses the database's data to parse an expression into a unit dimension - * <p> - * The expression is a series of any of the following: - * <ul> - * <li>The name of a unit dimension, which multiplies or divides the result - * based on preceding operators</li> - * <li>The operators '*' and '/', which multiply and divide (note that just - * putting two unit dimensions next to each other is equivalent to - * multiplication)</li> - * <li>The operator '^' which exponentiates. Exponents must be integers.</li> - * </ul> - * - * @param expression expression to parse - * @throws IllegalArgumentException if the expression cannot be parsed - * @throws NullPointerException if expression is null - * @since 2019-04-13 - * @since v0.2.0 - */ - public ObjectProduct<BaseDimension> getDimensionFromExpression( - final String expression) { - Objects.requireNonNull(expression, "expression must not be null."); - - // attempt to get a dimension as an alias first - if (this.containsDimensionName(expression)) - return this.getDimension(expression); - - // force operators to have spaces - String modifiedExpression = expression; - - // format expression - for (final Entry<Pattern, String> replacement : EXPRESSION_REPLACEMENTS - .entrySet()) { - modifiedExpression = replacement.getKey().matcher(modifiedExpression) - .replaceAll(replacement.getValue()); - } - modifiedExpression = modifiedExpression.replaceAll(" *\\^ *", "\\^"); - - return this.unitDimensionParser.parseExpression(modifiedExpression); - } - - /** - * Gets a unit. If it is linear, cast it to a LinearUnit and return it. - * Otherwise, throw an {@code IllegalArgumentException}. - * - * @param name unit's name - * @return unit - * @since 2019-03-22 - * @since v0.2.0 - */ - private LinearUnit getLinearUnit(final String name) { - // see if I am using a function-unit like tempC(100) - Objects.requireNonNull(name, "name may not be null"); - if (name.contains("(") && name.contains(")")) { - // break it into function name and value - final List<String> parts = Arrays.asList(name.split("\\(")); - if (parts.size() != 2) - throw new IllegalArgumentException( - "Format nonlinear units like: unit(value)."); - - // solve the function - final Unit unit = this.getUnit(parts.get(0)); - final double value = Double.parseDouble( - parts.get(1).substring(0, parts.get(1).length() - 1)); - return LinearUnit.fromUnitValue(unit, value); - } else { - // get a linear unit - final Unit unit = this.getUnit(name); - - if (unit instanceof LinearUnit) - return (LinearUnit) unit; - else - throw new IllegalArgumentException( - String.format("%s is not a linear unit.", name)); - } - } - - /** - * Gets a {@code LinearUnitValue} from a unit name. Nonlinear units will be - * converted to their base units. - * - * @param name name of unit - * @return {@code LinearUnitValue} instance - * @since 2020-08-04 - */ - private LinearUnitValue getLinearUnitValue(final String name) { - try { - // try to parse it as a number - otherwise it is not a number! - final BigDecimal number = new BigDecimal(name); - - final double uncertainty = Math.pow(10, -number.scale()); - return LinearUnitValue.of(SI.ONE, - UncertainDouble.of(number.doubleValue(), uncertainty)); - } catch (final NumberFormatException e) { - return LinearUnitValue.getExact(this.getLinearUnit(name), 1); - } - } - - /** - * Gets a unit prefix from the database from its name - * - * @param name prefix's name - * @return prefix - * @since 2019-01-10 - * @since v0.1.0 - */ - public UnitPrefix getPrefix(final String name) { - try { - return UnitPrefix.valueOf(Double.parseDouble(name)); - } catch (final NumberFormatException e) { - return this.prefixes.get(name); - } - } - - /** - * Gets all of the prefixes that are on a unit name, in application order. - * - * @param unitName name of unit - * @return prefixes - * @since 2020-08-26 - */ - List<UnitPrefix> getPrefixesFromName(final String unitName) { - final List<UnitPrefix> prefixes = new ArrayList<>(); - String name = unitName; - - while (!this.prefixlessUnits.containsKey(name)) { - // find the longest prefix - String longestPrefixName = null; - int longestLength = name.length(); - - while (longestPrefixName == null) { - longestLength--; - if (longestLength <= 0) - throw new AssertionError( - "No prefix found in " + name + ", but it is not a unit!"); - if (this.prefixes.containsKey(name.substring(0, longestLength))) { - longestPrefixName = name.substring(0, longestLength); - } - } - - // longest prefix found! - final UnitPrefix prefix = this.getPrefix(longestPrefixName); - prefixes.add(0, prefix); - name = name.substring(longestLength); - } - return prefixes; - } - - /** - * Gets a unit prefix from a prefix expression - * <p> - * Currently, prefix expressions are much simpler than unit expressions: They - * are either a number or the name of another prefix - * </p> - * - * @param expression expression to input - * @return prefix - * @throws IllegalArgumentException if expression cannot be parsed - * @throws NullPointerException if any argument is null - * @since 2019-01-14 - * @since v0.1.0 - */ - public UnitPrefix getPrefixFromExpression(final String expression) { - Objects.requireNonNull(expression, "expression must not be null."); - - // attempt to get a unit as an alias first - if (this.containsUnitName(expression)) - return this.getPrefix(expression); - - // force operators to have spaces - String modifiedExpression = expression; - - // format expression - for (final Entry<Pattern, String> replacement : EXPRESSION_REPLACEMENTS - .entrySet()) { - modifiedExpression = replacement.getKey().matcher(modifiedExpression) - .replaceAll(replacement.getValue()); - } - - return this.prefixExpressionParser.parseExpression(modifiedExpression); - } - - /** - * @return the prefixRepetitionRule - * @since 2020-08-26 - */ - public final Predicate<List<UnitPrefix>> getPrefixRepetitionRule() { - return this.prefixRepetitionRule; - } - - /** - * Gets a unit from the database from its name, looking for prefixes. - * - * @param name unit's name - * @return unit - * @since 2019-01-10 - * @since v0.1.0 - */ - public Unit getUnit(final String name) { - try { - final double value = Double.parseDouble(name); - return SI.ONE.times(value); - } catch (final NumberFormatException e) { - final Unit unit = this.units.get(name); - if (unit == null) - throw new NoSuchElementException("No unit " + name); - else if (unit.getPrimaryName().isEmpty()) - return unit.withName(NameSymbol.ofName(name)); - else if (!unit.getPrimaryName().get().equals(name)) { - final Set<String> otherNames = new HashSet<>(unit.getOtherNames()); - otherNames.add(unit.getPrimaryName().get()); - return unit.withName(NameSymbol.ofNullable(name, - unit.getSymbol().orElse(null), otherNames)); - } else if (!unit.getOtherNames().contains(name)) { - final Set<String> otherNames = new HashSet<>(unit.getOtherNames()); - otherNames.add(name); - return unit.withName( - NameSymbol.ofNullable(unit.getPrimaryName().orElse(null), - unit.getSymbol().orElse(null), otherNames)); - } else - return unit; - } - - } - - /** - * Uses the database's unit data to parse an expression into a unit - * <p> - * The expression is a series of any of the following: - * <ul> - * <li>The name of a unit, which multiplies or divides the result based on - * preceding operators</li> - * <li>The operators '*' and '/', which multiply and divide (note that just - * putting two units or values next to each other is equivalent to - * multiplication)</li> - * <li>The operator '^' which exponentiates. Exponents must be integers.</li> - * <li>A number which is multiplied or divided</li> - * </ul> - * This method only works with linear units. - * - * @param expression expression to parse - * @throws IllegalArgumentException if the expression cannot be parsed - * @throws NullPointerException if expression is null - * @since 2019-01-07 - * @since v0.1.0 - */ - public Unit getUnitFromExpression(final String expression) { - Objects.requireNonNull(expression, "expression must not be null."); - - // attempt to get a unit as an alias first - if (this.containsUnitName(expression)) - return this.getUnit(expression); - - // force operators to have spaces - String modifiedExpression = expression; - modifiedExpression = modifiedExpression.replaceAll("\\+", " \\+ "); - modifiedExpression = modifiedExpression.replaceAll("-", " - "); - - // format expression - for (final Entry<Pattern, String> replacement : EXPRESSION_REPLACEMENTS - .entrySet()) { - modifiedExpression = replacement.getKey().matcher(modifiedExpression) - .replaceAll(replacement.getValue()); - } - - // the previous operation breaks negative numbers, fix them! - // (i.e. -2 becomes - 2) - for (int i = 0; i < modifiedExpression.length(); i++) { - if (modifiedExpression.charAt(i) == '-' - && (i < 2 || Arrays.asList('+', '-', '*', '/', '^') - .contains(modifiedExpression.charAt(i - 2)))) { - // found a broken negative number - modifiedExpression = modifiedExpression.substring(0, i + 1) - + modifiedExpression.substring(i + 2); - } - } - - return this.unitExpressionParser.parseExpression(modifiedExpression); - } - - /** - * Adds all dimensions from a file, using data from the database to parse - * them. - * <p> - * Each line in the file should consist of a name and an expression (parsed - * by getDimensionFromExpression) separated by any number of tab characters. - * <p> - * <p> - * Allowed exceptions: - * <ul> - * <li>Anything after a '#' character is considered a comment and - * ignored.</li> - * <li>Blank lines are also ignored</li> - * <li>If an expression consists of a single exclamation point, instead of - * parsing it, this method will search the database for an existing unit. If - * no unit is found, an IllegalArgumentException is thrown. This is used to - * define initial units and ensure that the database contains them.</li> - * </ul> - * - * @param file file to read - * @throws IllegalArgumentException if the file cannot be parsed, found or - * read - * @throws NullPointerException if file is null - * @since 2019-01-13 - * @since v0.1.0 - */ - public void loadDimensionFile(final Path file) { - Objects.requireNonNull(file, "file must not be null."); - try { - long lineCounter = 0; - for (final String line : Files.readAllLines(file)) { - this.addDimensionFromLine(line, ++lineCounter); - } - } catch (final FileNotFoundException e) { - throw new IllegalArgumentException("Could not find file " + file, e); - } catch (final IOException e) { - throw new IllegalArgumentException("Could not read file " + file, e); - } - } - - /** - * Adds all units from a file, using data from the database to parse them. - * <p> - * Each line in the file should consist of a name and an expression (parsed - * by getUnitFromExpression) separated by any number of tab characters. - * <p> - * <p> - * Allowed exceptions: - * <ul> - * <li>Anything after a '#' character is considered a comment and - * ignored.</li> - * <li>Blank lines are also ignored</li> - * <li>If an expression consists of a single exclamation point, instead of - * parsing it, this method will search the database for an existing unit. If - * no unit is found, an IllegalArgumentException is thrown. This is used to - * define initial units and ensure that the database contains them.</li> - * </ul> - * - * @param file file to read - * @throws IllegalArgumentException if the file cannot be parsed, found or - * read - * @throws NullPointerException if file is null - * @since 2019-01-13 - * @since v0.1.0 - */ - public void loadUnitsFile(final Path file) { - Objects.requireNonNull(file, "file must not be null."); - try { - long lineCounter = 0; - for (final String line : Files.readAllLines(file)) { - this.addUnitOrPrefixFromLine(line, ++lineCounter); - } - } catch (final FileNotFoundException e) { - throw new IllegalArgumentException("Could not find file " + file, e); - } catch (final IOException e) { - throw new IllegalArgumentException("Could not read file " + file, e); - } - } - - /** - * @return a map mapping prefix names to prefixes - * @since 2019-04-13 - * @since v0.2.0 - */ - public Map<String, UnitPrefix> prefixMap() { - return Collections.unmodifiableMap(this.prefixes); - } - - /** - * @param prefixRepetitionRule the prefixRepetitionRule to set - * @since 2020-08-26 - */ - public final void setPrefixRepetitionRule( - Predicate<List<UnitPrefix>> prefixRepetitionRule) { - this.prefixRepetitionRule = prefixRepetitionRule; - } - - /** - * @return a string stating the number of units, prefixes and dimensions in - * the database - */ - @Override - public String toString() { - return String.format( - "Unit Database with %d units, %d unit prefixes and %d dimensions", - this.prefixlessUnits.size(), this.prefixes.size(), - this.dimensions.size()); - } - - /** - * Returns a map mapping unit names to units, including units with prefixes. - * <p> - * The returned map is infinite in size if there is at least one unit and at - * least one prefix. If it is infinite, some operations that only work with - * finite collections, like converting name/entry sets to arrays, will throw - * an {@code IllegalStateException}. - * </p> - * <p> - * Specifically, the operations that will throw an IllegalStateException if - * the map is infinite in size are: - * <ul> - * <li>{@code unitMap.entrySet().toArray()} (either overloading)</li> - * <li>{@code unitMap.keySet().toArray()} (either overloading)</li> - * </ul> - * </p> - * <p> - * Because of ambiguities between prefixes (i.e. kilokilo = mega), the map's - * {@link PrefixedUnitMap#containsValue containsValue} and - * {@link PrefixedUnitMap#values() values()} methods currently ignore - * prefixes. - * </p> - * - * @return a map mapping unit names to units, including prefixed names - * @since 2019-04-13 - * @since v0.2.0 - */ - public Map<String, Unit> unitMap() { - return this.units; // PrefixedUnitMap is immutable so I don't need to make - // an unmodifiable map. - } - - /** - * @return a map mapping unit names to units, ignoring prefixes - * @since 2019-04-13 - * @since v0.2.0 - */ - public Map<String, Unit> unitMapPrefixless() { - return Collections.unmodifiableMap(this.prefixlessUnits); - } -} diff --git a/src/org/unitConverter/unit/UnitDatabaseTest.java b/src/org/unitConverter/unit/UnitDatabaseTest.java deleted file mode 100644 index 2b981b6..0000000 --- a/src/org/unitConverter/unit/UnitDatabaseTest.java +++ /dev/null @@ -1,309 +0,0 @@ -/** - * 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.NoSuchElementException; -import java.util.Set; - -import org.junit.jupiter.api.Test; - -/** - * A test for the {@link UnitDatabase} class. This is NOT part of this program's - * public API. - * - * @author Adrien Hopkins - * @since 2019-04-14 - * @since v0.2.0 - */ -class UnitDatabaseTest { - // some linear units and one nonlinear - private static final Unit U = SI.METRE; - private static final Unit V = SI.KILOGRAM; - private static final Unit W = SI.SECOND; - - // used for testing expressions - // J = U^2 * V / W^2 - private static final LinearUnit J = SI.KILOGRAM.times(SI.METRE.toExponent(2)) - .dividedBy(SI.SECOND.toExponent(2)); - private static final LinearUnit K = SI.KELVIN; - - private static final Unit NONLINEAR = Unit - .fromConversionFunctions(SI.METRE.getBase(), o -> o + 1, o -> o - 1); - - // make the prefix values prime so I can tell which multiplications were made - private static final UnitPrefix A = UnitPrefix.valueOf(2) - .withName(NameSymbol.ofName("A")); - private static final UnitPrefix B = UnitPrefix.valueOf(3) - .withName(NameSymbol.ofName("B")); - private static final UnitPrefix C = UnitPrefix.valueOf(5) - .withName(NameSymbol.ofName("C")); - private static final UnitPrefix AB = UnitPrefix.valueOf(7); - private static final UnitPrefix BC = UnitPrefix.valueOf(11); - - /** - * Confirms that operations that shouldn't function for infinite databases - * throw an {@code IllegalStateException}. - * - * @since 2019-05-03 - */ - @Test - // @Timeout(value = 5, unit = TimeUnit.SECONDS) - public void testInfiniteSetExceptions() { - // load units - final UnitDatabase infiniteDatabase = new UnitDatabase(); - - infiniteDatabase.addUnit("J", J); - infiniteDatabase.addUnit("K", K); - - infiniteDatabase.addPrefix("A", A); - infiniteDatabase.addPrefix("B", B); - infiniteDatabase.addPrefix("C", C); - - final Set<Entry<String, Unit>> entrySet = infiniteDatabase.unitMap() - .entrySet(); - final Set<String> keySet = infiniteDatabase.unitMap().keySet(); - assertThrows(IllegalStateException.class, () -> entrySet.toArray()); - assertThrows(IllegalStateException.class, () -> keySet.toArray()); - } - - /** - * Test that prefixes correctly apply to units. - * - * @since 2019-04-14 - * @since v0.2.0 - */ - @Test - public void testPrefixes() { - final UnitDatabase database = new UnitDatabase(); - - database.addUnit("U", U); - database.addUnit("V", V); - database.addUnit("W", W); - - database.addPrefix("A", A); - database.addPrefix("B", B); - database.addPrefix("C", C); - - // test the getPrefixesFromName method - final List<UnitPrefix> expected = Arrays.asList(C, B, A); - assertEquals(expected, database.getPrefixesFromName("ABCU")); - - // get the product - final Unit abcuNonlinear = database.getUnit("ABCU"); - assert abcuNonlinear instanceof LinearUnit; - - final LinearUnit abcu = (LinearUnit) abcuNonlinear; - assertEquals(A.getMultiplier() * B.getMultiplier() * C.getMultiplier(), - abcu.getConversionFactor(), 1e-15); - } - - /** - * Tests the functionnalites of the prefixless unit map. - * - * <p> - * The map should be an auto-updating view of the units in the database. - * </p> - * - * @since 2019-04-14 - * @since v0.2.0 - */ - @Test - public void testPrefixlessUnitMap() { - final UnitDatabase database = new UnitDatabase(); - final Map<String, Unit> prefixlessUnits = database.unitMapPrefixless(); - - database.addUnit("U", U); - database.addUnit("V", V); - database.addUnit("W", W); - - // this should work because the map should be an auto-updating view - assertTrue(prefixlessUnits.containsKey("U")); - assertFalse(prefixlessUnits.containsKey("Z")); - - assertTrue(prefixlessUnits.containsValue(U)); - assertFalse(prefixlessUnits.containsValue(NONLINEAR)); - } - - /** - * Tests that the database correctly stores and retrieves units, ignoring - * prefixes. - * - * @since 2019-04-14 - * @since v0.2.0 - */ - @Test - public void testPrefixlessUnits() { - final UnitDatabase database = new UnitDatabase(); - - database.addUnit("U", U); - database.addUnit("V", V); - database.addUnit("W", W); - - assertTrue(database.containsUnitName("U")); - assertFalse(database.containsUnitName("Z")); - - assertEquals(U, database.getUnit("U")); - assertThrows(NoSuchElementException.class, () -> database.getUnit("Z")); - } - - /** - * Test that unit expressions return the correct value. - * - * @since 2019-04-14 - * @since v0.2.0 - */ - @Test - public void testUnitExpressions() { - // load units - final UnitDatabase database = new UnitDatabase(); - - database.addUnit("U", U); - database.addUnit("V", V); - database.addUnit("W", W); - database.addUnit("fj", J.times(5)); - database.addUnit("ej", J.times(8)); - - database.addPrefix("A", A); - database.addPrefix("B", B); - database.addPrefix("C", C); - - // first test - test prefixes and operations - final Unit expected1 = J.withPrefix(A).withPrefix(B).withPrefix(C) - .withPrefix(C); - final Unit actual1 = database.getUnitFromExpression("ABV * CU^2 / W / W"); - - assertEquals(expected1, actual1); - - // second test - test addition and subtraction - final Unit expected2 = J.times(58); - final Unit actual2 = database.getUnitFromExpression("2 fj + 6 ej"); - - assertEquals(expected2, actual2); - } - - /** - * Tests both the unit name iterator and the name-unit entry iterator - * - * @since 2019-04-14 - * @since v0.2.0 - */ - @Test - public void testUnitIterator() { - // load units - final UnitDatabase database = new UnitDatabase(); - - database.addUnit("J", J); - database.addUnit("K", K); - - database.addPrefix("A", A); - database.addPrefix("B", B); - database.addPrefix("C", C); - - final int NUM_UNITS = database.unitMapPrefixless().size(); - final int NUM_PREFIXES = database.prefixMap().size(); - - final Iterator<String> nameIterator = database.unitMap().keySet() - .iterator(); - final Iterator<Entry<String, Unit>> entryIterator = database.unitMap() - .entrySet().iterator(); - - int expectedLength = 1; - int unitsWithThisLengthSoFar = 0; - - // loop 1000 times - for (int i = 0; i < 1000; i++) { - // expected length of next - if (unitsWithThisLengthSoFar >= NUM_UNITS - * (int) Math.pow(NUM_PREFIXES, expectedLength - 1)) { - expectedLength++; - unitsWithThisLengthSoFar = 0; - } - - // test that stuff is valid - final String nextName = nameIterator.next(); - final Unit nextUnit = database.getUnit(nextName); - final Entry<String, Unit> nextEntry = entryIterator.next(); - - assertEquals(expectedLength, nextName.length()); - assertEquals(nextName, nextEntry.getKey()); - assertEquals(nextUnit, nextEntry.getValue()); - - unitsWithThisLengthSoFar++; - } - - // test toString for consistency - final String entryIteratorString = entryIterator.toString(); - for (int i = 0; i < 3; i++) { - assertEquals(entryIteratorString, entryIterator.toString()); - } - - final String nameIteratorString = nameIterator.toString(); - for (int i = 0; i < 3; i++) { - assertEquals(nameIteratorString, nameIterator.toString()); - } - } - - /** - * Determine, given a unit name that could mean multiple things, which - * meaning is chosen. - * <p> - * For example, "ABCU" could mean "A-B-C-U", "AB-C-U", or "A-BC-U". In this - * case, "AB-C-U" is the correct choice. - * </p> - * - * @since 2019-04-14 - * @since v0.2.0 - */ - @Test - public void testUnitPrefixCombinations() { - // load units - final UnitDatabase database = new UnitDatabase(); - - database.addUnit("J", J); - - database.addPrefix("A", A); - database.addPrefix("B", B); - database.addPrefix("C", C); - database.addPrefix("AB", AB); - database.addPrefix("BC", BC); - - // test 1 - AB-C-J vs A-BC-J vs A-B-C-J - final Unit expected1 = J.withPrefix(AB).withPrefix(C); - final Unit actual1 = database.getUnit("ABCJ"); - - assertEquals(expected1, actual1); - - // test 2 - ABC-J vs AB-CJ vs AB-C-J - database.addUnit("CJ", J.times(13)); - database.addPrefix("ABC", UnitPrefix.valueOf(17)); - - final Unit expected2 = J.times(17); - final Unit actual2 = database.getUnit("ABCJ"); - - assertEquals(expected2, actual2); - } -} diff --git a/src/org/unitConverter/unit/UnitPrefix.java b/src/org/unitConverter/unit/UnitPrefix.java deleted file mode 100644 index 31cc0b3..0000000 --- a/src/org/unitConverter/unit/UnitPrefix.java +++ /dev/null @@ -1,242 +0,0 @@ -/** - * 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -import java.util.Objects; -import java.util.Optional; -import java.util.Set; - -import org.unitConverter.math.DecimalComparison; - -/** - * A prefix that can be applied to a {@code LinearUnit} to multiply it by some value - * - * @author Adrien Hopkins - * @since 2019-10-16 - */ -public final class UnitPrefix { - /** - * Gets a {@code UnitPrefix} from a multiplier - * - * @param multiplier - * multiplier of prefix - * @return prefix - * @since 2019-10-16 - */ - public static UnitPrefix valueOf(final double multiplier) { - return new UnitPrefix(multiplier, NameSymbol.EMPTY); - } - - /** - * Gets a {@code UnitPrefix} from a multiplier and a name - * - * @param multiplier - * multiplier of prefix - * @param ns - * name(s) and symbol of prefix - * @return prefix - * @since 2019-10-16 - * @throws NullPointerException - * if ns is null - */ - public static UnitPrefix valueOf(final double multiplier, final NameSymbol ns) { - return new UnitPrefix(multiplier, Objects.requireNonNull(ns, "ns must not be null.")); - } - - /** - * This prefix's primary name - */ - private final Optional<String> primaryName; - - /** - * This prefix's symbol - */ - private final Optional<String> symbol; - - /** - * Other names and symbols used by this prefix - */ - private final Set<String> otherNames; - - /** - * The number that this prefix multiplies units by - * - * @since 2019-10-16 - */ - private final double multiplier; - - /** - * Creates the {@code DefaultUnitPrefix}. - * - * @param multiplier - * @since 2019-01-14 - * @since v0.2.0 - */ - private UnitPrefix(final double multiplier, final NameSymbol ns) { - this.multiplier = multiplier; - this.primaryName = ns.getPrimaryName(); - this.symbol = ns.getSymbol(); - this.otherNames = ns.getOtherNames(); - } - - /** - * Divides this prefix by a scalar - * - * @param divisor - * number to divide by - * @return quotient of prefix and scalar - * @since 2019-10-16 - */ - public UnitPrefix dividedBy(final double divisor) { - return valueOf(this.getMultiplier() / divisor); - } - - /** - * Divides this prefix by {@code other}. - * - * @param other - * prefix to divide by - * @return quotient of prefixes - * @since 2019-04-13 - * @since v0.2.0 - */ - public UnitPrefix dividedBy(final UnitPrefix other) { - return valueOf(this.getMultiplier() / other.getMultiplier()); - } - - /** - * {@inheritDoc} - * - * Uses the prefix's multiplier to determine equality. - */ - @Override - public boolean equals(final Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof UnitPrefix)) - return false; - final UnitPrefix other = (UnitPrefix) obj; - return DecimalComparison.equals(this.getMultiplier(), other.getMultiplier()); - } - - /** - * @return prefix's multiplier - * @since 2019-11-26 - */ - public double getMultiplier() { - return this.multiplier; - } - - /** - * @return other names - * @since 2019-11-26 - */ - public final Set<String> getOtherNames() { - return this.otherNames; - } - - /** - * @return primary name - * @since 2019-11-26 - */ - public final Optional<String> getPrimaryName() { - return this.primaryName; - } - - /** - * @return symbol - * @since 2019-11-26 - */ - public final Optional<String> getSymbol() { - return this.symbol; - } - - /** - * {@inheritDoc} - * - * Uses the prefix's multiplier to determine a hash code. - */ - @Override - public int hashCode() { - return DecimalComparison.hash(this.getMultiplier()); - } - - /** - * Multiplies this prefix by a scalar - * - * @param multiplicand - * number to multiply by - * @return product of prefix and scalar - * @since 2019-10-16 - */ - public UnitPrefix times(final double multiplicand) { - return valueOf(this.getMultiplier() * multiplicand); - } - - /** - * Multiplies this prefix by {@code other}. - * - * @param other - * prefix to multiply by - * @return product of prefixes - * @since 2019-04-13 - * @since v0.2.0 - */ - public UnitPrefix times(final UnitPrefix other) { - return valueOf(this.getMultiplier() * other.getMultiplier()); - } - - /** - * Raises this prefix to an exponent. - * - * @param exponent - * exponent to raise to - * @return result of exponentiation. - * @since 2019-04-13 - * @since v0.2.0 - */ - public UnitPrefix toExponent(final double exponent) { - return valueOf(Math.pow(this.getMultiplier(), exponent)); - } - - /** - * @return a string describing the prefix and its multiplier - */ - @Override - public String toString() { - if (this.primaryName.isPresent()) - return String.format("%s (\u00D7 %s)", this.primaryName.get(), this.multiplier); - else if (this.symbol.isPresent()) - return String.format("%s (\u00D7 %s)", this.symbol.get(), this.multiplier); - else - return String.format("Unit Prefix (\u00D7 %s)", this.multiplier); - } - - /** - * @param ns - * name(s) and symbol to use - * @return copy of this prefix with provided name(s) and symbol - * @since 2019-11-26 - * @throws NullPointerException - * if ns is null - */ - public UnitPrefix withName(final NameSymbol ns) { - return valueOf(this.multiplier, ns); - } -} diff --git a/src/org/unitConverter/unit/UnitTest.java b/src/org/unitConverter/unit/UnitTest.java deleted file mode 100644 index c0711dc..0000000 --- a/src/org/unitConverter/unit/UnitTest.java +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Copyright (C) 2018 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -import static org.junit.Assert.assertFalse; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.Random; -import java.util.concurrent.ThreadLocalRandom; - -import org.junit.jupiter.api.Test; -import org.unitConverter.math.DecimalComparison; - -/** - * Testing the various Unit classes. This is NOT part of this program's public - * API. - * - * @author Adrien Hopkins - * @since 2018-12-22 - * @since v0.1.0 - */ -class UnitTest { - /** A random number generator */ - private static final Random rng = ThreadLocalRandom.current(); - - @Test - public void testAdditionAndSubtraction() { - final LinearUnit inch = SI.METRE.times(0.0254) - .withName(NameSymbol.of("inch", "in")); - final LinearUnit foot = SI.METRE.times(0.3048) - .withName(NameSymbol.of("foot", "ft")); - - assertEquals(inch.plus(foot), SI.METRE.times(0.3302)); - assertEquals(foot.minus(inch), SI.METRE.times(0.2794)); - - // test with LinearUnitValue - final LinearUnitValue value1 = LinearUnitValue.getExact(SI.METRE, 15); - final LinearUnitValue value2 = LinearUnitValue.getExact(foot, 120); - final LinearUnitValue value3 = LinearUnitValue.getExact(SI.METRE, 0.5); - final LinearUnitValue value4 = LinearUnitValue.getExact(SI.KILOGRAM, 60); - - // make sure addition is done correctly - assertEquals(51.576, value1.plus(value2).getValueExact(), 0.001); - assertEquals(15.5, value1.plus(value3).getValueExact()); - assertEquals(52.076, value1.plus(value2).plus(value3).getValueExact(), - 0.001); - - // make sure addition uses the correct unit, and is still associative - // (ignoring floating-point rounding errors) - assertEquals(SI.METRE, value1.plus(value2).getUnit()); - assertEquals(SI.METRE, value1.plus(value2).plus(value3).getUnit()); - assertEquals(foot, value2.plus(value1).getUnit()); - assertTrue(value1.plus(value2).equals(value2.plus(value1), true)); - - // make sure errors happen when they should - assertThrows(IllegalArgumentException.class, () -> value1.plus(value4)); - } - - @Test - public void testConversion() { - final LinearUnit metre = SI.METRE; - final Unit inch = metre.times(0.0254); - - final UnitValue value = UnitValue.of(inch, 75); - - assertEquals(1.9, inch.convertTo(metre, 75), 0.01); - assertEquals(1.9, value.convertTo(metre).getValue(), 0.01); - - // try random stuff - for (int i = 0; i < 1000; i++) { - // initiate random values - final double conversionFactor = UnitTest.rng.nextDouble() * 1000000; - final double testValue = UnitTest.rng.nextDouble() * 1000000; - final double expected = testValue * conversionFactor; - - // test - final Unit unit = SI.METRE.times(conversionFactor); - final double actual = unit.convertToBase(testValue); - - assertEquals(actual, expected, - expected * DecimalComparison.DOUBLE_EPSILON); - } - } - - @Test - public void testEquals() { - final LinearUnit metre = SI.METRE; - final Unit meter = SI.BaseUnits.METRE.asLinearUnit(); - - assertEquals(metre, meter); - } - - @Test - public void testIsMetric() { - final Unit metre = SI.METRE; - final Unit megasecond = SI.SECOND.withPrefix(SI.MEGA); - final Unit hour = SI.HOUR; - - assertTrue(metre.isMetric()); - assertTrue(megasecond.isMetric()); - assertFalse(hour.isMetric()); - } - - @Test - public void testMultiplicationAndDivision() { - // test unit-times-unit multiplication - final LinearUnit generatedJoule = SI.KILOGRAM - .times(SI.METRE.toExponent(2)).dividedBy(SI.SECOND.toExponent(2)); - final LinearUnit actualJoule = SI.JOULE; - - assertEquals(generatedJoule, actualJoule); - - // test multiplication by conversion factors - final LinearUnit kilometre = SI.METRE.times(1000); - final LinearUnit hour = SI.SECOND.times(3600); - final LinearUnit generatedKPH = kilometre.dividedBy(hour); - - final LinearUnit actualKPH = SI.METRE.dividedBy(SI.SECOND).dividedBy(3.6); - - assertEquals(generatedKPH, actualKPH); - } - - @Test - public void testPrefixes() { - final LinearUnit generatedKilometre = SI.METRE.withPrefix(SI.KILO); - final LinearUnit actualKilometre = SI.METRE.times(1000); - - assertEquals(generatedKilometre, actualKilometre); - } -} diff --git a/src/org/unitConverter/unit/UnitValue.java b/src/org/unitConverter/unit/UnitValue.java deleted file mode 100644 index c138332..0000000 --- a/src/org/unitConverter/unit/UnitValue.java +++ /dev/null @@ -1,172 +0,0 @@ -/** - * 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 <https://www.gnu.org/licenses/>. - */ -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 <W> boolean canConvertTo(Unitlike<W> 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 <U extends Unitlike<W>, W> UnitlikeValue<U, W> 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<String> primaryName = this.getUnit().getPrimaryName(); - final Optional<String> 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; - } - } -} diff --git a/src/org/unitConverter/unit/Unitlike.java b/src/org/unitConverter/unit/Unitlike.java deleted file mode 100644 index 8077771..0000000 --- a/src/org/unitConverter/unit/Unitlike.java +++ /dev/null @@ -1,260 +0,0 @@ -/** - * Copyright (C) 2020 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.function.DoubleFunction; -import java.util.function.ToDoubleFunction; - -import org.unitConverter.math.ObjectProduct; - -/** - * An object that can convert a value between multiple forms (instances of the - * object); like a unit but the "converted value" can be any type. - * - * @since 2020-09-07 - */ -public abstract class Unitlike<V> implements Nameable { - /** - * Returns a unitlike form from its base and the functions it uses to convert - * to and from its base. - * - * @param base unitlike form's base - * @param converterFrom function that accepts a value expressed in the - * unitlike form's base and returns that value expressed - * in this unitlike form. - * @param converterTo function that accepts a value expressed in the - * unitlike form and returns that value expressed in the - * unit's base. - * @return a unitlike form that uses the provided functions to convert. - * @since 2020-09-07 - * @throws NullPointerException if any argument is null - */ - public static final <W> Unitlike<W> fromConversionFunctions( - final ObjectProduct<BaseUnit> base, - final DoubleFunction<W> converterFrom, - final ToDoubleFunction<W> converterTo) { - return new FunctionalUnitlike<>(base, NameSymbol.EMPTY, converterFrom, - converterTo); - } - - /** - * Returns a unitlike form from its base and the functions it uses to convert - * to and from its base. - * - * @param base unitlike form's base - * @param converterFrom function that accepts a value expressed in the - * unitlike form's base and returns that value expressed - * in this unitlike form. - * @param converterTo function that accepts a value expressed in the - * unitlike form and returns that value expressed in the - * unit's base. - * @param ns names and symbol of unit - * @return a unitlike form that uses the provided functions to convert. - * @since 2020-09-07 - * @throws NullPointerException if any argument is null - */ - public static final <W> Unitlike<W> fromConversionFunctions( - final ObjectProduct<BaseUnit> base, - final DoubleFunction<W> converterFrom, - final ToDoubleFunction<W> converterTo, final NameSymbol ns) { - return new FunctionalUnitlike<>(base, ns, converterFrom, converterTo); - } - - /** - * The combination of units that this unit is based on. - * - * @since 2019-10-16 - */ - private final ObjectProduct<BaseUnit> unitBase; - - /** - * This unit's name(s) and symbol - * - * @since 2020-09-07 - */ - private final NameSymbol nameSymbol; - - /** - * Cache storing the result of getDimension() - * - * @since 2019-10-16 - */ - private transient ObjectProduct<BaseDimension> dimension = null; - - /** - * @param unitBase - * @since 2020-09-07 - */ - protected Unitlike(ObjectProduct<BaseUnit> unitBase, NameSymbol ns) { - this.unitBase = Objects.requireNonNull(unitBase, - "unitBase may not be null"); - this.nameSymbol = Objects.requireNonNull(ns, "ns may not be null"); - } - - /** - * Checks if a value expressed in this unitlike form can be converted to a - * value expressed in {@code other} - * - * @param other unit or unitlike form to test with - * @return true if they are compatible - * @since 2019-01-13 - * @since v0.1.0 - * @throws NullPointerException if other is null - */ - public final boolean canConvertTo(final Unit other) { - Objects.requireNonNull(other, "other must not be null."); - return Objects.equals(this.getBase(), other.getBase()); - } - - /** - * Checks if a value expressed in this unitlike form can be converted to a - * value expressed in {@code other} - * - * @param other unit or unitlike form to test with - * @return true if they are compatible - * @since 2019-01-13 - * @since v0.1.0 - * @throws NullPointerException if other is null - */ - public final <W> boolean canConvertTo(final Unitlike<W> other) { - Objects.requireNonNull(other, "other must not be null."); - return Objects.equals(this.getBase(), other.getBase()); - } - - protected abstract V convertFromBase(double value); - - /** - * Converts a value expressed in this unitlike form to a value expressed in - * {@code other}. - * - * @implSpec If conversion is possible, this implementation returns - * {@code other.convertFromBase(this.convertToBase(value))}. - * Therefore, overriding either of those methods will change the - * output of this method. - * - * @param other unit to convert to - * @param value value to convert - * @return converted value - * @since 2019-05-22 - * @throws IllegalArgumentException if {@code other} is incompatible for - * conversion with this unitlike form (as - * tested by {@link Unit#canConvertTo}). - * @throws NullPointerException if other is null - */ - public final double convertTo(final Unit other, final V value) { - Objects.requireNonNull(other, "other must not be null."); - if (this.canConvertTo(other)) - return other.convertFromBase(this.convertToBase(value)); - else - throw new IllegalArgumentException( - String.format("Cannot convert from %s to %s.", this, other)); - } - - /** - * Converts a value expressed in this unitlike form to a value expressed in - * {@code other}. - * - * @implSpec If conversion is possible, this implementation returns - * {@code other.convertFromBase(this.convertToBase(value))}. - * Therefore, overriding either of those methods will change the - * output of this method. - * - * @param other unitlike form to convert to - * @param value value to convert - * @param <W> type of value to convert to - * @return converted value - * @since 2020-09-07 - * @throws IllegalArgumentException if {@code other} is incompatible for - * conversion with this unitlike form (as - * tested by {@link Unit#canConvertTo}). - * @throws NullPointerException if other is null - */ - public final <W> W convertTo(final Unitlike<W> other, final V value) { - Objects.requireNonNull(other, "other must not be null."); - if (this.canConvertTo(other)) - return other.convertFromBase(this.convertToBase(value)); - else - throw new IllegalArgumentException( - String.format("Cannot convert from %s to %s.", this, other)); - } - - protected abstract double convertToBase(V value); - - /** - * @return combination of units that this unit is based on - * @since 2018-12-22 - * @since v0.1.0 - */ - public final ObjectProduct<BaseUnit> getBase() { - return this.unitBase; - } - - /** - * @return dimension measured by this unit - * @since 2018-12-22 - * @since v0.1.0 - */ - public final ObjectProduct<BaseDimension> getDimension() { - if (this.dimension == null) { - final Map<BaseUnit, Integer> mapping = this.unitBase.exponentMap(); - final Map<BaseDimension, Integer> dimensionMap = new HashMap<>(); - - for (final BaseUnit key : mapping.keySet()) { - dimensionMap.put(key.getBaseDimension(), mapping.get(key)); - } - - this.dimension = ObjectProduct.fromExponentMapping(dimensionMap); - } - return this.dimension; - } - - /** - * @return the nameSymbol - * @since 2020-09-07 - */ - @Override - public final NameSymbol getNameSymbol() { - return this.nameSymbol; - } - - @Override - public String toString() { - return this.getPrimaryName().orElse("Unnamed unitlike form") - + (this.getSymbol().isPresent() - ? String.format(" (%s)", this.getSymbol().get()) - : "") - + ", derived from " - + this.getBase().toString(u -> u.getSymbol().get()) - + (this.getOtherNames().isEmpty() ? "" - : ", also called " + String.join(", ", this.getOtherNames())); - } - - /** - * @param ns name(s) and symbol to use - * @return a copy of this unitlike form with provided name(s) and symbol - * @since 2020-09-07 - * @throws NullPointerException if ns is null - */ - public Unitlike<V> withName(final NameSymbol ns) { - return fromConversionFunctions(this.getBase(), this::convertFromBase, - this::convertToBase, - Objects.requireNonNull(ns, "ns must not be null.")); - } -} diff --git a/src/org/unitConverter/unit/UnitlikeValue.java b/src/org/unitConverter/unit/UnitlikeValue.java deleted file mode 100644 index 79201c4..0000000 --- a/src/org/unitConverter/unit/UnitlikeValue.java +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Copyright (C) 2020 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 <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.unit; - -import java.util.Optional; - -/** - * - * @since 2020-09-07 - */ -final class UnitlikeValue<T extends Unitlike<V>, V> { - /** - * Gets a {@code UnitlikeValue<V>}. - * - * @since 2020-10-02 - */ - public static <T extends Unitlike<V>, V> UnitlikeValue<T, V> of(T unitlike, - V value) { - return new UnitlikeValue<>(unitlike, value); - } - - private final T unitlike; - private final V value; - - /** - * @param unitlike - * @param value - * @since 2020-09-07 - */ - private UnitlikeValue(T unitlike, V value) { - this.unitlike = unitlike; - 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.unitlike.canConvertTo(other); - } - - /** - * @return true if this value can be converted to {@code other}. - * @since 2020-10-01 - */ - public final <W> boolean canConvertTo(Unitlike<W> other) { - return this.unitlike.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 <U extends Unitlike<W>, W> UnitlikeValue<U, W> convertTo( - U other) { - return UnitlikeValue.of(other, - this.unitlike.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.unitlike.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.unitlike).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.getUnitlike().convertTo(other, this.getValue())); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!(obj instanceof UnitlikeValue)) - return false; - final UnitlikeValue<?, ?> other = (UnitlikeValue<?, ?>) obj; - if (this.getUnitlike() == null) { - if (other.getUnitlike() != null) - return false; - } else if (!this.getUnitlike().equals(other.getUnitlike())) - return false; - if (this.getValue() == null) { - if (other.getValue() != null) - return false; - } else if (!this.getValue().equals(other.getValue())) - return false; - return true; - } - - /** - * @return the unitlike - * @since 2020-09-29 - */ - public final Unitlike<V> getUnitlike() { - return this.unitlike; - } - - /** - * @return the value - * @since 2020-09-29 - */ - public final V getValue() { - return this.value; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + (this.getUnitlike() == null ? 0 : this.getUnitlike().hashCode()); - result = prime * result - + (this.getValue() == null ? 0 : this.getValue().hashCode()); - return result; - } - - @Override - public String toString() { - final Optional<String> primaryName = this.getUnitlike().getPrimaryName(); - final Optional<String> symbol = this.getUnitlike().getSymbol(); - if (primaryName.isEmpty() && symbol.isEmpty()) { - final double baseValue = this.getUnitlike() - .convertToBase(this.getValue()); - return String.format("%s unnamed unit (= %s %s)", this.getValue(), - baseValue, this.getUnitlike().getBase()); - } else { - final String unitName = symbol.orElse(primaryName.get()); - return this.getValue() + " " + unitName; - } - } -} diff --git a/src/org/unitConverter/unit/package-info.java b/src/org/unitConverter/unit/package-info.java deleted file mode 100644 index 2f0e097..0000000 --- a/src/org/unitConverter/unit/package-info.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 <https://www.gnu.org/licenses/>. - */ -/** - * Everything to do with the units that make up Unit Converter. - * - * @author Adrien Hopkins - * @since 2019-10-16 - * @since v0.1.0 - */ -package org.unitConverter.unit;
\ No newline at end of file |