From 6d7d172e2e706da44c2b30177a04648671aad69e Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Wed, 26 Aug 2020 12:37:12 -0500 Subject: Added the prefix repetition rule, and the two basic rules. --- src/org/unitConverter/unit/SI.java | 472 ++++++++++++++++++++++++------------- 1 file changed, 314 insertions(+), 158 deletions(-) (limited to 'src/org/unitConverter/unit/SI.java') 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. * *

- * 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}: * *

  * 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 EMPTY = ObjectProduct.empty();
-		public static final ObjectProduct LENGTH = ObjectProduct.oneOf(BaseDimensions.LENGTH);
-		public static final ObjectProduct MASS = ObjectProduct.oneOf(BaseDimensions.MASS);
-		public static final ObjectProduct TIME = ObjectProduct.oneOf(BaseDimensions.TIME);
+		public static final ObjectProduct EMPTY = ObjectProduct
+				.empty();
+		public static final ObjectProduct LENGTH = ObjectProduct
+				.oneOf(BaseDimensions.LENGTH);
+		public static final ObjectProduct MASS = ObjectProduct
+				.oneOf(BaseDimensions.MASS);
+		public static final ObjectProduct TIME = ObjectProduct
+				.oneOf(BaseDimensions.TIME);
 		public static final ObjectProduct ELECTRIC_CURRENT = ObjectProduct
 				.oneOf(BaseDimensions.ELECTRIC_CURRENT);
-		public static final ObjectProduct TEMPERATURE = ObjectProduct.oneOf(BaseDimensions.TEMPERATURE);
-		public static final ObjectProduct QUANTITY = ObjectProduct.oneOf(BaseDimensions.QUANTITY);
+		public static final ObjectProduct TEMPERATURE = ObjectProduct
+				.oneOf(BaseDimensions.TEMPERATURE);
+		public static final ObjectProduct QUANTITY = ObjectProduct
+				.oneOf(BaseDimensions.QUANTITY);
 		public static final ObjectProduct LUMINOUS_INTENSITY = ObjectProduct
 				.oneOf(BaseDimensions.LUMINOUS_INTENSITY);
-		public static final ObjectProduct INFORMATION = ObjectProduct.oneOf(BaseDimensions.INFORMATION);
-		public static final ObjectProduct CURRENCY = ObjectProduct.oneOf(BaseDimensions.CURRENCY);
+		public static final ObjectProduct INFORMATION = ObjectProduct
+				.oneOf(BaseDimensions.INFORMATION);
+		public static final ObjectProduct CURRENCY = ObjectProduct
+				.oneOf(BaseDimensions.CURRENCY);
 		// derived dimensions without named SI units
-		public static final ObjectProduct AREA = LENGTH.times(LENGTH);
-
-		public static final ObjectProduct VOLUME = AREA.times(LENGTH);
-		public static final ObjectProduct VELOCITY = LENGTH.dividedBy(TIME);
-		public static final ObjectProduct ACCELERATION = VELOCITY.dividedBy(TIME);
-		public static final ObjectProduct WAVENUMBER = EMPTY.dividedBy(LENGTH);
-		public static final ObjectProduct MASS_DENSITY = MASS.dividedBy(VOLUME);
-		public static final ObjectProduct SURFACE_DENSITY = MASS.dividedBy(AREA);
-		public static final ObjectProduct SPECIFIC_VOLUME = VOLUME.dividedBy(MASS);
-		public static final ObjectProduct CURRENT_DENSITY = ELECTRIC_CURRENT.dividedBy(AREA);
-		public static final ObjectProduct MAGNETIC_FIELD_STRENGTH = ELECTRIC_CURRENT.dividedBy(LENGTH);
-		public static final ObjectProduct CONCENTRATION = QUANTITY.dividedBy(VOLUME);
-		public static final ObjectProduct MASS_CONCENTRATION = CONCENTRATION.times(MASS);
-		public static final ObjectProduct LUMINANCE = LUMINOUS_INTENSITY.dividedBy(AREA);
-		public static final ObjectProduct REFRACTIVE_INDEX = VELOCITY.dividedBy(VELOCITY);
-		public static final ObjectProduct REFLACTIVE_PERMEABILITY = EMPTY.times(EMPTY);
-		public static final ObjectProduct ANGLE = LENGTH.dividedBy(LENGTH);
-		public static final ObjectProduct SOLID_ANGLE = AREA.dividedBy(AREA);
-
+		public static final ObjectProduct AREA = LENGTH
+				.times(LENGTH);
+		
+		public static final ObjectProduct VOLUME = AREA
+				.times(LENGTH);
+		public static final ObjectProduct VELOCITY = LENGTH
+				.dividedBy(TIME);
+		public static final ObjectProduct ACCELERATION = VELOCITY
+				.dividedBy(TIME);
+		public static final ObjectProduct WAVENUMBER = EMPTY
+				.dividedBy(LENGTH);
+		public static final ObjectProduct MASS_DENSITY = MASS
+				.dividedBy(VOLUME);
+		public static final ObjectProduct SURFACE_DENSITY = MASS
+				.dividedBy(AREA);
+		public static final ObjectProduct SPECIFIC_VOLUME = VOLUME
+				.dividedBy(MASS);
+		public static final ObjectProduct CURRENT_DENSITY = ELECTRIC_CURRENT
+				.dividedBy(AREA);
+		public static final ObjectProduct MAGNETIC_FIELD_STRENGTH = ELECTRIC_CURRENT
+				.dividedBy(LENGTH);
+		public static final ObjectProduct CONCENTRATION = QUANTITY
+				.dividedBy(VOLUME);
+		public static final ObjectProduct MASS_CONCENTRATION = CONCENTRATION
+				.times(MASS);
+		public static final ObjectProduct LUMINANCE = LUMINOUS_INTENSITY
+				.dividedBy(AREA);
+		public static final ObjectProduct REFRACTIVE_INDEX = VELOCITY
+				.dividedBy(VELOCITY);
+		public static final ObjectProduct REFLACTIVE_PERMEABILITY = EMPTY
+				.times(EMPTY);
+		public static final ObjectProduct ANGLE = LENGTH
+				.dividedBy(LENGTH);
+		public static final ObjectProduct SOLID_ANGLE = AREA
+				.dividedBy(AREA);
+		
 		// derived dimensions with named SI units
