diff options
-rw-r--r-- | src/org/unitConverter/converterGUI/DefaultPrefixRepetitionRule.java | 42 | ||||
-rw-r--r-- | src/org/unitConverter/converterGUI/UnitConverterGUI.java | 61 | ||||
-rw-r--r-- | src/org/unitConverter/unit/SI.java | 472 | ||||
-rw-r--r-- | src/org/unitConverter/unit/UnitDatabase.java | 88 | ||||
-rw-r--r-- | src/org/unitConverter/unit/UnitDatabaseTest.java | 142 |
5 files changed, 542 insertions, 263 deletions
diff --git a/src/org/unitConverter/converterGUI/DefaultPrefixRepetitionRule.java b/src/org/unitConverter/converterGUI/DefaultPrefixRepetitionRule.java new file mode 100644 index 0000000..34d8467 --- /dev/null +++ b/src/org/unitConverter/converterGUI/DefaultPrefixRepetitionRule.java @@ -0,0 +1,42 @@ +/** + * @since 2020-08-26 + */ +package org.unitConverter.converterGUI; + +import java.util.List; +import java.util.function.Predicate; + +import org.unitConverter.unit.UnitPrefix; + +/** + * A rule that specifies whether prefix repetition is allowed + * + * @since 2020-08-26 + */ +enum DefaultPrefixRepetitionRule implements Predicate<List<UnitPrefix>> { + NO_REPETITION { + @Override + public boolean test(List<UnitPrefix> prefixes) { + return prefixes.size() <= 1; + } + }, + NO_RESTRICTION { + @Override + public boolean test(List<UnitPrefix> prefixes) { + return true; + } + }, + /** + * You are allowed to have any number of Yotta/Yocto followed by possibly one + * Kilo-Zetta/Milli-Zepto followed by possibly one Deca/Hecto. Same for + * reducing prefixes, don't mix magnifying and reducing. Non-metric + * (including binary) prefixes can't be repeated. + */ + COMPLEX_REPETITION { + @Override + public boolean test(List<UnitPrefix> prefixes) { + // TODO method stub + return false; + } + }; +} diff --git a/src/org/unitConverter/converterGUI/UnitConverterGUI.java b/src/org/unitConverter/converterGUI/UnitConverterGUI.java index f7c3479..eff0c47 100644 --- a/src/org/unitConverter/converterGUI/UnitConverterGUI.java +++ b/src/org/unitConverter/converterGUI/UnitConverterGUI.java @@ -149,7 +149,8 @@ final class UnitConverterGUI { this.view = view; // load initial units - this.database = new UnitDatabase(); + this.database = new UnitDatabase( + DefaultPrefixRepetitionRule.NO_RESTRICTION); Presenter.addDefaults(this.database); this.database.loadUnitsFile(new File("unitsfile.txt")); @@ -308,7 +309,6 @@ final class UnitConverterGUI { this.view.setExpressionConverterOutputText((useSlash ? "1 / " : "") + String.format("%s = %s", fromUnitString, this.getRoundedString(converted, false))); - final String toString = this.getRoundedString(converted, false); return; } else { // convert to UnitValue @@ -347,45 +347,6 @@ final class UnitConverterGUI { } /** - * @param value value to round - * @return string of that value rounded to {@code significantDigits} - * significant digits. - * @since 2019-04-14 - * @since v0.2.0 - */ - private final String getRoundedString(final BigDecimal value) { - // round value based on rounding type - final BigDecimal roundedValue; - switch (this.roundingType) { - case DECIMAL_PLACES: - roundedValue = value.setScale(this.precision, - RoundingMode.HALF_EVEN); - break; - case SCIENTIFIC: - throw new UnsupportedOperationException("Not yet implemented."); - case SIGNIFICANT_DIGITS: - roundedValue = value.round(new MathContext(this.precision)); - break; - default: - throw new AssertionError("Invalid switch condition."); - } - - String output = roundedValue.toString(); - - // remove trailing zeroes - if (output.contains(".")) { - while (output.endsWith("0")) { - output = output.substring(0, output.length() - 1); - } - if (output.endsWith(".")) { - output = output.substring(0, output.length() - 1); - } - } - - return output; - } - - /** * Like {@link LinearUnitValue#toString(boolean)}, but obeys this unit * converter's rounding settings. * @@ -414,6 +375,7 @@ final class UnitConverterGUI { final BigDecimal unrounded = new BigDecimal(value.getValue()); final BigDecimal rounded; int precision = this.precision; + switch (this.roundingType) { case DECIMAL_PLACES: rounded = unrounded.setScale(precision, RoundingMode.HALF_EVEN); @@ -484,6 +446,15 @@ final class UnitConverterGUI { } /** + * @param prefixRepetitionRule the prefixRepetitionRule to set + * @since 2020-08-26 + */ + public void setPrefixRepetitionRule( + Predicate<List<UnitPrefix>> prefixRepetitionRule) { + this.database.setPrefixRepetitionRule(prefixRepetitionRule); + } + + /** * @param roundingType the roundingType to set * @since 2020-07-16 */ @@ -1031,7 +1002,9 @@ final class UnitConverterGUI { final JRadioButton noRepetition = new JRadioButton( "No Repetition"); - noRepetition.setEnabled(false); + noRepetition.addActionListener( + e -> this.presenter.setPrefixRepetitionRule( + DefaultPrefixRepetitionRule.NO_REPETITION)); prefixRuleButtons.add(noRepetition); prefixRepetitionPanel.add(noRepetition, new GridBagBuilder(0, 0) @@ -1041,7 +1014,9 @@ final class UnitConverterGUI { final JRadioButton noRestriction = new JRadioButton( "No Restriction"); noRestriction.setSelected(true); - noRestriction.setEnabled(false); + noRestriction.addActionListener( + e -> this.presenter.setPrefixRepetitionRule( + DefaultPrefixRepetitionRule.NO_RESTRICTION)); prefixRuleButtons.add(noRestriction); prefixRepetitionPanel.add(noRestriction, new GridBagBuilder(0, 1) diff --git a/src/org/unitConverter/unit/SI.java b/src/org/unitConverter/unit/SI.java index 1a9eaf1..a4fbd5f 100644 --- a/src/org/unitConverter/unit/SI.java +++ b/src/org/unitConverter/unit/SI.java @@ -16,13 +16,19 @@ */
package org.unitConverter.unit;
+import java.util.Arrays;
+import java.util.HashSet;
+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.
+ * 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}:
+ * This class does not include prefixed units. To obtain prefixed units, use
+ * {@link LinearUnit#withPrefix}:
*
* <pre>
* LinearUnit KILOMETRE = SI.METRE.withPrefix(SI.KILO);
@@ -36,42 +42,61 @@ 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
-
+ 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)
+ // 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 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", "$");
+
// You may NOT get SI.BaseUnits instances!
private BaseUnits() {
throw new AssertionError();
}
}
-
+
/**
* Constants that relate to the SI or other systems.
*
@@ -79,189 +104,320 @@ public final class SI { * @since 2019-11-08
*/
public static final class Constants {
- public static final LinearUnit EARTH_GRAVITY = METRE.dividedBy(SECOND).dividedBy(SECOND).times(9.80665);
+ 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> 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> 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);
+ 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> REFLACTIVE_PERMEABILITY = EMPTY.times(EMPTY);
- public static final ObjectProduct<BaseDimension> ANGLE = LENGTH.dividedBy(LENGTH);
- public static final ObjectProduct<BaseDimension> SOLID_ANGLE = AREA.dividedBy(AREA);
-
+ 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> REFLACTIVE_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);
-
+ 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 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", "$"));
-
+ 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"));
+ 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))
+ 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"));
+ 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"));
+ 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"));
+ 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"));
-
+ 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 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"));
+ .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"));
+ .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"));
-
+ .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)
+ .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 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)
+ 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 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
+ .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))
+ 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))
+ 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))
+ .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"));
-
+ 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"));
-
+ 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"));
-
+ 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"));
+
+ // sets of prefixes
+ public static final Set<UnitPrefix> ALL_PREFIXES = new HashSet<>(
+ Arrays.asList(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 = new HashSet<>(
+ Arrays.asList(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 = new HashSet<>(
+ Arrays.asList(KILO, MEGA, GIGA, TERA, PETA, EXA, ZETTA, YOTTA, MILLI,
+ MICRO, NANO, PICO, FEMTO, ATTO, ZEPTO, YOCTO));
+ public static final Set<UnitPrefix> MAGNIFYING_PREFIXES = new HashSet<>(
+ Arrays.asList(DEKA, HECTO, KILO, MEGA, GIGA, TERA, PETA, EXA, ZETTA,
+ YOTTA, KIBI, MEBI, GIBI, TEBI, PEBI, EXBI));
+ public static final Set<UnitPrefix> REDUCING_PREFIXES = new HashSet<>(
+ Arrays.asList(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/UnitDatabase.java b/src/org/unitConverter/unit/UnitDatabase.java index c5432f7..56846a1 100644 --- a/src/org/unitConverter/unit/UnitDatabase.java +++ b/src/org/unitConverter/unit/UnitDatabase.java @@ -42,6 +42,7 @@ 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; @@ -1179,6 +1180,18 @@ public final class UnitDatabase { 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 @@ -1240,10 +1253,25 @@ public final class UnitDatabase { * @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.units = new PrefixedUnitMap(this.prefixlessUnits, this.prefixes); + this.prefixRepetitionRule = prefixRepetitionRule; + this.units = ConditionalExistenceCollections.conditionalExistenceMap( + new PrefixedUnitMap(this.prefixlessUnits, this.prefixes), + entry -> this.prefixRepetitionRule + .test(this.getPrefixesFromName(entry.getKey()))); } /** @@ -1645,6 +1673,47 @@ public final class UnitDatabase { } /** + * 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) { + if (this.prefixlessUnits.containsKey(unitName)) + return new ArrayList<>(); + else { + // find the longest prefix + 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.containsUnitName(rest) + && this.getUnit(rest) instanceof LinearUnit) { + longestPrefix = prefixName; + longestLength = prefixName.length(); + } + } + } + + final List<UnitPrefix> prefixes = this + .getPrefixesFromName(unitName.substring(longestLength)); + prefixes.add(this.getPrefix(longestPrefix)); + return prefixes; + } + } + + /** * Gets a unit prefix from a prefix expression * <p> * Currently, prefix expressions are much simpler than unit expressions: They @@ -1679,6 +1748,14 @@ public final class UnitDatabase { } /** + * @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 @@ -1863,6 +1940,15 @@ public final class UnitDatabase { } /** + * @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 */ diff --git a/src/org/unitConverter/unit/UnitDatabaseTest.java b/src/org/unitConverter/unit/UnitDatabaseTest.java index 164172b..96a0b83 100644 --- a/src/org/unitConverter/unit/UnitDatabaseTest.java +++ b/src/org/unitConverter/unit/UnitDatabaseTest.java @@ -18,17 +18,22 @@ 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 static org.junit.jupiter.api.Assertions.fail; +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 org.junit.jupiter.api.Test; /** - * A test for the {@link UnitDatabase} class. This is NOT part of this program's public API. + * A test for the {@link UnitDatabase} class. This is NOT part of this program's + * public API. * * @author Adrien Hopkins * @since 2019-04-14 @@ -39,23 +44,26 @@ class UnitDatabaseTest { 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 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); - + + 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); private static final UnitPrefix B = UnitPrefix.valueOf(3); private static final UnitPrefix C = UnitPrefix.valueOf(5); 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}. + * Confirms that operations that shouldn't function for infinite databases + * throw an {@code IllegalStateException}. * * @since 2019-05-03 */ @@ -63,14 +71,14 @@ class UnitDatabaseTest { 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); - + { boolean exceptionThrown = false; try { @@ -84,7 +92,7 @@ class UnitDatabaseTest { } } } - + { boolean exceptionThrown = false; try { @@ -99,7 +107,7 @@ class UnitDatabaseTest { } } } - + /** * Test that prefixes correctly apply to units. * @@ -109,23 +117,28 @@ class UnitDatabaseTest { @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); + assertEquals(A.getMultiplier() * B.getMultiplier() * C.getMultiplier(), + abcu.getConversionFactor(), 1e-15); } - + /** * Tests the functionnalites of the prefixless unit map. * @@ -140,21 +153,22 @@ class UnitDatabaseTest { 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. + * Tests that the database correctly stores and retrieves units, ignoring + * prefixes. * * @since 2019-04-14 * @since v0.2.0 @@ -162,18 +176,18 @@ class UnitDatabaseTest { @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")); - assertEquals(null, database.getUnit("Z")); + assertThrows(NoSuchElementException.class, () -> database.getUnit("Z")); } - + /** * Test that unit expressions return the correct value. * @@ -184,30 +198,31 @@ class UnitDatabaseTest { 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 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 * @@ -218,59 +233,64 @@ class UnitDatabaseTest { 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(); - + + 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)) { + 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. + * 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. + * 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 @@ -280,28 +300,28 @@ class UnitDatabaseTest { 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); } } |