From 50a195ef78af5d15dd6e548d4d6928c281bbaac2 Mon Sep 17 00:00:00 2001
From: Adrien Hopkins
Date: Fri, 3 May 2019 15:07:16 -0400
Subject: Added toString to UnitsDatabase and its helper classes.
---
src/org/unitConverter/UnitsDatabase.java | 286 +++++++++++++++++++++++++------
src/test/java/UnitsDatabaseTest.java | 67 +++++++-
2 files changed, 295 insertions(+), 58 deletions(-)
diff --git a/src/org/unitConverter/UnitsDatabase.java b/src/org/unitConverter/UnitsDatabase.java
index e5d2f67..37d53d4 100755
--- a/src/org/unitConverter/UnitsDatabase.java
+++ b/src/org/unitConverter/UnitsDatabase.java
@@ -75,7 +75,11 @@ public final class UnitsDatabase {
*
* This map is infinite in size if there is at least one unit and at least one prefix. If it is infinite, some
* operations that only work with finite collections, like converting name/entry sets to arrays, will throw an
- * {@code UnsupportedOperationException}.
+ * {@code IllegalStateException}.
+ *
+ *
+ * Because of ambiguities between prefixes (i.e. kilokilo = mega), {@link #containsValue} and {@link #values()}
+ * currently ignore prefixes.
*
*
* @author Adrien Hopkins
@@ -86,6 +90,12 @@ public final class UnitsDatabase {
/**
* The class used for entry sets.
*
+ *
+ * If the map that created this set is infinite in size (has at least one unit and at least one prefix), this
+ * set is infinite as well. If this set is infinite in size, {@link #toArray} will fail with a
+ * {@code IllegalStateException} instead of creating an infinite-sized array.
+ *
+ *
* @author Adrien Hopkins
* @since 2019-04-13
* @since v0.2.0
@@ -117,6 +127,18 @@ public final class UnitsDatabase {
this.value = value;
}
+ /**
+ * @since 2019-05-03
+ */
+ @Override
+ public boolean equals(final Object o) {
+ if (!(o instanceof Map.Entry))
+ return false;
+ final Map.Entry, ?> other = (Map.Entry, ?>) o;
+ return Objects.equals(this.getKey(), other.getKey())
+ && Objects.equals(this.getValue(), other.getValue());
+ }
+
@Override
public String getKey() {
return this.key;
@@ -127,9 +149,29 @@ public final class UnitsDatabase {
return this.value;
}
+ /**
+ * @since 2019-05-03
+ */
+ @Override
+ public int hashCode() {
+ return (this.getKey() == null ? 0 : this.getKey().hashCode())
+ ^ (this.getValue() == null ? 0 : this.getValue().hashCode());
+ }
+
@Override
public Unit setValue(final Unit value) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot set value in an immutable entry");
+ }
+
+ /**
+ * Returns a string representation of the entry. The format of the string is the string representation
+ * of the key, then the equals ({@code =}) character, then the string representation of the value.
+ *
+ * @since 2019-05-03
+ */
+ @Override
+ public String toString() {
+ return this.getKey() + "=" + this.getValue();
}
}
@@ -148,8 +190,8 @@ public final class UnitsDatabase {
// values from the unit entry set
private final Map map;
- private final List unitNames;
- private final List prefixNames;
+ private transient final List unitNames;
+ private transient final List prefixNames;
/**
* Creates the {@code UnitsDatabase.PrefixedUnitMap.PrefixedUnitNameSet.PrefixedUnitNameIterator}.
@@ -157,10 +199,10 @@ public final class UnitsDatabase {
* @since 2019-04-14
* @since v0.2.0
*/
- public PrefixedUnitEntryIterator(final PrefixedUnitEntrySet set) {
- this.map = set.map;
- this.unitNames = new ArrayList<>(set.map.units.keySet());
- this.prefixNames = new ArrayList<>(set.map.prefixes.keySet());
+ public PrefixedUnitEntryIterator(final PrefixedUnitMap map) {
+ this.map = map;
+ this.unitNames = new ArrayList<>(map.units.keySet());
+ this.prefixNames = new ArrayList<>(map.prefixes.keySet());
}
/**
@@ -228,8 +270,23 @@ public final class UnitsDatabase {
@Override
public Entry next() {
+ // get next element
+ final Entry nextEntry = this.peek();
+
+ // iterate to next position
+ this.incrementPosition();
+
+ return nextEntry;
+ }
+
+ /**
+ * @return the next element in the iterator, without iterating over it
+ * @since 2019-05-03
+ */
+ private Entry peek() {
if (!this.hasNext())
throw new NoSuchElementException("No units left!");
+
// if I have prefixes, ensure I'm not using a nonlinear unit
// since all of the unprefixed stuff is done, just remove nonlinear units
if (!this.prefixCoordinates.isEmpty()) {
@@ -241,10 +298,20 @@ public final class UnitsDatabase {
final String nextName = this.getCurrentUnitName();
- this.incrementPosition();
-
return new PrefixedUnitEntry(nextName, this.map.get(nextName));
}
+
+ /**
+ * Returns a string representation of the object. The exact details of the representation are
+ * unspecified and subject to change.
+ *
+ * @since 2019-05-03
+ */
+ @Override
+ public String toString() {
+ return String.format("Iterator iterating over name-unit entries; next value is \"%s\"",
+ this.peek());
+ }
}
// the map that created this set
@@ -264,17 +331,17 @@ public final class UnitsDatabase {
@Override
public boolean add(final Map.Entry e) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot add to an immutable set");
}
@Override
public boolean addAll(final Collection extends Map.Entry> c) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot add to an immutable set");
}
@Override
public void clear() {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot clear an immutable set");
}
@Override
@@ -283,7 +350,7 @@ public final class UnitsDatabase {
final Entry entry;
try {
- // This is OK because I'm in a try-catch block.
+ // This is OK because I'm in a try-catch block, catching the exact exception that would be thrown.
@SuppressWarnings("unchecked")
final Entry tempEntry = (Entry) o;
entry = tempEntry;
@@ -309,27 +376,27 @@ public final class UnitsDatabase {
@Override
public Iterator> iterator() {
- return new PrefixedUnitEntryIterator(this);
+ return new PrefixedUnitEntryIterator(this.map);
}
@Override
public boolean remove(final Object o) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot remove from an immutable set");
}
@Override
public boolean removeAll(final Collection> c) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot remove from an immutable set");
}
@Override
public boolean removeIf(final Predicate super Entry> filter) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot remove from an immutable set");
}
@Override
public boolean retainAll(final Collection> c) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot remove from an immutable set");
}
@Override
@@ -345,29 +412,51 @@ public final class UnitsDatabase {
}
}
+ /**
+ * @throws IllegalStateException
+ * if the set is infinite in size
+ */
@Override
public Object[] toArray() {
if (this.map.units.isEmpty() || this.map.prefixes.isEmpty())
return super.toArray();
else
// infinite set
- throw new UnsupportedOperationException("Cannot make an infinite set into an array.");
+ throw new IllegalStateException("Cannot make an infinite set into an array.");
}
+ /**
+ * @throws IllegalStateException
+ * if the set is infinite in size
+ */
@Override
public T[] toArray(final T[] a) {
if (this.map.units.isEmpty() || this.map.prefixes.isEmpty())
return super.toArray(a);
else
// infinite set
- throw new UnsupportedOperationException("Cannot make an infinite set into an array.");
+ throw new IllegalStateException("Cannot make an infinite set into an array.");
}
+ @Override
+ public String toString() {
+ if (this.map.units.isEmpty() || this.map.prefixes.isEmpty())
+ return super.toString();
+ else
+ return String.format("Infinite set of name-unit entries created from units %s and prefixes %s",
+ this.map.units, this.map.prefixes);
+ }
}
/**
* The class used for unit name sets.
*
+ *
+ * If the map that created this set is infinite in size (has at least one unit and at least one prefix), this
+ * set is infinite as well. If this set is infinite in size, {@link #toArray} will fail with a
+ * {@code IllegalStateException} instead of creating an infinite-sized array.
+ *
+ *
* @author Adrien Hopkins
* @since 2019-04-13
* @since v0.2.0
@@ -388,8 +477,8 @@ public final class UnitsDatabase {
// values from the unit name set
private final Map map;
- private final List unitNames;
- private final List prefixNames;
+ private transient final List unitNames;
+ private transient final List prefixNames;
/**
* Creates the {@code UnitsDatabase.PrefixedUnitMap.PrefixedUnitNameSet.PrefixedUnitNameIterator}.
@@ -397,10 +486,10 @@ public final class UnitsDatabase {
* @since 2019-04-14
* @since v0.2.0
*/
- public PrefixedUnitNameIterator(final PrefixedUnitNameSet set) {
- this.map = set.map;
- this.unitNames = new ArrayList<>(set.map.units.keySet());
- this.prefixNames = new ArrayList<>(set.map.prefixes.keySet());
+ public PrefixedUnitNameIterator(final PrefixedUnitMap map) {
+ this.map = map;
+ this.unitNames = new ArrayList<>(map.units.keySet());
+ this.prefixNames = new ArrayList<>(map.prefixes.keySet());
}
/**
@@ -468,6 +557,18 @@ public final class UnitsDatabase {
@Override
public String next() {
+ final String nextName = this.peek();
+
+ this.incrementPosition();
+
+ return nextName;
+ }
+
+ /**
+ * @return the next element in the iterator, without iterating over it
+ * @since 2019-05-03
+ */
+ private String peek() {
if (!this.hasNext())
throw new NoSuchElementException("No units left!");
// if I have prefixes, ensure I'm not using a nonlinear unit
@@ -479,11 +580,18 @@ public final class UnitsDatabase {
}
}
- final String nextName = this.getCurrentUnitName();
-
- this.incrementPosition();
+ return this.getCurrentUnitName();
+ }
- return nextName;
+ /**
+ * Returns a string representation of the object. The exact details of the representation are
+ * unspecified and subject to change.
+ *
+ * @since 2019-05-03
+ */
+ @Override
+ public String toString() {
+ return String.format("Iterator iterating over unit names; next value is \"%s\"", this.peek());
}
}
@@ -504,17 +612,17 @@ public final class UnitsDatabase {
@Override
public boolean add(final String e) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot add to an immutable set");
}
@Override
public boolean addAll(final Collection extends String> c) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot add to an immutable set");
}
@Override
public void clear() {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot clear an immutable set");
}
@Override
@@ -537,27 +645,27 @@ public final class UnitsDatabase {
@Override
public Iterator iterator() {
- return new PrefixedUnitNameIterator(this);
+ return new PrefixedUnitNameIterator(this.map);
}
@Override
public boolean remove(final Object o) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot remove from an immutable set");
}
@Override
public boolean removeAll(final Collection> c) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot remove from an immutable set");
}
@Override
public boolean removeIf(final Predicate super String> filter) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot remove from an immutable set");
}
@Override
public boolean retainAll(final Collection> c) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot remove from an immutable set");
}
@Override
@@ -573,23 +681,40 @@ public final class UnitsDatabase {
}
}
+ /**
+ * @throws IllegalStateException
+ * if the set is infinite in size
+ */
@Override
public Object[] toArray() {
if (this.map.units.isEmpty() || this.map.prefixes.isEmpty())
return super.toArray();
else
// infinite set
- throw new UnsupportedOperationException("Cannot make an infinite set into an array.");
+ throw new IllegalStateException("Cannot make an infinite set into an array.");
}
+ /**
+ * @throws IllegalStateException
+ * if the set is infinite in size
+ */
@Override
public T[] toArray(final T[] a) {
if (this.map.units.isEmpty() || this.map.prefixes.isEmpty())
return super.toArray(a);
else
// infinite set
- throw new UnsupportedOperationException("Cannot make an infinite set into an array.");
+ throw new IllegalStateException("Cannot make an infinite set into an array.");
+ }
+
+ @Override
+ public String toString() {
+ if (this.map.units.isEmpty() || this.map.prefixes.isEmpty())
+ return super.toString();
+ else
+ return String.format("Infinite set of name-unit entries created from units %s and prefixes %s",
+ this.map.units, this.map.prefixes);
}
}
@@ -610,9 +735,9 @@ public final class UnitsDatabase {
private final Map prefixes;
// caches
- private Collection values = null;
- private Set keySet = null;
- private Set> entrySet = null;
+ private transient Collection values = null;
+ private transient Set keySet = null;
+ private transient Set> entrySet = null;
/**
* Creates the {@code PrefixedUnitMap}.
@@ -632,24 +757,24 @@ public final class UnitsDatabase {
@Override
public void clear() {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot clear an immutable map");
}
@Override
public Unit compute(final String key,
final BiFunction super String, ? super Unit, ? extends Unit> remappingFunction) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot edit an immutable map");
}
@Override
public Unit computeIfAbsent(final String key, final Function super String, ? extends Unit> mappingFunction) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot edit an immutable map");
}
@Override
public Unit computeIfPresent(final String key,
final BiFunction super String, ? super Unit, ? extends Unit> remappingFunction) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot edit an immutable map");
}
@Override
@@ -685,6 +810,13 @@ public final class UnitsDatabase {
return longestPrefix != null;
}
+ /**
+ * {@inheritDoc}
+ *
+ *
+ * Because of ambiguities between prefixes (i.e. kilokilo = mega), this method only tests for prefixless units.
+ *
+ */
@Override
public boolean containsValue(final Object value) {
return this.units.containsValue(value);
@@ -758,47 +890,47 @@ public final class UnitsDatabase {
@Override
public Unit merge(final String key, final Unit value,
final BiFunction super Unit, ? super Unit, ? extends Unit> remappingFunction) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot merge into an immutable map");
}
@Override
public Unit put(final String key, final Unit value) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot add entries to an immutable map");
}
@Override
public void putAll(final Map extends String, ? extends Unit> m) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot add entries to an immutable map");
}
@Override
public Unit putIfAbsent(final String key, final Unit value) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot add entries to an immutable map");
}
@Override
public Unit remove(final Object key) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot remove entries from an immutable map");
}
@Override
public boolean remove(final Object key, final Object value) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot remove entries from an immutable map");
}
@Override
public Unit replace(final String key, final Unit value) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot replace entries in an immutable map");
}
@Override
public boolean replace(final String key, final Unit oldValue, final Unit newValue) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot replace entries in an immutable map");
}
@Override
public void replaceAll(final BiFunction super String, ? super Unit, ? extends Unit> function) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Cannot replace entries in an immutable map");
}
@Override
@@ -814,6 +946,22 @@ public final class UnitsDatabase {
}
}
+ @Override
+ public String toString() {
+ if (this.units.isEmpty() || this.prefixes.isEmpty())
+ return super.toString();
+ else
+ return String.format("Infinite map of name-unit entries created from units %s and prefixes %s",
+ this.units, this.prefixes);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ *
+ * Because of ambiguities between prefixes (i.e. kilokilo = mega), this method ignores prefixes.
+ *
+ */
@Override
public Collection values() {
if (this.values == null) {
@@ -1459,7 +1607,31 @@ public final class UnitsDatabase {
return Collections.unmodifiableMap(this.prefixes);
}
+ @Override
+ public String toString() {
+ return String.format("Unit Database with %d units and %d unit prefixes", this.prefixlessUnits.size(),
+ this.prefixes.size());
+ }
+
/**
+ * Returns a map mapping unit names to units, including units with prefixes.
+ *
+ * The returned map is infinite in size if there is at least one unit and at least one prefix. If it is infinite,
+ * some operations that only work with finite collections, like converting name/entry sets to arrays, will throw an
+ * {@code IllegalStateException}.
+ *
+ *
+ * Specifically, the operations that will throw an IllegalStateException if the map is infinite in size are:
+ *
+ * Because of ambiguities between prefixes (i.e. kilokilo = mega), the map's {@link PrefixedUnitMap#containsValue
+ * containsValue} and {@link PrefixedUnitMap#values() values()} methods currently ignore prefixes.
+ *
+ *
* @return a map mapping unit names to units, including prefixed names
* @since 2019-04-13
* @since v0.2.0
diff --git a/src/test/java/UnitsDatabaseTest.java b/src/test/java/UnitsDatabaseTest.java
index 9222740..6d2247f 100644
--- a/src/test/java/UnitsDatabaseTest.java
+++ b/src/test/java/UnitsDatabaseTest.java
@@ -19,6 +19,7 @@ package test.java;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.util.Iterator;
import java.util.Map;
@@ -49,6 +50,8 @@ public class UnitsDatabaseTest {
// used for testing expressions
// J = U^2 * V / W^2
private static final LinearUnit J = SI.KILOGRAM.times(SI.METRE.toExponent(2)).dividedBy(SI.SECOND.toExponent(2));
+ private static final LinearUnit K = SI.KELVIN;
+
private static final Unit NONLINEAR = new AbstractUnit(SI.METRE) {
@Override
@@ -69,6 +72,52 @@ public class UnitsDatabaseTest {
private static final UnitPrefix AB = new DefaultUnitPrefix(7);
private static final UnitPrefix BC = new DefaultUnitPrefix(11);
+ /**
+ * Confirms that operations that shouldn't function for infinite databases throw an {@code IllegalStateException}.
+ *
+ * @since 2019-05-03
+ */
+ @Test
+ public void testInfiniteSetExceptions() {
+ // load units
+ final UnitsDatabase infiniteDatabase = new UnitsDatabase();
+
+ infiniteDatabase.addUnit("J", J);
+ infiniteDatabase.addUnit("K", K);
+
+ infiniteDatabase.addPrefix("A", A);
+ infiniteDatabase.addPrefix("B", B);
+ infiniteDatabase.addPrefix("C", C);
+
+ {
+ boolean exceptionThrown = false;
+ try {
+ infiniteDatabase.unitMap().entrySet().toArray();
+ } catch (final IllegalStateException e) {
+ exceptionThrown = true;
+ // pass!
+ } finally {
+ if (!exceptionThrown) {
+ fail("No IllegalStateException thrown");
+ }
+ }
+ }
+
+ {
+ boolean exceptionThrown = false;
+ try {
+ infiniteDatabase.unitMap().keySet().toArray();
+ } catch (final IllegalStateException e) {
+ exceptionThrown = true;
+ // pass!
+ } finally {
+ if (!exceptionThrown) {
+ fail("No IllegalStateException thrown");
+ }
+ }
+ }
+ }
+
/**
* Test that prefixes correctly apply to units.
*
@@ -189,11 +238,15 @@ public class UnitsDatabaseTest {
final UnitsDatabase database = new UnitsDatabase();
database.addUnit("J", J);
+ database.addUnit("K", K);
database.addPrefix("A", A);
database.addPrefix("B", B);
database.addPrefix("C", C);
+ final int NUM_UNITS = database.unitMapPrefixless().size();
+ final int NUM_PREFIXES = database.prefixMap().size();
+
final Iterator nameIterator = database.unitMap().keySet().iterator();
final Iterator> entryIterator = database.unitMap().entrySet().iterator();
@@ -203,11 +256,12 @@ public class UnitsDatabaseTest {
// loop 1000 times
for (int i = 0; i < 1000; i++) {
// expected length of next
- if (unitsWithThisLengthSoFar >= (int) Math.pow(3, expectedLength - 1)) {
+ if (unitsWithThisLengthSoFar >= NUM_UNITS * (int) Math.pow(NUM_PREFIXES, expectedLength - 1)) {
expectedLength++;
unitsWithThisLengthSoFar = 0;
}
+ // test that stuff is valid
final String nextName = nameIterator.next();
final Unit nextUnit = database.getUnit(nextName);
final Entry nextEntry = entryIterator.next();
@@ -218,6 +272,17 @@ public class UnitsDatabaseTest {
unitsWithThisLengthSoFar++;
}
+
+ // test toString for consistency
+ final String entryIteratorString = entryIterator.toString();
+ for (int i = 0; i < 3; i++) {
+ assertEquals(entryIteratorString, entryIterator.toString());
+ }
+
+ final String nameIteratorString = nameIterator.toString();
+ for (int i = 0; i < 3; i++) {
+ assertEquals(nameIteratorString, nameIterator.toString());
+ }
}
/**
--
cgit v1.2.3
From 987fd8406d65505aedecd17e51216eb0ce393fbb Mon Sep 17 00:00:00 2001
From: Adrien Hopkins
Date: Wed, 22 May 2019 17:32:40 -0400
Subject: Added new default methods to the Unit interface.
---
.../converterGUI/UnitConverterGUI.java | 4 +-
src/org/unitConverter/math/ExpressionParser.java | 8 +-
src/org/unitConverter/unit/AbstractUnit.java | 7 ++
src/org/unitConverter/unit/FunctionalUnit.java | 91 ++++++++++++++++++++++
src/org/unitConverter/unit/NonlinearUnits.java | 28 +------
src/org/unitConverter/unit/Unit.java | 46 +++++++++++
src/test/java/UnitTest.java | 2 +-
src/test/java/UnitsDatabaseTest.java | 14 +---
8 files changed, 156 insertions(+), 44 deletions(-)
create mode 100644 src/org/unitConverter/unit/FunctionalUnit.java
diff --git a/src/org/unitConverter/converterGUI/UnitConverterGUI.java b/src/org/unitConverter/converterGUI/UnitConverterGUI.java
index e258c6f..2d3d1a5 100755
--- a/src/org/unitConverter/converterGUI/UnitConverterGUI.java
+++ b/src/org/unitConverter/converterGUI/UnitConverterGUI.java
@@ -191,7 +191,7 @@ final class UnitConverterGUI {
return;
}
final double beforeValue = Double.parseDouble(input);
- final double value = to.convertFromBase(from.convertToBase(beforeValue));
+ final double value = from.convertTo(to, beforeValue);
final String output = this.getRoundedString(value);
@@ -254,7 +254,7 @@ final class UnitConverterGUI {
return;
}
- value = to.convertFromBase(from.convertToBase(1));
+ value = from.convertTo(to, 1);
// round value
final String output = this.getRoundedString(value);
diff --git a/src/org/unitConverter/math/ExpressionParser.java b/src/org/unitConverter/math/ExpressionParser.java
index b2261ed..8a0e97d 100644
--- a/src/org/unitConverter/math/ExpressionParser.java
+++ b/src/org/unitConverter/math/ExpressionParser.java
@@ -55,7 +55,7 @@ public final class ExpressionParser {
* @since 2019-03-14
* @since v0.2.0
*/
- private final Function objectObtainer;
+ private final Function objectObtainer;
/**
* The function of the space as an operator (like 3 x y)
@@ -91,7 +91,7 @@ public final class ExpressionParser {
* @since 2019-03-17
* @since v0.2.0
*/
- public Builder(final Function objectObtainer) {
+ public Builder(final Function objectObtainer) {
this.objectObtainer = Objects.requireNonNull(objectObtainer, "objectObtainer must not be null.");
this.unaryOperators = new HashMap<>();
this.binaryOperators = new HashMap<>();
@@ -397,7 +397,7 @@ public final class ExpressionParser {
* @since 2019-03-14
* @since v0.2.0
*/
- private final Function objectObtainer;
+ private final Function objectObtainer;
/**
* A map mapping operator strings to operator functions, for unary operators.
@@ -437,7 +437,7 @@ public final class ExpressionParser {
* @since 2019-03-14
* @since v0.2.0
*/
- private ExpressionParser(final Function objectObtainer,
+ private ExpressionParser(final Function objectObtainer,
final Map> unaryOperators,
final Map> binaryOperators, final String spaceOperator) {
this.objectObtainer = objectObtainer;
diff --git a/src/org/unitConverter/unit/AbstractUnit.java b/src/org/unitConverter/unit/AbstractUnit.java
index 05a6c17..6045127 100644
--- a/src/org/unitConverter/unit/AbstractUnit.java
+++ b/src/org/unitConverter/unit/AbstractUnit.java
@@ -23,6 +23,13 @@ import org.unitConverter.dimension.UnitDimension;
/**
* The default abstract implementation of the {@code Unit} interface.
*
+ *
+ * With the addition of {@link Unit#fromConversionFunctions}, there is no longer any reason to use {@code AbstractUnit}
+ * for any purpose other than making subclasses. Units should never be declared as {@code AbstractUnit}, they should be
+ * declared as {@code Unit}. Now that {@code Unit.fromConversionFunctions} exists, it is preferred to creating anonymous
+ * inner types of {@code AbstractUnit}.
+ *
+ *
* @author Adrien Hopkins
* @since 2018-12-22
* @since v0.1.0
diff --git a/src/org/unitConverter/unit/FunctionalUnit.java b/src/org/unitConverter/unit/FunctionalUnit.java
new file mode 100644
index 0000000..c2aae6d
--- /dev/null
+++ b/src/org/unitConverter/unit/FunctionalUnit.java
@@ -0,0 +1,91 @@
+/**
+ * Copyright (C) 2019 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.unitConverter.unit;
+
+import java.util.function.DoubleUnaryOperator;
+
+/**
+ * A unit that uses functional objects to convert to and from its base.
+ *
+ * @author Adrien Hopkins
+ * @since 2019-05-22
+ */
+final class FunctionalUnit extends AbstractUnit {
+ /**
+ * Returns a unit from its base and the functions it uses to convert to and from its 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
+ */
+ public static FunctionalUnit valueOf(final BaseUnit base, final DoubleUnaryOperator converterFrom,
+ final DoubleUnaryOperator converterTo) {
+ return new FunctionalUnit(base, converterFrom, converterTo);
+ }
+
+ /**
+ * A function that accepts a value expressed in the unit's base and returns that value expressed in this unit.
+ *
+ * @since 2019-05-22
+ */
+ private final DoubleUnaryOperator converterFrom;
+
+ /**
+ * A function that accepts a value expressed in the unit and returns that value expressed in the unit's base.
+ *
+ * @since 2019-05-22
+ */
+ private final DoubleUnaryOperator converterTo;
+
+ /**
+ * Creates the {@code FunctionalUnit}.
+ *
+ * @param base
+ * unit's base
+ * @param converterFrom
+ * function that accepts a value expressed in the unit's base and returns that value expressed in this
+ * unit.
+ * @param converterTo
+ * function that accepts a value expressed in the unit and returns that value expressed in the unit's
+ * base.
+ * @since 2019-05-22
+ */
+ private FunctionalUnit(final BaseUnit base, final DoubleUnaryOperator converterFrom,
+ final DoubleUnaryOperator converterTo) {
+ super(base);
+ this.converterFrom = converterFrom;
+ this.converterTo = converterTo;
+ }
+
+ @Override
+ public double convertFromBase(final double value) {
+ return this.converterFrom.applyAsDouble(value);
+ }
+
+ @Override
+ public double convertToBase(final double value) {
+ return this.converterTo.applyAsDouble(value);
+ }
+
+}
diff --git a/src/org/unitConverter/unit/NonlinearUnits.java b/src/org/unitConverter/unit/NonlinearUnits.java
index e47c28f..eda4a74 100755
--- a/src/org/unitConverter/unit/NonlinearUnits.java
+++ b/src/org/unitConverter/unit/NonlinearUnits.java
@@ -24,31 +24,11 @@ package org.unitConverter.unit;
* @since v0.1.0
*/
public final class NonlinearUnits {
- public static final Unit CELSIUS = new AbstractUnit(SI.KELVIN) {
+ public static final Unit CELSIUS = Unit.fromConversionFunctions(SI.KELVIN, tempK -> tempK - 273.15,
+ tempC -> tempC + 273.15);
- @Override
- public double convertFromBase(final double value) {
- return value - 273.15;
- }
-
- @Override
- public double convertToBase(final double value) {
- return value + 273.15;
- }
- };
-
- public static final Unit FAHRENHEIT = new AbstractUnit(SI.KELVIN) {
-
- @Override
- public double convertFromBase(final double value) {
- return 1.8 * value - 459.67;
- }
-
- @Override
- public double convertToBase(final double value) {
- return (value + 459.67) / 1.8;
- }
- };
+ public static final Unit FAHRENHEIT = Unit.fromConversionFunctions(SI.KELVIN, tempK -> tempK * 1.8 - 459.67,
+ tempF -> (tempF + 459.67) / 1.8);
// You may NOT get a NonlinearUnits instance.
private NonlinearUnits() {
diff --git a/src/org/unitConverter/unit/Unit.java b/src/org/unitConverter/unit/Unit.java
index 86fc5a2..2ac107e 100755
--- a/src/org/unitConverter/unit/Unit.java
+++ b/src/org/unitConverter/unit/Unit.java
@@ -17,6 +17,7 @@
package org.unitConverter.unit;
import java.util.Objects;
+import java.util.function.DoubleUnaryOperator;
import org.unitConverter.dimension.UnitDimension;
@@ -28,6 +29,31 @@ import org.unitConverter.dimension.UnitDimension;
* @since v0.1.0
*/
public interface Unit {
+ /**
+ * 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:
+ *
+ * {@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.
+ * @return a unit that uses the provided functions to convert.
+ * @since 2019-05-22
+ */
+ public static Unit fromConversionFunctions(final BaseUnit base, final DoubleUnaryOperator converterFrom,
+ final DoubleUnaryOperator converterTo) {
+ return FunctionalUnit.valueOf(base, converterFrom, converterTo);
+ }
+
/**
* Checks if a value expressed in this unit can be converted to a value expressed in {@code other}
*
@@ -59,6 +85,26 @@ public interface Unit {
*/
double convertFromBase(double value);
+ /**
+ * Converts a value expressed in this unit to a value expressed in {@code other}.
+ *
+ * @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}).
+ */
+ default double convertTo(final Unit other, final double value) {
+ if (this.canConvertTo(other))
+ return other.convertFromBase(this.convertToBase(value));
+ else
+ throw new IllegalArgumentException(String.format("Cannot convert from %s to %s.", this, other));
+ }
+
/**
* Converts from a value expressed in this unit to a value expressed in this unit's base unit.
*
diff --git a/src/test/java/UnitTest.java b/src/test/java/UnitTest.java
index 00fcf3c..7a3f29d 100755
--- a/src/test/java/UnitTest.java
+++ b/src/test/java/UnitTest.java
@@ -63,7 +63,7 @@ public class UnitTest {
final BaseUnit metre = SI.METRE;
final Unit inch = metre.times(0.0254);
- assertEquals(1.9, inch.convertToBase(75), 0.01);
+ assertEquals(1.9, inch.convertTo(metre, 75), 0.01);
// try random stuff
for (int i = 0; i < 1000; i++) {
diff --git a/src/test/java/UnitsDatabaseTest.java b/src/test/java/UnitsDatabaseTest.java
index 6d2247f..0d67c20 100644
--- a/src/test/java/UnitsDatabaseTest.java
+++ b/src/test/java/UnitsDatabaseTest.java
@@ -27,7 +27,6 @@ import java.util.Map.Entry;
import org.junit.Test;
import org.unitConverter.UnitsDatabase;
-import org.unitConverter.unit.AbstractUnit;
import org.unitConverter.unit.DefaultUnitPrefix;
import org.unitConverter.unit.LinearUnit;
import org.unitConverter.unit.SI;
@@ -52,18 +51,7 @@ public class UnitsDatabaseTest {
private static final LinearUnit J = SI.KILOGRAM.times(SI.METRE.toExponent(2)).dividedBy(SI.SECOND.toExponent(2));
private static final LinearUnit K = SI.KELVIN;
- private static final Unit NONLINEAR = new AbstractUnit(SI.METRE) {
-
- @Override
- public double convertFromBase(final double value) {
- return value + 1;
- }
-
- @Override
- public double convertToBase(final double value) {
- return value - 1;
- }
- };
+ private static final Unit NONLINEAR = Unit.fromConversionFunctions(SI.METRE, o -> o + 1, o -> o - 1);
// make the prefix values prime so I can tell which multiplications were made
private static final UnitPrefix A = new DefaultUnitPrefix(2);
--
cgit v1.2.3
From 01b072b98fdd19a2d57afc15a4ee4a80d0bfc0cd Mon Sep 17 00:00:00 2001
From: Adrien Hopkins
Date: Wed, 22 May 2019 18:47:05 -0400
Subject: Added null checks to Unit's methods, including the new methods.
---
src/org/unitConverter/unit/FunctionalUnit.java | 9 +++++++--
src/org/unitConverter/unit/Unit.java | 8 ++++++++
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/src/org/unitConverter/unit/FunctionalUnit.java b/src/org/unitConverter/unit/FunctionalUnit.java
index c2aae6d..e3db43a 100644
--- a/src/org/unitConverter/unit/FunctionalUnit.java
+++ b/src/org/unitConverter/unit/FunctionalUnit.java
@@ -16,6 +16,7 @@
*/
package org.unitConverter.unit;
+import java.util.Objects;
import java.util.function.DoubleUnaryOperator;
/**
@@ -38,6 +39,8 @@ final class FunctionalUnit extends AbstractUnit {
* base.
* @return a unit that uses the provided functions to convert.
* @since 2019-05-22
+ * @throws NullPointerException
+ * if any argument is null
*/
public static FunctionalUnit valueOf(final BaseUnit base, final DoubleUnaryOperator converterFrom,
final DoubleUnaryOperator converterTo) {
@@ -69,13 +72,15 @@ final class FunctionalUnit extends AbstractUnit {
* @param converterTo
* function that accepts a value expressed in the unit and returns that value expressed in the unit's
* base.
+ * @throws NullPointerException
+ * if any argument is null
* @since 2019-05-22
*/
private FunctionalUnit(final BaseUnit base, final DoubleUnaryOperator converterFrom,
final DoubleUnaryOperator converterTo) {
super(base);
- this.converterFrom = converterFrom;
- this.converterTo = converterTo;
+ this.converterFrom = Objects.requireNonNull(converterFrom, "converterFrom must not be null.");
+ this.converterTo = Objects.requireNonNull(converterTo, "converterTo must not be null.");
}
@Override
diff --git a/src/org/unitConverter/unit/Unit.java b/src/org/unitConverter/unit/Unit.java
index 2ac107e..54f0ab5 100755
--- a/src/org/unitConverter/unit/Unit.java
+++ b/src/org/unitConverter/unit/Unit.java
@@ -48,6 +48,8 @@ public interface Unit {
* base.
* @return a unit that uses the provided functions to convert.
* @since 2019-05-22
+ * @throws NullPointerException
+ * if any argument is null
*/
public static Unit fromConversionFunctions(final BaseUnit base, final DoubleUnaryOperator converterFrom,
final DoubleUnaryOperator converterTo) {
@@ -62,8 +64,11 @@ public interface Unit {
* @return true if the units are compatible
* @since 2019-01-13
* @since v0.1.0
+ * @throws NullPointerException
+ * if other is null
*/
default boolean canConvertTo(final Unit other) {
+ Objects.requireNonNull(other, "other must not be null.");
return Objects.equals(this.getBase(), other.getBase());
}
@@ -97,8 +102,11 @@ public interface Unit {
* @throws IllegalArgumentException
* if {@code other} is incompatible for conversion with this unit (as tested by
* {@link Unit#canConvertTo}).
+ * @throws NullPointerException
+ * if other is null
*/
default 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
--
cgit v1.2.3
From 2692c918fac83c4060e0912a7b92a6d028882afc Mon Sep 17 00:00:00 2001
From: Adrien Hopkins
Date: Mon, 8 Jul 2019 20:31:32 -0400
Subject: The unit file reader now uses regular expressions.
---
src/org/unitConverter/UnitsDatabase.java | 27 +++++++++++++++++----------
1 file changed, 17 insertions(+), 10 deletions(-)
diff --git a/src/org/unitConverter/UnitsDatabase.java b/src/org/unitConverter/UnitsDatabase.java
index 37d53d4..520195c 100755
--- a/src/org/unitConverter/UnitsDatabase.java
+++ b/src/org/unitConverter/UnitsDatabase.java
@@ -36,6 +36,8 @@ import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.unitConverter.dimension.UnitDimension;
import org.unitConverter.math.DecimalComparison;
@@ -971,6 +973,11 @@ public final class UnitsDatabase {
}
}
+ /**
+ * A regular expression that separates names and expressions in unit files.
+ */
+ private static final Pattern NAME_EXPRESSION = Pattern.compile("(\\S+)\\s+(\\S.*)");
+
/**
* The exponent operator
*
@@ -1114,13 +1121,13 @@ public final class UnitsDatabase {
}
// divide line into name and expression
- final String[] parts = line.split("\t");
- if (parts.length < 2)
+ final Matcher lineMatcher = NAME_EXPRESSION.matcher(line);
+ if (!lineMatcher.matches())
throw new IllegalArgumentException(String.format(
- "Lines must consist of a dimension name and its definition, separated by tab(s) (line %d).",
+ "Error at line %d: Lines of a dimension file must consist of a dimension name, then spaces or tabs, then a dimension expression.",
lineCounter));
- final String name = parts[0];
- final String expression = parts[parts.length - 1];
+ final String name = lineMatcher.group(1);
+ final String expression = lineMatcher.group(2);
if (name.endsWith(" ")) {
System.err.printf("Warning - line %d's dimension name ends in a space", lineCounter);
@@ -1200,13 +1207,13 @@ public final class UnitsDatabase {
}
// divide line into name and expression
- final String[] parts = line.split("\t");
- if (parts.length < 2)
+ final Matcher lineMatcher = NAME_EXPRESSION.matcher(line);
+ if (!lineMatcher.matches())
throw new IllegalArgumentException(String.format(
- "Lines must consist of a unit name and its definition, separated by tab(s) (line %d).",
+ "Error at line %d: Lines of a unit file must consist of a unit name, then spaces or tabs, then a unit expression.",
lineCounter));
- final String name = parts[0];
- final String expression = parts[parts.length - 1];
+ final String name = lineMatcher.group(1);
+ final String expression = lineMatcher.group(2);
if (name.endsWith(" ")) {
System.err.printf("Warning - line %d's unit name ends in a space", lineCounter);
--
cgit v1.2.3
From 740c5a4c13e98bd04b385eee9b881e53eb88d3b8 Mon Sep 17 00:00:00 2001
From: Adrien Hopkins
Date: Mon, 26 Aug 2019 16:52:11 -0400
Subject: The unit file parser now uses regular expressions.
---
.settings/org.eclipse.jdt.core.prefs | 2 ++
CHANGELOG.org | 20 +++++++++++------
src/org/unitConverter/UnitsDatabase.java | 26 +++++++++++++++++-----
.../converterGUI/DelegateListModel.java | 0
.../converterGUI/FilterComparator.java | 0
.../unitConverter/converterGUI/GridBagBuilder.java | 0
.../converterGUI/UnitConverterGUI.java | 0
src/org/unitConverter/dimension/BaseDimension.java | 0
.../dimension/OtherBaseDimension.java | 0
.../unitConverter/dimension/SIBaseDimension.java | 0
.../dimension/StandardDimensions.java | 0
src/org/unitConverter/dimension/UnitDimension.java | 0
src/org/unitConverter/dimension/package-info.java | 0
src/org/unitConverter/unit/BaseUnit.java | 0
src/org/unitConverter/unit/DefaultUnitPrefix.java | 0
src/org/unitConverter/unit/NonlinearUnits.java | 0
src/org/unitConverter/unit/SIPrefix.java | 0
src/org/unitConverter/unit/Unit.java | 0
src/org/unitConverter/unit/UnitPrefix.java | 0
src/org/unitConverter/unit/UnitSystem.java | 0
src/test/java/UnitDimensionTest.java | 0
src/test/java/UnitTest.java | 0
unitsfile.txt | 0
23 files changed, 36 insertions(+), 12 deletions(-)
mode change 100755 => 100644 src/org/unitConverter/UnitsDatabase.java
mode change 100755 => 100644 src/org/unitConverter/converterGUI/DelegateListModel.java
mode change 100755 => 100644 src/org/unitConverter/converterGUI/FilterComparator.java
mode change 100755 => 100644 src/org/unitConverter/converterGUI/GridBagBuilder.java
mode change 100755 => 100644 src/org/unitConverter/converterGUI/UnitConverterGUI.java
mode change 100755 => 100644 src/org/unitConverter/dimension/BaseDimension.java
mode change 100755 => 100644 src/org/unitConverter/dimension/OtherBaseDimension.java
mode change 100755 => 100644 src/org/unitConverter/dimension/SIBaseDimension.java
mode change 100755 => 100644 src/org/unitConverter/dimension/StandardDimensions.java
mode change 100755 => 100644 src/org/unitConverter/dimension/UnitDimension.java
mode change 100755 => 100644 src/org/unitConverter/dimension/package-info.java
mode change 100755 => 100644 src/org/unitConverter/unit/BaseUnit.java
mode change 100755 => 100644 src/org/unitConverter/unit/DefaultUnitPrefix.java
mode change 100755 => 100644 src/org/unitConverter/unit/NonlinearUnits.java
mode change 100755 => 100644 src/org/unitConverter/unit/SIPrefix.java
mode change 100755 => 100644 src/org/unitConverter/unit/Unit.java
mode change 100755 => 100644 src/org/unitConverter/unit/UnitPrefix.java
mode change 100755 => 100644 src/org/unitConverter/unit/UnitSystem.java
mode change 100755 => 100644 src/test/java/UnitDimensionTest.java
mode change 100755 => 100644 src/test/java/UnitTest.java
mode change 100755 => 100644 unitsfile.txt
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
index 8445b6b..db24ee7 100644
--- a/.settings/org.eclipse.jdt.core.prefs
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -7,7 +7,9 @@ org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=1.8
diff --git a/CHANGELOG.org b/CHANGELOG.org
index 77e7593..1a49709 100644
--- a/CHANGELOG.org
+++ b/CHANGELOG.org
@@ -1,7 +1,20 @@
* Changelog
All notable changes in this project will be shown in this file.
+** Unreleased
+*** Added
+ - Added a simple unit conversion method to the Unit interface
+ - Added a static factory to create nonlinear units to the Unit interface
+*** Changed
+ - You can now use tabs or spaces to separate unit names and their definitions in unit files.
** v0.2.0 - [2019-04-14]
+*** Added
+ - A selection-based unit converter which allows you to select two units, input a value, and convert.
+ - The UnitDatabase now stores dimensions.
+ - A system to parse mathematical expressions, used to parse unit expressions.
+ - You can now add and subtract in unit expressions!
+ - Instructions for obtaining unit instances are provided in the relevant classes
+ - The UnitPrefix interface now provides default times, dividedBy and toExponent methods.
*** Changed
- When searching for units, units with no prefixes are searched for before prefixed units
- Smaller prefixes are searched for before larger prefixes
@@ -10,13 +23,6 @@ All notable changes in this project will be shown in this file.
- BaseUnit is now a subclass of LinearUnit
- In unit files, Comments can now start in the middle of lines
- UnitsDatabase.addAllFromFile() has been renamed to loadUnitsFile()
-*** Added
- - A selection-based unit converter which allows you to select two units, input a value, and convert.
- - The UnitDatabase now stores dimensions.
- - A system to parse mathematical expressions, used to parse unit expressions.
- - You can now add and subtract in unit expressions!
- - Instructions for obtaining unit instances are provided in the relevant classes
- - The UnitPrefix interface now provides default times, dividedBy and toExponent methods.
** v0.1.0 - [2019-02-01]
NOTE: At this stage, the API is subject to significant change.
*** Added
diff --git a/src/org/unitConverter/UnitsDatabase.java b/src/org/unitConverter/UnitsDatabase.java
old mode 100755
new mode 100644
index 37d53d4..dcc98df
--- a/src/org/unitConverter/UnitsDatabase.java
+++ b/src/org/unitConverter/UnitsDatabase.java
@@ -36,6 +36,8 @@ import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.unitConverter.dimension.UnitDimension;
import org.unitConverter.math.DecimalComparison;
@@ -971,6 +973,11 @@ public final class UnitsDatabase {
}
}
+ /**
+ * A regular expression that separates names and expressions in unit files.
+ */
+ private static final Pattern NAME_EXPRESSION = Pattern.compile("(\\S+)\\s+(\\S.*)");
+
/**
* The exponent operator
*
@@ -1200,13 +1207,22 @@ public final class UnitsDatabase {
}
// divide line into name and expression
- final String[] parts = line.split("\t");
- if (parts.length < 2)
+ final Matcher lineMatcher = NAME_EXPRESSION.matcher(line);
+ if (!lineMatcher.matches())
throw new IllegalArgumentException(String.format(
- "Lines must consist of a unit name and its definition, separated by tab(s) (line %d).",
+ "Error at line %d: Lines of a unit file must consist of a unit name, then spaces or tabs, then a unit expression.",
lineCounter));
- final String name = parts[0];
- final String expression = parts[parts.length - 1];
+ final String name = lineMatcher.group(1);
+ final String expression = lineMatcher.group(2);
+
+ // divide line into name and expression
+ // final String[] parts = line.split("\t");
+ // if (parts.length < 2)
+ // throw new IllegalArgumentException(String.format(
+ // "Lines must consist of a unit name and its definition, separated by tab(s) (line %d).",
+ // lineCounter));
+ // final String name = parts[0];
+ // final String expression = parts[parts.length - 1];
if (name.endsWith(" ")) {
System.err.printf("Warning - line %d's unit name ends in a space", lineCounter);
diff --git a/src/org/unitConverter/converterGUI/DelegateListModel.java b/src/org/unitConverter/converterGUI/DelegateListModel.java
old mode 100755
new mode 100644
diff --git a/src/org/unitConverter/converterGUI/FilterComparator.java b/src/org/unitConverter/converterGUI/FilterComparator.java
old mode 100755
new mode 100644
diff --git a/src/org/unitConverter/converterGUI/GridBagBuilder.java b/src/org/unitConverter/converterGUI/GridBagBuilder.java
old mode 100755
new mode 100644
diff --git a/src/org/unitConverter/converterGUI/UnitConverterGUI.java b/src/org/unitConverter/converterGUI/UnitConverterGUI.java
old mode 100755
new mode 100644
diff --git a/src/org/unitConverter/dimension/BaseDimension.java b/src/org/unitConverter/dimension/BaseDimension.java
old mode 100755
new mode 100644
diff --git a/src/org/unitConverter/dimension/OtherBaseDimension.java b/src/org/unitConverter/dimension/OtherBaseDimension.java
old mode 100755
new mode 100644
diff --git a/src/org/unitConverter/dimension/SIBaseDimension.java b/src/org/unitConverter/dimension/SIBaseDimension.java
old mode 100755
new mode 100644
diff --git a/src/org/unitConverter/dimension/StandardDimensions.java b/src/org/unitConverter/dimension/StandardDimensions.java
old mode 100755
new mode 100644
diff --git a/src/org/unitConverter/dimension/UnitDimension.java b/src/org/unitConverter/dimension/UnitDimension.java
old mode 100755
new mode 100644
diff --git a/src/org/unitConverter/dimension/package-info.java b/src/org/unitConverter/dimension/package-info.java
old mode 100755
new mode 100644
diff --git a/src/org/unitConverter/unit/BaseUnit.java b/src/org/unitConverter/unit/BaseUnit.java
old mode 100755
new mode 100644
diff --git a/src/org/unitConverter/unit/DefaultUnitPrefix.java b/src/org/unitConverter/unit/DefaultUnitPrefix.java
old mode 100755
new mode 100644
diff --git a/src/org/unitConverter/unit/NonlinearUnits.java b/src/org/unitConverter/unit/NonlinearUnits.java
old mode 100755
new mode 100644
diff --git a/src/org/unitConverter/unit/SIPrefix.java b/src/org/unitConverter/unit/SIPrefix.java
old mode 100755
new mode 100644
diff --git a/src/org/unitConverter/unit/Unit.java b/src/org/unitConverter/unit/Unit.java
old mode 100755
new mode 100644
diff --git a/src/org/unitConverter/unit/UnitPrefix.java b/src/org/unitConverter/unit/UnitPrefix.java
old mode 100755
new mode 100644
diff --git a/src/org/unitConverter/unit/UnitSystem.java b/src/org/unitConverter/unit/UnitSystem.java
old mode 100755
new mode 100644
diff --git a/src/test/java/UnitDimensionTest.java b/src/test/java/UnitDimensionTest.java
old mode 100755
new mode 100644
diff --git a/src/test/java/UnitTest.java b/src/test/java/UnitTest.java
old mode 100755
new mode 100644
diff --git a/unitsfile.txt b/unitsfile.txt
old mode 100755
new mode 100644
--
cgit v1.2.3
From 074d48df131f073965fa1a1106c4be1bed9d2b51 Mon Sep 17 00:00:00 2001
From: Adrien Hopkins
Date: Tue, 15 Oct 2019 17:18:22 -0400
Subject: Moved project out of Maven.
---
.classpath | 1 +
.project | 6 ------
pom.xml | 55 -------------------------------------------------------
3 files changed, 1 insertion(+), 61 deletions(-)
delete mode 100644 pom.xml
diff --git a/.classpath b/.classpath
index ef141e6..60b8884 100644
--- a/.classpath
+++ b/.classpath
@@ -5,6 +5,7 @@
+
diff --git a/.project b/.project
index 56f686e..656186b 100644
--- a/.project
+++ b/.project
@@ -10,14 +10,8 @@
-
- org.eclipse.m2e.core.maven2Builder
-
-
-
- org.eclipse.m2e.core.maven2Natureorg.eclipse.jdt.core.javanature
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index 5b3e468..0000000
--- a/pom.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-
- 4.0.0
- org.unitConverter
- unitConverter
- 0.1.0
- Unit Converter
- A Java unit converter inspired by GNU Units
-
- src
-
-
- maven-compiler-plugin
- 3.8.0
-
- 1.8
- 1.8
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
- false
-
-
-
- org.codehaus.mojo
- exec-maven-plugin
-
- org.unitConverter.converterGUI.UnitConverterGUI
-
-
-
- org.apache.maven.plugins
- maven-jar-plugin
-
-
-
- org.unitConverter.converterGUI.UnitConverterGUI
-
-
-
-
-
-
-
-
- junit
- junit
- 4.11
-
-
-
--
cgit v1.2.3
From bb844c776230a2d51ec1fbdacec43148b9c5b424 Mon Sep 17 00:00:00 2001
From: Adrien Hopkins
Date: Tue, 15 Oct 2019 21:38:56 +0000
Subject: Revert "Moved project out of Maven."
This reverts commit 074d48df131f073965fa1a1106c4be1bed9d2b51
---
.classpath | 1 -
.project | 6 ++++++
pom.xml | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 61 insertions(+), 1 deletion(-)
create mode 100644 pom.xml
diff --git a/.classpath b/.classpath
index 60b8884..ef141e6 100644
--- a/.classpath
+++ b/.classpath
@@ -5,7 +5,6 @@
-
diff --git a/.project b/.project
index 656186b..56f686e 100644
--- a/.project
+++ b/.project
@@ -10,8 +10,14 @@
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+ org.eclipse.m2e.core.maven2Natureorg.eclipse.jdt.core.javanature
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..5b3e468
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,55 @@
+
+ 4.0.0
+ org.unitConverter
+ unitConverter
+ 0.1.0
+ Unit Converter
+ A Java unit converter inspired by GNU Units
+
+ src
+
+
+ maven-compiler-plugin
+ 3.8.0
+
+ 1.8
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ false
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+
+ org.unitConverter.converterGUI.UnitConverterGUI
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ org.unitConverter.converterGUI.UnitConverterGUI
+
+
+
+
+
+
+
+
+ junit
+ junit
+ 4.11
+
+
+
--
cgit v1.2.3
From efb8729719257c4a766058c5c7122648d94dd8b3 Mon Sep 17 00:00:00 2001
From: Adrien Hopkins
Date: Tue, 15 Oct 2019 17:53:40 -0400
Subject: Upgraded to JUnit 5 and moved tests to their proper directories
---
.classpath | 6 +-
.project | 6 -
src/org/unitConverter/UnitsDatabaseTest.java | 312 ++++++++++++++++++++
.../unitConverter/dimension/UnitDimensionTest.java | 77 +++++
.../unitConverter/math/ExpressionParserTest.java | 52 ++++
src/org/unitConverter/unit/UnitTest.java | 111 ++++++++
src/test/java/ExpressionParserTest.java | 53 ----
src/test/java/UnitDimensionTest.java | 79 ------
src/test/java/UnitTest.java | 116 --------
src/test/java/UnitsDatabaseTest.java | 313 ---------------------
src/test/java/package-info.java | 24 --
11 files changed, 553 insertions(+), 596 deletions(-)
create mode 100644 src/org/unitConverter/UnitsDatabaseTest.java
create mode 100644 src/org/unitConverter/dimension/UnitDimensionTest.java
create mode 100644 src/org/unitConverter/math/ExpressionParserTest.java
create mode 100644 src/org/unitConverter/unit/UnitTest.java
delete mode 100644 src/test/java/ExpressionParserTest.java
delete mode 100644 src/test/java/UnitDimensionTest.java
delete mode 100644 src/test/java/UnitTest.java
delete mode 100644 src/test/java/UnitsDatabaseTest.java
delete mode 100644 src/test/java/package-info.java
diff --git a/.classpath b/.classpath
index ef141e6..8fc330a 100644
--- a/.classpath
+++ b/.classpath
@@ -5,16 +5,12 @@
+
-
-
-
-
-
diff --git a/.project b/.project
index 56f686e..656186b 100644
--- a/.project
+++ b/.project
@@ -10,14 +10,8 @@
-
- org.eclipse.m2e.core.maven2Builder
-
-
-
- org.eclipse.m2e.core.maven2Natureorg.eclipse.jdt.core.javanature
diff --git a/src/org/unitConverter/UnitsDatabaseTest.java b/src/org/unitConverter/UnitsDatabaseTest.java
new file mode 100644
index 0000000..c46d598
--- /dev/null
+++ b/src/org/unitConverter/UnitsDatabaseTest.java
@@ -0,0 +1,312 @@
+/**
+ * Copyright (C) 2019 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.unitConverter;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.junit.jupiter.api.Test;
+import org.unitConverter.unit.DefaultUnitPrefix;
+import org.unitConverter.unit.LinearUnit;
+import org.unitConverter.unit.SI;
+import org.unitConverter.unit.Unit;
+import org.unitConverter.unit.UnitPrefix;
+
+/**
+ * A test for the {@link UnitsDatabase} class. This is NOT part of this program's public API.
+ *
+ * @author Adrien Hopkins
+ * @since 2019-04-14
+ * @since v0.2.0
+ */
+class UnitsDatabaseTest {
+ // some linear units and one nonlinear
+ private static final Unit U = SI.METRE;
+ private static final Unit V = SI.KILOGRAM;
+ private static final Unit W = SI.SECOND;
+
+ // used for testing expressions
+ // J = U^2 * V / W^2
+ private static final LinearUnit J = SI.KILOGRAM.times(SI.METRE.toExponent(2)).dividedBy(SI.SECOND.toExponent(2));
+ private static final LinearUnit K = SI.KELVIN;
+
+ private static final Unit NONLINEAR = Unit.fromConversionFunctions(SI.METRE, o -> o + 1, o -> o - 1);
+
+ // make the prefix values prime so I can tell which multiplications were made
+ private static final UnitPrefix A = new DefaultUnitPrefix(2);
+ private static final UnitPrefix B = new DefaultUnitPrefix(3);
+ private static final UnitPrefix C = new DefaultUnitPrefix(5);
+ private static final UnitPrefix AB = new DefaultUnitPrefix(7);
+ private static final UnitPrefix BC = new DefaultUnitPrefix(11);
+
+ /**
+ * Confirms that operations that shouldn't function for infinite databases throw an {@code IllegalStateException}.
+ *
+ * @since 2019-05-03
+ */
+ @Test
+ public void testInfiniteSetExceptions() {
+ // load units
+ final UnitsDatabase infiniteDatabase = new UnitsDatabase();
+
+ infiniteDatabase.addUnit("J", J);
+ infiniteDatabase.addUnit("K", K);
+
+ infiniteDatabase.addPrefix("A", A);
+ infiniteDatabase.addPrefix("B", B);
+ infiniteDatabase.addPrefix("C", C);
+
+ {
+ boolean exceptionThrown = false;
+ try {
+ infiniteDatabase.unitMap().entrySet().toArray();
+ } catch (final IllegalStateException e) {
+ exceptionThrown = true;
+ // pass!
+ } finally {
+ if (!exceptionThrown) {
+ fail("No IllegalStateException thrown");
+ }
+ }
+ }
+
+ {
+ boolean exceptionThrown = false;
+ try {
+ infiniteDatabase.unitMap().keySet().toArray();
+ } catch (final IllegalStateException e) {
+ exceptionThrown = true;
+ // pass!
+ } finally {
+ if (!exceptionThrown) {
+ fail("No IllegalStateException thrown");
+ }
+ }
+ }
+ }
+
+ /**
+ * Test that prefixes correctly apply to units.
+ *
+ * @since 2019-04-14
+ * @since v0.2.0
+ */
+ @Test
+ public void testPrefixes() {
+ final UnitsDatabase database = new UnitsDatabase();
+
+ database.addUnit("U", U);
+ database.addUnit("V", V);
+ database.addUnit("W", W);
+
+ database.addPrefix("A", A);
+ database.addPrefix("B", B);
+ database.addPrefix("C", C);
+
+ // get the product
+ final Unit abcuNonlinear = database.getUnit("ABCU");
+ assert abcuNonlinear instanceof LinearUnit;
+
+ final LinearUnit abcu = (LinearUnit) abcuNonlinear;
+ assertEquals(A.getMultiplier() * B.getMultiplier() * C.getMultiplier(), abcu.getConversionFactor(), 1e-15);
+ }
+
+ /**
+ * Tests the functionnalites of the prefixless unit map.
+ *
+ *
+ * The map should be an auto-updating view of the units in the database.
+ *
+ *
+ * @since 2019-04-14
+ * @since v0.2.0
+ */
+ @Test
+ public void testPrefixlessUnitMap() {
+ final UnitsDatabase database = new UnitsDatabase();
+ final Map prefixlessUnits = database.unitMapPrefixless();
+
+ database.addUnit("U", U);
+ database.addUnit("V", V);
+ database.addUnit("W", W);
+
+ // this should work because the map should be an auto-updating view
+ assertTrue(prefixlessUnits.containsKey("U"));
+ assertFalse(prefixlessUnits.containsKey("Z"));
+
+ assertTrue(prefixlessUnits.containsValue(U));
+ assertFalse(prefixlessUnits.containsValue(NONLINEAR));
+ }
+
+ /**
+ * Tests that the database correctly stores and retrieves units, ignoring prefixes.
+ *
+ * @since 2019-04-14
+ * @since v0.2.0
+ */
+ @Test
+ public void testPrefixlessUnits() {
+ final UnitsDatabase database = new UnitsDatabase();
+
+ database.addUnit("U", U);
+ database.addUnit("V", V);
+ database.addUnit("W", W);
+
+ assertTrue(database.containsUnitName("U"));
+ assertFalse(database.containsUnitName("Z"));
+
+ assertEquals(U, database.getUnit("U"));
+ assertEquals(null, database.getUnit("Z"));
+ }
+
+ /**
+ * Test that unit expressions return the correct value.
+ *
+ * @since 2019-04-14
+ * @since v0.2.0
+ */
+ @Test
+ public void testUnitExpressions() {
+ // load units
+ final UnitsDatabase database = new UnitsDatabase();
+
+ database.addUnit("U", U);
+ database.addUnit("V", V);
+ database.addUnit("W", W);
+ database.addUnit("fj", J.times(5));
+ database.addUnit("ej", J.times(8));
+
+ database.addPrefix("A", A);
+ database.addPrefix("B", B);
+ database.addPrefix("C", C);
+
+ // first test - test prefixes and operations
+ final Unit expected1 = J.withPrefix(A).withPrefix(B).withPrefix(C).withPrefix(C);
+ final Unit actual1 = database.getUnitFromExpression("ABV * CU^2 / W / W");
+
+ assertEquals(expected1, actual1);
+
+ // second test - test addition and subtraction
+ final Unit expected2 = J.times(58);
+ final Unit actual2 = database.getUnitFromExpression("2 fj + 6 ej");
+
+ assertEquals(expected2, actual2);
+ }
+
+ /**
+ * Tests both the unit name iterator and the name-unit entry iterator
+ *
+ * @since 2019-04-14
+ * @since v0.2.0
+ */
+ @Test
+ public void testUnitIterator() {
+ // load units
+ final UnitsDatabase database = new UnitsDatabase();
+
+ database.addUnit("J", J);
+ database.addUnit("K", K);
+
+ database.addPrefix("A", A);
+ database.addPrefix("B", B);
+ database.addPrefix("C", C);
+
+ final int NUM_UNITS = database.unitMapPrefixless().size();
+ final int NUM_PREFIXES = database.prefixMap().size();
+
+ final Iterator nameIterator = database.unitMap().keySet().iterator();
+ final Iterator> entryIterator = database.unitMap().entrySet().iterator();
+
+ int expectedLength = 1;
+ int unitsWithThisLengthSoFar = 0;
+
+ // loop 1000 times
+ for (int i = 0; i < 1000; i++) {
+ // expected length of next
+ if (unitsWithThisLengthSoFar >= NUM_UNITS * (int) Math.pow(NUM_PREFIXES, expectedLength - 1)) {
+ expectedLength++;
+ unitsWithThisLengthSoFar = 0;
+ }
+
+ // test that stuff is valid
+ final String nextName = nameIterator.next();
+ final Unit nextUnit = database.getUnit(nextName);
+ final Entry nextEntry = entryIterator.next();
+
+ assertEquals(expectedLength, nextName.length());
+ assertEquals(nextName, nextEntry.getKey());
+ assertEquals(nextUnit, nextEntry.getValue());
+
+ unitsWithThisLengthSoFar++;
+ }
+
+ // test toString for consistency
+ final String entryIteratorString = entryIterator.toString();
+ for (int i = 0; i < 3; i++) {
+ assertEquals(entryIteratorString, entryIterator.toString());
+ }
+
+ final String nameIteratorString = nameIterator.toString();
+ for (int i = 0; i < 3; i++) {
+ assertEquals(nameIteratorString, nameIterator.toString());
+ }
+ }
+
+ /**
+ * Determine, given a unit name that could mean multiple things, which meaning is chosen.
+ *
+ * For example, "ABCU" could mean "A-B-C-U", "AB-C-U", or "A-BC-U". In this case, "AB-C-U" is the correct choice.
+ *
+ *
+ * @since 2019-04-14
+ * @since v0.2.0
+ */
+ @Test
+ public void testUnitPrefixCombinations() {
+ // load units
+ final UnitsDatabase database = new UnitsDatabase();
+
+ database.addUnit("J", J);
+
+ database.addPrefix("A", A);
+ database.addPrefix("B", B);
+ database.addPrefix("C", C);
+ database.addPrefix("AB", AB);
+ database.addPrefix("BC", BC);
+
+ // test 1 - AB-C-J vs A-BC-J vs A-B-C-J
+ final Unit expected1 = J.withPrefix(AB).withPrefix(C);
+ final Unit actual1 = database.getUnit("ABCJ");
+
+ assertEquals(expected1, actual1);
+
+ // test 2 - ABC-J vs AB-CJ vs AB-C-J
+ database.addUnit("CJ", J.times(13));
+ database.addPrefix("ABC", new DefaultUnitPrefix(17));
+
+ final Unit expected2 = J.times(17);
+ final Unit actual2 = database.getUnit("ABCJ");
+
+ assertEquals(expected2, actual2);
+ }
+}
diff --git a/src/org/unitConverter/dimension/UnitDimensionTest.java b/src/org/unitConverter/dimension/UnitDimensionTest.java
new file mode 100644
index 0000000..017e3d2
--- /dev/null
+++ b/src/org/unitConverter/dimension/UnitDimensionTest.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright (C) 2018 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.unitConverter.dimension;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.unitConverter.dimension.StandardDimensions.AREA;
+import static org.unitConverter.dimension.StandardDimensions.ENERGY;
+import static org.unitConverter.dimension.StandardDimensions.LENGTH;
+import static org.unitConverter.dimension.StandardDimensions.MASS;
+import static org.unitConverter.dimension.StandardDimensions.MASS_DENSITY;
+import static org.unitConverter.dimension.StandardDimensions.QUANTITY;
+import static org.unitConverter.dimension.StandardDimensions.TIME;
+import static org.unitConverter.dimension.StandardDimensions.VOLUME;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link UnitDimension}. This is NOT part of this program's public API.
+ *
+ * @author Adrien Hopkins
+ * @since 2018-12-12
+ * @since v0.1.0
+ */
+class UnitDimensionTest {
+ /**
+ * Tests {@link UnitDimension#equals}
+ *
+ * @since 2018-12-12
+ * @since v0.1.0
+ */
+ @Test
+ public void testEquals() {
+ assertEquals(LENGTH, LENGTH);
+ assertFalse(LENGTH.equals(QUANTITY));
+ }
+
+ /**
+ * Tests {@code UnitDimension}'s exponentiation
+ *
+ * @since 2019-01-15
+ * @since v0.1.0
+ */
+ @Test
+ public void testExponents() {
+ assertEquals(1, LENGTH.getExponent(SIBaseDimension.LENGTH));
+ assertEquals(3, VOLUME.getExponent(SIBaseDimension.LENGTH));
+ }
+
+ /**
+ * Tests {@code UnitDimension}'s multiplication and division.
+ *
+ * @since 2018-12-12
+ * @since v0.1.0
+ */
+ @Test
+ public void testMultiplicationAndDivision() {
+ assertEquals(AREA, LENGTH.times(LENGTH));
+ assertEquals(MASS_DENSITY, MASS.dividedBy(VOLUME));
+ assertEquals(ENERGY, AREA.times(MASS).dividedBy(TIME).dividedBy(TIME));
+ assertEquals(LENGTH, LENGTH.times(TIME).dividedBy(TIME));
+ }
+}
diff --git a/src/org/unitConverter/math/ExpressionParserTest.java b/src/org/unitConverter/math/ExpressionParserTest.java
new file mode 100644
index 0000000..f3180c1
--- /dev/null
+++ b/src/org/unitConverter/math/ExpressionParserTest.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright (C) 2019 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.unitConverter.math;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * A test for the {@code ExpressionParser} class. This is NOT part of this program's public API.
+ *
+ * @author Adrien Hopkins
+ * @since 2019-03-22
+ * @since v0.2.0
+ */
+class ExpressionParserTest {
+ private static final ExpressionParser numberParser = new ExpressionParser.Builder<>(Integer::parseInt)
+ .addBinaryOperator("+", (o1, o2) -> o1 + o2, 0).addBinaryOperator("-", (o1, o2) -> o1 - o2, 0)
+ .addBinaryOperator("*", (o1, o2) -> o1 * o2, 1).addBinaryOperator("/", (o1, o2) -> o1 / o2, 1)
+ .addBinaryOperator("^", (o1, o2) -> (int) Math.pow(o1, o2), 2).build();
+
+ /**
+ * Test method for {@link org.unitConverter.math.ExpressionParser#parseExpression(java.lang.String)}.
+ */
+ @Test
+ public void testParseExpression() {
+ // test parsing of expressions
+ assertEquals((int) numberParser.parseExpression("1 + 2 ^ 5 * 3"), 97);
+ assertEquals((int) numberParser.parseExpression("(1 + 2) ^ 5 * 3"), 729);
+
+ // ensure it normally goes left to right
+ assertEquals((int) numberParser.parseExpression("1 + 2 + 3 + 4"), 10);
+ assertEquals((int) numberParser.parseExpression("12 - 4 - 3"), 5);
+ assertEquals((int) numberParser.parseExpression("12 - (4 - 3)"), 11);
+ assertEquals((int) numberParser.parseExpression("1 / 2 + 3"), 3);
+ }
+
+}
diff --git a/src/org/unitConverter/unit/UnitTest.java b/src/org/unitConverter/unit/UnitTest.java
new file mode 100644
index 0000000..7ae5fbf
--- /dev/null
+++ b/src/org/unitConverter/unit/UnitTest.java
@@ -0,0 +1,111 @@
+/**
+ * Copyright (C) 2018 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.unitConverter.unit;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
+import org.junit.jupiter.api.Test;
+import org.unitConverter.dimension.StandardDimensions;
+import org.unitConverter.math.DecimalComparison;
+
+/**
+ * Testing the various Unit classes. This is NOT part of this program's public API.
+ *
+ * @author Adrien Hopkins
+ * @since 2018-12-22
+ * @since v0.1.0
+ */
+class UnitTest {
+ /** A random number generator */
+ private static final Random rng = ThreadLocalRandom.current();
+
+ @Test
+ public void testAdditionAndSubtraction() {
+ final LinearUnit inch = SI.METRE.times(0.0254);
+ final LinearUnit foot = SI.METRE.times(0.3048);
+
+ assertEquals(inch.plus(foot), SI.METRE.times(0.3302));
+ assertEquals(foot.minus(inch), SI.METRE.times(0.2794));
+ }
+
+ @Test
+ public void testBaseUnitExclusives() {
+ // this test should have a compile error if I am doing something wrong
+ final BaseUnit metrePerSecondSquared = SI.METRE.dividedBy(SI.SECOND.toExponent(2));
+
+ assertEquals(metrePerSecondSquared, SI.SI.getBaseUnit(StandardDimensions.ACCELERATION));
+ }
+
+ @Test
+ public void testConversion() {
+ final BaseUnit metre = SI.METRE;
+ final Unit inch = metre.times(0.0254);
+
+ assertEquals(1.9, inch.convertTo(metre, 75), 0.01);
+
+ // try random stuff
+ for (int i = 0; i < 1000; i++) {
+ // initiate random values
+ final double conversionFactor = rng.nextDouble() * 1000000;
+ final double testValue = rng.nextDouble() * 1000000;
+ final double expected = testValue * conversionFactor;
+
+ // test
+ final Unit unit = SI.METRE.times(conversionFactor);
+ final double actual = unit.convertToBase(testValue);
+
+ assertEquals(actual, expected, expected * DecimalComparison.DOUBLE_EPSILON);
+ }
+ }
+
+ @Test
+ public void testEquals() {
+ final BaseUnit metre = SI.METRE;
+ final Unit meter = SI.SI.getBaseUnit(StandardDimensions.LENGTH);
+
+ assertEquals(metre, meter);
+ }
+
+ @Test
+ public void testMultiplicationAndDivision() {
+ // test unit-times-unit multiplication
+ final LinearUnit generatedJoule = SI.KILOGRAM.times(SI.METRE.toExponent(2)).dividedBy(SI.SECOND.toExponent(2));
+ final LinearUnit actualJoule = SI.SI.getBaseUnit(StandardDimensions.ENERGY);
+
+ assertEquals(generatedJoule, actualJoule);
+
+ // test multiplication by conversion factors
+ final LinearUnit kilometre = SI.METRE.times(1000);
+ final LinearUnit hour = SI.SECOND.times(3600);
+ final LinearUnit generatedKPH = kilometre.dividedBy(hour);
+
+ final LinearUnit actualKPH = SI.SI.getBaseUnit(StandardDimensions.VELOCITY).dividedBy(3.6);
+
+ assertEquals(generatedKPH, actualKPH);
+ }
+
+ @Test
+ public void testPrefixes() {
+ final LinearUnit generatedKilometre = SI.METRE.withPrefix(SIPrefix.KILO);
+ final LinearUnit actualKilometre = SI.METRE.times(1000);
+
+ assertEquals(generatedKilometre, actualKilometre);
+ }
+}
diff --git a/src/test/java/ExpressionParserTest.java b/src/test/java/ExpressionParserTest.java
deleted file mode 100644
index 40c91ac..0000000
--- a/src/test/java/ExpressionParserTest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-package test.java;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-import org.unitConverter.math.ExpressionParser;
-
-/**
- * A test for the {@code ExpressionParser} class. This is NOT part of this program's public API.
- *
- * @author Adrien Hopkins
- * @since 2019-03-22
- * @since v0.2.0
- */
-public class ExpressionParserTest {
- private static final ExpressionParser numberParser = new ExpressionParser.Builder<>(Integer::parseInt)
- .addBinaryOperator("+", (o1, o2) -> o1 + o2, 0).addBinaryOperator("-", (o1, o2) -> o1 - o2, 0)
- .addBinaryOperator("*", (o1, o2) -> o1 * o2, 1).addBinaryOperator("/", (o1, o2) -> o1 / o2, 1)
- .addBinaryOperator("^", (o1, o2) -> (int) Math.pow(o1, o2), 2).build();
-
- /**
- * Test method for {@link org.unitConverter.math.ExpressionParser#parseExpression(java.lang.String)}.
- */
- @Test
- public void testParseExpression() {
- // test parsing of expressions
- assertEquals((int) numberParser.parseExpression("1 + 2 ^ 5 * 3"), 97);
- assertEquals((int) numberParser.parseExpression("(1 + 2) ^ 5 * 3"), 729);
-
- // ensure it normally goes left to right
- assertEquals((int) numberParser.parseExpression("1 + 2 + 3 + 4"), 10);
- assertEquals((int) numberParser.parseExpression("12 - 4 - 3"), 5);
- assertEquals((int) numberParser.parseExpression("12 - (4 - 3)"), 11);
- assertEquals((int) numberParser.parseExpression("1 / 2 + 3"), 3);
- }
-
-}
diff --git a/src/test/java/UnitDimensionTest.java b/src/test/java/UnitDimensionTest.java
deleted file mode 100644
index 587cf4c..0000000
--- a/src/test/java/UnitDimensionTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- * Copyright (C) 2018 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-package test.java;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.unitConverter.dimension.StandardDimensions.AREA;
-import static org.unitConverter.dimension.StandardDimensions.ENERGY;
-import static org.unitConverter.dimension.StandardDimensions.LENGTH;
-import static org.unitConverter.dimension.StandardDimensions.MASS;
-import static org.unitConverter.dimension.StandardDimensions.MASS_DENSITY;
-import static org.unitConverter.dimension.StandardDimensions.QUANTITY;
-import static org.unitConverter.dimension.StandardDimensions.TIME;
-import static org.unitConverter.dimension.StandardDimensions.VOLUME;
-
-import org.junit.Test;
-import org.unitConverter.dimension.SIBaseDimension;
-import org.unitConverter.dimension.UnitDimension;
-
-/**
- * Tests for {@link UnitDimension}. This is NOT part of this program's public API.
- *
- * @author Adrien Hopkins
- * @since 2018-12-12
- * @since v0.1.0
- */
-public class UnitDimensionTest {
- /**
- * Tests {@link UnitDimension#equals}
- *
- * @since 2018-12-12
- * @since v0.1.0
- */
- @Test
- public void testEquals() {
- assertEquals(LENGTH, LENGTH);
- assertFalse(LENGTH.equals(QUANTITY));
- }
-
- /**
- * Tests {@code UnitDimension}'s exponentiation
- *
- * @since 2019-01-15
- * @since v0.1.0
- */
- @Test
- public void testExponents() {
- assertEquals(1, LENGTH.getExponent(SIBaseDimension.LENGTH));
- assertEquals(3, VOLUME.getExponent(SIBaseDimension.LENGTH));
- }
-
- /**
- * Tests {@code UnitDimension}'s multiplication and division.
- *
- * @since 2018-12-12
- * @since v0.1.0
- */
- @Test
- public void testMultiplicationAndDivision() {
- assertEquals(AREA, LENGTH.times(LENGTH));
- assertEquals(MASS_DENSITY, MASS.dividedBy(VOLUME));
- assertEquals(ENERGY, AREA.times(MASS).dividedBy(TIME).dividedBy(TIME));
- assertEquals(LENGTH, LENGTH.times(TIME).dividedBy(TIME));
- }
-}
diff --git a/src/test/java/UnitTest.java b/src/test/java/UnitTest.java
deleted file mode 100644
index 7a3f29d..0000000
--- a/src/test/java/UnitTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/**
- * Copyright (C) 2018 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-package test.java;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.Random;
-import java.util.concurrent.ThreadLocalRandom;
-
-import org.junit.Test;
-import org.unitConverter.dimension.StandardDimensions;
-import org.unitConverter.math.DecimalComparison;
-import org.unitConverter.unit.BaseUnit;
-import org.unitConverter.unit.LinearUnit;
-import org.unitConverter.unit.SI;
-import org.unitConverter.unit.SIPrefix;
-import org.unitConverter.unit.Unit;
-
-/**
- * Testing the various Unit classes. This is NOT part of this program's public API.
- *
- * @author Adrien Hopkins
- * @since 2018-12-22
- * @since v0.1.0
- */
-public class UnitTest {
- /** A random number generator */
- private static final Random rng = ThreadLocalRandom.current();
-
- @Test
- public void testAdditionAndSubtraction() {
- final LinearUnit inch = SI.METRE.times(0.0254);
- final LinearUnit foot = SI.METRE.times(0.3048);
-
- assertEquals(inch.plus(foot), SI.METRE.times(0.3302));
- assertEquals(foot.minus(inch), SI.METRE.times(0.2794));
- }
-
- @Test
- public void testBaseUnitExclusives() {
- // this test should have a compile error if I am doing something wrong
- final BaseUnit metrePerSecondSquared = SI.METRE.dividedBy(SI.SECOND.toExponent(2));
-
- assertEquals(metrePerSecondSquared, SI.SI.getBaseUnit(StandardDimensions.ACCELERATION));
- }
-
- @Test
- public void testConversion() {
- final BaseUnit metre = SI.METRE;
- final Unit inch = metre.times(0.0254);
-
- assertEquals(1.9, inch.convertTo(metre, 75), 0.01);
-
- // try random stuff
- for (int i = 0; i < 1000; i++) {
- // initiate random values
- final double conversionFactor = rng.nextDouble() * 1000000;
- final double testValue = rng.nextDouble() * 1000000;
- final double expected = testValue * conversionFactor;
-
- // test
- final Unit unit = SI.METRE.times(conversionFactor);
- final double actual = unit.convertToBase(testValue);
-
- assertEquals(actual, expected, expected * DecimalComparison.DOUBLE_EPSILON);
- }
- }
-
- @Test
- public void testEquals() {
- final BaseUnit metre = SI.METRE;
- final Unit meter = SI.SI.getBaseUnit(StandardDimensions.LENGTH);
-
- assertEquals(metre, meter);
- }
-
- @Test
- public void testMultiplicationAndDivision() {
- // test unit-times-unit multiplication
- final LinearUnit generatedJoule = SI.KILOGRAM.times(SI.METRE.toExponent(2)).dividedBy(SI.SECOND.toExponent(2));
- final LinearUnit actualJoule = SI.SI.getBaseUnit(StandardDimensions.ENERGY);
-
- assertEquals(generatedJoule, actualJoule);
-
- // test multiplication by conversion factors
- final LinearUnit kilometre = SI.METRE.times(1000);
- final LinearUnit hour = SI.SECOND.times(3600);
- final LinearUnit generatedKPH = kilometre.dividedBy(hour);
-
- final LinearUnit actualKPH = SI.SI.getBaseUnit(StandardDimensions.VELOCITY).dividedBy(3.6);
-
- assertEquals(generatedKPH, actualKPH);
- }
-
- @Test
- public void testPrefixes() {
- final LinearUnit generatedKilometre = SI.METRE.withPrefix(SIPrefix.KILO);
- final LinearUnit actualKilometre = SI.METRE.times(1000);
-
- assertEquals(generatedKilometre, actualKilometre);
- }
-}
diff --git a/src/test/java/UnitsDatabaseTest.java b/src/test/java/UnitsDatabaseTest.java
deleted file mode 100644
index 0d67c20..0000000
--- a/src/test/java/UnitsDatabaseTest.java
+++ /dev/null
@@ -1,313 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-package test.java;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import org.junit.Test;
-import org.unitConverter.UnitsDatabase;
-import org.unitConverter.unit.DefaultUnitPrefix;
-import org.unitConverter.unit.LinearUnit;
-import org.unitConverter.unit.SI;
-import org.unitConverter.unit.Unit;
-import org.unitConverter.unit.UnitPrefix;
-
-/**
- * A test for the {@link UnitsDatabase} class. This is NOT part of this program's public API.
- *
- * @author Adrien Hopkins
- * @since 2019-04-14
- * @since v0.2.0
- */
-public class UnitsDatabaseTest {
- // some linear units and one nonlinear
- private static final Unit U = SI.METRE;
- private static final Unit V = SI.KILOGRAM;
- private static final Unit W = SI.SECOND;
-
- // used for testing expressions
- // J = U^2 * V / W^2
- private static final LinearUnit J = SI.KILOGRAM.times(SI.METRE.toExponent(2)).dividedBy(SI.SECOND.toExponent(2));
- private static final LinearUnit K = SI.KELVIN;
-
- private static final Unit NONLINEAR = Unit.fromConversionFunctions(SI.METRE, o -> o + 1, o -> o - 1);
-
- // make the prefix values prime so I can tell which multiplications were made
- private static final UnitPrefix A = new DefaultUnitPrefix(2);
- private static final UnitPrefix B = new DefaultUnitPrefix(3);
- private static final UnitPrefix C = new DefaultUnitPrefix(5);
- private static final UnitPrefix AB = new DefaultUnitPrefix(7);
- private static final UnitPrefix BC = new DefaultUnitPrefix(11);
-
- /**
- * Confirms that operations that shouldn't function for infinite databases throw an {@code IllegalStateException}.
- *
- * @since 2019-05-03
- */
- @Test
- public void testInfiniteSetExceptions() {
- // load units
- final UnitsDatabase infiniteDatabase = new UnitsDatabase();
-
- infiniteDatabase.addUnit("J", J);
- infiniteDatabase.addUnit("K", K);
-
- infiniteDatabase.addPrefix("A", A);
- infiniteDatabase.addPrefix("B", B);
- infiniteDatabase.addPrefix("C", C);
-
- {
- boolean exceptionThrown = false;
- try {
- infiniteDatabase.unitMap().entrySet().toArray();
- } catch (final IllegalStateException e) {
- exceptionThrown = true;
- // pass!
- } finally {
- if (!exceptionThrown) {
- fail("No IllegalStateException thrown");
- }
- }
- }
-
- {
- boolean exceptionThrown = false;
- try {
- infiniteDatabase.unitMap().keySet().toArray();
- } catch (final IllegalStateException e) {
- exceptionThrown = true;
- // pass!
- } finally {
- if (!exceptionThrown) {
- fail("No IllegalStateException thrown");
- }
- }
- }
- }
-
- /**
- * Test that prefixes correctly apply to units.
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- @Test
- public void testPrefixes() {
- final UnitsDatabase database = new UnitsDatabase();
-
- database.addUnit("U", U);
- database.addUnit("V", V);
- database.addUnit("W", W);
-
- database.addPrefix("A", A);
- database.addPrefix("B", B);
- database.addPrefix("C", C);
-
- // get the product
- final Unit abcuNonlinear = database.getUnit("ABCU");
- assert abcuNonlinear instanceof LinearUnit;
-
- final LinearUnit abcu = (LinearUnit) abcuNonlinear;
- assertEquals(A.getMultiplier() * B.getMultiplier() * C.getMultiplier(), abcu.getConversionFactor(), 1e-15);
- }
-
- /**
- * Tests the functionnalites of the prefixless unit map.
- *
- *
- * The map should be an auto-updating view of the units in the database.
- *
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- @Test
- public void testPrefixlessUnitMap() {
- final UnitsDatabase database = new UnitsDatabase();
- final Map prefixlessUnits = database.unitMapPrefixless();
-
- database.addUnit("U", U);
- database.addUnit("V", V);
- database.addUnit("W", W);
-
- // this should work because the map should be an auto-updating view
- assertTrue(prefixlessUnits.containsKey("U"));
- assertFalse(prefixlessUnits.containsKey("Z"));
-
- assertTrue(prefixlessUnits.containsValue(U));
- assertFalse(prefixlessUnits.containsValue(NONLINEAR));
- }
-
- /**
- * Tests that the database correctly stores and retrieves units, ignoring prefixes.
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- @Test
- public void testPrefixlessUnits() {
- final UnitsDatabase database = new UnitsDatabase();
-
- database.addUnit("U", U);
- database.addUnit("V", V);
- database.addUnit("W", W);
-
- assertTrue(database.containsUnitName("U"));
- assertFalse(database.containsUnitName("Z"));
-
- assertEquals(U, database.getUnit("U"));
- assertEquals(null, database.getUnit("Z"));
- }
-
- /**
- * Test that unit expressions return the correct value.
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- @Test
- public void testUnitExpressions() {
- // load units
- final UnitsDatabase database = new UnitsDatabase();
-
- database.addUnit("U", U);
- database.addUnit("V", V);
- database.addUnit("W", W);
- database.addUnit("fj", J.times(5));
- database.addUnit("ej", J.times(8));
-
- database.addPrefix("A", A);
- database.addPrefix("B", B);
- database.addPrefix("C", C);
-
- // first test - test prefixes and operations
- final Unit expected1 = J.withPrefix(A).withPrefix(B).withPrefix(C).withPrefix(C);
- final Unit actual1 = database.getUnitFromExpression("ABV * CU^2 / W / W");
-
- assertEquals(expected1, actual1);
-
- // second test - test addition and subtraction
- final Unit expected2 = J.times(58);
- final Unit actual2 = database.getUnitFromExpression("2 fj + 6 ej");
-
- assertEquals(expected2, actual2);
- }
-
- /**
- * Tests both the unit name iterator and the name-unit entry iterator
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- @Test
- public void testUnitIterator() {
- // load units
- final UnitsDatabase database = new UnitsDatabase();
-
- database.addUnit("J", J);
- database.addUnit("K", K);
-
- database.addPrefix("A", A);
- database.addPrefix("B", B);
- database.addPrefix("C", C);
-
- final int NUM_UNITS = database.unitMapPrefixless().size();
- final int NUM_PREFIXES = database.prefixMap().size();
-
- final Iterator nameIterator = database.unitMap().keySet().iterator();
- final Iterator> entryIterator = database.unitMap().entrySet().iterator();
-
- int expectedLength = 1;
- int unitsWithThisLengthSoFar = 0;
-
- // loop 1000 times
- for (int i = 0; i < 1000; i++) {
- // expected length of next
- if (unitsWithThisLengthSoFar >= NUM_UNITS * (int) Math.pow(NUM_PREFIXES, expectedLength - 1)) {
- expectedLength++;
- unitsWithThisLengthSoFar = 0;
- }
-
- // test that stuff is valid
- final String nextName = nameIterator.next();
- final Unit nextUnit = database.getUnit(nextName);
- final Entry nextEntry = entryIterator.next();
-
- assertEquals(expectedLength, nextName.length());
- assertEquals(nextName, nextEntry.getKey());
- assertEquals(nextUnit, nextEntry.getValue());
-
- unitsWithThisLengthSoFar++;
- }
-
- // test toString for consistency
- final String entryIteratorString = entryIterator.toString();
- for (int i = 0; i < 3; i++) {
- assertEquals(entryIteratorString, entryIterator.toString());
- }
-
- final String nameIteratorString = nameIterator.toString();
- for (int i = 0; i < 3; i++) {
- assertEquals(nameIteratorString, nameIterator.toString());
- }
- }
-
- /**
- * Determine, given a unit name that could mean multiple things, which meaning is chosen.
- *
- * For example, "ABCU" could mean "A-B-C-U", "AB-C-U", or "A-BC-U". In this case, "AB-C-U" is the correct choice.
- *
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- @Test
- public void testUnitPrefixCombinations() {
- // load units
- final UnitsDatabase database = new UnitsDatabase();
-
- database.addUnit("J", J);
-
- database.addPrefix("A", A);
- database.addPrefix("B", B);
- database.addPrefix("C", C);
- database.addPrefix("AB", AB);
- database.addPrefix("BC", BC);
-
- // test 1 - AB-C-J vs A-BC-J vs A-B-C-J
- final Unit expected1 = J.withPrefix(AB).withPrefix(C);
- final Unit actual1 = database.getUnit("ABCJ");
-
- assertEquals(expected1, actual1);
-
- // test 2 - ABC-J vs AB-CJ vs AB-C-J
- database.addUnit("CJ", J.times(13));
- database.addPrefix("ABC", new DefaultUnitPrefix(17));
-
- final Unit expected2 = J.times(17);
- final Unit actual2 = database.getUnit("ABCJ");
-
- assertEquals(expected2, actual2);
- }
-}
diff --git a/src/test/java/package-info.java b/src/test/java/package-info.java
deleted file mode 100644
index 3da7fcb..0000000
--- a/src/test/java/package-info.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-/**
- * All of the Unit Converter tests. Everything in this package is NOT part of Unit Converter's public API.
- *
- * @author Adrien Hopkins
- * @since 2019-03-16
- * @since v0.2.0
- */
-package test.java;
\ No newline at end of file
--
cgit v1.2.3
From 511fe144da142082a02b5a5b07e67bb76df1331e Mon Sep 17 00:00:00 2001
From: Adrien Hopkins
Date: Tue, 15 Oct 2019 20:13:04 -0400
Subject: Finalized the removal of Maven from Unit Converter
---
.classpath | 5 +++--
.gitlab-ci.yml | 14 --------------
CHANGELOG.org | 1 +
3 files changed, 4 insertions(+), 16 deletions(-)
delete mode 100644 .gitlab-ci.yml
diff --git a/.classpath b/.classpath
index 8fc330a..12fc059 100644
--- a/.classpath
+++ b/.classpath
@@ -6,11 +6,12 @@
-
+
+
-
+
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index 7c2efb0..0000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-# attempt at ci
-
-image: maven:3-jdk-8
-
-
-build:
- stage: build
- script: "mvn clean -B"
- script: "mvn compile -B"
-
-
-test:
- stage: test
- script: "mvn verify"
diff --git a/CHANGELOG.org b/CHANGELOG.org
index 1a49709..6c78f27 100644
--- a/CHANGELOG.org
+++ b/CHANGELOG.org
@@ -7,6 +7,7 @@ All notable changes in this project will be shown in this file.
- Added a static factory to create nonlinear units to the Unit interface
*** Changed
- You can now use tabs or spaces to separate unit names and their definitions in unit files.
+ - Unit Converter no longer uses Maven.
** v0.2.0 - [2019-04-14]
*** Added
- A selection-based unit converter which allows you to select two units, input a value, and convert.
--
cgit v1.2.3
From 69f2849d25a41ae7c0383636557deda1bc64247d Mon Sep 17 00:00:00 2001
From: Adrien Hopkins
Date: Wed, 16 Oct 2019 12:29:19 -0400
Subject: Created more generalized objects for use in the new units.
---
src/org/unitConverter/math/ObjectProduct.java | 261 +++++++++++++++++++++
src/org/unitConverter/math/ObjectProductTest.java | 79 +++++++
src/org/unitConverter/math/StandardDimensions.java | 86 +++++++
src/org/unitConverter/math/ZeroIsNullMap.java | 129 ++++++++++
src/org/unitConverter/math/ZeroIsNullMapTest.java | 113 +++++++++
5 files changed, 668 insertions(+)
create mode 100644 src/org/unitConverter/math/ObjectProduct.java
create mode 100644 src/org/unitConverter/math/ObjectProductTest.java
create mode 100644 src/org/unitConverter/math/StandardDimensions.java
create mode 100644 src/org/unitConverter/math/ZeroIsNullMap.java
create mode 100644 src/org/unitConverter/math/ZeroIsNullMapTest.java
diff --git a/src/org/unitConverter/math/ObjectProduct.java b/src/org/unitConverter/math/ObjectProduct.java
new file mode 100644
index 0000000..ec4d2d6
--- /dev/null
+++ b/src/org/unitConverter/math/ObjectProduct.java
@@ -0,0 +1,261 @@
+/**
+ * Copyright (C) 2018 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.unitConverter.math;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * An immutable product of multiple objects of a type, such as base units. The objects can be multiplied and
+ * exponentiated.
+ *
+ * @author Adrien Hopkins
+ * @since 2019-10-16
+ */
+public final class ObjectProduct {
+ /**
+ * Returns an empty ObjectProduct of a certain type
+ *
+ * @param
+ * type of objects that can be multiplied
+ * @return empty product
+ * @since 2019-10-16
+ */
+ public static final ObjectProduct empty() {
+ return new ObjectProduct<>(new HashMap<>());
+ }
+
+ /**
+ * Gets an {@code ObjectProduct} from an object-to-integer mapping
+ *
+ * @param
+ * type of object in product
+ * @param map
+ * map mapping objects to exponents
+ * @return object product
+ * @since 2019-10-16
+ */
+ public static final ObjectProduct fromExponentMapping(final Map map) {
+ return new ObjectProduct<>(new HashMap<>(map));
+ }
+
+ /**
+ * Gets an ObjectProduct that has one of the inputted argument, and nothing else.
+ *
+ * @param object
+ * object that will be in the product
+ * @return product
+ * @since 2019-10-16
+ * @throws NullPointerException
+ * if object is null
+ */
+ public static final ObjectProduct oneOf(final T object) {
+ Objects.requireNonNull(object, "object must not be null.");
+ final Map map = new HashMap<>();
+ map.put(object, 1);
+ return new ObjectProduct<>(map);
+ }
+
+ /**
+ * The objects that make up the product, mapped to their exponents. This map treats zero as null, and is immutable.
+ *
+ * @since 2019-10-16
+ */
+ final Map exponents;
+
+ /**
+ * Creates the {@code ObjectProduct}.
+ *
+ * @param exponents
+ * objects that make up this product
+ * @since 2019-10-16
+ */
+ private ObjectProduct(final Map exponents) {
+ this.exponents = Collections.unmodifiableMap(ZeroIsNullMap.create(new HashMap<>(exponents)));
+ }
+
+ /**
+ * Calculates the quotient of two products
+ *
+ * @param other
+ * other product
+ * @return quotient of two products
+ * @since 2019-10-16
+ * @throws NullPointerException
+ * if other is null
+ */
+ public ObjectProduct dividedBy(final ObjectProduct other) {
+ Objects.requireNonNull(other, "other must not be null.");
+ // get a list of all objects in both sets
+ final Set objects = new HashSet<>();
+ objects.addAll(this.getBaseSet());
+ objects.addAll(other.getBaseSet());
+
+ // get a list of all exponents
+ final Map map = new HashMap<>(objects.size());
+ for (final T key : objects) {
+ map.put(key, this.getExponent(key) - other.getExponent(key));
+ }
+
+ // create the product
+ return new ObjectProduct<>(map);
+ }
+
+ // this method relies on the use of ZeroIsNullMap
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj)
+ return true;
+ if (!(obj instanceof ObjectProduct))
+ return false;
+ final ObjectProduct> other = (ObjectProduct>) obj;
+ return Objects.equals(this.exponents, other.exponents);
+ }
+
+ /**
+ * @return immutable map mapping objects to exponents
+ * @since 2019-10-16
+ */
+ public Map exponentMap() {
+ return this.exponents;
+ }
+
+ /**
+ * @return a set of all of the base objects with non-zero exponents that make up this dimension.
+ * @since 2018-12-12
+ * @since v0.1.0
+ */
+ public final Set getBaseSet() {
+ final Set dimensions = new HashSet<>();
+
+ // add all dimensions with a nonzero exponent - zero exponents shouldn't be there in the first place
+ for (final T dimension : this.exponents.keySet()) {
+ if (!this.exponents.get(dimension).equals(0)) {
+ dimensions.add(dimension);
+ }
+ }
+
+ return dimensions;
+ }
+
+ /**
+ * Gets the exponent for a specific dimension.
+ *
+ * @param dimension
+ * dimension to check
+ * @return exponent for that dimension
+ * @since 2018-12-12
+ * @since v0.1.0
+ */
+ public int getExponent(final T dimension) {
+ return this.exponents.getOrDefault(dimension, 0);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.exponents);
+ }
+
+ /**
+ * @return true if this product is a "base", i.e. it has one exponent of one and no other nonzero exponents
+ * @since 2019-10-16
+ */
+ public boolean isBase() {
+ int oneCount = 0;
+ boolean twoOrMore = false; // has exponents of 2 or more
+ for (final T b : this.getBaseSet()) {
+ if (this.getExponent(b) == 1) {
+ oneCount++;
+ } else if (this.getExponent(b) != 0) {
+ twoOrMore = true;
+ }
+ }
+ return oneCount == 1 && !twoOrMore;
+ }
+
+ /**
+ * Multiplies this product by another
+ *
+ * @param other
+ * other product
+ * @return product of two products
+ * @since 2019-10-16
+ * @throws NullPointerException
+ * if other is null
+ */
+ public ObjectProduct times(final ObjectProduct other) {
+ Objects.requireNonNull(other, "other must not be null.");
+ // get a list of all objects in both sets
+ final Set objects = new HashSet<>();
+ objects.addAll(this.getBaseSet());
+ objects.addAll(other.getBaseSet());
+
+ // get a list of all exponents
+ final Map map = new HashMap<>(objects.size());
+ for (final T key : objects) {
+ map.put(key, this.getExponent(key) + other.getExponent(key));
+ }
+
+ // create the product
+ return new ObjectProduct<>(map);
+ }
+
+ /**
+ * Returns this product, but to an exponent
+ *
+ * @param exponent
+ * exponent
+ * @return result of exponentiation
+ * @since 2019-10-16
+ */
+ public ObjectProduct toExponent(final int exponent) {
+ final Map map = new HashMap<>(this.exponents);
+ for (final T key : this.exponents.keySet()) {
+ map.put(key, this.getExponent(key) * exponent);
+ }
+ return new ObjectProduct<>(map);
+ }
+
+ @Override
+ public String toString() {
+ final List positiveStringComponents = new ArrayList<>();
+ final List negativeStringComponents = new ArrayList<>();
+
+ // for each base dimension that makes up this dimension, add it and its exponent
+ for (final T dimension : this.getBaseSet()) {
+ final int exponent = this.exponents.get(dimension);
+ if (exponent > 0) {
+ positiveStringComponents.add(String.format("%s^%d", dimension, exponent));
+ } else if (exponent < 0) {
+ negativeStringComponents.add(String.format("%s^%d", dimension, -exponent));
+ }
+ }
+
+ final String positiveString = positiveStringComponents.isEmpty() ? "1"
+ : String.join(" * ", positiveStringComponents);
+ final String negativeString = negativeStringComponents.isEmpty() ? ""
+ : " / " + String.join(" * ", negativeStringComponents);
+
+ return positiveString + negativeString;
+ }
+}
diff --git a/src/org/unitConverter/math/ObjectProductTest.java b/src/org/unitConverter/math/ObjectProductTest.java
new file mode 100644
index 0000000..03c767c
--- /dev/null
+++ b/src/org/unitConverter/math/ObjectProductTest.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright (C) 2018 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.unitConverter.math;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.unitConverter.dimension.StandardDimensions.AREA;
+import static org.unitConverter.dimension.StandardDimensions.ENERGY;
+import static org.unitConverter.dimension.StandardDimensions.LENGTH;
+import static org.unitConverter.dimension.StandardDimensions.MASS;
+import static org.unitConverter.dimension.StandardDimensions.MASS_DENSITY;
+import static org.unitConverter.dimension.StandardDimensions.QUANTITY;
+import static org.unitConverter.dimension.StandardDimensions.TIME;
+import static org.unitConverter.dimension.StandardDimensions.VOLUME;
+
+import org.junit.jupiter.api.Test;
+import org.unitConverter.dimension.SIBaseDimension;
+import org.unitConverter.dimension.UnitDimension;
+
+/**
+ * Tests for {@link ObjectProduct} using BaseDimension as a test object. This is NOT part of this program's public API.
+ *
+ * @author Adrien Hopkins
+ * @since 2018-12-12
+ * @since v0.1.0
+ */
+class ObjectProductTest {
+ /**
+ * Tests {@link UnitDimension#equals}
+ *
+ * @since 2018-12-12
+ * @since v0.1.0
+ */
+ @Test
+ public void testEquals() {
+ assertEquals(LENGTH, LENGTH);
+ assertFalse(LENGTH.equals(QUANTITY));
+ }
+
+ /**
+ * Tests {@code UnitDimension}'s exponentiation
+ *
+ * @since 2019-01-15
+ * @since v0.1.0
+ */
+ @Test
+ public void testExponents() {
+ assertEquals(1, LENGTH.getExponent(SIBaseDimension.LENGTH));
+ assertEquals(3, VOLUME.getExponent(SIBaseDimension.LENGTH));
+ }
+
+ /**
+ * Tests {@code UnitDimension}'s multiplication and division.
+ *
+ * @since 2018-12-12
+ * @since v0.1.0
+ */
+ @Test
+ public void testMultiplicationAndDivision() {
+ assertEquals(AREA, LENGTH.times(LENGTH));
+ assertEquals(MASS_DENSITY, MASS.dividedBy(VOLUME));
+ assertEquals(ENERGY, AREA.times(MASS).dividedBy(TIME).dividedBy(TIME));
+ assertEquals(LENGTH, LENGTH.times(TIME).dividedBy(TIME));
+ }
+}
diff --git a/src/org/unitConverter/math/StandardDimensions.java b/src/org/unitConverter/math/StandardDimensions.java
new file mode 100644
index 0000000..db5efc3
--- /dev/null
+++ b/src/org/unitConverter/math/StandardDimensions.java
@@ -0,0 +1,86 @@
+/**
+ * Copyright (C) 2018 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.unitConverter.math;
+
+import org.unitConverter.dimension.BaseDimension;
+import org.unitConverter.dimension.OtherBaseDimension;
+import org.unitConverter.dimension.SIBaseDimension;
+
+/**
+ * All of the dimensions that are used by the SI. (Test data for the ObjectProductTest)
+ *
+ * @author Adrien Hopkins
+ * @since 2018-12-11
+ * @since v0.1.0
+ */
+final class StandardDimensions {
+ // base dimensions
+ public static final ObjectProduct EMPTY = ObjectProduct.empty();
+ public static final ObjectProduct LENGTH = ObjectProduct.oneOf(SIBaseDimension.LENGTH);
+ public static final ObjectProduct MASS = ObjectProduct.oneOf(SIBaseDimension.MASS);
+ public static final ObjectProduct TIME = ObjectProduct.oneOf(SIBaseDimension.TIME);
+ public static final ObjectProduct ELECTRIC_CURRENT = ObjectProduct
+ .oneOf(SIBaseDimension.ELECTRIC_CURRENT);
+ public static final ObjectProduct TEMPERATURE = ObjectProduct.oneOf(SIBaseDimension.TEMPERATURE);
+ public static final ObjectProduct QUANTITY = ObjectProduct.oneOf(SIBaseDimension.QUANTITY);
+ public static final ObjectProduct LUMINOUS_INTENSITY = ObjectProduct
+ .oneOf(SIBaseDimension.LUMINOUS_INTENSITY);
+ public static final ObjectProduct INFORMATION = ObjectProduct.oneOf(OtherBaseDimension.INFORMATION);
+ public static final ObjectProduct CURRENCY = ObjectProduct.oneOf(OtherBaseDimension.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);
+ // 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);
+
+ // You may NOT get StandardDimensions instances!
+ private StandardDimensions() {
+ throw new AssertionError();
+ }
+}
diff --git a/src/org/unitConverter/math/ZeroIsNullMap.java b/src/org/unitConverter/math/ZeroIsNullMap.java
new file mode 100644
index 0000000..10e4d52
--- /dev/null
+++ b/src/org/unitConverter/math/ZeroIsNullMap.java
@@ -0,0 +1,129 @@
+/**
+ * Copyright (C) 2019 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.unitConverter.math;
+
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A "wrapper" for an existing map that treats a zero value as equivalent to null.
+ *
+ * @author Adrien Hopkins
+ * @since 2019-10-16
+ * @param
+ * type of key
+ */
+public final class ZeroIsNullMap extends AbstractMap {
+ /**
+ * Create a ZeroIsNullMap. This method always creates a new map.
+ *
+ * @param
+ * type of key
+ * @param map
+ * map to input
+ * @return map that treats zero as null
+ * @throws NullPointerException
+ * if map is null
+ * @since 2019-10-16
+ */
+ public static Map create(final Map map) {
+ return new ZeroIsNullMap<>(Objects.requireNonNull(map, "map must not be null."));
+ }
+
+ private final Map map;
+
+ /**
+ * Creates the {@code ObjectProductMap}.
+ *
+ * @param map
+ * @since 2019-10-16
+ */
+ private ZeroIsNullMap(final Map map) {
+ this.map = map;
+ }
+
+ @Override
+ public void clear() {
+ this.map.clear();
+ }
+
+ @Override
+ public boolean containsKey(final Object key) {
+ return this.map.containsKey(key) && this.map.get(key) != 0;
+ }
+
+ @Override
+ public boolean containsValue(final Object value) {
+ return this.values().contains(value);
+ }
+
+ @Override
+ public Set> entrySet() {
+ final Set> entrySet = new HashSet<>(this.map.entrySet());
+ entrySet.removeIf(e -> e.getValue() == 0);
+ return entrySet;
+ }
+
+ @Override
+ public Integer get(final Object key) {
+ final Integer i = this.map.get(key);
+ if (Objects.equals(i, 0))
+ return null;
+ else
+ return i;
+ }
+
+ @Override
+ public Set keySet() {
+ final Set keySet = new HashSet<>(this.map.keySet());
+ keySet.removeIf(k -> this.map.get(k) == 0);
+ return keySet;
+ }
+
+ @Override
+ public Integer put(final T key, final Integer value) {
+ if (value != 0)
+ return this.map.put(key, value);
+ else
+ return null;
+ }
+
+ @Override
+ public void putAll(final Map extends T, ? extends Integer> m) {
+ for (final T key : m.keySet()) {
+ this.put(key, m.get(key));
+ }
+ }
+
+ @Override
+ public Integer remove(final Object key) {
+ return this.map.remove(key);
+ }
+
+ @Override
+ public Collection values() {
+ final List values = new ArrayList<>(this.map.values());
+ values.removeIf(i -> i == 0);
+ return values;
+ }
+}
\ No newline at end of file
diff --git a/src/org/unitConverter/math/ZeroIsNullMapTest.java b/src/org/unitConverter/math/ZeroIsNullMapTest.java
new file mode 100644
index 0000000..c3db951
--- /dev/null
+++ b/src/org/unitConverter/math/ZeroIsNullMapTest.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright (C) 2019 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.unitConverter.math;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author Adrien Hopkins
+ * @since 2019-10-16
+ */
+class ZeroIsNullMapTest {
+
+ /**
+ * @return map to be used for test data
+ * @since 2019-10-16
+ */
+ Map getTestMap() {
+ final Map map = new HashMap<>();
+ map.put("one", 1);
+ map.put("two", 2);
+ map.put("zero", 0);
+ map.put("ten", 10);
+ return ZeroIsNullMap.create(map);
+ }
+
+ /**
+ * Test method for {@link org.unitConverter.math.ZeroIsNullMap#containsKey(java.lang.Object)}.
+ */
+ @Test
+ void testContainsKeyObject() {
+ final Map map = this.getTestMap();
+ assertTrue(map.containsKey("one"));
+ assertTrue(map.containsKey("ten"));
+ assertFalse(map.containsKey("five"));
+ assertFalse(map.containsKey("zero"));
+ }
+
+ /**
+ * Test method for {@link org.unitConverter.math.ZeroIsNullMap#containsValue(java.lang.Object)}.
+ */
+ @Test
+ void testContainsValueObject() {
+ final Map map = this.getTestMap();
+ assertTrue(map.containsValue(1));
+ assertTrue(map.containsValue(10));
+ assertFalse(map.containsValue(5));
+ assertFalse(map.containsValue(0));
+ }
+
+ /**
+ * Test method for {@link org.unitConverter.math.ZeroIsNullMap#entrySet()}.
+ */
+ @Test
+ void testEntrySet() {
+ final Map map = this.getTestMap();
+ for (final Entry e : map.entrySet()) {
+ assertTrue(e.getValue() != 0);
+ }
+ }
+
+ /**
+ * Test method for {@link org.unitConverter.math.ZeroIsNullMap#get(java.lang.Object)}.
+ */
+ @Test
+ void testGetObject() {
+ final Map map = this.getTestMap();
+ assertEquals(1, map.get("one"));
+ assertEquals(10, map.get("ten"));
+ assertEquals(null, map.get("five"));
+ assertEquals(null, map.get("zero"));
+ }
+
+ /**
+ * Test method for {@link org.unitConverter.math.ZeroIsNullMap#keySet()}.
+ */
+ @Test
+ void testKeySet() {
+ final Map map = this.getTestMap();
+ assertFalse(map.keySet().contains("zero"));
+ }
+
+ /**
+ * Test method for {@link org.unitConverter.math.ZeroIsNullMap#values()}.
+ */
+ @Test
+ void testValues() {
+ final Map map = this.getTestMap();
+ assertFalse(map.values().contains(0));
+ }
+
+}
--
cgit v1.2.3
From 145f524f19b3d61cd12441c2f1a8de05d7dcfe60 Mon Sep 17 00:00:00 2001
From: Adrien Hopkins
Date: Wed, 16 Oct 2019 12:31:09 -0400
Subject: Created the new BaseUnit, Unit and AbstractUnit implementations.
---
src/org/unitConverter/newUnits/AbstractUnit.java | 76 +++++++++++
src/org/unitConverter/newUnits/BaseUnit.java | 102 +++++++++++++++
src/org/unitConverter/newUnits/FunctionalUnit.java | 98 ++++++++++++++
src/org/unitConverter/newUnits/Unit.java | 145 +++++++++++++++++++++
src/org/unitConverter/newUnits/package-info.java | 23 ++++
5 files changed, 444 insertions(+)
create mode 100644 src/org/unitConverter/newUnits/AbstractUnit.java
create mode 100644 src/org/unitConverter/newUnits/BaseUnit.java
create mode 100644 src/org/unitConverter/newUnits/FunctionalUnit.java
create mode 100644 src/org/unitConverter/newUnits/Unit.java
create mode 100644 src/org/unitConverter/newUnits/package-info.java
diff --git a/src/org/unitConverter/newUnits/AbstractUnit.java b/src/org/unitConverter/newUnits/AbstractUnit.java
new file mode 100644
index 0000000..909ea8b
--- /dev/null
+++ b/src/org/unitConverter/newUnits/AbstractUnit.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (C) 2019 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.unitConverter.newUnits;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import org.unitConverter.dimension.BaseDimension;
+import org.unitConverter.math.ObjectProduct;
+
+/**
+ * @author Adrien Hopkins
+ * @since 2019-10-16
+ */
+public abstract class AbstractUnit implements Unit {
+ /**
+ * The combination of units that this unit is based on.
+ */
+ private final ObjectProduct unitBase;
+
+ /**
+ * Cache storing the result of getDimension()
+ */
+ private transient ObjectProduct dimension = null;
+
+ /**
+ * Creates the {@code AbstractUnit}.
+ *
+ * @param unitBase
+ * @since 2019-10-16
+ * @throws NullPointerException
+ * if unitBase is null
+ */
+ public AbstractUnit(final ObjectProduct unitBase) {
+ this.unitBase = Objects.requireNonNull(unitBase, "unitBase must not be null.");
+ }
+
+ /**
+ * @return unitBase
+ * @since 2019-10-16
+ */
+ @Override
+ public final ObjectProduct getBase() {
+ return this.unitBase;
+ }
+
+ @Override
+ public ObjectProduct getDimension() {
+ 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;
+ }
+}
diff --git a/src/org/unitConverter/newUnits/BaseUnit.java b/src/org/unitConverter/newUnits/BaseUnit.java
new file mode 100644
index 0000000..69e8b8b
--- /dev/null
+++ b/src/org/unitConverter/newUnits/BaseUnit.java
@@ -0,0 +1,102 @@
+/**
+ * Copyright (C) 2019 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.unitConverter.newUnits;
+
+import java.util.Objects;
+
+import org.unitConverter.dimension.BaseDimension;
+import org.unitConverter.math.ObjectProduct;
+
+/**
+ * A unit that other units are defined by.
+ *
+ * @author Adrien Hopkins
+ * @since 2019-10-16
+ */
+public final class BaseUnit implements Unit {
+ private final BaseDimension dimension;
+ private final String name;
+ private final String symbol;
+
+ /**
+ * Creates the {@code BaseUnit}.
+ *
+ * @param dimension
+ * dimension of unit
+ * @param name
+ * name of unit
+ * @param symbol
+ * symbol of unit
+ * @throws NullPointerException
+ * if any argument is null
+ * @since 2019-10-16
+ */
+ private BaseUnit(final BaseDimension dimension, final String name, final String symbol) {
+ this.dimension = Objects.requireNonNull(dimension, "dimension must not be null.");
+ this.name = Objects.requireNonNull(name, "name must not be null.");
+ this.symbol = Objects.requireNonNull(symbol, "symbol must not be null.");
+ }
+
+ @Override
+ public double convertFromBase(final double value) {
+ return value;
+ }
+
+ @Override
+ public double convertToBase(final double value) {
+ return value;
+ }
+
+ @Override
+ public ObjectProduct getBase() {
+ return ObjectProduct.oneOf(this);
+ }
+
+ /**
+ * @return dimension
+ * @since 2019-10-16
+ */
+ public final BaseDimension getBaseDimension() {
+ return this.dimension;
+ }
+
+ @Override
+ public ObjectProduct getDimension() {
+ return ObjectProduct.oneOf(this.getBaseDimension());
+ }
+
+ /**
+ * @return name
+ * @since 2019-10-16
+ */
+ public final String getName() {
+ return this.name;
+ }
+
+ /**
+ * @return symbol
+ * @since 2019-10-16
+ */
+ public final String getSymbol() {
+ return this.symbol;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s (%s)", this.getName(), this.getSymbol());
+ }
+}
diff --git a/src/org/unitConverter/newUnits/FunctionalUnit.java b/src/org/unitConverter/newUnits/FunctionalUnit.java
new file mode 100644
index 0000000..99ff833
--- /dev/null
+++ b/src/org/unitConverter/newUnits/FunctionalUnit.java
@@ -0,0 +1,98 @@
+/**
+ * Copyright (C) 2019 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.unitConverter.newUnits;
+
+import java.util.Objects;
+import java.util.function.DoubleUnaryOperator;
+
+import org.unitConverter.math.ObjectProduct;
+
+/**
+ * A unit that uses functional objects to convert to and from its base.
+ *
+ * @author Adrien Hopkins
+ * @since 2019-05-22
+ */
+final class FunctionalUnit extends AbstractUnit {
+ /**
+ * Returns a unit from its base and the functions it uses to convert to and from its 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
+ */
+ public static FunctionalUnit valueOf(final ObjectProduct base, final DoubleUnaryOperator converterFrom,
+ final DoubleUnaryOperator converterTo) {
+ return new FunctionalUnit(base, converterFrom, converterTo);
+ }
+
+ /**
+ * A function that accepts a value expressed in the unit's base and returns that value expressed in this unit.
+ *
+ * @since 2019-05-22
+ */
+ private final DoubleUnaryOperator converterFrom;
+
+ /**
+ * A function that accepts a value expressed in the unit and returns that value expressed in the unit's base.
+ *
+ * @since 2019-05-22
+ */
+ private final DoubleUnaryOperator converterTo;
+
+ /**
+ * Creates the {@code FunctionalUnit}.
+ *
+ * @param base
+ * unit's base
+ * @param converterFrom
+ * function that accepts a value expressed in the unit's base and returns that value expressed in this
+ * unit.
+ * @param converterTo
+ * function that accepts a value expressed in the unit and returns that value expressed in the unit's
+ * base.
+ * @throws NullPointerException
+ * if any argument is null
+ * @since 2019-05-22
+ */
+ private FunctionalUnit(final ObjectProduct base, final DoubleUnaryOperator converterFrom,
+ final DoubleUnaryOperator converterTo) {
+ super(base);
+ this.converterFrom = Objects.requireNonNull(converterFrom, "converterFrom must not be null.");
+ this.converterTo = Objects.requireNonNull(converterTo, "converterTo must not be null.");
+ }
+
+ @Override
+ public double convertFromBase(final double value) {
+ return this.converterFrom.applyAsDouble(value);
+ }
+
+ @Override
+ public double convertToBase(final double value) {
+ return this.converterTo.applyAsDouble(value);
+ }
+
+}
diff --git a/src/org/unitConverter/newUnits/Unit.java b/src/org/unitConverter/newUnits/Unit.java
new file mode 100644
index 0000000..339afde
--- /dev/null
+++ b/src/org/unitConverter/newUnits/Unit.java
@@ -0,0 +1,145 @@
+/**
+ * Copyright (C) 2019 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.unitConverter.newUnits;
+
+import java.util.Objects;
+import java.util.function.DoubleUnaryOperator;
+
+import org.unitConverter.dimension.BaseDimension;
+import org.unitConverter.math.ObjectProduct;
+
+/**
+ * @author Adrien Hopkins
+ * @since 2019-10-16
+ */
+public interface Unit {
+ /**
+ * 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:
+ *
+ * {@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.
+ * @return a unit that uses the provided functions to convert.
+ * @since 2019-05-22
+ * @throws NullPointerException
+ * if any argument is null
+ */
+ public static Unit fromConversionFunctions(final ObjectProduct base,
+ final DoubleUnaryOperator converterFrom, final DoubleUnaryOperator converterTo) {
+ return FunctionalUnit.valueOf(base, converterFrom, converterTo);
+ }
+
+ /**
+ * Checks if a value expressed in this unit can be converted to a value expressed in {@code other}
+ *
+ * @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
+ */
+ default 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.
+ *
+ * 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}.
+ *
+ *
+ * @param value
+ * value expressed in base unit
+ * @return value expressed in this unit
+ * @since 2018-12-22
+ * @since v0.1.0
+ */
+ double convertFromBase(double value);
+
+ /**
+ * Converts a value expressed in this unit to a value expressed in {@code other}.
+ *
+ * @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
+ */
+ default double convertTo(final Unit other, final double value) {
+ Objects.requireNonNull(other, "other must not be null.");
+ if (this.canConvertTo(other))
+ return other.convertFromBase(this.convertToBase(value));
+ else
+ throw new IllegalArgumentException(String.format("Cannot convert from %s to %s.", this, other));
+ }
+
+ /**
+ * Converts 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.
+ *
+ *
+ * If this unit is a base unit, this method should return {@code value}.
+ *
+ *
+ * @param value
+ * value expressed in this unit
+ * @return value expressed in base unit
+ * @since 2018-12-22
+ * @since v0.1.0
+ */
+ double convertToBase(double value);
+
+ /**
+ * @return combination of units that this unit is based on
+ * @since 2018-12-22
+ * @since v0.1.0
+ */
+ ObjectProduct getBase();
+
+ /**
+ * @return dimension measured by this unit
+ * @since 2018-12-22
+ * @since v0.1.0
+ */
+ ObjectProduct getDimension();
+}
\ No newline at end of file
diff --git a/src/org/unitConverter/newUnits/package-info.java b/src/org/unitConverter/newUnits/package-info.java
new file mode 100644
index 0000000..9cd0d1a
--- /dev/null
+++ b/src/org/unitConverter/newUnits/package-info.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright (C) 2019 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+/**
+ * The new definition for units.
+ *
+ * @author Adrien Hopkins
+ * @since 2019-10-16
+ */
+package org.unitConverter.newUnits;
\ No newline at end of file
--
cgit v1.2.3
From 45286c30f96f152f821098d878ede64ecbabe48a Mon Sep 17 00:00:00 2001
From: Adrien Hopkins
Date: Wed, 16 Oct 2019 13:53:29 -0400
Subject: Added the LinearUnit to the new units definition.
---
src/org/unitConverter/math/ObjectProduct.java | 28 ++-
src/org/unitConverter/newUnits/AbstractUnit.java | 7 +-
src/org/unitConverter/newUnits/BaseUnit.java | 11 +
src/org/unitConverter/newUnits/LinearUnit.java | 261 +++++++++++++++++++++++
src/org/unitConverter/newUnits/Unit.java | 2 +
5 files changed, 301 insertions(+), 8 deletions(-)
create mode 100644 src/org/unitConverter/newUnits/LinearUnit.java
diff --git a/src/org/unitConverter/math/ObjectProduct.java b/src/org/unitConverter/math/ObjectProduct.java
index ec4d2d6..21ab207 100644
--- a/src/org/unitConverter/math/ObjectProduct.java
+++ b/src/org/unitConverter/math/ObjectProduct.java
@@ -24,6 +24,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.function.Function;
/**
* An immutable product of multiple objects of a type, such as base units. The objects can be multiplied and
@@ -177,10 +178,10 @@ public final class ObjectProduct {
}
/**
- * @return true if this product is a "base", i.e. it has one exponent of one and no other nonzero exponents
+ * @return true if this product is a single object, i.e. it has one exponent of one and no other nonzero exponents
* @since 2019-10-16
*/
- public boolean isBase() {
+ public boolean isSingleObject() {
int oneCount = 0;
boolean twoOrMore = false; // has exponents of 2 or more
for (final T b : this.getBaseSet()) {
@@ -238,16 +239,29 @@ public final class ObjectProduct {
@Override
public String toString() {
+ return this.toString(Object::toString);
+ }
+
+ /**
+ * Converts this product to a string. The objects that make up this product are represented by
+ * {@code objectToString}
+ *
+ * @param objectToString
+ * function to convert objects to strings
+ * @return string representation of product
+ * @since 2019-10-16
+ */
+ public String toString(final Function objectToString) {
final List positiveStringComponents = new ArrayList<>();
final List negativeStringComponents = new ArrayList<>();
- // for each base dimension that makes up this dimension, add it and its exponent
- for (final T dimension : this.getBaseSet()) {
- final int exponent = this.exponents.get(dimension);
+ // for each base object that makes up this object, add it and its exponent
+ for (final T object : this.getBaseSet()) {
+ final int exponent = this.exponents.get(object);
if (exponent > 0) {
- positiveStringComponents.add(String.format("%s^%d", dimension, exponent));
+ positiveStringComponents.add(String.format("%s^%d", objectToString.apply(object), exponent));
} else if (exponent < 0) {
- negativeStringComponents.add(String.format("%s^%d", dimension, -exponent));
+ negativeStringComponents.add(String.format("%s^%d", objectToString.apply(object), -exponent));
}
}
diff --git a/src/org/unitConverter/newUnits/AbstractUnit.java b/src/org/unitConverter/newUnits/AbstractUnit.java
index 909ea8b..bc4608e 100644
--- a/src/org/unitConverter/newUnits/AbstractUnit.java
+++ b/src/org/unitConverter/newUnits/AbstractUnit.java
@@ -60,7 +60,7 @@ public abstract class AbstractUnit implements Unit {
}
@Override
- public ObjectProduct getDimension() {
+ public final ObjectProduct getDimension() {
if (this.dimension == null) {
final Map mapping = this.unitBase.exponentMap();
final Map dimensionMap = new HashMap<>();
@@ -73,4 +73,9 @@ public abstract class AbstractUnit implements Unit {
}
return this.dimension;
}
+
+ @Override
+ public String toString() {
+ return "Unit derived from base " + this.getBase().toString();
+ }
}
diff --git a/src/org/unitConverter/newUnits/BaseUnit.java b/src/org/unitConverter/newUnits/BaseUnit.java
index 69e8b8b..b7577ff 100644
--- a/src/org/unitConverter/newUnits/BaseUnit.java
+++ b/src/org/unitConverter/newUnits/BaseUnit.java
@@ -51,6 +51,17 @@ public final class BaseUnit implements Unit {
this.symbol = Objects.requireNonNull(symbol, "symbol must not be null.");
}
+ /**
+ * Returns a {@code LinearUnit} with this unit as a base and a conversion factor of 1. This operation must be done
+ * in order to allow units to be created with operations.
+ *
+ * @return this unit as a {@code LinearUnit}
+ * @since 2019-10-16
+ */
+ public LinearUnit asLinearUnit() {
+ return LinearUnit.valueOf(this.getBase(), 1);
+ }
+
@Override
public double convertFromBase(final double value) {
return value;
diff --git a/src/org/unitConverter/newUnits/LinearUnit.java b/src/org/unitConverter/newUnits/LinearUnit.java
new file mode 100644
index 0000000..5a589b7
--- /dev/null
+++ b/src/org/unitConverter/newUnits/LinearUnit.java
@@ -0,0 +1,261 @@
+/**
+ * Copyright (C) 2019 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.unitConverter.newUnits;
+
+import java.util.Objects;
+
+import org.unitConverter.math.DecimalComparison;
+import org.unitConverter.math.ObjectProduct;
+
+/**
+ * A unit that can be expressed as a product of its base and a number. For example, kilometres, inches and pounds.
+ *
+ * @author Adrien Hopkins
+ * @since 2019-10-16
+ */
+public final class LinearUnit extends AbstractUnit {
+ /**
+ * Gets a {@code LinearUnit} from a unit base and a conversion factor. In other words, gets the product of
+ * {@code unitBase} and {@code conversionFactor}, expressed as a {@code LinearUnit}.
+ *
+ * @param unitBase
+ * unit base to multiply by
+ * @param conversionFactor
+ * number to multiply base by
+ * @return product of base and conversion factor
+ * @since 2019-10-16
+ */
+ public static LinearUnit valueOf(final ObjectProduct unitBase, final double conversionFactor) {
+ return new LinearUnit(unitBase, conversionFactor);
+ }
+
+ /**
+ * The value of this unit as represented in its base form. Mathematically,
+ *
+ *
+ * this = conversionFactor * getBase()
+ *
+ *
+ * @since 2019-10-16
+ */
+ private final double conversionFactor;
+
+ /**
+ * Creates the {@code LinearUnit}.
+ *
+ * @param unitBase
+ * base of linear unit
+ * @param conversionFactor
+ * conversion factor between base and unit
+ * @since 2019-10-16
+ */
+ private LinearUnit(final ObjectProduct unitBase, final double conversionFactor) {
+ super(unitBase);
+ this.conversionFactor = conversionFactor;
+ }
+
+ @Override
+ public double convertFromBase(final double value) {
+ return value / this.getConversionFactor();
+ }
+
+ @Override
+ public double convertToBase(final double value) {
+ return value * this.getConversionFactor();
+ }
+
+ /**
+ * Divides this unit by a scalar.
+ *
+ * @param divisor
+ * scalar to divide by
+ * @return quotient
+ * @since 2018-12-23
+ * @since v0.1.0
+ */
+ public LinearUnit dividedBy(final double divisor) {
+ return valueOf(this.getBase(), this.getConversionFactor() / divisor);
+ }
+
+ /**
+ * Returns the quotient of this unit and another.
+ *
+ * @param divisor
+ * unit to divide by
+ * @return quotient of two units
+ * @throws NullPointerException
+ * if {@code divisor} is null
+ * @since 2018-12-22
+ * @since v0.1.0
+ */
+ public LinearUnit dividedBy(final LinearUnit divisor) {
+ Objects.requireNonNull(divisor, "other must not be null");
+
+ // divide the units
+ final ObjectProduct base = this.getBase().dividedBy(divisor.getBase());
+ return valueOf(base, this.getConversionFactor() / divisor.getConversionFactor());
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (!(obj instanceof LinearUnit))
+ return false;
+ final LinearUnit other = (LinearUnit) obj;
+ return Objects.equals(this.getBase(), other.getBase())
+ && DecimalComparison.equals(this.getConversionFactor(), other.getConversionFactor());
+ }
+
+ /**
+ * @return conversion factor
+ * @since 2019-10-16
+ */
+ public double getConversionFactor() {
+ return this.conversionFactor;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.getBase(), this.getConversionFactor());
+ }
+
+ /**
+ * @return whether this unit is equivalent to a {@code BaseUnit} (i.e. there is a {@code BaseUnit b} where
+ * {@code b.asLinearUnit().equals(this)} returns {@code true}.)
+ * @since 2019-10-16
+ */
+ public boolean isBase() {
+ return this.isCoherent() && this.getBase().isSingleObject();
+ }
+
+ /**
+ * @return whether this unit is coherent (i.e. has conversion factor 1)
+ * @since 2019-10-16
+ */
+ public boolean isCoherent() {
+ return this.getConversionFactor() == 1;
+ }
+
+ /**
+ * Returns the difference of this unit and another.
+ *
+ * Two units can be subtracted if they have the same base. If {@code subtrahend} does not meet this condition, an
+ * {@code IllegalArgumentException} will be thrown.
+ *
+ *
+ * @param subtrahend
+ * unit to subtract
+ * @return difference of units
+ * @throws IllegalArgumentException
+ * if {@code subtrahend} is not compatible for subtraction as described above
+ * @throws NullPointerException
+ * if {@code subtrahend} is null
+ * @since 2019-03-17
+ * @since v0.2.0
+ */
+ public LinearUnit minus(final LinearUnit subtrahendend) {
+ Objects.requireNonNull(subtrahendend, "addend must not be null.");
+
+ // reject subtrahends that cannot be added to this unit
+ if (!this.getBase().equals(subtrahendend.getBase()))
+ throw new IllegalArgumentException(
+ String.format("Incompatible units for subtraction \"%s\" and \"%s\".", this, subtrahendend));
+
+ // add the units
+ return valueOf(this.getBase(), this.getConversionFactor() - subtrahendend.getConversionFactor());
+ }
+
+ /**
+ * Returns the sum of this unit and another.
+ *
+ * Two units can be added if they have the same base. If {@code addend} does not meet this condition, an
+ * {@code IllegalArgumentException} will be thrown.
+ *
+ *
+ * @param addend
+ * unit to add
+ * @return sum of units
+ * @throws IllegalArgumentException
+ * if {@code addend} is not compatible for addition as described above
+ * @throws NullPointerException
+ * if {@code addend} is null
+ * @since 2019-03-17
+ * @since v0.2.0
+ */
+ public LinearUnit plus(final LinearUnit addend) {
+ Objects.requireNonNull(addend, "addend must not be null.");
+
+ // reject addends that cannot be added to this unit
+ if (!this.getBase().equals(addend.getBase()))
+ throw new IllegalArgumentException(
+ String.format("Incompatible units for addition \"%s\" and \"%s\".", this, addend));
+
+ // add the units
+ return valueOf(this.getBase(), this.getConversionFactor() + addend.getConversionFactor());
+ }
+
+ /**
+ * Multiplies this unit by a scalar.
+ *
+ * @param multiplier
+ * scalar to multiply by
+ * @return product
+ * @since 2018-12-23
+ * @since v0.1.0
+ */
+ public LinearUnit times(final double multiplier) {
+ return valueOf(this.getBase(), this.getConversionFactor() * multiplier);
+ }
+
+ /**
+ * Returns the product of this unit and another.
+ *
+ * @param multiplier
+ * unit to multiply by
+ * @return product of two units
+ * @throws NullPointerException
+ * if {@code multiplier} is null
+ * @since 2018-12-22
+ * @since v0.1.0
+ */
+ public LinearUnit times(final LinearUnit multiplier) {
+ Objects.requireNonNull(multiplier, "other must not be null");
+
+ // multiply the units
+ final ObjectProduct base = this.getBase().times(multiplier.getBase());
+ return valueOf(base, this.getConversionFactor() * multiplier.getConversionFactor());
+ }
+
+ /**
+ * Returns this unit but to an exponent.
+ *
+ * @param exponent
+ * exponent to exponentiate unit to
+ * @return exponentiated unit
+ * @since 2019-01-15
+ * @since v0.1.0
+ */
+ public LinearUnit toExponent(final int exponent) {
+ return valueOf(this.getBase().toExponent(exponent), Math.pow(this.conversionFactor, exponent));
+ }
+
+ // returns a definition of the unit
+ @Override
+ public String toString() {
+ return Double.toString(this.conversionFactor) + " * " + this.getBase().toString(BaseUnit::getSymbol);
+ }
+
+}
diff --git a/src/org/unitConverter/newUnits/Unit.java b/src/org/unitConverter/newUnits/Unit.java
index 339afde..b50115d 100644
--- a/src/org/unitConverter/newUnits/Unit.java
+++ b/src/org/unitConverter/newUnits/Unit.java
@@ -23,6 +23,8 @@ import org.unitConverter.dimension.BaseDimension;
import org.unitConverter.math.ObjectProduct;
/**
+ * A unit described in terms of base units.
+ *
* @author Adrien Hopkins
* @since 2019-10-16
*/
--
cgit v1.2.3
From 9eba2e72ecba1f33c73d358eb509f0a0816aa810 Mon Sep 17 00:00:00 2001
From: Adrien Hopkins
Date: Wed, 16 Oct 2019 14:41:44 -0400
Subject: Added unit prefixes.
---
src/org/unitConverter/math/DecimalComparison.java | 67 +++++++++++
src/org/unitConverter/newUnits/LinearUnit.java | 29 ++++-
src/org/unitConverter/newUnits/UnitPrefix.java | 134 ++++++++++++++++++++++
3 files changed, 229 insertions(+), 1 deletion(-)
create mode 100644 src/org/unitConverter/newUnits/UnitPrefix.java
diff --git a/src/org/unitConverter/math/DecimalComparison.java b/src/org/unitConverter/math/DecimalComparison.java
index 7cdbe5b..859e8da 100644
--- a/src/org/unitConverter/math/DecimalComparison.java
+++ b/src/org/unitConverter/math/DecimalComparison.java
@@ -16,6 +16,8 @@
*/
package org.unitConverter.math;
+import java.math.BigDecimal;
+
/**
* A class that contains methods to compare float and double values.
*
@@ -44,6 +46,18 @@ public final class DecimalComparison {
/**
* Tests for equality of double values using {@link #DOUBLE_EPSILON}.
+ *
+ * WARNING: this method is not technically transitive. If a and b are off by slightly less than
+ * {@code epsilon * max(abs(a), abs(b))}, and b and c are off by slightly less than
+ * {@code epsilon * max(abs(b), abs(c))}, then equals(a, b) and equals(b, c) will both return true, but equals(a, c)
+ * will return false. However, this situation is very unlikely to ever happen in a real programming situation.
+ *
+ * If this does become a concern, some ways to solve this problem:
+ *
+ *
Raise the value of epsilon using {@link #equals(double, double, double)} (this does not make a violation of
+ * transitivity impossible, it just significantly reduces the chances of it happening)
+ *
Use {@link BigDecimal} instead of {@code double} (this will make a violation of transitivity 100% impossible)
+ *
*
* @param a
* first value to test
@@ -52,6 +66,7 @@ public final class DecimalComparison {
* @return whether they are equal
* @since 2019-03-18
* @since v0.2.0
+ * @see #hashCode(double)
*/
public static final boolean equals(final double a, final double b) {
return DecimalComparison.equals(a, b, DOUBLE_EPSILON);
@@ -60,6 +75,19 @@ public final class DecimalComparison {
/**
* Tests for double equality using a custom epsilon value.
*
+ *
+ * WARNING: this method is not technically transitive. If a and b are off by slightly less than
+ * {@code epsilon * max(abs(a), abs(b))}, and b and c are off by slightly less than
+ * {@code epsilon * max(abs(b), abs(c))}, then equals(a, b) and equals(b, c) will both return true, but equals(a, c)
+ * will return false. However, this situation is very unlikely to ever happen in a real programming situation.
+ *
+ * If this does become a concern, some ways to solve this problem:
+ *
+ *
Raise the value of epsilon (this does not make a violation of transitivity impossible, it just significantly
+ * reduces the chances of it happening)
+ *
Use {@link BigDecimal} instead of {@code double} (this will make a violation of transitivity 100% impossible)
+ *
+ *
* @param a
* first value to test
* @param b
@@ -77,6 +105,19 @@ public final class DecimalComparison {
/**
* Tests for equality of float values using {@link #FLOAT_EPSILON}.
*
+ *
+ * WARNING: this method is not technically transitive. If a and b are off by slightly less than
+ * {@code epsilon * max(abs(a), abs(b))}, and b and c are off by slightly less than
+ * {@code epsilon * max(abs(b), abs(c))}, then equals(a, b) and equals(b, c) will both return true, but equals(a, c)
+ * will return false. However, this situation is very unlikely to ever happen in a real programming situation.
+ *
+ * If this does become a concern, some ways to solve this problem:
+ *
+ *
Raise the value of epsilon using {@link #equals(float, float, float)} (this does not make a violation of
+ * transitivity impossible, it just significantly reduces the chances of it happening)
+ *
Use {@link BigDecimal} instead of {@code float} (this will make a violation of transitivity 100% impossible)
+ *
+ *
* @param a
* first value to test
* @param b
@@ -92,6 +133,19 @@ public final class DecimalComparison {
/**
* Tests for float equality using a custom epsilon value.
*
+ *
+ * WARNING: this method is not technically transitive. If a and b are off by slightly less than
+ * {@code epsilon * max(abs(a), abs(b))}, and b and c are off by slightly less than
+ * {@code epsilon * max(abs(b), abs(c))}, then equals(a, b) and equals(b, c) will both return true, but equals(a, c)
+ * will return false. However, this situation is very unlikely to ever happen in a real programming situation.
+ *
+ * If this does become a concern, some ways to solve this problem:
+ *
+ *
Raise the value of epsilon (this does not make a violation of transitivity impossible, it just significantly
+ * reduces the chances of it happening)
+ *
Use {@link BigDecimal} instead of {@code float} (this will make a violation of transitivity 100% impossible)
+ *
+ *
* @param a
* first value to test
* @param b
@@ -106,6 +160,19 @@ public final class DecimalComparison {
return Math.abs(a - b) <= epsilon * Math.max(Math.abs(a), Math.abs(b));
}
+ /**
+ * Takes the hash code of doubles. Values that are equal according to {@link #equals(double, double)} will have the
+ * same hash code.
+ *
+ * @param d
+ * double to hash
+ * @return hash code of double
+ * @since 2019-10-16
+ */
+ public static final int hash(final double d) {
+ return Float.hashCode((float) d);
+ }
+
// You may NOT get any DecimalComparison instances
private DecimalComparison() {
throw new AssertionError();
diff --git a/src/org/unitConverter/newUnits/LinearUnit.java b/src/org/unitConverter/newUnits/LinearUnit.java
index 5a589b7..7600dad 100644
--- a/src/org/unitConverter/newUnits/LinearUnit.java
+++ b/src/org/unitConverter/newUnits/LinearUnit.java
@@ -28,6 +28,21 @@ import org.unitConverter.math.ObjectProduct;
* @since 2019-10-16
*/
public final class LinearUnit extends AbstractUnit {
+ /**
+ * Gets a {@code LinearUnit} from a unit and a value. For example, converts '59 °F' to a linear unit with the value
+ * of '288.15 K'
+ *
+ * @param unit
+ * unit to convert
+ * @param value
+ * value to convert
+ * @return value expressed as a {@code LinearUnit}
+ * @since 2019-10-16
+ */
+ public static LinearUnit fromValue(final Unit unit, final double value) {
+ return new LinearUnit(unit.getBase(), unit.convertToBase(value));
+ }
+
/**
* Gets a {@code LinearUnit} from a unit base and a conversion factor. In other words, gets the product of
* {@code unitBase} and {@code conversionFactor}, expressed as a {@code LinearUnit}.
@@ -129,7 +144,7 @@ public final class LinearUnit extends AbstractUnit {
@Override
public int hashCode() {
- return Objects.hash(this.getBase(), this.getConversionFactor());
+ return 31 * this.getBase().hashCode() + DecimalComparison.hash(this.getConversionFactor());
}
/**
@@ -258,4 +273,16 @@ public final class LinearUnit extends AbstractUnit {
return Double.toString(this.conversionFactor) + " * " + this.getBase().toString(BaseUnit::getSymbol);
}
+ /**
+ * Returns the result of applying {@code prefix} to this unit.
+ *
+ * @param prefix
+ * prefix to apply
+ * @return unit with prefix
+ * @since 2019-03-18
+ * @since v0.2.0
+ */
+ public LinearUnit withPrefix(final UnitPrefix prefix) {
+ return this.times(prefix.getMultiplier());
+ }
}
diff --git a/src/org/unitConverter/newUnits/UnitPrefix.java b/src/org/unitConverter/newUnits/UnitPrefix.java
new file mode 100644
index 0000000..905ca19
--- /dev/null
+++ b/src/org/unitConverter/newUnits/UnitPrefix.java
@@ -0,0 +1,134 @@
+/**
+ * Copyright (C) 2019 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.unitConverter.newUnits;
+
+import org.unitConverter.math.DecimalComparison;
+
+/**
+ * A prefix that can be applied to a {@code LinearUnit} to multiply it by some value
+ *
+ * @author Adrien Hopkins
+ * @since 2019-10-16
+ */
+public final class UnitPrefix {
+ /**
+ * The number that this prefix multiplies units by
+ *
+ * @since 2019-10-16
+ */
+ private final double multiplier;
+
+ /**
+ * Creates the {@code DefaultUnitPrefix}.
+ *
+ * @param multiplier
+ * @since 2019-01-14
+ * @since v0.2.0
+ */
+ private UnitPrefix(final double multiplier) {
+ this.multiplier = multiplier;
+ }
+
+ /**
+ * Divides this prefix by a scalar
+ *
+ * @param divisor
+ * number to divide by
+ * @return quotient of prefix and scalar
+ * @since 2019-10-16
+ */
+ public UnitPrefix dividedBy(final double divisor) {
+ return new UnitPrefix(this.getMultiplier() / divisor);
+ }
+
+ /**
+ * Divides this prefix by {@code other}.
+ *
+ * @param other
+ * prefix to divide by
+ * @return quotient of prefixes
+ * @since 2019-04-13
+ * @since v0.2.0
+ */
+ public UnitPrefix dividedBy(final UnitPrefix other) {
+ return new UnitPrefix(this.getMultiplier() / other.getMultiplier());
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (!(obj instanceof UnitPrefix))
+ return false;
+ final UnitPrefix other = (UnitPrefix) obj;
+ return DecimalComparison.equals(this.getMultiplier(), other.getMultiplier());
+ }
+
+ public double getMultiplier() {
+ return this.multiplier;
+ }
+
+ @Override
+ public int hashCode() {
+ return DecimalComparison.hash(this.getMultiplier());
+ }
+
+ /**
+ * Multiplies this prefix by a scalar
+ *
+ * @param multiplicand
+ * number to multiply by
+ * @return product of prefix and scalar
+ * @since 2019-10-16
+ */
+ public UnitPrefix times(final double multiplicand) {
+ return new UnitPrefix(this.getMultiplier() * multiplicand);
+ }
+
+ /**
+ * Multiplies this prefix by {@code other}.
+ *
+ * @param other
+ * prefix to multiply by
+ * @return product of prefixes
+ * @since 2019-04-13
+ * @since v0.2.0
+ */
+ public UnitPrefix times(final UnitPrefix other) {
+ return new UnitPrefix(this.getMultiplier() * other.getMultiplier());
+ }
+
+ /**
+ * Raises this prefix to an exponent.
+ *
+ * @param exponent
+ * exponent to raise to
+ * @return result of exponentiation.
+ * @since 2019-04-13
+ * @since v0.2.0
+ */
+ public UnitPrefix toExponent(final double exponent) {
+ return new UnitPrefix(Math.pow(this.getMultiplier(), exponent));
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Unit prefix equal to %s", this.multiplier);
+ }
+}
--
cgit v1.2.3
From abe715a30844537693ae186308adcab62c66f121 Mon Sep 17 00:00:00 2001
From: Adrien Hopkins
Date: Wed, 16 Oct 2019 15:35:30 -0400
Subject: Made Unit an abstract class.
The abstract unit conversion methods are now protected.
---
src/org/unitConverter/newUnits/AbstractUnit.java | 81 --------------------
src/org/unitConverter/newUnits/BaseUnit.java | 14 +---
src/org/unitConverter/newUnits/FunctionalUnit.java | 2 +-
src/org/unitConverter/newUnits/LinearUnit.java | 8 +-
src/org/unitConverter/newUnits/Unit.java | 88 ++++++++++++++++++----
5 files changed, 79 insertions(+), 114 deletions(-)
delete mode 100644 src/org/unitConverter/newUnits/AbstractUnit.java
diff --git a/src/org/unitConverter/newUnits/AbstractUnit.java b/src/org/unitConverter/newUnits/AbstractUnit.java
deleted file mode 100644
index bc4608e..0000000
--- a/src/org/unitConverter/newUnits/AbstractUnit.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-package org.unitConverter.newUnits;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
-import org.unitConverter.dimension.BaseDimension;
-import org.unitConverter.math.ObjectProduct;
-
-/**
- * @author Adrien Hopkins
- * @since 2019-10-16
- */
-public abstract class AbstractUnit implements Unit {
- /**
- * The combination of units that this unit is based on.
- */
- private final ObjectProduct unitBase;
-
- /**
- * Cache storing the result of getDimension()
- */
- private transient ObjectProduct dimension = null;
-
- /**
- * Creates the {@code AbstractUnit}.
- *
- * @param unitBase
- * @since 2019-10-16
- * @throws NullPointerException
- * if unitBase is null
- */
- public AbstractUnit(final ObjectProduct unitBase) {
- this.unitBase = Objects.requireNonNull(unitBase, "unitBase must not be null.");
- }
-
- /**
- * @return unitBase
- * @since 2019-10-16
- */
- @Override
- public final ObjectProduct getBase() {
- return this.unitBase;
- }
-
- @Override
- public final ObjectProduct getDimension() {
- 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;
- }
-
- @Override
- public String toString() {
- return "Unit derived from base " + this.getBase().toString();
- }
-}
diff --git a/src/org/unitConverter/newUnits/BaseUnit.java b/src/org/unitConverter/newUnits/BaseUnit.java
index b7577ff..2c4d748 100644
--- a/src/org/unitConverter/newUnits/BaseUnit.java
+++ b/src/org/unitConverter/newUnits/BaseUnit.java
@@ -19,7 +19,6 @@ package org.unitConverter.newUnits;
import java.util.Objects;
import org.unitConverter.dimension.BaseDimension;
-import org.unitConverter.math.ObjectProduct;
/**
* A unit that other units are defined by.
@@ -27,7 +26,7 @@ import org.unitConverter.math.ObjectProduct;
* @author Adrien Hopkins
* @since 2019-10-16
*/
-public final class BaseUnit implements Unit {
+public final class BaseUnit extends Unit {
private final BaseDimension dimension;
private final String name;
private final String symbol;
@@ -46,6 +45,7 @@ public final class BaseUnit implements Unit {
* @since 2019-10-16
*/
private BaseUnit(final BaseDimension dimension, final String name, final String symbol) {
+ super();
this.dimension = Objects.requireNonNull(dimension, "dimension must not be null.");
this.name = Objects.requireNonNull(name, "name must not be null.");
this.symbol = Objects.requireNonNull(symbol, "symbol must not be null.");
@@ -72,11 +72,6 @@ public final class BaseUnit implements Unit {
return value;
}
- @Override
- public ObjectProduct getBase() {
- return ObjectProduct.oneOf(this);
- }
-
/**
* @return dimension
* @since 2019-10-16
@@ -85,11 +80,6 @@ public final class BaseUnit implements Unit {
return this.dimension;
}
- @Override
- public ObjectProduct getDimension() {
- return ObjectProduct.oneOf(this.getBaseDimension());
- }
-
/**
* @return name
* @since 2019-10-16
diff --git a/src/org/unitConverter/newUnits/FunctionalUnit.java b/src/org/unitConverter/newUnits/FunctionalUnit.java
index 99ff833..6bff3e8 100644
--- a/src/org/unitConverter/newUnits/FunctionalUnit.java
+++ b/src/org/unitConverter/newUnits/FunctionalUnit.java
@@ -27,7 +27,7 @@ import org.unitConverter.math.ObjectProduct;
* @author Adrien Hopkins
* @since 2019-05-22
*/
-final class FunctionalUnit extends AbstractUnit {
+final class FunctionalUnit extends Unit {
/**
* Returns a unit from its base and the functions it uses to convert to and from its base.
*
diff --git a/src/org/unitConverter/newUnits/LinearUnit.java b/src/org/unitConverter/newUnits/LinearUnit.java
index 7600dad..c8c610e 100644
--- a/src/org/unitConverter/newUnits/LinearUnit.java
+++ b/src/org/unitConverter/newUnits/LinearUnit.java
@@ -27,7 +27,7 @@ import org.unitConverter.math.ObjectProduct;
* @author Adrien Hopkins
* @since 2019-10-16
*/
-public final class LinearUnit extends AbstractUnit {
+public final class LinearUnit extends Unit {
/**
* Gets a {@code LinearUnit} from a unit and a value. For example, converts '59 °F' to a linear unit with the value
* of '288.15 K'
@@ -39,7 +39,7 @@ public final class LinearUnit extends AbstractUnit {
* @return value expressed as a {@code LinearUnit}
* @since 2019-10-16
*/
- public static LinearUnit fromValue(final Unit unit, final double value) {
+ public static LinearUnit fromUnitValue(final Unit unit, final double value) {
return new LinearUnit(unit.getBase(), unit.convertToBase(value));
}
@@ -84,12 +84,12 @@ public final class LinearUnit extends AbstractUnit {
}
@Override
- public double convertFromBase(final double value) {
+ protected double convertFromBase(final double value) {
return value / this.getConversionFactor();
}
@Override
- public double convertToBase(final double value) {
+ protected double convertToBase(final double value) {
return value * this.getConversionFactor();
}
diff --git a/src/org/unitConverter/newUnits/Unit.java b/src/org/unitConverter/newUnits/Unit.java
index b50115d..feeb25e 100644
--- a/src/org/unitConverter/newUnits/Unit.java
+++ b/src/org/unitConverter/newUnits/Unit.java
@@ -19,16 +19,15 @@ package org.unitConverter.newUnits;
import java.util.Objects;
import java.util.function.DoubleUnaryOperator;
-import org.unitConverter.dimension.BaseDimension;
import org.unitConverter.math.ObjectProduct;
/**
- * A unit described in terms of base units.
+ * A unit that is composed of base units.
*
* @author Adrien Hopkins
* @since 2019-10-16
*/
-public interface Unit {
+public abstract class Unit {
/**
* Returns a unit from its base and the functions it uses to convert to and from its base.
*
@@ -56,6 +55,44 @@ public interface Unit {
return FunctionalUnit.valueOf(base, converterFrom, converterTo);
}
+ /**
+ * The combination of units that this unit is based on.
+ *
+ * @since 2019-10-16
+ */
+ private final ObjectProduct unitBase;
+
+ // /**
+ // * Cache storing the result of getDimension()
+ // *
+ // * @since 2019-10-16
+ // */
+ // private transient ObjectProduct dimension = null;
+
+ /**
+ * A constructor that constructs {@code BaseUnit} instances.
+ *
+ * @since 2019-10-16
+ */
+ Unit() {
+ if (this instanceof BaseUnit) {
+ this.unitBase = ObjectProduct.oneOf((BaseUnit) this);
+ } else
+ throw new AssertionError();
+ }
+
+ /**
+ * Creates the {@code AbstractUnit}.
+ *
+ * @param unitBase
+ * @since 2019-10-16
+ * @throws NullPointerException
+ * if unitBase is null
+ */
+ protected Unit(final ObjectProduct unitBase) {
+ this.unitBase = Objects.requireNonNull(unitBase, "unitBase must not be null.");
+ }
+
/**
* Checks if a value expressed in this unit can be converted to a value expressed in {@code other}
*
@@ -67,7 +104,7 @@ public interface Unit {
* @throws NullPointerException
* if other is null
*/
- default boolean canConvertTo(final Unit other) {
+ public final boolean canConvertTo(final Unit other) {
Objects.requireNonNull(other, "other must not be null.");
return Objects.equals(this.getBase(), other.getBase());
}
@@ -88,7 +125,7 @@ public interface Unit {
* @since 2018-12-22
* @since v0.1.0
*/
- double convertFromBase(double value);
+ protected abstract double convertFromBase(double value);
/**
* Converts a value expressed in this unit to a value expressed in {@code other}.
@@ -101,11 +138,11 @@ public interface Unit {
* @since 2019-05-22
* @throws IllegalArgumentException
* if {@code other} is incompatible for conversion with this unit (as tested by
- * {@link Unit#canConvertTo}).
+ * {@link IUnit#canConvertTo}).
* @throws NullPointerException
* if other is null
*/
- default double convertTo(final Unit other, final double value) {
+ 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));
@@ -129,19 +166,38 @@ public interface Unit {
* @since 2018-12-22
* @since v0.1.0
*/
- double convertToBase(double value);
+ protected abstract double convertToBase(double value);
/**
* @return combination of units that this unit is based on
* @since 2018-12-22
* @since v0.1.0
*/
- ObjectProduct getBase();
+ public final ObjectProduct getBase() {
+ return this.unitBase;
+ }
- /**
- * @return dimension measured by this unit
- * @since 2018-12-22
- * @since v0.1.0
- */
- ObjectProduct getDimension();
-}
\ No newline at end of file
+ // /**
+ // * @return dimension measured by this unit
+ // * @since 2018-12-22
+ // * @since v0.1.0
+ // */
+ // private final ObjectProduct getDimension() {
+ // 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;
+ // }
+
+ @Override
+ public String toString() {
+ return "Unit derived from base " + this.getBase().toString();
+ }
+}
--
cgit v1.2.3
From df06497dc4d7359de006c5885074f3356dbb81de Mon Sep 17 00:00:00 2001
From: Adrien Hopkins
Date: Wed, 16 Oct 2019 16:50:04 -0400
Subject: Added new constructors for BaseUnit and BaseDimension.
---
src/org/unitConverter/newUnits/BaseUnit.java | 18 ++++++++--
src/org/unitConverter/newUnits/Unit.java | 50 +++++++++++++-------------
src/org/unitConverter/newUnits/UnitPrefix.java | 22 +++++++++---
3 files changed, 59 insertions(+), 31 deletions(-)
diff --git a/src/org/unitConverter/newUnits/BaseUnit.java b/src/org/unitConverter/newUnits/BaseUnit.java
index 2c4d748..6a57faa 100644
--- a/src/org/unitConverter/newUnits/BaseUnit.java
+++ b/src/org/unitConverter/newUnits/BaseUnit.java
@@ -18,8 +18,6 @@ package org.unitConverter.newUnits;
import java.util.Objects;
-import org.unitConverter.dimension.BaseDimension;
-
/**
* A unit that other units are defined by.
*
@@ -27,6 +25,22 @@ import org.unitConverter.dimension.BaseDimension;
* @since 2019-10-16
*/
public final class BaseUnit extends Unit {
+ /**
+ * Gets a base unit from the dimension it measures, its name and its symbol.
+ *
+ * @param dimension
+ * dimension measured by this unit
+ * @param name
+ * name of unit
+ * @param symbol
+ * symbol of unit
+ * @return base unit
+ * @since 2019-10-16
+ */
+ public static BaseUnit valueOf(final BaseDimension dimension, final String name, final String symbol) {
+ return new BaseUnit(dimension, name, symbol);
+ }
+
private final BaseDimension dimension;
private final String name;
private final String symbol;
diff --git a/src/org/unitConverter/newUnits/Unit.java b/src/org/unitConverter/newUnits/Unit.java
index feeb25e..339ab95 100644
--- a/src/org/unitConverter/newUnits/Unit.java
+++ b/src/org/unitConverter/newUnits/Unit.java
@@ -16,6 +16,8 @@
*/
package org.unitConverter.newUnits;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
import java.util.function.DoubleUnaryOperator;
@@ -62,12 +64,12 @@ public abstract class Unit {
*/
private final ObjectProduct unitBase;
- // /**
- // * Cache storing the result of getDimension()
- // *
- // * @since 2019-10-16
- // */
- // private transient ObjectProduct dimension = null;
+ /**
+ * Cache storing the result of getDimension()
+ *
+ * @since 2019-10-16
+ */
+ private transient ObjectProduct dimension = null;
/**
* A constructor that constructs {@code BaseUnit} instances.
@@ -177,24 +179,24 @@ public abstract class Unit {
return this.unitBase;
}
- // /**
- // * @return dimension measured by this unit
- // * @since 2018-12-22
- // * @since v0.1.0
- // */
- // private final ObjectProduct getDimension() {
- // 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 dimension measured by this unit
+ * @since 2018-12-22
+ * @since v0.1.0
+ */
+ public final ObjectProduct getDimension() {
+ 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;
+ }
@Override
public String toString() {
diff --git a/src/org/unitConverter/newUnits/UnitPrefix.java b/src/org/unitConverter/newUnits/UnitPrefix.java
index 905ca19..5608098 100644
--- a/src/org/unitConverter/newUnits/UnitPrefix.java
+++ b/src/org/unitConverter/newUnits/UnitPrefix.java
@@ -25,6 +25,18 @@ import org.unitConverter.math.DecimalComparison;
* @since 2019-10-16
*/
public final class UnitPrefix {
+ /**
+ * Gets a {@code UnitPrefix} from a multiplier
+ *
+ * @param multiplier
+ * multiplier of prefix
+ * @return prefix
+ * @since 2019-10-16
+ */
+ public static UnitPrefix valueOf(final double multiplier) {
+ return new UnitPrefix(multiplier);
+ }
+
/**
* The number that this prefix multiplies units by
*
@@ -52,7 +64,7 @@ public final class UnitPrefix {
* @since 2019-10-16
*/
public UnitPrefix dividedBy(final double divisor) {
- return new UnitPrefix(this.getMultiplier() / divisor);
+ return valueOf(this.getMultiplier() / divisor);
}
/**
@@ -65,7 +77,7 @@ public final class UnitPrefix {
* @since v0.2.0
*/
public UnitPrefix dividedBy(final UnitPrefix other) {
- return new UnitPrefix(this.getMultiplier() / other.getMultiplier());
+ return valueOf(this.getMultiplier() / other.getMultiplier());
}
@Override
@@ -98,7 +110,7 @@ public final class UnitPrefix {
* @since 2019-10-16
*/
public UnitPrefix times(final double multiplicand) {
- return new UnitPrefix(this.getMultiplier() * multiplicand);
+ return valueOf(this.getMultiplier() * multiplicand);
}
/**
@@ -111,7 +123,7 @@ public final class UnitPrefix {
* @since v0.2.0
*/
public UnitPrefix times(final UnitPrefix other) {
- return new UnitPrefix(this.getMultiplier() * other.getMultiplier());
+ return valueOf(this.getMultiplier() * other.getMultiplier());
}
/**
@@ -124,7 +136,7 @@ public final class UnitPrefix {
* @since v0.2.0
*/
public UnitPrefix toExponent(final double exponent) {
- return new UnitPrefix(Math.pow(this.getMultiplier(), exponent));
+ return valueOf(Math.pow(this.getMultiplier(), exponent));
}
@Override
--
cgit v1.2.3
From 2ff6c4ea25beeab58239ddf576fb89254ba98630 Mon Sep 17 00:00:00 2001
From: Adrien Hopkins
Date: Wed, 16 Oct 2019 17:17:41 -0400
Subject: Added an 'SI' class with all units, dimensions and prefixes in SI.
---
src/org/unitConverter/math/StandardDimensions.java | 86 --------
src/org/unitConverter/newUnits/BaseDimension.java | 81 ++++++++
src/org/unitConverter/newUnits/SI.java | 228 +++++++++++++++++++++
3 files changed, 309 insertions(+), 86 deletions(-)
delete mode 100644 src/org/unitConverter/math/StandardDimensions.java
create mode 100644 src/org/unitConverter/newUnits/BaseDimension.java
create mode 100644 src/org/unitConverter/newUnits/SI.java
diff --git a/src/org/unitConverter/math/StandardDimensions.java b/src/org/unitConverter/math/StandardDimensions.java
deleted file mode 100644
index db5efc3..0000000
--- a/src/org/unitConverter/math/StandardDimensions.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/**
- * Copyright (C) 2018 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-package org.unitConverter.math;
-
-import org.unitConverter.dimension.BaseDimension;
-import org.unitConverter.dimension.OtherBaseDimension;
-import org.unitConverter.dimension.SIBaseDimension;
-
-/**
- * All of the dimensions that are used by the SI. (Test data for the ObjectProductTest)
- *
- * @author Adrien Hopkins
- * @since 2018-12-11
- * @since v0.1.0
- */
-final class StandardDimensions {
- // base dimensions
- public static final ObjectProduct EMPTY = ObjectProduct.empty();
- public static final ObjectProduct LENGTH = ObjectProduct.oneOf(SIBaseDimension.LENGTH);
- public static final ObjectProduct MASS = ObjectProduct.oneOf(SIBaseDimension.MASS);
- public static final ObjectProduct TIME = ObjectProduct.oneOf(SIBaseDimension.TIME);
- public static final ObjectProduct ELECTRIC_CURRENT = ObjectProduct
- .oneOf(SIBaseDimension.ELECTRIC_CURRENT);
- public static final ObjectProduct TEMPERATURE = ObjectProduct.oneOf(SIBaseDimension.TEMPERATURE);
- public static final ObjectProduct QUANTITY = ObjectProduct.oneOf(SIBaseDimension.QUANTITY);
- public static final ObjectProduct LUMINOUS_INTENSITY = ObjectProduct
- .oneOf(SIBaseDimension.LUMINOUS_INTENSITY);
- public static final ObjectProduct INFORMATION = ObjectProduct.oneOf(OtherBaseDimension.INFORMATION);
- public static final ObjectProduct CURRENCY = ObjectProduct.oneOf(OtherBaseDimension.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);
- // 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);
-
- // You may NOT get StandardDimensions instances!
- private StandardDimensions() {
- throw new AssertionError();
- }
-}
diff --git a/src/org/unitConverter/newUnits/BaseDimension.java b/src/org/unitConverter/newUnits/BaseDimension.java
new file mode 100644
index 0000000..a1cde46
--- /dev/null
+++ b/src/org/unitConverter/newUnits/BaseDimension.java
@@ -0,0 +1,81 @@
+/**
+ * Copyright (C) 2019 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.unitConverter.newUnits;
+
+import java.util.Objects;
+
+/**
+ * A dimension that defines a {@code BaseUnit}
+ *
+ * @author Adrien Hopkins
+ * @since 2019-10-16
+ */
+public final class BaseDimension {
+ /**
+ * Gets a {@code BaseDimension} with the provided name and symbol.
+ *
+ * @param name
+ * name of dimension
+ * @param symbol
+ * symbol used for dimension
+ * @return dimension
+ * @since 2019-10-16
+ */
+ public static BaseDimension valueOf(final String name, final String symbol) {
+ return new BaseDimension(name, symbol);
+ }
+
+ private final String name;
+ private final String symbol;
+
+ /**
+ * Creates the {@code BaseDimension}.
+ *
+ * @param name
+ * name of unit
+ * @param symbol
+ * symbol of unit
+ * @throws NullPointerException
+ * if any argument is null
+ * @since 2019-10-16
+ */
+ private BaseDimension(final String name, final String symbol) {
+ this.name = Objects.requireNonNull(name, "name must not be null.");
+ this.symbol = Objects.requireNonNull(symbol, "symbol must not be null.");
+ }
+
+ /**
+ * @return name
+ * @since 2019-10-16
+ */
+ public final String getName() {
+ return this.name;
+ }
+
+ /**
+ * @return symbol
+ * @since 2019-10-16
+ */
+ public final String getSymbol() {
+ return this.symbol;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s (%s)", this.getName(), this.getSymbol());
+ }
+}
diff --git a/src/org/unitConverter/newUnits/SI.java b/src/org/unitConverter/newUnits/SI.java
new file mode 100644
index 0000000..b7a117a
--- /dev/null
+++ b/src/org/unitConverter/newUnits/SI.java
@@ -0,0 +1,228 @@
+/**
+ * Copyright (C) 2018 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.unitConverter.newUnits;
+
+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.
+ *
+ *
+ * This class does not include prefixed units. To obtain prefixed units, use {@link LinearUnit#withPrefix}:
+ *
+ *
+ * LinearUnit KILOMETRE = SI.METRE.withPrefix(SI.KILO);
+ *
+ *
+ *
+ * @author Adrien Hopkins
+ * @since 2019-10-16
+ */
+public final class SI {
+ /// dimensions used by SI units
+ // base dimensions, as BaseDimensions
+ public static final class BaseDimensions {
+ public static final BaseDimension LENGTH = BaseDimension.valueOf("Length", "L");
+ public static final BaseDimension MASS = BaseDimension.valueOf("Mass", "M");
+ public static final BaseDimension TIME = BaseDimension.valueOf("Time", "T");
+ public static final BaseDimension ELECTRIC_CURRENT = BaseDimension.valueOf("Electric Current", "I");
+ public static final BaseDimension TEMPERATURE = BaseDimension.valueOf("Temperature", "\u0398"); // theta symbol
+ public static final BaseDimension QUANTITY = BaseDimension.valueOf("Quantity", "N");
+ public static final BaseDimension LUMINOUS_INTENSITY = BaseDimension.valueOf("Luminous Intensity", "J");
+ public static final BaseDimension INFORMATION = BaseDimension.valueOf("Information", "Info"); // non-SI
+ public static final BaseDimension CURRENCY = BaseDimension.valueOf("Currency", "$$"); // non-SI
+
+ // You may NOT get SI.BaseDimensions instances!
+ private BaseDimensions() {
+ throw new AssertionError();
+ }
+ }
+
+ /// base units of the SI
+ // suppressing warnings since these are the same object, but in a different form (class)
+ @SuppressWarnings("hiding")
+ public static final class BaseUnits {
+ public static final BaseUnit METRE = BaseUnit.valueOf(BaseDimensions.LENGTH, "metre", "m");
+ public static final BaseUnit KILOGRAM = BaseUnit.valueOf(BaseDimensions.MASS, "kilogram", "kg");
+ public static final BaseUnit SECOND = BaseUnit.valueOf(BaseDimensions.TIME, "second", "s");
+ public static final BaseUnit AMPERE = BaseUnit.valueOf(BaseDimensions.ELECTRIC_CURRENT, "ampere", "A");
+ public static final BaseUnit KELVIN = BaseUnit.valueOf(BaseDimensions.TEMPERATURE, "kelvin", "K");
+ public static final BaseUnit MOLE = BaseUnit.valueOf(BaseDimensions.QUANTITY, "mole", "mol");
+ public static final BaseUnit CANDELA = BaseUnit.valueOf(BaseDimensions.LUMINOUS_INTENSITY, "candela", "cd");
+ public static final BaseUnit BIT = BaseUnit.valueOf(BaseDimensions.INFORMATION, "bit", "b");
+ public static final BaseUnit DOLLAR = BaseUnit.valueOf(BaseDimensions.CURRENCY, "dollar", "$");
+
+ // You may NOT get SI.BaseUnits instances!
+ private BaseUnits() {
+ throw new AssertionError();
+ }
+ }
+
+ // 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 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 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);
+ // 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