-		public static final ObjectProduct FREQUENCY = EMPTY.dividedBy(TIME);
-		public static final ObjectProduct FORCE = MASS.times(ACCELERATION);
-		public static final ObjectProduct ENERGY = FORCE.times(LENGTH);
-		public static final ObjectProduct POWER = ENERGY.dividedBy(TIME);
-		public static final ObjectProduct ELECTRIC_CHARGE = ELECTRIC_CURRENT.times(TIME);
-		public static final ObjectProduct VOLTAGE = ENERGY.dividedBy(ELECTRIC_CHARGE);
-		public static final ObjectProduct CAPACITANCE = ELECTRIC_CHARGE.dividedBy(VOLTAGE);
-		public static final ObjectProduct ELECTRIC_RESISTANCE = VOLTAGE.dividedBy(ELECTRIC_CURRENT);
-		public static final ObjectProduct ELECTRIC_CONDUCTANCE = ELECTRIC_CURRENT.dividedBy(VOLTAGE);
-		public static final ObjectProduct MAGNETIC_FLUX = VOLTAGE.times(TIME);
-		public static final ObjectProduct MAGNETIC_FLUX_DENSITY = MAGNETIC_FLUX.dividedBy(AREA);
-		public static final ObjectProduct INDUCTANCE = MAGNETIC_FLUX.dividedBy(ELECTRIC_CURRENT);
-		public static final ObjectProduct LUMINOUS_FLUX = LUMINOUS_INTENSITY.times(SOLID_ANGLE);
-		public static final ObjectProduct ILLUMINANCE = LUMINOUS_FLUX.dividedBy(AREA);
-		public static final ObjectProduct SPECIFIC_ENERGY = ENERGY.dividedBy(MASS);
-		public static final ObjectProduct CATALYTIC_ACTIVITY = QUANTITY.dividedBy(TIME);
-
+		public static final ObjectProduct FREQUENCY = EMPTY
+				.dividedBy(TIME);
+		public static final ObjectProduct FORCE = MASS
+				.times(ACCELERATION);
+		public static final ObjectProduct ENERGY = FORCE
+				.times(LENGTH);
+		public static final ObjectProduct POWER = ENERGY
+				.dividedBy(TIME);
+		public static final ObjectProduct ELECTRIC_CHARGE = ELECTRIC_CURRENT
+				.times(TIME);
+		public static final ObjectProduct VOLTAGE = ENERGY
+				.dividedBy(ELECTRIC_CHARGE);
+		public static final ObjectProduct CAPACITANCE = ELECTRIC_CHARGE
+				.dividedBy(VOLTAGE);
+		public static final ObjectProduct ELECTRIC_RESISTANCE = VOLTAGE
+				.dividedBy(ELECTRIC_CURRENT);
+		public static final ObjectProduct ELECTRIC_CONDUCTANCE = ELECTRIC_CURRENT
+				.dividedBy(VOLTAGE);
+		public static final ObjectProduct MAGNETIC_FLUX = VOLTAGE
+				.times(TIME);
+		public static final ObjectProduct MAGNETIC_FLUX_DENSITY = MAGNETIC_FLUX
+				.dividedBy(AREA);
+		public static final ObjectProduct INDUCTANCE = MAGNETIC_FLUX
+				.dividedBy(ELECTRIC_CURRENT);
+		public static final ObjectProduct LUMINOUS_FLUX = LUMINOUS_INTENSITY
+				.times(SOLID_ANGLE);
+		public static final ObjectProduct ILLUMINANCE = LUMINOUS_FLUX
+				.dividedBy(AREA);
+		public static final ObjectProduct SPECIFIC_ENERGY = ENERGY
+				.dividedBy(MASS);
+		public static final ObjectProduct 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 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 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 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 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 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();
-- 
cgit v1.2.3


