summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrien Hopkins <adrien.p.hopkins@gmail.com>2019-04-13 19:48:25 -0400
committerAdrien Hopkins <adrien.p.hopkins@gmail.com>2019-04-13 19:48:25 -0400
commit63dd50e5d7a5daa0bcbdd00608543d4572c870ea (patch)
treef35c4619e05ddb3dd33b614dccefd9839dc35512
parentf0f4898f796b9cc26294ba9feb22692143d00a9e (diff)
Edited the UnitsDatabase API; it now favours prefixless units.
-rw-r--r--CHANGELOG.org2
-rwxr-xr-xsrc/org/unitConverter/UnitsDatabase.java753
-rwxr-xr-xsrc/org/unitConverter/converterGUI/UnitConverterGUI.java18
-rw-r--r--src/org/unitConverter/math/ExpressionParser.java2
4 files changed, 689 insertions, 86 deletions
diff --git a/CHANGELOG.org b/CHANGELOG.org
index e7748ba..db9766b 100644
--- a/CHANGELOG.org
+++ b/CHANGELOG.org
@@ -3,6 +3,8 @@ All notable changes in this project will be shown in this file.
** Unreleased
*** Changed
+ - When searching for units, units with no prefixes are searched for before prefixed units
+ - Smaller prefixes are searched for before larger prefixes
- Moved project to Maven
- Downgraded JUnit to 4.11
- BaseUnit is now a subclass of LinearUnit
diff --git a/src/org/unitConverter/UnitsDatabase.java b/src/org/unitConverter/UnitsDatabase.java
index a7e6047..9749e9c 100755
--- a/src/org/unitConverter/UnitsDatabase.java
+++ b/src/org/unitConverter/UnitsDatabase.java
@@ -21,14 +21,20 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
+import java.util.AbstractSet;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.Predicate;
import org.unitConverter.dimension.UnitDimension;
import org.unitConverter.math.DecimalComparison;
@@ -40,7 +46,7 @@ import org.unitConverter.unit.Unit;
import org.unitConverter.unit.UnitPrefix;
/**
- * A database of units and prefixes, and their names.
+ * A database of units, prefixes and dimensions, and their names.
*
* @author Adrien Hopkins
* @since 2019-01-07
@@ -48,6 +54,645 @@ import org.unitConverter.unit.UnitPrefix;
*/
public final class UnitsDatabase {
/**
+ * A map for units that allows the use of prefixes.
+ * <p>
+ * As this map implementation is intended to be used as a sort of "augmented view" of a unit and prefix map, it is
+ * unmodifiable but instead reflects the changes to the maps passed into it. Do not edit this map, instead edit the
+ * maps that were passed in during construction.
+ * </p>
+ * <p>
+ * The rules for applying prefixes onto units are the following:
+ * <ul>
+ * <li>Prefixes can only be applied to linear units.</li>
+ * <li>Before attempting to search for prefixes in a unit name, this map will first search for a unit name. So, if
+ * there are two units, "B" and "AB", and a prefix "A", this map will favour the unit "AB" over the unit "B" with
+ * the prefix "A", even though they have the same string.</li>
+ * <li>Shorter prefixes are preferred to longer prefixes. So, if you have units "BC" and "C", and prefixes "AB" and
+ * "A", inputting "ABC" will return the unit "BC" with the prefix "A", not "C" with the prefix "AB".</li>
+ * </ul>
+ * </p>
+ *
+ * @author Adrien Hopkins
+ * @since 2019-04-13
+ */
+ private static final class PrefixedUnitMap implements Map<String, Unit> {
+ /**
+ * The class used for entry sets.
+ *
+ * @author Adrien Hopkins
+ * @since 2019-04-13
+ */
+ private static final class PrefixedUnitEntrySet extends AbstractSet<Map.Entry<String, Unit>> {
+ // the map that created this set
+ private final PrefixedUnitMap map;
+
+ /**
+ * Creates the {@code PrefixedUnitNameSet}.
+ *
+ * @param map
+ * @since 2019-04-13
+ */
+ public PrefixedUnitEntrySet(final PrefixedUnitMap map) {
+ this.map = map;
+ }
+
+ @Override
+ public boolean add(final Map.Entry<String, Unit> e) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean addAll(final Collection<? extends Map.Entry<String, Unit>> c) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean contains(final Object o) {
+ // get the entry
+ final Entry<String, Unit> entry;
+
+ try {
+ // This is OK because I'm in a try-catch block.
+ @SuppressWarnings("unchecked")
+ final Entry<String, Unit> tempEntry = (Entry<String, Unit>) o;
+ entry = tempEntry;
+ } catch (final ClassCastException e) {
+ throw new IllegalArgumentException("Attempted to test for an entry using a non-entry.");
+ }
+
+ return this.map.containsKey(entry.getKey()) && this.map.get(entry.getKey()).equals(entry.getValue());
+ }
+
+ @Override
+ public boolean containsAll(final Collection<?> c) {
+ for (final Object o : c)
+ if (!this.contains(o))
+ return false;
+ return true;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return this.map.isEmpty();
+ }
+
+ @Override
+ public Iterator<Entry<String, Unit>> iterator() {
+ return new Iterator<Entry<String, Unit>>() {
+ // position in the unit list
+ int unitNamePosition = -1;
+ // the indices of the prefixes attached to the current unit
+ List<Integer> prefixCoordinates = new ArrayList<>();
+
+ List<String> unitNames = new ArrayList<>(PrefixedUnitEntrySet.this.map.units.keySet());
+ List<String> prefixNames = new ArrayList<>(PrefixedUnitEntrySet.this.map.prefixes.keySet());
+
+ @Override
+ public boolean hasNext() {
+ if (this.unitNames.isEmpty())
+ return false;
+ else {
+ if (this.prefixNames.isEmpty())
+ return this.unitNamePosition >= this.unitNames.size() - 1;
+ else
+ return true;
+ }
+ }
+
+ @Override
+ public Entry<String, Unit> next() {
+ // increment unit name position
+ this.unitNamePosition++;
+
+ // if I have prefixes, ensure I'm not using a nonlinear unit
+ // since all of the unprefixed stuff is done, just remove nonlinear units
+ if (!this.prefixCoordinates.isEmpty()) {
+ while (!(PrefixedUnitEntrySet.this.map
+ .get(this.unitNames.get(this.unitNamePosition)) instanceof LinearUnit)) {
+ this.unitNames.remove(this.unitNamePosition);
+ }
+ }
+
+ // carry over
+ if (!this.prefixNames.isEmpty() && this.unitNamePosition >= this.unitNames.size() - 1) {
+ // handle prefix position
+ this.unitNamePosition = 0;
+ int i = this.prefixCoordinates.size() - 1;
+ this.prefixCoordinates.set(i, this.prefixCoordinates.get(i) + 1);
+
+ while (this.prefixCoordinates.get(i) >= this.prefixNames.size() - 1) {
+ this.prefixCoordinates.set(i, 0);
+ i--;
+ if (i < 0) {
+ this.prefixCoordinates.add(0, 0);
+ }
+ }
+ }
+
+ final StringBuilder unitNameBuilder = new StringBuilder();
+ for (final int i : this.prefixCoordinates) {
+ unitNameBuilder.append(this.prefixNames.get(i));
+ }
+ unitNameBuilder.append(this.unitNames.get(this.unitNamePosition));
+
+ final String unitName = unitNameBuilder.toString();
+ return new Entry<String, Unit>() {
+ @Override
+ public String getKey() {
+ return unitName;
+ }
+
+ @Override
+ public Unit getValue() {
+ return PrefixedUnitEntrySet.this.map.get(unitName);
+ }
+
+ @Override
+ public Unit setValue(final Unit value) {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ };
+ }
+
+ @Override
+ public boolean remove(final Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean removeAll(final Collection<?> c) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean removeIf(final Predicate<? super Entry<String, Unit>> filter) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean retainAll(final Collection<?> c) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int size() {
+ if (this.map.units.isEmpty())
+ return 0;
+ else {
+ if (this.map.prefixes.isEmpty())
+ return this.map.units.size();
+ else
+ // infinite set
+ return Integer.MAX_VALUE;
+ }
+ }
+
+ @Override
+ public Object[] toArray() {
+ if (this.map.units.isEmpty())
+ // finite, it will work
+ return super.toArray();
+ else {
+ if (this.map.prefixes.isEmpty())
+ // finite, it will work
+ return super.toArray();
+ else
+ // infinite set
+ throw new UnsupportedOperationException("Cannot make an infinite set into an array.");
+ }
+ }
+
+ @Override
+ public <T> T[] toArray(final T[] a) {
+ if (this.map.units.isEmpty())
+ // finite, it will work
+ return super.toArray(a);
+ else {
+ if (this.map.prefixes.isEmpty())
+ // finite, it will work
+ return super.toArray(a);
+ else
+ // infinite set
+ throw new UnsupportedOperationException("Cannot make an infinite set into an array.");
+ }
+ }
+
+ }
+
+ /**
+ * The class used for unit name sets.
+ *
+ * @author Adrien Hopkins
+ * @since 2019-04-13
+ */
+ private static final class PrefixedUnitNameSet extends AbstractSet<String> {
+ // the map that created this set
+ private final PrefixedUnitMap map;
+
+ /**
+ * Creates the {@code PrefixedUnitNameSet}.
+ *
+ * @param map
+ * @since 2019-04-13
+ */
+ public PrefixedUnitNameSet(final PrefixedUnitMap map) {
+ this.map = map;
+ }
+
+ @Override
+ public boolean add(final String e) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean addAll(final Collection<? extends String> c) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean contains(final Object o) {
+ return this.map.containsKey(o);
+ }
+
+ @Override
+ public boolean containsAll(final Collection<?> c) {
+ for (final Object o : c)
+ if (!this.contains(o))
+ return false;
+ return true;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return this.map.isEmpty();
+ }
+
+ @Override
+ public Iterator<String> iterator() {
+ return new Iterator<String>() {
+ // position in the unit list
+ int unitNamePosition = -1;
+ // the indices of the prefixes attached to the current unit
+ List<Integer> prefixCoordinates = new ArrayList<>();
+
+ List<String> unitNames = new ArrayList<>(PrefixedUnitNameSet.this.map.units.keySet());
+ List<String> prefixNames = new ArrayList<>(PrefixedUnitNameSet.this.map.prefixes.keySet());
+
+ @Override
+ public boolean hasNext() {
+ if (this.unitNames.isEmpty())
+ return false;
+ else {
+ if (this.prefixNames.isEmpty())
+ return this.unitNamePosition >= this.unitNames.size() - 1;
+ else
+ return true;
+ }
+ }
+
+ @Override
+ public String next() {
+ // increment unit name position
+ this.unitNamePosition++;
+
+ // if I have prefixes, ensure I'm not using a nonlinear unit
+ // since all of the unprefixed stuff is done, just remove nonlinear units
+ if (!this.prefixCoordinates.isEmpty()) {
+ while (!(PrefixedUnitNameSet.this.map
+ .get(this.unitNames.get(this.unitNamePosition)) instanceof LinearUnit)) {
+ this.unitNames.remove(this.unitNamePosition);
+ }
+ }
+
+ // carry over
+ if (!this.prefixNames.isEmpty() && this.unitNamePosition >= this.unitNames.size() - 1) {
+ // handle prefix position
+ this.unitNamePosition = 0;
+ int i = this.prefixCoordinates.size() - 1;
+ this.prefixCoordinates.set(i, this.prefixCoordinates.get(i) + 1);
+
+ while (this.prefixCoordinates.get(i) >= this.prefixNames.size() - 1) {
+ this.prefixCoordinates.set(i, 0);
+ i--;
+ if (i < 0) {
+ this.prefixCoordinates.add(0, 0);
+ }
+ }
+ }
+
+ final StringBuilder unitName = new StringBuilder();
+ for (final int i : this.prefixCoordinates) {
+ unitName.append(this.prefixNames.get(i));
+ }
+ unitName.append(this.unitNames.get(this.unitNamePosition));
+ return unitName.toString();
+ }
+ };
+ }
+
+ @Override
+ public boolean remove(final Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean removeAll(final Collection<?> c) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean removeIf(final Predicate<? super String> filter) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean retainAll(final Collection<?> c) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int size() {
+ if (this.map.units.isEmpty())
+ return 0;
+ else {
+ if (this.map.prefixes.isEmpty())
+ return this.map.units.size();
+ else
+ // infinite set
+ return Integer.MAX_VALUE;
+ }
+ }
+
+ @Override
+ public Object[] toArray() {
+ if (this.map.units.isEmpty())
+ // finite, it will work
+ return super.toArray();
+ else {
+ if (this.map.prefixes.isEmpty())
+ // finite, it will work
+ return super.toArray();
+ else
+ // infinite set
+ throw new UnsupportedOperationException("Cannot make an infinite set into an array.");
+ }
+ }
+
+ @Override
+ public <T> T[] toArray(final T[] a) {
+ if (this.map.units.isEmpty())
+ // finite, it will work
+ return super.toArray(a);
+ else {
+ if (this.map.prefixes.isEmpty())
+ // finite, it will work
+ return super.toArray(a);
+ else
+ // infinite set
+ throw new UnsupportedOperationException("Cannot make an infinite set into an array.");
+ }
+ }
+
+ }
+
+ /**
+ * The units stored in this collection, without prefixes.
+ *
+ * @since 2019-04-13
+ */
+ private final Map<String, Unit> units;
+
+ /**
+ * The available prefixes for use.
+ *
+ * @since 2019-04-13
+ */
+ private final Map<String, UnitPrefix> prefixes;
+
+ // caches
+ private Collection<Unit> values = null;
+ private Set<String> keySet = null;
+ private Set<Entry<String, Unit>> entrySet = null;
+
+ /**
+ * Creates the {@code PrefixedUnitMap}.
+ *
+ * @param units
+ * @param prefixes
+ * @since 2019-04-13
+ */
+ public PrefixedUnitMap(final Map<String, Unit> units, final Map<String, UnitPrefix> prefixes) {
+ // I am making unmodifiable maps to ensure I don't accidentally make changes.
+ this.units = Collections.unmodifiableMap(units);
+ this.prefixes = Collections.unmodifiableMap(prefixes);
+ }
+
+ @Override
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Unit compute(final String key,
+ final BiFunction<? super String, ? super Unit, ? extends Unit> remappingFunction) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Unit computeIfAbsent(final String key, final Function<? super String, ? extends Unit> mappingFunction) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Unit computeIfPresent(final String key,
+ final BiFunction<? super String, ? super Unit, ? extends Unit> remappingFunction) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean containsKey(final Object key) {
+ // First, test if there is a unit with the key
+ if (this.units.containsKey(key))
+ return true;
+
+ // Next, try to cast it to String
+ if (!(key instanceof String))
+ throw new IllegalArgumentException("Attempted to test for a unit using a non-string name.");
+ final String unitName = (String) key;
+
+ // Then, look for the shortest prefix that is attached to a valid unit
+ String shortestPrefix = null;
+ int shortestLength = Integer.MAX_VALUE;
+
+ 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 shorter than the existing largest prefix (since I am looking for the smallest 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() < shortestLength) {
+ final String rest = unitName.substring(prefixName.length());
+ if (this.containsKey(rest) && this.get(rest) instanceof LinearUnit) {
+ shortestPrefix = prefixName;
+ shortestLength = prefixName.length();
+ }
+ }
+ }
+
+ return shortestPrefix != null;
+ }
+
+ @Override
+ public boolean containsValue(final Object value) {
+ return this.units.containsValue(value);
+ }
+
+ @Override
+ public Set<Entry<String, Unit>> entrySet() {
+ if (this.entrySet == null) {
+ this.entrySet = new PrefixedUnitEntrySet(this);
+ }
+ return this.entrySet;
+ }
+
+ @Override
+ public Unit get(final Object key) {
+ // First, test if there is a unit with the key
+ if (this.units.containsKey(key))
+ return this.units.get(key);
+
+ // Next, try to cast it to String
+ if (!(key instanceof String))
+ throw new IllegalArgumentException("Attempted to obtain a unit using a non-string name.");
+ final String unitName = (String) key;
+
+ // Then, look for the shortest prefix that is attached to a valid unit
+ String shortestPrefix = null;
+ int shortestLength = Integer.MAX_VALUE;
+
+ 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 shorter than the existing largest prefix (since I am looking for the smallest 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() < shortestLength) {
+ final String rest = unitName.substring(prefixName.length());
+ if (this.containsKey(rest) && this.get(rest) instanceof LinearUnit) {
+ shortestPrefix = prefixName;
+ shortestLength = prefixName.length();
+ }
+ }
+ }
+
+ // if none found, returns null
+ if (shortestPrefix == null)
+ return null;
+ else {
+ // get necessary data
+ final String rest = unitName.substring(shortestLength);
+ // this cast will not fail because I verified that it would work before selecting this prefix
+ final LinearUnit unit = (LinearUnit) this.get(rest);
+ final UnitPrefix prefix = this.prefixes.get(shortestPrefix);
+
+ return unit.withPrefix(prefix);
+ }
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return this.units.isEmpty();
+ }
+
+ @Override
+ public Set<String> keySet() {
+ if (this.keySet == null) {
+ this.keySet = new PrefixedUnitNameSet(this);
+ }
+ return this.keySet;
+ }
+
+ @Override
+ public Unit merge(final String key, final Unit value,
+ final BiFunction<? super Unit, ? super Unit, ? extends Unit> remappingFunction) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Unit put(final String key, final Unit value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void putAll(final Map<? extends String, ? extends Unit> m) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Unit putIfAbsent(final String key, final Unit value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Unit remove(final Object key) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean remove(final Object key, final Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Unit replace(final String key, final Unit value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean replace(final String key, final Unit oldValue, final Unit newValue) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void replaceAll(final BiFunction<? super String, ? super Unit, ? extends Unit> function) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int size() {
+ if (this.units.isEmpty())
+ return 0;
+ else {
+ if (this.prefixes.isEmpty())
+ return this.units.size();
+ else
+ // infinite set
+ return Integer.MAX_VALUE;
+ }
+ }
+
+ @Override
+ public Collection<Unit> values() {
+ if (this.values == null) {
+ this.values = Collections.unmodifiableCollection(this.units.values());
+ }
+ return this.values;
+ }
+ }
+
+ /**
* The exponent operator
*
* @param base
@@ -57,7 +702,7 @@ public final class UnitsDatabase {
* @return result
* @since 2019-04-10
*/
- private static final LinearUnit exponent(final LinearUnit base, final LinearUnit exponentUnit) {
+ private static final LinearUnit exponentiateUnits(final LinearUnit base, final LinearUnit exponentUnit) {
// exponent function - first check if o2 is a number,
if (exponentUnit.getBase().equals(SI.SI.getBaseUnit(UnitDimension.EMPTY))) {
// then check if it is an integer,
@@ -74,12 +719,12 @@ public final class UnitsDatabase {
}
/**
- * The units in this system.
+ * The units in this system, excluding prefixes.
*
* @since 2019-01-07
* @since v0.1.0
*/
- private final Map<String, Unit> units;
+ private final Map<String, Unit> prefixlessUnits;
/**
* The unit prefixes in this system.
@@ -97,6 +742,13 @@ public final class UnitsDatabase {
private final Map<String, UnitDimension> dimensions;
/**
+ * A map mapping strings to units (including prefixes)
+ *
+ * @since 2019-04-13
+ */
+ private final Map<String, Unit> units;
+
+ /**
* A parser that can parse unit expressions.
*
* @since 2019-03-22
@@ -106,7 +758,7 @@ public final class UnitsDatabase {
.addBinaryOperator("-", (o1, o2) -> o1.minus(o2), 0)
.addBinaryOperator("*", (o1, o2) -> o1.times(o2), 1).addSpaceFunction("*")
.addBinaryOperator("/", (o1, o2) -> o1.dividedBy(o2), 1)
- .addBinaryOperator("^", UnitsDatabase::exponent, 2).build();
+ .addBinaryOperator("^", UnitsDatabase::exponentiateUnits, 2).build();
/**
* A parser that can parse unit prefix expressions
@@ -134,9 +786,10 @@ public final class UnitsDatabase {
* @since v0.1.0
*/
public UnitsDatabase() {
- this.units = new HashMap<>();
+ this.prefixlessUnits = new HashMap<>();
this.prefixes = new HashMap<>();
this.dimensions = new HashMap<>();
+ this.units = new PrefixedUnitMap(this.prefixlessUnits, this.prefixes);
}
/**
@@ -236,7 +889,7 @@ public final class UnitsDatabase {
* @since v0.1.0
*/
public void addUnit(final String name, final Unit unit) {
- this.units.put(Objects.requireNonNull(name, "name must not be null."),
+ this.prefixlessUnits.put(Objects.requireNonNull(name, "name must not be null."),
Objects.requireNonNull(unit, "unit must not be null."));
}
@@ -314,19 +967,6 @@ public final class UnitsDatabase {
}
/**
- * Tests if the database has a unit with this name, ignoring prefixes
- *
- * @param name
- * name to test
- * @return if database contains name
- * @since 2019-01-13
- * @since v0.1.0
- */
- public boolean containsPrefixlessUnitName(final String name) {
- return this.units.containsKey(name);
- }
-
- /**
* Tests if the database has a unit prefix with this name.
*
* @param name
@@ -349,21 +989,15 @@ public final class UnitsDatabase {
* @since v0.1.0
*/
public boolean containsUnitName(final String name) {
- // check for prefixes
- for (final String prefixName : this.prefixNameSet()) {
- if (name.startsWith(prefixName))
- if (this.containsUnitName(name.substring(prefixName.length())))
- return true;
- }
return this.units.containsKey(name);
}
/**
- * @return an immutable set of all of the dimension names in this database.
- * @since 2019-03-14
+ * @return a map mapping dimension names to dimensions
+ * @since 2019-04-13
*/
- public Set<String> dimensionNameSet() {
- return Collections.unmodifiableSet(this.dimensions.keySet());
+ public Map<String, UnitDimension> dimensionMap() {
+ return Collections.unmodifiableMap(this.dimensions);
}
/**
@@ -520,19 +1154,6 @@ public final class UnitsDatabase {
}
/**
- * Gets a unit from the database from its name, ignoring prefixes.
- *
- * @param name
- * unit's name
- * @return unit
- * @since 2019-01-10
- * @since v0.1.0
- */
- public Unit getPrefixlessUnit(final String name) {
- return this.units.get(name);
- }
-
- /**
* Gets a unit from the database from its name, looking for prefixes.
*
* @param name
@@ -546,24 +1167,6 @@ public final class UnitsDatabase {
final double value = Double.parseDouble(name);
return SI.SI.getBaseUnit(UnitDimension.EMPTY).times(value);
} catch (final NumberFormatException e) {
- for (final String prefixName : this.prefixNameSet()) {
- // check for a prefix
- if (name.startsWith(prefixName)) {
- // prefix found! Make sure what comes after it is actually a unit!
- final String prefixless = name.substring(prefixName.length());
- if (this.containsUnitName(prefixless)) {
- // yep, it's a proper prefix! Get the unit!
- final Unit unit = this.getUnit(prefixless);
- final UnitPrefix prefix = this.getPrefix(prefixName);
-
- // Prefixes only work with linear and base units, so make sure it's one of those
- if (unit instanceof LinearUnit) {
- final LinearUnit linearUnit = (LinearUnit) unit;
- return linearUnit.withPrefix(prefix);
- }
- }
- }
- }
return this.units.get(name);
}
@@ -703,28 +1306,26 @@ public final class UnitsDatabase {
}
/**
- * @return an immutable set of all of the unit names in this database, ignoring prefixes
- * @since 2019-01-14
- * @since v0.1.0
+ * @return a map mapping prefix names to prefixes
+ * @since 2019-04-13
*/
- public Set<String> prefixlessUnitNameSet() {
- return Collections.unmodifiableSet(this.units.keySet());
+ public Map<String, UnitPrefix> prefixMap() {
+ return Collections.unmodifiableMap(this.prefixes);
}
/**
- * @return an immutable set of all of the units in this database, ignoring prefixes.
- * @since 2019-04-10
+ * @return a map mapping unit names to units, including prefixed names
+ * @since 2019-04-13
*/
- public Set<Unit> prefixlessUnitSet() {
- return Collections.unmodifiableSet(new HashSet<>(this.units.values()));
+ public Map<String, Unit> unitMap() {
+ return this.units; // PrefixedUnitMap is immutable so I don't need to make an unmodifiable map.
}
/**
- * @return an immutable set of all of the prefix names in this database
- * @since 2019-01-14
- * @since v0.1.0
+ * @return a map mapping unit names to units, ignoring prefixes
+ * @since 2019-04-13
*/
- public Set<String> prefixNameSet() {
- return Collections.unmodifiableSet(this.prefixes.keySet());
+ public Map<String, Unit> unitMapPrefixless() {
+ return Collections.unmodifiableMap(this.prefixlessUnits);
}
}
diff --git a/src/org/unitConverter/converterGUI/UnitConverterGUI.java b/src/org/unitConverter/converterGUI/UnitConverterGUI.java
index 9314510..49a40d6 100755
--- a/src/org/unitConverter/converterGUI/UnitConverterGUI.java
+++ b/src/org/unitConverter/converterGUI/UnitConverterGUI.java
@@ -24,6 +24,7 @@ import java.math.MathContext;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
@@ -143,19 +144,19 @@ final class UnitConverterGUI {
return o1.compareTo(o2);
};
- this.unitNames = new ArrayList<>(this.units.prefixlessUnitNameSet());
+ this.unitNames = new ArrayList<>(this.units.unitMapPrefixless().keySet());
this.unitNames.sort(null); // sorts it using Comparable
- this.unitNamesFiltered = new DelegateListModel<>(new ArrayList<>(this.units.prefixlessUnitNameSet()));
+ this.unitNamesFiltered = new DelegateListModel<>(new ArrayList<>(this.units.unitMapPrefixless().keySet()));
this.unitNamesFiltered.sort(null); // sorts it using Comparable
- this.prefixNames = new ArrayList<>(this.units.prefixNameSet());
+ this.prefixNames = new ArrayList<>(this.units.prefixMap().keySet());
this.prefixNames.sort(this.prefixNameComparator); // sorts it using my comparator
- this.prefixNamesFiltered = new DelegateListModel<>(new ArrayList<>(this.units.prefixNameSet()));
+ this.prefixNamesFiltered = new DelegateListModel<>(new ArrayList<>(this.units.prefixMap().keySet()));
this.prefixNamesFiltered.sort(this.prefixNameComparator); // sorts it using my comparator
- this.dimensionNames = new DelegateListModel<>(new ArrayList<>(this.units.dimensionNameSet()));
+ this.dimensionNames = new DelegateListModel<>(new ArrayList<>(this.units.dimensionMap().keySet()));
this.dimensionNames.sort(null); // sorts it using Comparable
// a Predicate that returns true iff the argument is a full base unit
@@ -163,8 +164,9 @@ final class UnitConverterGUI {
// print out unit counts
System.out.printf("Successfully loaded %d units with %d unit names (%d base units).%n",
- this.units.prefixlessUnitSet().size(), this.units.prefixlessUnitNameSet().size(),
- this.units.prefixlessUnitSet().stream().filter(isFullBase).count());
+ new HashSet<>(this.units.unitMapPrefixless().values()).size(),
+ this.units.unitMapPrefixless().size(),
+ new HashSet<>(this.units.unitMapPrefixless().values()).stream().filter(isFullBase).count());
}
/**
@@ -449,7 +451,7 @@ final class UnitConverterGUI {
}
public final Set<String> unitNameSet() {
- return this.units.prefixlessUnitNameSet();
+ return this.units.unitMapPrefixless().keySet();
}
}
diff --git a/src/org/unitConverter/math/ExpressionParser.java b/src/org/unitConverter/math/ExpressionParser.java
index b56fa71..d01afaa 100644
--- a/src/org/unitConverter/math/ExpressionParser.java
+++ b/src/org/unitConverter/math/ExpressionParser.java
@@ -510,8 +510,6 @@ public final class ExpressionParser<T> {
expressionRPN = expressionRPN.substring(0, expressionRPN.length() - 1);
}
return expressionRPN;
-
- // TODO document org.unitConverter.expressionParser.ExpressionParser.convertExpressionToPolish(expression)
}
/**