From e2f141427e441daa9d6be0ba8a30b844ca4391e0 Mon Sep 17 00:00:00 2001
From: Adrien Hopkins 
Date: Thu, 27 Aug 2020 08:07:21 -0500
Subject: Added the ability to restrict conversion to customary->metric.

---
 .../unitConverter/converterGUI/SearchBoxList.java  | 122 +++++-----
 .../converterGUI/UnitConverterGUI.java             |  89 ++++++--
 src/org/unitConverter/unit/SI.java                 |  59 ++---
 src/org/unitConverter/unit/Unit.java               | 250 ++++++++++++---------
 src/org/unitConverter/unit/UnitTest.java           |  12 +
 5 files changed, 333 insertions(+), 199 deletions(-)

(limited to 'src/org/unitConverter/unit/SI.java')

diff --git a/src/org/unitConverter/converterGUI/SearchBoxList.java b/src/org/unitConverter/converterGUI/SearchBoxList.java
index 1995466..10ef589 100644
--- a/src/org/unitConverter/converterGUI/SearchBoxList.java
+++ b/src/org/unitConverter/converterGUI/SearchBoxList.java
@@ -36,13 +36,13 @@ import javax.swing.JTextField;
  * @since v0.2.0
  */
 final class SearchBoxList extends JPanel {
-
+	
 	/**
 	 * @since 2019-04-13
 	 * @since v0.2.0
 	 */
 	private static final long serialVersionUID = 6226930279415983433L;
-
+	
 	/**
 	 * The text to place in an empty search box.
 	 * 
@@ -50,7 +50,7 @@ final class SearchBoxList extends JPanel {
 	 * @since v0.2.0
 	 */
 	private static final String EMPTY_TEXT = "Search...";
-
+	
 	/**
 	 * The color to use for an empty foreground.
 	 * 
@@ -58,94 +58,92 @@ final class SearchBoxList extends JPanel {
 	 * @since v0.2.0
 	 */
 	private static final Color EMPTY_FOREGROUND = new Color(192, 192, 192);
-
+	
 	// the components
 	private final Collection itemsToFilter;
 	private final DelegateListModel listModel;
 	private final JTextField searchBox;
 	private final JList searchItems;
-
+	
 	private boolean searchBoxEmpty = true;
-
-	// I need to do this because, for some reason, Swing is auto-focusing my search box without triggering a focus
+	
+	// I need to do this because, for some reason, Swing is auto-focusing my
+	// search box without triggering a focus
 	// event.
 	private boolean searchBoxFocused = false;
-
+	
 	private Predicate customSearchFilter = o -> true;
 	private final Comparator defaultOrdering;
 	private final boolean caseSensitive;
-
+	
 	/**
 	 * Creates the {@code SearchBoxList}.
 	 * 
-	 * @param itemsToFilter
-	 *            items to put in the list
+	 * @param itemsToFilter items to put in the list
 	 * @since 2019-04-14
 	 */
 	public SearchBoxList(final Collection itemsToFilter) {
 		this(itemsToFilter, null, false);
 	}
-
+	
 	/**
 	 * Creates the {@code SearchBoxList}.
 	 * 
-	 * @param itemsToFilter
-	 *            items to put in the list
-	 * @param defaultOrdering
-	 *            default ordering of items after filtration (null=Comparable)
-	 * @param caseSensitive
-	 *            whether or not the filtration is case-sensitive
+	 * @param itemsToFilter   items to put in the list
+	 * @param defaultOrdering default ordering of items after filtration
+	 *                        (null=Comparable)
+	 * @param caseSensitive   whether or not the filtration is case-sensitive
 	 * 
 	 * @since 2019-04-13
 	 * @since v0.2.0
 	 */
-	public SearchBoxList(final Collection itemsToFilter, final Comparator defaultOrdering,
+	public SearchBoxList(final Collection itemsToFilter,
+			final Comparator defaultOrdering,
 			final boolean caseSensitive) {
 		super(new BorderLayout(), true);
 		this.itemsToFilter = itemsToFilter;
 		this.defaultOrdering = defaultOrdering;
 		this.caseSensitive = caseSensitive;
-
+		
 		// create the components
 		this.listModel = new DelegateListModel<>(new ArrayList<>(itemsToFilter));
 		this.searchItems = new JList<>(this.listModel);
-
+		
 		this.searchBox = new JTextField(EMPTY_TEXT);
 		this.searchBox.setForeground(EMPTY_FOREGROUND);
-
+		
 		// add them to the panel
 		this.add(this.searchBox, BorderLayout.PAGE_START);
 		this.add(new JScrollPane(this.searchItems), BorderLayout.CENTER);
-
+		
 		// set up the search box
 		this.searchBox.addFocusListener(new FocusListener() {
 			@Override
 			public void focusGained(final FocusEvent e) {
 				SearchBoxList.this.searchBoxFocusGained(e);
 			}
-
+			
 			@Override
 			public void focusLost(final FocusEvent e) {
 				SearchBoxList.this.searchBoxFocusLost(e);
 			}
 		});
-
+		
 		this.searchBox.addCaretListener(e -> this.searchBoxTextChanged());
 		this.searchBoxEmpty = true;
 	}
-
+	
 	/**
 	 * Adds an additional filter for searching.
 	 * 
-	 * @param filter
-	 *            filter to add.
+	 * @param filter filter to add.
 	 * @since 2019-04-13
 	 * @since v0.2.0
 	 */
 	public void addSearchFilter(final Predicate filter) {
 		this.customSearchFilter = this.customSearchFilter.and(filter);
 	}
-
+	
 	/**
 	 * Resets the search filter.
 	 * 
@@ -155,7 +153,7 @@ final class SearchBoxList extends JPanel {
 	public void clearSearchFilters() {
 		this.customSearchFilter = o -> true;
 	}
-
+	
 	/**
 	 * @return this component's search box component
 	 * @since 2019-04-14
@@ -164,11 +162,11 @@ final class SearchBoxList extends JPanel {
 	public final JTextField getSearchBox() {
 		return this.searchBox;
 	}
-
+	
 	/**
-	 * @param searchText
-	 *            text to search for
-	 * @return a filter that filters out that text, based on this list's case sensitive setting
+	 * @param searchText text to search for
+	 * @return a filter that filters out that text, based on this list's case
+	 *         sensitive setting
 	 * @since 2019-04-14
 	 * @since v0.2.0
 	 */
@@ -176,9 +174,10 @@ final class SearchBoxList extends JPanel {
 		if (this.caseSensitive)
 			return string -> string.contains(searchText);
 		else
-			return string -> string.toLowerCase().contains(searchText.toLowerCase());
+			return string -> string.toLowerCase()
+					.contains(searchText.toLowerCase());
 	}
-
+	
 	/**
 	 * @return this component's list component
 	 * @since 2019-04-14
@@ -187,7 +186,7 @@ final class SearchBoxList extends JPanel {
 	public final JList getSearchList() {
 		return this.searchItems;
 	}
-
+	
 	/**
 	 * @return index selected in item list
 	 * @since 2019-04-14
@@ -196,7 +195,7 @@ final class SearchBoxList extends JPanel {
 	public int getSelectedIndex() {
 		return this.searchItems.getSelectedIndex();
 	}
-
+	
 	/**
 	 * @return value selected in item list
 	 * @since 2019-04-13
@@ -205,7 +204,7 @@ final class SearchBoxList extends JPanel {
 	public String getSelectedValue() {
 		return this.searchItems.getSelectedValue();
 	}
-
+	
 	/**
 	 * Re-applies the filters.
 	 * 
@@ -213,29 +212,30 @@ final class SearchBoxList extends JPanel {
 	 * @since v0.2.0
 	 */
 	public void reapplyFilter() {
-		final String searchText = this.searchBoxEmpty ? "" : this.searchBox.getText();
-		final FilterComparator comparator = new FilterComparator(searchText, this.defaultOrdering, this.caseSensitive);
+		final String searchText = this.searchBoxEmpty ? ""
+				: this.searchBox.getText();
+		final FilterComparator comparator = new FilterComparator(searchText,
+				this.defaultOrdering, this.caseSensitive);
 		final Predicate searchFilter = this.getSearchFilter(searchText);
-
+		
 		this.listModel.clear();
 		this.itemsToFilter.forEach(string -> {
 			if (searchFilter.test(string)) {
 				this.listModel.add(string);
 			}
 		});
-
+		
 		// applies the custom filters
 		this.listModel.removeIf(this.customSearchFilter.negate());
-
+		
 		// sorts the remaining items
 		this.listModel.sort(comparator);
 	}
-
+	
 	/**
 	 * Runs whenever the search box gains focus.
 	 * 
-	 * @param e
-	 *            focus event
+	 * @param e focus event
 	 * @since 2019-04-13
 	 * @since v0.2.0
 	 */
@@ -246,12 +246,11 @@ final class SearchBoxList extends JPanel {
 			this.searchBox.setForeground(Color.BLACK);
 		}
 	}
-
+	
 	/**
 	 * Runs whenever the search box loses focus.
 	 * 
-	 * @param e
-	 *            focus event
+	 * @param e focus event
 	 * @since 2019-04-13
 	 * @since v0.2.0
 	 */
@@ -262,7 +261,7 @@ final class SearchBoxList extends JPanel {
 			this.searchBox.setForeground(EMPTY_FOREGROUND);
 		}
 	}
-
+	
 	/**
 	 * Runs whenever the text in the search box is changed.
 	 * 

@@ -276,10 +275,12 @@ final class SearchBoxList extends JPanel { if (this.searchBoxFocused) { this.searchBoxEmpty = this.searchBox.getText().equals(""); } - final String searchText = this.searchBoxEmpty ? "" : this.searchBox.getText(); - final FilterComparator comparator = new FilterComparator(searchText, this.defaultOrdering, this.caseSensitive); + final String searchText = this.searchBoxEmpty ? "" + : this.searchBox.getText(); + final FilterComparator comparator = new FilterComparator(searchText, + this.defaultOrdering, this.caseSensitive); final Predicate searchFilter = this.getSearchFilter(searchText); - + // initialize list with items that match the filter then sort this.listModel.clear(); this.itemsToFilter.forEach(string -> { @@ -287,11 +288,20 @@ final class SearchBoxList extends JPanel { this.listModel.add(string); } }); - + // applies the custom filters this.listModel.removeIf(this.customSearchFilter.negate()); - + // sorts the remaining items this.listModel.sort(comparator); } + + /** + * Manually updates the search box's item list. + * + * @since 2020-08-27 + */ + public void updateList() { + this.searchBoxTextChanged(); + } } diff --git a/src/org/unitConverter/converterGUI/UnitConverterGUI.java b/src/org/unitConverter/converterGUI/UnitConverterGUI.java index 5fe4ee5..75ab16d 100644 --- a/src/org/unitConverter/converterGUI/UnitConverterGUI.java +++ b/src/org/unitConverter/converterGUI/UnitConverterGUI.java @@ -56,6 +56,7 @@ import javax.swing.JTextField; import javax.swing.WindowConstants; import javax.swing.border.TitledBorder; +import org.unitConverter.math.ConditionalExistenceCollections; import org.unitConverter.math.ObjectProduct; import org.unitConverter.unit.BaseDimension; import org.unitConverter.unit.BritishImperial; @@ -129,6 +130,13 @@ final class UnitConverterGUI { private final Comparator prefixNameComparator; + // conditions for existence of From and To entries + // used for one-way conversion + private final MutablePredicate fromExistenceCondition = new MutablePredicate<>( + s -> true); + private final MutablePredicate toExistenceCondition = new MutablePredicate<>( + s -> true); + /* * Rounding-related settings. I am using my own system, and not * MathContext, because MathContext does not support decimal place based @@ -337,6 +345,16 @@ final class UnitConverterGUI { return this.dimensionNames; } + /** + * @return a list of all the entries in the dimension-based converter's + * From box + * @since 2020-08-27 + */ + public final Set fromEntries() { + return ConditionalExistenceCollections.conditionalExistenceSet( + this.unitNameSet(), this.fromExistenceCondition); + } + /** * @return a comparator to compare prefix names * @since 2019-04-14 @@ -436,6 +454,25 @@ final class UnitConverterGUI { } } + /** + * Enables or disables one-way conversion. + * + * @param oneWay whether one-way conversion should be on (true) or off + * (false) + * @since 2020-08-27 + */ + public final void setOneWay(boolean oneWay) { + if (oneWay) { + this.fromExistenceCondition.setPredicate( + unitName -> !this.database.getUnit(unitName).isMetric()); + this.toExistenceCondition.setPredicate( + unitName -> this.database.getUnit(unitName).isMetric()); + } else { + this.fromExistenceCondition.setPredicate(unitName -> true); + this.toExistenceCondition.setPredicate(unitName -> true); + } + } + /** * @param precision new value of precision * @since 2019-01-15 @@ -462,6 +499,16 @@ final class UnitConverterGUI { this.roundingType = roundingType; } + /** + * @return a list of all the entries in the dimension-based converter's To + * box + * @since 2020-08-27 + */ + public final Set toEntries() { + return ConditionalExistenceCollections.conditionalExistenceSet( + this.unitNameSet(), this.toExistenceCondition); + } + /** * Returns true if and only if the unit represented by {@code unitName} * has the dimension represented by {@code dimensionName}. @@ -505,7 +552,7 @@ final class UnitConverterGUI { * @since 2019-04-14 * @since v0.2.0 */ - public final Set unitNameSet() { + private final Set unitNameSet() { return this.database.unitMapPrefixless().keySet(); } } @@ -579,8 +626,8 @@ final class UnitConverterGUI { this.presenter.getPrefixNameComparator(), true); this.unitTextBox = new JTextArea(); this.prefixTextBox = new JTextArea(); - this.fromSearch = new SearchBoxList(this.presenter.unitNameSet()); - this.toSearch = new SearchBoxList(this.presenter.unitNameSet()); + this.fromSearch = new SearchBoxList(this.presenter.fromEntries()); + this.toSearch = new SearchBoxList(this.presenter.toEntries()); this.valueInput = new JFormattedTextField(NUMBER_FORMATTER); this.dimensionBasedOutput = new JTextArea(2, 32); this.fromEntry = new JTextField(); @@ -629,15 +676,6 @@ final class UnitConverterGUI { throw new AssertionError(); } - /** - * @return text inputted into dimension-based converter - * @since 2019-04-13 - * @since v0.2.0 - */ - public String getDimensionConverterText() { - return this.valueInput.getText(); - } - /** * @return selection in "From" selector in dimension-based converter * @since 2019-04-13 @@ -717,6 +755,9 @@ final class UnitConverterGUI { { // pane with all of the tabs masterPanel.add(this.masterPane, BorderLayout.CENTER); + // update stuff + this.masterPane.addChangeListener(e -> this.update()); + { // a panel for unit conversion using a selector final JPanel convertUnitPanel = new JPanel(); this.masterPane.addTab("Convert Units", convertUnitPanel); @@ -1080,17 +1121,25 @@ final class UnitConverterGUI { .setBorder(new TitledBorder("Miscellaneous Settings")); miscPanel.setLayout(new GridBagLayout()); + final JCheckBox oneWay = new JCheckBox( + "Convert One Way Only"); + oneWay.setSelected(false); + oneWay.addItemListener( + e -> this.presenter.setOneWay(e.getStateChange() == 1)); + miscPanel.add(oneWay, new GridBagBuilder(0, 0) + .setAnchor(GridBagConstraints.LINE_START).build()); + final JCheckBox showAllVariations = new JCheckBox( "Show Symbols in \"Convert Units\""); showAllVariations.setSelected(true); showAllVariations.setEnabled(false); - miscPanel.add(showAllVariations, new GridBagBuilder(0, 0) + miscPanel.add(showAllVariations, new GridBagBuilder(0, 1) .setAnchor(GridBagConstraints.LINE_START).build()); final JButton unitFileButton = new JButton( "Manage Unit Data Files"); unitFileButton.setEnabled(false); - miscPanel.add(unitFileButton, new GridBagBuilder(0, 1) + miscPanel.add(unitFileButton, new GridBagBuilder(0, 2) .setAnchor(GridBagConstraints.LINE_START).build()); } } @@ -1153,6 +1202,18 @@ final class UnitConverterGUI { JOptionPane.showMessageDialog(this.frame, message, title, JOptionPane.ERROR_MESSAGE); } + + public void update() { + switch (this.getActivePane()) { + case UNIT_CONVERTER: + this.fromSearch.updateList(); + this.toSearch.updateList(); + break; + default: + // do nothing, for now + break; + } + } } public static void main(final String[] args) { diff --git a/src/org/unitConverter/unit/SI.java b/src/org/unitConverter/unit/SI.java index a4fbd5f..f36cf28 100644 --- a/src/org/unitConverter/unit/SI.java +++ b/src/org/unitConverter/unit/SI.java @@ -17,6 +17,7 @@ package org.unitConverter.unit; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -91,6 +92,9 @@ public final class SI { public static final BaseUnit DOLLAR = BaseUnit .valueOf(BaseDimensions.CURRENCY, "dollar", "$"); + public static final Set BASE_UNITS = setOf(METRE, KILOGRAM, + SECOND, AMPERE, KELVIN, MOLE, CANDELA, BIT); + // You may NOT get SI.BaseUnits instances! private BaseUnits() { throw new AssertionError(); @@ -210,6 +214,7 @@ public final class SI { /// 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() @@ -228,10 +233,10 @@ public final class SI { .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) @@ -277,10 +282,10 @@ public final class SI { // 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")); @@ -290,12 +295,12 @@ public final class SI { 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) @@ -324,7 +329,7 @@ public final class SI { .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 + // 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) @@ -339,11 +344,11 @@ public final class SI { .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) @@ -358,10 +363,10 @@ public final class SI { .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) @@ -376,10 +381,10 @@ public final class SI { .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) @@ -398,25 +403,29 @@ public final class SI { .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 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 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 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 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 REDUCING_PREFIXES = new HashSet<>( - Arrays.asList(DECI, CENTI, MILLI, MICRO, NANO, PICO, FEMTO, ATTO, - ZEPTO, YOCTO)); + public static final Set ALL_PREFIXES = setOf(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 DECIMAL_PREFIXES = setOf(DEKA, HECTO, + KILO, MEGA, GIGA, TERA, PETA, EXA, ZETTA, YOTTA, DECI, CENTI, MILLI, + MICRO, NANO, PICO, FEMTO, ATTO, ZEPTO, YOCTO); + public static final Set THOUSAND_PREFIXES = setOf(KILO, MEGA, + GIGA, TERA, PETA, EXA, ZETTA, YOTTA, MILLI, MICRO, NANO, PICO, FEMTO, + ATTO, ZEPTO, YOCTO); + public static final Set MAGNIFYING_PREFIXES = setOf(DEKA, HECTO, + KILO, MEGA, GIGA, TERA, PETA, EXA, ZETTA, YOTTA, KIBI, MEBI, GIBI, + TEBI, PEBI, EXBI); + public static final Set REDUCING_PREFIXES = setOf(DECI, CENTI, + MILLI, MICRO, NANO, PICO, FEMTO, ATTO, ZEPTO, YOCTO); + + // this method just calls Arrays.asList, which is itself safe. + @SafeVarargs + private static final Set setOf(T... args) { + return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(args))); + } // You may NOT get SI instances! private SI() { diff --git a/src/org/unitConverter/unit/Unit.java b/src/org/unitConverter/unit/Unit.java index 35b32fc..eb9b000 100644 --- a/src/org/unitConverter/unit/Unit.java +++ b/src/org/unitConverter/unit/Unit.java @@ -25,6 +25,7 @@ import java.util.Optional; import java.util.Set; import java.util.function.DoubleUnaryOperator; +import org.unitConverter.math.DecimalComparison; import org.unitConverter.math.ObjectProduct; /** @@ -35,209 +36,211 @@ import org.unitConverter.math.ObjectProduct; */ public abstract class Unit { /** - * Returns a unit from its base and the functions it uses to convert to and from its base. + * Returns a unit from its base and the functions it uses to convert to and + * from its base. * *

- * For example, to get a unit representing the degree Celsius, the following code can be used: + * 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);} *

* - * @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 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 + * @throws NullPointerException if any argument is null */ - public static final Unit fromConversionFunctions(final ObjectProduct base, - final DoubleUnaryOperator converterFrom, final DoubleUnaryOperator converterTo) { + public static final Unit fromConversionFunctions( + final ObjectProduct 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. + * Returns a unit from its base and the functions it uses to convert to and + * from its base. * *

- * For example, to get a unit representing the degree Celsius, the following code can be used: + * 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);} *

* - * @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 + * @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 + * @throws NullPointerException if any argument is null */ - public static final Unit fromConversionFunctions(final ObjectProduct base, - final DoubleUnaryOperator converterFrom, final DoubleUnaryOperator converterTo, final NameSymbol ns) { + public static final Unit fromConversionFunctions( + final ObjectProduct 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 unitBase; - + /** * The primary name used by this unit. */ private final Optional primaryName; - + /** * A short symbol used to represent this unit. */ private final Optional symbol; - + /** * A set of any additional names and/or spellings that the unit uses. */ private final Set otherNames; - + /** * Cache storing the result of getDimension() * * @since 2019-10-16 */ private transient ObjectProduct dimension = null; - + /** * Creates the {@code AbstractUnit}. * - * @param unitBase - * base of unit - * @param ns - * names and symbol of 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 + * @throws NullPointerException if unitBase or ns is null */ protected Unit(final ObjectProduct unitBase, final NameSymbol ns) { - this.unitBase = Objects.requireNonNull(unitBase, "unitBase must not be null."); - this.primaryName = Objects.requireNonNull(ns, "ns must not be null.").getPrimaryName(); + this.unitBase = Objects.requireNonNull(unitBase, + "unitBase must not be null."); + this.primaryName = Objects.requireNonNull(ns, "ns must not be null.") + .getPrimaryName(); this.symbol = ns.getSymbol(); this.otherNames = ns.getOtherNames(); } - + /** * A constructor that constructs {@code BaseUnit} instances. * * @since 2019-10-16 */ - Unit(final String primaryName, final String symbol, final Set otherNames) { + Unit(final String primaryName, final String symbol, + final Set otherNames) { if (this instanceof BaseUnit) { this.unitBase = ObjectProduct.oneOf((BaseUnit) this); } else throw new AssertionError(); this.primaryName = Optional.of(primaryName); this.symbol = Optional.of(symbol); - this.otherNames = Collections.unmodifiableSet( - new HashSet<>(Objects.requireNonNull(otherNames, "additionalNames must not be null."))); + this.otherNames = Collections.unmodifiableSet(new HashSet<>(Objects + .requireNonNull(otherNames, "additionalNames must not be null."))); } - + /** - * Checks if a value expressed in this unit can be converted to a value expressed in {@code other} + * Checks if a value expressed in this unit can be converted to a value + * expressed in {@code other} * - * @param other - * unit to test with + * @param other unit to test with * @return true if the units are compatible * @since 2019-01-13 * @since v0.1.0 - * @throws NullPointerException - * if other is null + * @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()); } - + /** - * Converts from a value expressed in this unit's base unit to a value expressed in this unit. + * Converts from a value expressed in this unit's base unit to a value + * expressed in this unit. *

- * 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. + * 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. *

*

- * If this unit is a base unit, this method should return {@code value}. + * If this unit is a base unit, this method should return + * {@code value}. *

* - * @implSpec This method is used by {@link #convertTo}, and its behaviour affects the behaviour of - * {@code convertTo}. + * @implSpec This method is used by {@link #convertTo}, and its behaviour + * affects the behaviour of {@code convertTo}. * - * @param value - * value expressed in base unit + * @param value value expressed in base unit * @return value expressed in this 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}. + * 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. + * {@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 + * @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 + * @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)); + 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. + * Converts from a value expressed in this unit to a value expressed in this + * unit's base unit. *

- * 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. + * 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. *

*

- * If this unit is a base unit, this method should return {@code value}. + * If this unit is a base unit, this method should return + * {@code value}. *

* - * @implSpec This method is used by {@link #convertTo}, and its behaviour affects the behaviour of - * {@code convertTo}. + * @implSpec This method is used by {@link #convertTo}, and its behaviour + * affects the behaviour of {@code convertTo}. * - * @param value - * value expressed in this unit + * @param value value expressed in this unit * @return value expressed in base 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 @@ -246,7 +249,7 @@ public abstract class Unit { public final ObjectProduct getBase() { return this.unitBase; } - + /** * @return dimension measured by this unit * @since 2018-12-22 @@ -256,16 +259,16 @@ public abstract class Unit { if (this.dimension == null) { final Map mapping = this.unitBase.exponentMap(); final Map 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 additionalNames * @since 2019-10-21 @@ -273,7 +276,7 @@ public abstract class Unit { public final Set getOtherNames() { return this.otherNames; } - + /** * @return primaryName * @since 2019-10-21 @@ -281,7 +284,7 @@ public abstract class Unit { public final Optional getPrimaryName() { return this.primaryName; } - + /** * @return symbol * @since 2019-10-21 @@ -289,25 +292,64 @@ public abstract class Unit { public final Optional getSymbol() { return this.symbol; } - + + /** + * Returns true iff this unit is metric. + *

+ * "Metric" is defined by three conditions: + *

    + *
  • Must be an instance of {@link LinearUnit}.
  • + *
  • Must be based on the SI base units (as determined by getBase())
  • + *
  • The conversion factor must be a power of 10.
  • + *
+ *

+ * 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). + *

+ * 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())); + + (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 + * @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 + * @throws NullPointerException if ns is null */ public Unit withName(final NameSymbol ns) { - return fromConversionFunctions(this.getBase(), this::convertFromBase, this::convertToBase, + return fromConversionFunctions(this.getBase(), this::convertFromBase, + this::convertToBase, Objects.requireNonNull(ns, "ns must not be null.")); } } diff --git a/src/org/unitConverter/unit/UnitTest.java b/src/org/unitConverter/unit/UnitTest.java index 2cf3126..ff83805 100644 --- a/src/org/unitConverter/unit/UnitTest.java +++ b/src/org/unitConverter/unit/UnitTest.java @@ -16,6 +16,7 @@ */ 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; @@ -104,6 +105,17 @@ class UnitTest { 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 -- cgit v1.2.3