summaryrefslogtreecommitdiff
path: root/src/org
diff options
context:
space:
mode:
authorAdrien Hopkins <ahopk127@my.yorku.ca>2021-03-27 16:36:39 -0500
committerAdrien Hopkins <ahopk127@my.yorku.ca>2021-03-27 16:36:39 -0500
commit2eee97c9e64dca79fc6b1614b304b398d25a7f4b (patch)
treef14d4237a3ce71d21d02b29719a5fb6595fe4091 /src/org
parentfe4135a68cfed92ef336eec663e9c42c2c97dcbc (diff)
Added automatic building with Gradle
Diffstat (limited to 'src/org')
-rw-r--r--src/org/unitConverter/converterGUI/DefaultPrefixRepetitionRule.java95
-rw-r--r--src/org/unitConverter/converterGUI/DelegateListModel.java242
-rw-r--r--src/org/unitConverter/converterGUI/FilterComparator.java129
-rw-r--r--src/org/unitConverter/converterGUI/GridBagBuilder.java479
-rw-r--r--src/org/unitConverter/converterGUI/MutablePredicate.java70
-rw-r--r--src/org/unitConverter/converterGUI/SearchBoxList.java307
-rw-r--r--src/org/unitConverter/converterGUI/UnitConverterGUI.java1424
-rw-r--r--src/org/unitConverter/converterGUI/package-info.java24
-rw-r--r--src/org/unitConverter/math/ConditionalExistenceCollections.java468
-rw-r--r--src/org/unitConverter/math/ConditionalExistenceCollectionsTest.java159
-rw-r--r--src/org/unitConverter/math/DecimalComparison.java256
-rw-r--r--src/org/unitConverter/math/ExpressionParser.java708
-rw-r--r--src/org/unitConverter/math/ExpressionParserTest.java52
-rw-r--r--src/org/unitConverter/math/ObjectProduct.java284
-rw-r--r--src/org/unitConverter/math/ObjectProductTest.java78
-rw-r--r--src/org/unitConverter/math/UncertainDouble.java419
-rw-r--r--src/org/unitConverter/math/package-info.java24
-rw-r--r--src/org/unitConverter/package-info.java24
-rw-r--r--src/org/unitConverter/unit/BaseDimension.java87
-rw-r--r--src/org/unitConverter/unit/BaseUnit.java133
-rw-r--r--src/org/unitConverter/unit/BritishImperial.java116
-rw-r--r--src/org/unitConverter/unit/FunctionalUnit.java109
-rw-r--r--src/org/unitConverter/unit/FunctionalUnitlike.java72
-rw-r--r--src/org/unitConverter/unit/LinearUnit.java441
-rw-r--r--src/org/unitConverter/unit/LinearUnitValue.java341
-rw-r--r--src/org/unitConverter/unit/MultiUnit.java160
-rw-r--r--src/org/unitConverter/unit/MultiUnitTest.java106
-rw-r--r--src/org/unitConverter/unit/NameSymbol.java280
-rw-r--r--src/org/unitConverter/unit/Nameable.java59
-rw-r--r--src/org/unitConverter/unit/SI.java479
-rw-r--r--src/org/unitConverter/unit/USCustomary.java135
-rw-r--r--src/org/unitConverter/unit/Unit.java377
-rw-r--r--src/org/unitConverter/unit/UnitDatabase.java1991
-rw-r--r--src/org/unitConverter/unit/UnitDatabaseTest.java309
-rw-r--r--src/org/unitConverter/unit/UnitPrefix.java242
-rw-r--r--src/org/unitConverter/unit/UnitTest.java146
-rw-r--r--src/org/unitConverter/unit/UnitValue.java172
-rw-r--r--src/org/unitConverter/unit/Unitlike.java260
-rw-r--r--src/org/unitConverter/unit/UnitlikeValue.java174
-rw-r--r--src/org/unitConverter/unit/package-info.java24
40 files changed, 0 insertions, 11455 deletions
diff --git a/src/org/unitConverter/converterGUI/DefaultPrefixRepetitionRule.java b/src/org/unitConverter/converterGUI/DefaultPrefixRepetitionRule.java
deleted file mode 100644
index bdc3a2e..0000000
--- a/src/org/unitConverter/converterGUI/DefaultPrefixRepetitionRule.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/**
- * @since 2020-08-26
- */
-package org.unitConverter.converterGUI;
-
-import java.util.List;
-import java.util.function.Predicate;
-
-import org.unitConverter.unit.SI;
-import org.unitConverter.unit.UnitPrefix;
-
-/**
- * A rule that specifies whether prefix repetition is allowed
- *
- * @since 2020-08-26
- */
-enum DefaultPrefixRepetitionRule implements Predicate<List<UnitPrefix>> {
- NO_REPETITION {
- @Override
- public boolean test(List<UnitPrefix> prefixes) {
- return prefixes.size() <= 1;
- }
- },
- NO_RESTRICTION {
- @Override
- public boolean test(List<UnitPrefix> prefixes) {
- return true;
- }
- },
- /**
- * You are allowed to have any number of Yotta/Yocto followed by possibly one
- * Kilo-Zetta/Milli-Zepto followed by possibly one Deca/Hecto. Same for
- * reducing prefixes, don't mix magnifying and reducing. Non-metric
- * (including binary) prefixes can't be repeated.
- */
- COMPLEX_REPETITION {
- @Override
- public boolean test(List<UnitPrefix> prefixes) {
- // determine whether we are magnifying or reducing
- final boolean magnifying;
- if (prefixes.isEmpty())
- return true;
- else if (prefixes.get(0).getMultiplier() > 1) {
- magnifying = true;
- } else {
- magnifying = false;
- }
-
- // if the first prefix is non-metric (including binary prefixes),
- // assume we are using non-metric prefixes
- // non-metric prefixes are allowed, but can't be repeated.
- if (!SI.DECIMAL_PREFIXES.contains(prefixes.get(0)))
- return NO_REPETITION.test(prefixes);
-
- int part = 0; // 0=yotta/yoctos, 1=kilo-zetta/milli-zepto,
- // 2=deka,hecto,deci,centi
-
- for (final UnitPrefix prefix : prefixes) {
- // check that the current prefix is metric and appropriately
- // magnifying/reducing
- if (!SI.DECIMAL_PREFIXES.contains(prefix))
- return false;
- if (magnifying != prefix.getMultiplier() > 1)
- return false;
-
- // check if the current prefix is correct
- // since part is set *after* this check, part designates the state
- // of the *previous* prefix
- switch (part) {
- case 0:
- // do nothing, any prefix is valid after a yotta
- break;
- case 1:
- // after a kilo-zetta, only deka/hecto are valid
- if (SI.THOUSAND_PREFIXES.contains(prefix))
- return false;
- break;
- case 2:
- // deka/hecto must be the last prefix, so this is always invalid
- return false;
- }
-
- // set part
- if (SI.YOTTA.equals(prefix) || SI.YOCTO.equals(prefix)) {
- part = 0;
- } else if (SI.THOUSAND_PREFIXES.contains(prefix)) {
- part = 1;
- } else {
- part = 2;
- }
- }
- return true;
- }
- };
-}
diff --git a/src/org/unitConverter/converterGUI/DelegateListModel.java b/src/org/unitConverter/converterGUI/DelegateListModel.java
deleted file mode 100644
index b80f63d..0000000
--- a/src/org/unitConverter/converterGUI/DelegateListModel.java
+++ /dev/null
@@ -1,242 +0,0 @@
-/**
- * Copyright (C) 2018 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.converterGUI;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
-
-import javax.swing.AbstractListModel;
-
-/**
- * A list model that delegates to a list.
- * <p>
- * It is recommended to use the delegate methods in DelegateListModel instead of the delegated list's methods because
- * the delegate methods handle updating the list.
- * </p>
- *
- * @author Adrien Hopkins
- * @since 2019-01-14
- * @since v0.1.0
- */
-final class DelegateListModel<E> extends AbstractListModel<E> implements List<E> {
- /**
- * @since 2019-01-14
- * @since v0.1.0
- */
- private static final long serialVersionUID = 8985494428224810045L;
-
- /**
- * The list that this model is a delegate to.
- *
- * @since 2019-01-14
- * @since v0.1.0
- */
- private final List<E> delegate;
-
- /**
- * Creates an empty {@code DelegateListModel}.
- *
- * @since 2019-04-13
- */
- public DelegateListModel() {
- this(new ArrayList<>());
- }
-
- /**
- * Creates the {@code DelegateListModel}.
- *
- * @param delegate
- * list to delegate
- * @since 2019-01-14
- * @since v0.1.0
- */
- public DelegateListModel(final List<E> delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public boolean add(final E element) {
- final int index = this.delegate.size();
- final boolean success = this.delegate.add(element);
- this.fireIntervalAdded(this, index, index);
- return success;
- }
-
- @Override
- public void add(final int index, final E element) {
- this.delegate.add(index, element);
- this.fireIntervalAdded(this, index, index);
- }
-
- @Override
- public boolean addAll(final Collection<? extends E> c) {
- boolean changed = false;
- for (final E e : c) {
- if (this.add(e)) {
- changed = true;
- }
- }
- return changed;
- }
-
- @Override
- public boolean addAll(final int index, final Collection<? extends E> c) {
- for (final E e : c) {
- this.add(index, e);
- }
- return !c.isEmpty(); // Since this is a list, it will always change if c has elements.
- }
-
- @Override
- public void clear() {
- final int oldSize = this.delegate.size();
- this.delegate.clear();
- if (oldSize >= 1) {
- this.fireIntervalRemoved(this, 0, oldSize - 1);
- }
- }
-
- @Override
- public boolean contains(final Object elem) {
- return this.delegate.contains(elem);
- }
-
- @Override
- public boolean containsAll(final Collection<?> c) {
- for (final Object e : c) {
- if (!c.contains(e))
- return false;
- }
- return true;
- }
-
- @Override
- public E get(final int index) {
- return this.delegate.get(index);
- }
-
- @Override
- public E getElementAt(final int index) {
- return this.delegate.get(index);
- }
-
- @Override
- public int getSize() {
- return this.delegate.size();
- }
-
- @Override
- public int indexOf(final Object elem) {
- return this.delegate.indexOf(elem);
- }
-
- @Override
- public boolean isEmpty() {
- return this.delegate.isEmpty();
- }
-
- @Override
- public Iterator<E> iterator() {
- return this.delegate.iterator();
- }
-
- @Override
- public int lastIndexOf(final Object elem) {
- return this.delegate.lastIndexOf(elem);
- }
-
- @Override
- public ListIterator<E> listIterator() {
- return this.delegate.listIterator();
- }
-
- @Override
- public ListIterator<E> listIterator(final int index) {
- return this.delegate.listIterator(index);
- }
-
- @Override
- public E remove(final int index) {
- final E returnValue = this.delegate.get(index);
- this.delegate.remove(index);
- this.fireIntervalRemoved(this, index, index);
- return returnValue;
- }
-
- @Override
- public boolean remove(final Object o) {
- final int index = this.delegate.indexOf(o);
- final boolean returnValue = this.delegate.remove(o);
- this.fireIntervalRemoved(this, index, index);
- return returnValue;
- }
-
- @Override
- public boolean removeAll(final Collection<?> c) {
- boolean changed = false;
- for (final Object e : c) {
- if (this.remove(e)) {
- changed = true;
- }
- }
- return changed;
- }
-
- @Override
- public boolean retainAll(final Collection<?> c) {
- final int oldSize = this.size();
- final boolean returnValue = this.delegate.retainAll(c);
- this.fireIntervalRemoved(this, this.size(), oldSize - 1);
- return returnValue;
- }
-
- @Override
- public E set(final int index, final E element) {
- final E returnValue = this.delegate.get(index);
- this.delegate.set(index, element);
- this.fireContentsChanged(this, index, index);
- return returnValue;
- }
-
- @Override
- public int size() {
- return this.delegate.size();
- }
-
- @Override
- public List<E> subList(final int fromIndex, final int toIndex) {
- return this.delegate.subList(fromIndex, toIndex);
- }
-
- @Override
- public Object[] toArray() {
- return this.delegate.toArray();
- }
-
- @Override
- public <T> T[] toArray(final T[] a) {
- return this.delegate.toArray(a);
- }
-
- @Override
- public String toString() {
- return this.delegate.toString();
- }
-}
diff --git a/src/org/unitConverter/converterGUI/FilterComparator.java b/src/org/unitConverter/converterGUI/FilterComparator.java
deleted file mode 100644
index 7b17bfc..0000000
--- a/src/org/unitConverter/converterGUI/FilterComparator.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/**
- * Copyright (C) 2018 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.converterGUI;
-
-import java.util.Comparator;
-import java.util.Objects;
-
-/**
- * A comparator that compares strings using a filter.
- *
- * @author Adrien Hopkins
- * @since 2019-01-15
- * @since v0.1.0
- */
-final class FilterComparator implements Comparator<String> {
- /**
- * The filter that the comparator is filtered by.
- *
- * @since 2019-01-15
- * @since v0.1.0
- */
- private final String filter;
- /**
- * The comparator to use if the arguments are otherwise equal.
- *
- * @since 2019-01-15
- * @since v0.1.0
- */
- private final Comparator<String> comparator;
- /**
- * Whether or not the comparison is case-sensitive.
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- private final boolean caseSensitive;
-
- /**
- * Creates the {@code FilterComparator}.
- *
- * @param filter
- * @since 2019-01-15
- * @since v0.1.0
- */
- public FilterComparator(final String filter) {
- this(filter, null);
- }
-
- /**
- * Creates the {@code FilterComparator}.
- *
- * @param filter
- * string to filter by
- * @param comparator
- * comparator to fall back to if all else fails, null is compareTo.
- * @throws NullPointerException
- * if filter is null
- * @since 2019-01-15
- * @since v0.1.0
- */
- public FilterComparator(final String filter, final Comparator<String> comparator) {
- this(filter, comparator, false);
- }
-
- /**
- * Creates the {@code FilterComparator}.
- *
- * @param filter
- * string to filter by
- * @param comparator
- * comparator to fall back to if all else fails, null is compareTo.
- * @param caseSensitive
- * whether or not the comparator is case-sensitive
- * @throws NullPointerException
- * if filter is null
- * @since 2019-04-14
- * @since v0.2.0
- */
- public FilterComparator(final String filter, final Comparator<String> comparator, final boolean caseSensitive) {
- this.filter = Objects.requireNonNull(filter, "filter must not be null.");
- this.comparator = comparator;
- this.caseSensitive = caseSensitive;
- }
-
- @Override
- public int compare(final String arg0, final String arg1) {
- // if this is case insensitive, make them lowercase
- final String str0, str1;
- if (this.caseSensitive) {
- str0 = arg0;
- str1 = arg1;
- } else {
- str0 = arg0.toLowerCase();
- str1 = arg1.toLowerCase();
- }
-
- // elements that start with the filter always go first
- if (str0.startsWith(this.filter) && !str1.startsWith(this.filter))
- return -1;
- else if (!str0.startsWith(this.filter) && str1.startsWith(this.filter))
- return 1;
-
- // elements that contain the filter but don't start with them go next
- if (str0.contains(this.filter) && !str1.contains(this.filter))
- return -1;
- else if (!str0.contains(this.filter) && !str1.contains(this.filter))
- return 1;
-
- // other elements go last
- if (this.comparator == null)
- return str0.compareTo(str1);
- else
- return this.comparator.compare(str0, str1);
- }
-}
diff --git a/src/org/unitConverter/converterGUI/GridBagBuilder.java b/src/org/unitConverter/converterGUI/GridBagBuilder.java
deleted file mode 100644
index f1229b2..0000000
--- a/src/org/unitConverter/converterGUI/GridBagBuilder.java
+++ /dev/null
@@ -1,479 +0,0 @@
-/**
- * Copyright (C) 2018 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.converterGUI;
-
-import java.awt.GridBagConstraints;
-import java.awt.Insets;
-
-/**
- * A builder for Java's {@link java.awt.GridBagConstraints} class.
- *
- * @author Adrien Hopkins
- * @since 2018-11-30
- * @since v0.1.0
- */
-final class GridBagBuilder {
- /**
- * The built {@code GridBagConstraints}'s {@code gridx} property.
- * <p>
- * Specifies the cell containing the leading edge of the component's display area, where the first cell in a row has
- * <code>gridx=0</code>. The leading edge of a component's display area is its left edge for a horizontal,
- * left-to-right container and its right edge for a horizontal, right-to-left container. The value
- * <code>RELATIVE</code> specifies that the component be placed immediately following the component that was added
- * to the container just before this component was added.
- * <p>
- * The default value is <code>RELATIVE</code>. <code>gridx</code> should be a non-negative value.
- *
- * @serial
- * @see #clone()
- * @see java.awt.GridBagConstraints#gridy
- * @see java.awt.ComponentOrientation
- */
- private final int gridx;
-
- /**
- * The built {@code GridBagConstraints}'s {@code gridy} property.
- * <p>
- * Specifies the cell at the top of the component's display area, where the topmost cell has <code>gridy=0</code>.
- * The value <code>RELATIVE</code> specifies that the component be placed just below the component that was added to
- * the container just before this component was added.
- * <p>
- * The default value is <code>RELATIVE</code>. <code>gridy</code> should be a non-negative value.
- *
- * @serial
- * @see #clone()
- * @see java.awt.GridBagConstraints#gridx
- */
- private final int gridy;
-
- /**
- * The built {@code GridBagConstraints}'s {@code gridwidth} property.
- * <p>
- * Specifies the number of cells in a row for the component's display area.
- * <p>
- * Use <code>REMAINDER</code> to specify that the component's display area will be from <code>gridx</code> to the
- * last cell in the row. Use <code>RELATIVE</code> to specify that the component's display area will be from
- * <code>gridx</code> to the next to the last one in its row.
- * <p>
- * <code>gridwidth</code> should be non-negative and the default value is 1.
- *
- * @serial
- * @see #clone()
- * @see java.awt.GridBagConstraints#gridheight
- */
- private final int gridwidth;
-
- /**
- * The built {@code GridBagConstraints}'s {@code gridheight} property.
- * <p>
- * Specifies the number of cells in a column for the component's display area.
- * <p>
- * Use <code>REMAINDER</code> to specify that the component's display area will be from <code>gridy</code> to the
- * last cell in the column. Use <code>RELATIVE</code> to specify that the component's display area will be from
- * <code>gridy</code> to the next to the last one in its column.
- * <p>
- * <code>gridheight</code> should be a non-negative value and the default value is 1.
- *
- * @serial
- * @see #clone()
- * @see java.awt.GridBagConstraints#gridwidth
- */
- private final int gridheight;
-
- /**
- * The built {@code GridBagConstraints}'s {@code weightx} property.
- * <p>
- * Specifies how to distribute extra horizontal space.
- * <p>
- * The grid bag layout manager calculates the weight of a column to be the maximum <code>weightx</code> of all the
- * components in a column. If the resulting layout is smaller horizontally than the area it needs to fill, the extra
- * space is distributed to each column in proportion to its weight. A column that has a weight of zero receives no
- * extra space.
- * <p>
- * If all the weights are zero, all the extra space appears between the grids of the cell and the left and right
- * edges.
- * <p>
- * The default value of this field is <code>0</code>. <code>weightx</code> should be a non-negative value.
- *
- * @serial
- * @see #clone()
- * @see java.awt.GridBagConstraints#weighty
- */
- private double weightx;
-
- /**
- * The built {@code GridBagConstraints}'s {@code weighty} property.
- * <p>
- * Specifies how to distribute extra vertical space.
- * <p>
- * The grid bag layout manager calculates the weight of a row to be the maximum <code>weighty</code> of all the
- * components in a row. If the resulting layout is smaller vertically than the area it needs to fill, the extra
- * space is distributed to each row in proportion to its weight. A row that has a weight of zero receives no extra
- * space.
- * <p>
- * If all the weights are zero, all the extra space appears between the grids of the cell and the top and bottom
- * edges.
- * <p>
- * The default value of this field is <code>0</code>. <code>weighty</code> should be a non-negative value.
- *
- * @serial
- * @see #clone()
- * @see java.awt.GridBagConstraints#weightx
- */
- private double weighty;
-
- /**
- * The built {@code GridBagConstraints}'s {@code anchor} property.
- * <p>
- * This field is used when the component is smaller than its display area. It determines where, within the display
- * area, to place the component.
- * <p>
- * There are three kinds of possible values: orientation relative, baseline relative and absolute. Orientation
- * relative values are interpreted relative to the container's component orientation property, baseline relative
- * values are interpreted relative to the baseline and absolute values are not. The absolute values are:
- * <code>CENTER</code>, <code>NORTH</code>, <code>NORTHEAST</code>, <code>EAST</code>, <code>SOUTHEAST</code>,
- * <code>SOUTH</code>, <code>SOUTHWEST</code>, <code>WEST</code>, and <code>NORTHWEST</code>. The orientation
- * relative values are: <code>PAGE_START</code>, <code>PAGE_END</code>, <code>LINE_START</code>,
- * <code>LINE_END</code>, <code>FIRST_LINE_START</code>, <code>FIRST_LINE_END</code>, <code>LAST_LINE_START</code>
- * and <code>LAST_LINE_END</code>. The baseline relative values are: <code>BASELINE</code>,
- * <code>BASELINE_LEADING</code>, <code>BASELINE_TRAILING</code>, <code>ABOVE_BASELINE</code>,
- * <code>ABOVE_BASELINE_LEADING</code>, <code>ABOVE_BASELINE_TRAILING</code>, <code>BELOW_BASELINE</code>,
- * <code>BELOW_BASELINE_LEADING</code>, and <code>BELOW_BASELINE_TRAILING</code>. The default value is
- * <code>CENTER</code>.
- *
- * @serial
- * @see #clone()
- * @see java.awt.ComponentOrientation
- */
- private int anchor;
-
- /**
- * The built {@code GridBagConstraints}'s {@code fill} property.
- * <p>
- * This field is used when the component's display area is larger than the component's requested size. It determines
- * whether to resize the component, and if so, how.
- * <p>
- * The following values are valid for <code>fill</code>:
- *
- * <ul>
- * <li><code>NONE</code>: Do not resize the component.
- * <li><code>HORIZONTAL</code>: Make the component wide enough to fill its display area horizontally, but do not
- * change its height.
- * <li><code>VERTICAL</code>: Make the component tall enough to fill its display area vertically, but do not change
- * its width.
- * <li><code>BOTH</code>: Make the component fill its display area entirely.
- * </ul>
- * <p>
- * The default value is <code>NONE</code>.
- *
- * @serial
- * @see #clone()
- */
- private int fill;
-
- /**
- * The built {@code GridBagConstraints}'s {@code insets} property.
- * <p>
- * This field specifies the external padding of the component, the minimum amount of space between the component and
- * the edges of its display area.
- * <p>
- * The default value is <code>new Insets(0, 0, 0, 0)</code>.
- *
- * @serial
- * @see #clone()
- */
- private Insets insets;
-
- /**
- * The built {@code GridBagConstraints}'s {@code ipadx} property.
- * <p>
- * This field specifies the internal padding of the component, how much space to add to the minimum width of the
- * component. The width of the component is at least its minimum width plus <code>ipadx</code> pixels.
- * <p>
- * The default value is <code>0</code>.
- *
- * @serial
- * @see #clone()
- * @see java.awt.GridBagConstraints#ipady
- */
- private int ipadx;
-
- /**
- * The built {@code GridBagConstraints}'s {@code ipady} property.
- * <p>
- * This field specifies the internal padding, that is, how much space to add to the minimum height of the component.
- * The height of the component is at least its minimum height plus <code>ipady</code> pixels.
- * <p>
- * The default value is 0.
- *
- * @serial
- * @see #clone()
- * @see java.awt.GridBagConstraints#ipadx
- */
- private int ipady;
-
- /**
- * @param gridx
- * x position
- * @param gridy
- * y position
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagBuilder(final int gridx, final int gridy) {
- this(gridx, gridy, 1, 1);
- }
-
- /**
- * @param gridx
- * x position
- * @param gridy
- * y position
- * @param gridwidth
- * number of cells occupied horizontally
- * @param gridheight
- * number of cells occupied vertically
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagBuilder(final int gridx, final int gridy, final int gridwidth, final int gridheight) {
- this(gridx, gridy, gridwidth, gridheight, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
- new Insets(0, 0, 0, 0), 0, 0);
- }
-
- /**
- * @param gridx
- * x position
- * @param gridy
- * y position
- * @param gridwidth
- * number of cells occupied horizontally
- * @param gridheight
- * number of cells occupied vertically
- * @param weightx
- * @param weighty
- * @param anchor
- * @param fill
- * @param insets
- * @param ipadx
- * @param ipady
- * @since 2018-11-30
- * @since v0.1.0
- */
- private GridBagBuilder(final int gridx, final int gridy, final int gridwidth, final int gridheight,
- final double weightx, final double weighty, final int anchor, final int fill, final Insets insets,
- final int ipadx, final int ipady) {
- super();
- this.gridx = gridx;
- this.gridy = gridy;
- this.gridwidth = gridwidth;
- this.gridheight = gridheight;
- this.weightx = weightx;
- this.weighty = weighty;
- this.anchor = anchor;
- this.fill = fill;
- this.insets = (Insets) insets.clone();
- this.ipadx = ipadx;
- this.ipady = ipady;
- }
-
- /**
- * @return {@code GridBagConstraints} created by this builder
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagConstraints build() {
- return new GridBagConstraints(this.gridx, this.gridy, this.gridwidth, this.gridheight, this.weightx,
- this.weighty, this.anchor, this.fill, this.insets, this.ipadx, this.ipady);
- }
-
- /**
- * @return anchor
- * @since 2018-11-30
- * @since v0.1.0
- */
- public int getAnchor() {
- return this.anchor;
- }
-
- /**
- * @return fill
- * @since 2018-11-30
- * @since v0.1.0
- */
- public int getFill() {
- return this.fill;
- }
-
- /**
- * @return gridheight
- * @since 2018-11-30
- * @since v0.1.0
- */
- public int getGridheight() {
- return this.gridheight;
- }
-
- /**
- * @return gridwidth
- * @since 2018-11-30
- * @since v0.1.0
- */
- public int getGridwidth() {
- return this.gridwidth;
- }
-
- /**
- * @return gridx
- * @since 2018-11-30
- * @since v0.1.0
- */
- public int getGridx() {
- return this.gridx;
- }
-
- /**
- * @return gridy
- * @since 2018-11-30
- * @since v0.1.0
- */
- public int getGridy() {
- return this.gridy;
- }
-
- /**
- * @return insets
- * @since 2018-11-30
- * @since v0.1.0
- */
- public Insets getInsets() {
- return this.insets;
- }
-
- /**
- * @return ipadx
- * @since 2018-11-30
- * @since v0.1.0
- */
- public int getIpadx() {
- return this.ipadx;
- }
-
- /**
- * @return ipady
- * @since 2018-11-30
- * @since v0.1.0
- */
- public int getIpady() {
- return this.ipady;
- }
-
- /**
- * @return weightx
- * @since 2018-11-30
- * @since v0.1.0
- */
- public double getWeightx() {
- return this.weightx;
- }
-
- /**
- * @return weighty
- * @since 2018-11-30
- * @since v0.1.0
- */
- public double getWeighty() {
- return this.weighty;
- }
-
- /**
- * @param anchor
- * anchor to set
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagBuilder setAnchor(final int anchor) {
- this.anchor = anchor;
- return this;
- }
-
- /**
- * @param fill
- * fill to set
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagBuilder setFill(final int fill) {
- this.fill = fill;
- return this;
- }
-
- /**
- * @param insets
- * insets to set
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagBuilder setInsets(final Insets insets) {
- this.insets = insets;
- return this;
- }
-
- /**
- * @param ipadx
- * ipadx to set
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagBuilder setIpadx(final int ipadx) {
- this.ipadx = ipadx;
- return this;
- }
-
- /**
- * @param ipady
- * ipady to set
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagBuilder setIpady(final int ipady) {
- this.ipady = ipady;
- return this;
- }
-
- /**
- * @param weightx
- * weightx to set
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagBuilder setWeightx(final double weightx) {
- this.weightx = weightx;
- return this;
- }
-
- /**
- * @param weighty
- * weighty to set
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagBuilder setWeighty(final double weighty) {
- this.weighty = weighty;
- return this;
- }
-}
diff --git a/src/org/unitConverter/converterGUI/MutablePredicate.java b/src/org/unitConverter/converterGUI/MutablePredicate.java
deleted file mode 100644
index e15b3cd..0000000
--- a/src/org/unitConverter/converterGUI/MutablePredicate.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.converterGUI;
-
-import java.util.function.Predicate;
-
-/**
- * A container for a predicate, which can be changed later.
- *
- * @author Adrien Hopkins
- * @since 2019-04-13
- * @since v0.2.0
- */
-final class MutablePredicate<T> implements Predicate<T> {
- /**
- * The predicate stored in this {@code MutablePredicate}
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- private Predicate<T> predicate;
-
- /**
- * Creates the {@code MutablePredicate}.
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- public MutablePredicate(final Predicate<T> predicate) {
- this.predicate = predicate;
- }
-
- /**
- * @return predicate
- * @since 2019-04-13
- * @since v0.2.0
- */
- public final Predicate<T> getPredicate() {
- return this.predicate;
- }
-
- /**
- * @param predicate
- * new value of predicate
- * @since 2019-04-13
- * @since v0.2.0
- */
- public final void setPredicate(final Predicate<T> predicate) {
- this.predicate = predicate;
- }
-
- @Override
- public boolean test(final T t) {
- return this.predicate.test(t);
- }
-}
diff --git a/src/org/unitConverter/converterGUI/SearchBoxList.java b/src/org/unitConverter/converterGUI/SearchBoxList.java
deleted file mode 100644
index 10ef589..0000000
--- a/src/org/unitConverter/converterGUI/SearchBoxList.java
+++ /dev/null
@@ -1,307 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.converterGUI;
-
-import java.awt.BorderLayout;
-import java.awt.Color;
-import java.awt.event.FocusEvent;
-import java.awt.event.FocusListener;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.function.Predicate;
-
-import javax.swing.JList;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JTextField;
-
-/**
- * @author Adrien Hopkins
- * @since 2019-04-13
- * @since v0.2.0
- */
-final class SearchBoxList extends JPanel {
-
- /**
- * @since 2019-04-13
- * @since v0.2.0
- */
- private static final long serialVersionUID = 6226930279415983433L;
-
- /**
- * The text to place in an empty search box.
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- private static final String EMPTY_TEXT = "Search...";
-
- /**
- * The color to use for an empty foreground.
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- private static final Color EMPTY_FOREGROUND = new Color(192, 192, 192);
-
- // the components
- private final Collection<String> itemsToFilter;
- private final DelegateListModel<String> listModel;
- private final JTextField searchBox;
- private final JList<String> searchItems;
-
- private boolean searchBoxEmpty = true;
-
- // I need to do this because, for some reason, Swing is auto-focusing my
- // search box without triggering a focus
- // event.
- private boolean searchBoxFocused = false;
-
- private Predicate<String> customSearchFilter = o -> true;
- private final Comparator<String> defaultOrdering;
- private final boolean caseSensitive;
-
- /**
- * Creates the {@code SearchBoxList}.
- *
- * @param itemsToFilter items to put in the list
- * @since 2019-04-14
- */
- public SearchBoxList(final Collection<String> itemsToFilter) {
- this(itemsToFilter, null, false);
- }
-
- /**
- * Creates the {@code SearchBoxList}.
- *
- * @param itemsToFilter items to put in the list
- * @param defaultOrdering default ordering of items after filtration
- * (null=Comparable)
- * @param caseSensitive whether or not the filtration is case-sensitive
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- public SearchBoxList(final Collection<String> itemsToFilter,
- final Comparator<String> defaultOrdering,
- final boolean caseSensitive) {
- super(new BorderLayout(), true);
- this.itemsToFilter = itemsToFilter;
- this.defaultOrdering = defaultOrdering;
- this.caseSensitive = caseSensitive;
-
- // create the components
- this.listModel = new DelegateListModel<>(new ArrayList<>(itemsToFilter));
- this.searchItems = new JList<>(this.listModel);
-
- this.searchBox = new JTextField(EMPTY_TEXT);
- this.searchBox.setForeground(EMPTY_FOREGROUND);
-
- // add them to the panel
- this.add(this.searchBox, BorderLayout.PAGE_START);
- this.add(new JScrollPane(this.searchItems), BorderLayout.CENTER);
-
- // set up the search box
- this.searchBox.addFocusListener(new FocusListener() {
- @Override
- public void focusGained(final FocusEvent e) {
- SearchBoxList.this.searchBoxFocusGained(e);
- }
-
- @Override
- public void focusLost(final FocusEvent e) {
- SearchBoxList.this.searchBoxFocusLost(e);
- }
- });
-
- this.searchBox.addCaretListener(e -> this.searchBoxTextChanged());
- this.searchBoxEmpty = true;
- }
-
- /**
- * Adds an additional filter for searching.
- *
- * @param filter filter to add.
- * @since 2019-04-13
- * @since v0.2.0
- */
- public void addSearchFilter(final Predicate<String> filter) {
- this.customSearchFilter = this.customSearchFilter.and(filter);
- }
-
- /**
- * Resets the search filter.
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- public void clearSearchFilters() {
- this.customSearchFilter = o -> true;
- }
-
- /**
- * @return this component's search box component
- * @since 2019-04-14
- * @since v0.2.0
- */
- public final JTextField getSearchBox() {
- return this.searchBox;
- }
-
- /**
- * @param searchText text to search for
- * @return a filter that filters out that text, based on this list's case
- * sensitive setting
- * @since 2019-04-14
- * @since v0.2.0
- */
- private Predicate<String> getSearchFilter(final String searchText) {
- if (this.caseSensitive)
- return string -> string.contains(searchText);
- else
- return string -> string.toLowerCase()
- .contains(searchText.toLowerCase());
- }
-
- /**
- * @return this component's list component
- * @since 2019-04-14
- * @since v0.2.0
- */
- public final JList<String> getSearchList() {
- return this.searchItems;
- }
-
- /**
- * @return index selected in item list
- * @since 2019-04-14
- * @since v0.2.0
- */
- public int getSelectedIndex() {
- return this.searchItems.getSelectedIndex();
- }
-
- /**
- * @return value selected in item list
- * @since 2019-04-13
- * @since v0.2.0
- */
- public String getSelectedValue() {
- return this.searchItems.getSelectedValue();
- }
-
- /**
- * Re-applies the filters.
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- public void reapplyFilter() {
- final String searchText = this.searchBoxEmpty ? ""
- : this.searchBox.getText();
- final FilterComparator comparator = new FilterComparator(searchText,
- this.defaultOrdering, this.caseSensitive);
- final Predicate<String> searchFilter = this.getSearchFilter(searchText);
-
- this.listModel.clear();
- this.itemsToFilter.forEach(string -> {
- if (searchFilter.test(string)) {
- this.listModel.add(string);
- }
- });
-
- // applies the custom filters
- this.listModel.removeIf(this.customSearchFilter.negate());
-
- // sorts the remaining items
- this.listModel.sort(comparator);
- }
-
- /**
- * Runs whenever the search box gains focus.
- *
- * @param e focus event
- * @since 2019-04-13
- * @since v0.2.0
- */
- private void searchBoxFocusGained(final FocusEvent e) {
- this.searchBoxFocused = true;
- if (this.searchBoxEmpty) {
- this.searchBox.setText("");
- this.searchBox.setForeground(Color.BLACK);
- }
- }
-
- /**
- * Runs whenever the search box loses focus.
- *
- * @param e focus event
- * @since 2019-04-13
- * @since v0.2.0
- */
- private void searchBoxFocusLost(final FocusEvent e) {
- this.searchBoxFocused = false;
- if (this.searchBoxEmpty) {
- this.searchBox.setText(EMPTY_TEXT);
- this.searchBox.setForeground(EMPTY_FOREGROUND);
- }
- }
-
- /**
- * Runs whenever the text in the search box is changed.
- * <p>
- * Reapplies the search filter, and custom filters.
- * </p>
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- private void searchBoxTextChanged() {
- if (this.searchBoxFocused) {
- this.searchBoxEmpty = this.searchBox.getText().equals("");
- }
- final String searchText = this.searchBoxEmpty ? ""
- : this.searchBox.getText();
- final FilterComparator comparator = new FilterComparator(searchText,
- this.defaultOrdering, this.caseSensitive);
- final Predicate<String> searchFilter = this.getSearchFilter(searchText);
-
- // initialize list with items that match the filter then sort
- this.listModel.clear();
- this.itemsToFilter.forEach(string -> {
- if (searchFilter.test(string)) {
- this.listModel.add(string);
- }
- });
-
- // applies the custom filters
- this.listModel.removeIf(this.customSearchFilter.negate());
-
- // sorts the remaining items
- this.listModel.sort(comparator);
- }
-
- /**
- * Manually updates the search box's item list.
- *
- * @since 2020-08-27
- */
- public void updateList() {
- this.searchBoxTextChanged();
- }
-}
diff --git a/src/org/unitConverter/converterGUI/UnitConverterGUI.java b/src/org/unitConverter/converterGUI/UnitConverterGUI.java
deleted file mode 100644
index 6ddc4a0..0000000
--- a/src/org/unitConverter/converterGUI/UnitConverterGUI.java
+++ /dev/null
@@ -1,1424 +0,0 @@
-/**
- * Copyright (C) 2018-2021 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.converterGUI;
-
-import java.awt.BorderLayout;
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
-import java.awt.GridLayout;
-import java.awt.event.KeyEvent;
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.math.BigDecimal;
-import java.math.MathContext;
-import java.math.RoundingMode;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
-import javax.swing.BorderFactory;
-import javax.swing.BoxLayout;
-import javax.swing.ButtonGroup;
-import javax.swing.JButton;
-import javax.swing.JCheckBox;
-import javax.swing.JComboBox;
-import javax.swing.JFormattedTextField;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JRadioButton;
-import javax.swing.JScrollPane;
-import javax.swing.JSlider;
-import javax.swing.JTabbedPane;
-import javax.swing.JTextArea;
-import javax.swing.JTextField;
-import javax.swing.UIManager;
-import javax.swing.UnsupportedLookAndFeelException;
-import javax.swing.WindowConstants;
-import javax.swing.border.TitledBorder;
-
-import org.unitConverter.math.ConditionalExistenceCollections;
-import org.unitConverter.math.ObjectProduct;
-import org.unitConverter.unit.BaseDimension;
-import org.unitConverter.unit.BritishImperial;
-import org.unitConverter.unit.LinearUnit;
-import org.unitConverter.unit.LinearUnitValue;
-import org.unitConverter.unit.NameSymbol;
-import org.unitConverter.unit.SI;
-import org.unitConverter.unit.Unit;
-import org.unitConverter.unit.UnitDatabase;
-import org.unitConverter.unit.UnitPrefix;
-import org.unitConverter.unit.UnitValue;
-
-/**
- * @author Adrien Hopkins
- * @since 2018-12-27
- * @since v0.1.0
- */
-final class UnitConverterGUI {
- /**
- * A tab in the View.
- */
- private enum Pane {
- UNIT_CONVERTER, EXPRESSION_CONVERTER, UNIT_VIEWER, PREFIX_VIEWER, ABOUT,
- SETTINGS;
- }
-
- private static class Presenter {
- /** The default place where settings are stored. */
- private static final String DEFAULT_SETTINGS_FILEPATH = "settings.txt";
- /** The default place where units are stored. */
- private static final Path DEFAULT_UNITS_FILE = Path.of("unitsfile.txt");
- /** The default place where dimensions are stored. */
- private static final Path DEFAULT_DIMENSION_FILE = Path
- .of("dimensionfile.txt");
- /** The default place where exceptions are stored. */
- private static final Path DEFAULT_EXCEPTIONS_FILE = Path
- .of("metric_exceptions.txt");
-
- /**
- * Adds default units and dimensions to a database.
- *
- * @param database database to add to
- * @since 2019-04-14
- * @since v0.2.0
- */
- private static void addDefaults(final UnitDatabase database) {
- database.addUnit("metre", SI.METRE);
- database.addUnit("kilogram", SI.KILOGRAM);
- database.addUnit("gram", SI.KILOGRAM.dividedBy(1000));
- database.addUnit("second", SI.SECOND);
- database.addUnit("ampere", SI.AMPERE);
- database.addUnit("kelvin", SI.KELVIN);
- database.addUnit("mole", SI.MOLE);
- database.addUnit("candela", SI.CANDELA);
- database.addUnit("bit", SI.BIT);
- database.addUnit("unit", SI.ONE);
- // nonlinear units - must be loaded manually
- database.addUnit("tempCelsius", SI.CELSIUS);
- database.addUnit("tempFahrenheit", BritishImperial.FAHRENHEIT);
-
- // load initial dimensions
- database.addDimension("LENGTH", SI.Dimensions.LENGTH);
- database.addDimension("MASS", SI.Dimensions.MASS);
- database.addDimension("TIME", SI.Dimensions.TIME);
- database.addDimension("TEMPERATURE", SI.Dimensions.TEMPERATURE);
- }
-
- /**
- * @return {@code line} with any comments removed.
- * @since 2021-03-13
- */
- private static final String withoutComments(String line) {
- final int index = line.indexOf('#');
- return index == -1 ? line : line.substring(index);
- }
-
- /** The presenter's associated view. */
- private final View view;
-
- /** The units known by the program. */
- private final UnitDatabase database;
-
- /** The names of all of the units */
- private final List<String> unitNames;
-
- /** The names of all of the prefixes */
- private final List<String> prefixNames;
-
- /** The names of all of the dimensions */
- private final List<String> dimensionNames;
-
- /** Unit names that are ignored by the metric-only/imperial-only filter */
- private final Set<String> metricExceptions;
-
- private final Comparator<String> prefixNameComparator;
-
- /** A boolean remembering whether or not one-way conversion is on */
- private boolean oneWay = true;
-
- /** The prefix rule */
- private DefaultPrefixRepetitionRule prefixRule = null;
- // conditions for existence of From and To entries
- // used for one-way conversion
- private final MutablePredicate<String> fromExistenceCondition = new MutablePredicate<>(
- s -> true);
-
- private final MutablePredicate<String> toExistenceCondition = new MutablePredicate<>(
- s -> true);
-
- /*
- * Rounding-related settings. I am using my own system, and not
- * MathContext, because MathContext does not support decimal place based
- * or scientific rounding, only significant digit based rounding.
- */
- private int precision = 6;
-
- private RoundingType roundingType = RoundingType.SIGNIFICANT_DIGITS;
-
- /**
- * Creates the presenter.
- *
- * @param view presenter's associated view
- * @since 2018-12-27
- * @since v0.1.0
- */
- Presenter(final View view) {
- this.view = view;
-
- // load initial units
- this.database = new UnitDatabase(
- DefaultPrefixRepetitionRule.NO_RESTRICTION);
- Presenter.addDefaults(this.database);
-
- this.database.loadUnitsFile(DEFAULT_UNITS_FILE);
- this.database.loadDimensionFile(DEFAULT_DIMENSION_FILE);
-
- // load metric exceptions
- try {
- this.metricExceptions = Files.readAllLines(DEFAULT_EXCEPTIONS_FILE)
- .stream().map(Presenter::withoutComments)
- .filter(s -> !s.isBlank()).collect(Collectors.toSet());
- } catch (final IOException e) {
- throw new AssertionError("Loading of metric_exceptions.txt failed.",
- e);
- }
-
- // load settings - requires database to exist
- this.loadSettings();
-
- // a comparator that can be used to compare prefix names
- // any name that does not exist is less than a name that does.
- // otherwise, they are compared by value
- this.prefixNameComparator = (o1, o2) -> {
- if (!Presenter.this.database.containsPrefixName(o1))
- return -1;
- else if (!Presenter.this.database.containsPrefixName(o2))
- return 1;
-
- final UnitPrefix p1 = Presenter.this.database.getPrefix(o1);
- final UnitPrefix p2 = Presenter.this.database.getPrefix(o2);
-
- if (p1.getMultiplier() < p2.getMultiplier())
- return -1;
- else if (p1.getMultiplier() > p2.getMultiplier())
- return 1;
-
- return o1.compareTo(o2);
- };
-
- this.unitNames = new ArrayList<>(
- this.database.unitMapPrefixless().keySet());
- this.unitNames.sort(null); // sorts it using Comparable
-
- this.prefixNames = new ArrayList<>(this.database.prefixMap().keySet());
- this.prefixNames.sort(this.prefixNameComparator); // sorts it using my
- // comparator
-
- this.dimensionNames = new DelegateListModel<>(
- new ArrayList<>(this.database.dimensionMap().keySet()));
- this.dimensionNames.sort(null); // sorts it using Comparable
-
- // a Predicate that returns true iff the argument is a full base unit
- final Predicate<Unit> isFullBase = unit -> unit instanceof LinearUnit
- && ((LinearUnit) unit).isBase();
-
- // print out unit counts
- System.out.printf(
- "Successfully loaded %d units with %d unit names (%d base units).%n",
- new HashSet<>(this.database.unitMapPrefixless().values()).size(),
- this.database.unitMapPrefixless().size(),
- new HashSet<>(this.database.unitMapPrefixless().values())
- .stream().filter(isFullBase).count());
- }
-
- /**
- * Converts in the dimension-based converter
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- public final void convertDimensionBased() {
- final String fromSelection = this.view.getFromSelection();
- if (fromSelection == null) {
- this.view.showErrorDialog("Error",
- "No unit selected in From field");
- return;
- }
- final String toSelection = this.view.getToSelection();
- if (toSelection == null) {
- this.view.showErrorDialog("Error", "No unit selected in To field");
- return;
- }
-
- final Unit from = this.database.getUnit(fromSelection);
- final Unit to = this.database.getUnit(toSelection)
- .withName(NameSymbol.ofName(toSelection));
-
- final UnitValue beforeValue;
- try {
- beforeValue = UnitValue.of(from,
- this.view.getDimensionConverterInput());
- } catch (final ParseException e) {
- this.view.showErrorDialog("Error",
- "Error in parsing: " + e.getMessage());
- return;
- }
- final UnitValue value = beforeValue.convertTo(to);
-
- final String output = this.getRoundedString(value);
-
- this.view.setDimensionConverterOutputText(
- String.format("%s = %s", beforeValue, output));
- }
-
- /**
- * Runs whenever the convert button is pressed.
- *
- * <p>
- * Reads and parses a unit expression from the from and to boxes, then
- * converts {@code from} to {@code to}. Any errors are shown in
- * JOptionPanes.
- * </p>
- *
- * @since 2019-01-26
- * @since v0.1.0
- */
- public final void convertExpressions() {
- final String fromUnitString = this.view.getFromText();
- final String toUnitString = this.view.getToText();
-
- if (fromUnitString.isEmpty()) {
- this.view.showErrorDialog("Parse Error",
- "Please enter a unit expression in the From: box.");
- return;
- }
- if (toUnitString.isEmpty()) {
- this.view.showErrorDialog("Parse Error",
- "Please enter a unit expression in the To: box.");
- return;
- }
-
- final LinearUnitValue from;
- final Unit to;
- try {
- from = this.database.evaluateUnitExpression(fromUnitString);
- } catch (final IllegalArgumentException | NoSuchElementException e) {
- this.view.showErrorDialog("Parse Error",
- "Could not recognize text in From entry: " + e.getMessage());
- return;
- }
- try {
- to = this.database.getUnitFromExpression(toUnitString);
- } catch (final IllegalArgumentException | NoSuchElementException e) {
- this.view.showErrorDialog("Parse Error",
- "Could not recognize text in To entry: " + e.getMessage());
- return;
- }
-
- if (to instanceof LinearUnit) {
- // convert to LinearUnitValue
- final LinearUnitValue from2;
- final LinearUnit to2 = ((LinearUnit) to)
- .withName(NameSymbol.ofName(toUnitString));
- final boolean useSlash;
-
- if (from.canConvertTo(to2)) {
- from2 = from;
- useSlash = false;
- } else if (LinearUnitValue.ONE.dividedBy(from).canConvertTo(to2)) {
- from2 = LinearUnitValue.ONE.dividedBy(from);
- useSlash = true;
- } else {
- // if I can't convert, leave
- this.view.showErrorDialog("Conversion Error",
- String.format("Cannot convert between %s and %s",
- fromUnitString, toUnitString));
- return;
- }
-
- final LinearUnitValue converted = from2.convertTo(to2);
- this.view.setExpressionConverterOutputText((useSlash ? "1 / " : "")
- + String.format("%s = %s", fromUnitString,
- this.getRoundedString(converted, false)));
- return;
- } else {
- // convert to UnitValue
- final UnitValue from2 = from.asUnitValue();
- if (from2.canConvertTo(to)) {
- final UnitValue converted = from2.convertTo(to);
-
- this.view
- .setExpressionConverterOutputText(String.format("%s = %s",
- fromUnitString, this.getRoundedString(converted)));
- } else {
- // if I can't convert, leave
- this.view.showErrorDialog("Conversion Error",
- String.format("Cannot convert between %s and %s",
- fromUnitString, toUnitString));
- }
- }
- }
-
- /**
- * @return a list of all of the unit dimensions
- * @since 2019-04-13
- * @since v0.2.0
- */
- public final List<String> dimensionNameList() {
- return this.dimensionNames;
- }
-
- /**
- * @return a list of all the entries in the dimension-based converter's
- * From box
- * @since 2020-08-27
- */
- public final Set<String> fromEntries() {
- return ConditionalExistenceCollections.conditionalExistenceSet(
- this.unitNameSet(), this.fromExistenceCondition);
- }
-
- /**
- * @return a comparator to compare prefix names
- * @since 2019-04-14
- * @since v0.2.0
- */
- public final Comparator<String> getPrefixNameComparator() {
- return this.prefixNameComparator;
- }
-
- /**
- * Like {@link LinearUnitValue#toString(boolean)}, but obeys this unit
- * converter's rounding settings.
- *
- * @since 2020-08-04
- */
- private final String getRoundedString(final LinearUnitValue value,
- boolean showUncertainty) {
- switch (this.roundingType) {
- case DECIMAL_PLACES:
- case SIGNIFICANT_DIGITS:
- return this.getRoundedString(value.asUnitValue());
- case SCIENTIFIC:
- return value.toString(showUncertainty);
- default:
- throw new AssertionError("Invalid switch condition.");
- }
- }
-
- /**
- * Like {@link UnitValue#toString()}, but obeys this unit converter's
- * rounding settings.
- *
- * @since 2020-08-04
- */
- private final String getRoundedString(final UnitValue value) {
- final BigDecimal unrounded = new BigDecimal(value.getValue());
- final BigDecimal rounded;
- int precision = this.precision;
-
- switch (this.roundingType) {
- case DECIMAL_PLACES:
- rounded = unrounded.setScale(precision, RoundingMode.HALF_EVEN);
- break;
- case SCIENTIFIC:
- precision = 12;
- //$FALL-THROUGH$
- case SIGNIFICANT_DIGITS:
- rounded = unrounded
- .round(new MathContext(precision, RoundingMode.HALF_EVEN));
- break;
- default:
- throw new AssertionError("Invalid switch condition.");
- }
-
- String output = rounded.toString();
-
- // remove trailing zeroes
- if (output.contains(".")) {
- while (output.endsWith("0")) {
- output = output.substring(0, output.length() - 1);
- }
- if (output.endsWith(".")) {
- output = output.substring(0, output.length() - 1);
- }
- }
-
- return output + " " + value.getUnit().getPrimaryName().get();
- }
-
- /**
- * @return The file where settings are stored;
- * @since 2020-12-11
- */
- private final Path getSettingsFile() {
- return Path.of(DEFAULT_SETTINGS_FILEPATH);
- }
-
- /**
- * Loads settings from the settings file.
- *
- * @since 2021-02-17
- */
- public final void loadSettings() {
- try {
- // read file line by line
- final int lineNum = 0;
- for (final String line : Files
- .readAllLines(this.getSettingsFile())) {
- final int equalsIndex = line.indexOf('=');
- if (equalsIndex == -1)
- throw new IllegalStateException(
- "Settings file is malformed at line " + lineNum);
-
- final String param = line.substring(0, equalsIndex);
- final String value = line.substring(equalsIndex + 1);
-
- switch (param) {
- // set manually to avoid the unnecessary saving of the non-manual
- // methods
- case "precision":
- this.precision = Integer.valueOf(value);
- break;
- case "rounding_type":
- this.roundingType = RoundingType.valueOf(value);
- break;
- case "prefix_rule":
- this.prefixRule = DefaultPrefixRepetitionRule.valueOf(value);
- this.database.setPrefixRepetitionRule(this.prefixRule);
- break;
- case "one_way":
- this.oneWay = Boolean.valueOf(value);
- if (this.oneWay) {
- this.fromExistenceCondition.setPredicate(
- unitName -> this.metricExceptions.contains(unitName)
- || !this.database.getUnit(unitName)
- .isMetric());
- this.toExistenceCondition.setPredicate(
- unitName -> this.metricExceptions.contains(unitName)
- || this.database.getUnit(unitName).isMetric());
- } else {
- this.fromExistenceCondition.setPredicate(unitName -> true);
- this.toExistenceCondition.setPredicate(unitName -> true);
- }
- break;
- default:
- System.err.printf("Warning: unrecognized setting \"%s\".",
- param);
- break;
- }
- }
- } catch (final IOException e) {}
- }
-
- /**
- * @return a set of all prefix names in the database
- * @since 2019-04-14
- * @since v0.2.0
- */
- public final Set<String> prefixNameSet() {
- return this.database.prefixMap().keySet();
- }
-
- /**
- * Runs whenever a prefix is selected in the viewer.
- * <p>
- * Shows its information in the text box to the right.
- * </p>
- *
- * @since 2019-01-15
- * @since v0.1.0
- */
- public final void prefixSelected() {
- final String prefixName = this.view.getPrefixViewerSelection();
- if (prefixName == null)
- return;
- else {
- final UnitPrefix prefix = this.database.getPrefix(prefixName);
-
- this.view.setPrefixTextBoxText(String.format("%s%nMultiplier: %s",
- prefixName, prefix.getMultiplier()));
- }
- }
-
- /**
- * Saves the settings to the settings file.
- *
- * @since 2021-02-17
- */
- public final void saveSettings() {
- try (BufferedWriter writer = Files
- .newBufferedWriter(this.getSettingsFile())) {
- writer.write(String.format("precision=%d\n", this.precision));
- writer.write(
- String.format("rounding_type=%s\n", this.roundingType));
- writer.write(String.format("prefix_rule=%s\n", this.prefixRule));
- writer.write(String.format("one_way=%s\n", this.oneWay));
- } catch (final IOException e) {
- e.printStackTrace();
- this.view.showErrorDialog("I/O Error",
- "Error occurred while saving settings: "
- + e.getLocalizedMessage());
- }
- }
-
- /**
- * Enables or disables one-way conversion.
- *
- * @param oneWay whether one-way conversion should be on (true) or off
- * (false)
- * @since 2020-08-27
- */
- public final void setOneWay(boolean oneWay) {
- this.oneWay = oneWay;
- if (oneWay) {
- this.fromExistenceCondition.setPredicate(
- unitName -> this.metricExceptions.contains(unitName)
- || !this.database.getUnit(unitName).isMetric());
- this.toExistenceCondition.setPredicate(
- unitName -> this.metricExceptions.contains(unitName)
- || this.database.getUnit(unitName).isMetric());
- } else {
- this.fromExistenceCondition.setPredicate(unitName -> true);
- this.toExistenceCondition.setPredicate(unitName -> true);
- }
-
- this.saveSettings();
- }
-
- /**
- * @param precision new value of precision
- * @since 2019-01-15
- * @since v0.1.0
- */
- public final void setPrecision(final int precision) {
- this.precision = precision;
-
- this.saveSettings();
- }
-
- /**
- * @param prefixRepetitionRule the prefixRepetitionRule to set
- * @since 2020-08-26
- */
- public void setPrefixRepetitionRule(
- Predicate<List<UnitPrefix>> prefixRepetitionRule) {
- if (prefixRepetitionRule instanceof DefaultPrefixRepetitionRule) {
- this.prefixRule = (DefaultPrefixRepetitionRule) prefixRepetitionRule;
- } else {
- this.prefixRule = null;
- }
- this.database.setPrefixRepetitionRule(prefixRepetitionRule);
-
- this.saveSettings();
- }
-
- /**
- * @param roundingType the roundingType to set
- * @since 2020-07-16
- */
- public final void setRoundingType(RoundingType roundingType) {
- this.roundingType = roundingType;
-
- this.saveSettings();
- }
-
- /**
- * @return a list of all the entries in the dimension-based converter's To
- * box
- * @since 2020-08-27
- */
- public final Set<String> toEntries() {
- return ConditionalExistenceCollections.conditionalExistenceSet(
- this.unitNameSet(), this.toExistenceCondition);
- }
-
- /**
- * Returns true if and only if the unit represented by {@code unitName}
- * has the dimension represented by {@code dimensionName}.
- *
- * @param unitName name of unit to test
- * @param dimensionName name of dimension to test
- * @return whether unit has dimenision
- * @since 2019-04-13
- * @since v0.2.0
- */
- public final boolean unitMatchesDimension(final String unitName,
- final String dimensionName) {
- final Unit unit = this.database.getUnit(unitName);
- final ObjectProduct<BaseDimension> dimension = this.database
- .getDimension(dimensionName);
- return unit.getDimension().equals(dimension);
- }
-
- /**
- * Runs whenever a unit is selected in the viewer.
- * <p>
- * Shows its information in the text box to the right.
- * </p>
- *
- * @since 2019-01-15
- * @since v0.1.0
- */
- public final void unitNameSelected() {
- final String unitName = this.view.getUnitViewerSelection();
- if (unitName == null)
- return;
- else {
- final Unit unit = this.database.getUnit(unitName);
-
- this.view.setUnitTextBoxText(unit.toString());
- }
- }
-
- /**
- * @return a set of all of the unit names
- * @since 2019-04-14
- * @since v0.2.0
- */
- private final Set<String> unitNameSet() {
- return this.database.unitMapPrefixless().keySet();
- }
- }
-
- /**
- * Different types of rounding.
- *
- * Significant digits: Rounds to a number of digits. i.e. with precision 5,
- * 12345.6789 rounds to 12346. Decimal places: Rounds to a number of digits
- * after the decimal point, i.e. with precision 5, 12345.6789 rounds to
- * 12345.67890. Scientific: Rounds based on the number of digits and
- * operations, following standard scientific rounding.
- */
- private static enum RoundingType {
- SIGNIFICANT_DIGITS, DECIMAL_PLACES, SCIENTIFIC;
- }
-
- private static class View {
- private static final NumberFormat NUMBER_FORMATTER = new DecimalFormat();
-
- /** The view's frame. */
- private final JFrame frame;
- /** The view's associated presenter. */
- private final Presenter presenter;
- /** The master pane containing all of the tabs. */
- private final JTabbedPane masterPane;
-
- // DIMENSION-BASED CONVERTER
- /** The panel for inputting values in the dimension-based converter */
- private final JTextField valueInput;
- /** The panel for "From" in the dimension-based converter */
- private final SearchBoxList fromSearch;
- /** The panel for "To" in the dimension-based converter */
- private final SearchBoxList toSearch;
- /** The output area in the dimension-based converter */
- private final JTextArea dimensionBasedOutput;
-
- // EXPRESSION-BASED CONVERTER
- /** The "From" entry in the conversion panel */
- private final JTextField fromEntry;
- /** The "To" entry in the conversion panel */
- private final JTextField toEntry;
- /** The output area in the conversion panel */
- private final JTextArea output;
-
- // UNIT AND PREFIX VIEWERS
- /** The searchable list of unit names in the unit viewer */
- private final SearchBoxList unitNameList;
- /** The searchable list of prefix names in the prefix viewer */
- private final SearchBoxList prefixNameList;
- /** The text box for unit data in the unit viewer */
- private final JTextArea unitTextBox;
- /** The text box for prefix data in the prefix viewer */
- private final JTextArea prefixTextBox;
-
- /**
- * Creates the {@code View}.
- *
- * @since 2019-01-14
- * @since v0.1.0
- */
- public View() {
- this.presenter = new Presenter(this);
- this.frame = new JFrame("Unit Converter");
- this.frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
-
- // enable system look and feel
- try {
- UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
- } catch (ClassNotFoundException | InstantiationException
- | IllegalAccessException | UnsupportedLookAndFeelException e) {
- // oh well, just use default theme
- System.err.println("Failed to enable system look-and-feel.");
- e.printStackTrace();
- }
-
- // create the components
- this.masterPane = new JTabbedPane();
- this.unitNameList = new SearchBoxList(this.presenter.unitNameSet());
- this.prefixNameList = new SearchBoxList(this.presenter.prefixNameSet(),
- this.presenter.getPrefixNameComparator(), true);
- this.unitTextBox = new JTextArea();
- this.prefixTextBox = new JTextArea();
- this.fromSearch = new SearchBoxList(this.presenter.fromEntries());
- this.toSearch = new SearchBoxList(this.presenter.toEntries());
- this.valueInput = new JFormattedTextField(NUMBER_FORMATTER);
- this.dimensionBasedOutput = new JTextArea(2, 32);
- this.fromEntry = new JTextField();
- this.toEntry = new JTextField();
- this.output = new JTextArea(2, 32);
-
- // create more components
- this.initComponents();
-
- this.frame.pack();
- }
-
- /**
- * @return the currently selected pane.
- * @throws AssertionError if no pane (or an invalid pane) is selected
- */
- public Pane getActivePane() {
- switch (this.masterPane.getSelectedIndex()) {
- case 0:
- return Pane.UNIT_CONVERTER;
- case 1:
- return Pane.EXPRESSION_CONVERTER;
- case 2:
- return Pane.UNIT_VIEWER;
- case 3:
- return Pane.PREFIX_VIEWER;
- case 4:
- return Pane.ABOUT;
- case 5:
- return Pane.SETTINGS;
- default:
- throw new AssertionError("No selected pane, or invalid pane.");
- }
- }
-
- /**
- * @return value in dimension-based converter
- * @throws ParseException
- * @since 2020-07-07
- */
- public double getDimensionConverterInput() throws ParseException {
- final Number value = NUMBER_FORMATTER.parse(this.valueInput.getText());
- if (value instanceof Double)
- return (double) value;
- else if (value instanceof Long)
- return ((Long) value).longValue();
- else
- throw new AssertionError();
- }
-
- /**
- * @return selection in "From" selector in dimension-based converter
- * @since 2019-04-13
- * @since v0.2.0
- */
- public String getFromSelection() {
- return this.fromSearch.getSelectedValue();
- }
-
- /**
- * @return text in "From" box in converter panel
- * @since 2019-01-15
- * @since v0.1.0
- */
- public String getFromText() {
- return this.fromEntry.getText();
- }
-
- /**
- * @return index of selected prefix in prefix viewer
- * @since 2019-01-15
- * @since v0.1.0
- */
- public String getPrefixViewerSelection() {
- return this.prefixNameList.getSelectedValue();
- }
-
- /**
- * @return selection in "To" selector in dimension-based converter
- * @since 2019-04-13
- * @since v0.2.0
- */
- public String getToSelection() {
- return this.toSearch.getSelectedValue();
- }
-
- /**
- * @return text in "To" box in converter panel
- * @since 2019-01-26
- * @since v0.1.0
- */
- public String getToText() {
- return this.toEntry.getText();
- }
-
- /**
- * @return index of selected unit in unit viewer
- * @since 2019-01-15
- * @since v0.1.0
- */
- public String getUnitViewerSelection() {
- return this.unitNameList.getSelectedValue();
- }
-
- /**
- * Starts up the application.
- *
- * @since 2018-12-27
- * @since v0.1.0
- */
- public final void init() {
- this.frame.setVisible(true);
- }
-
- /**
- * Initializes the view's components.
- *
- * @since 2018-12-27
- * @since v0.1.0
- */
- private final void initComponents() {
- final JPanel masterPanel = new JPanel();
- this.frame.add(masterPanel);
-
- masterPanel.setLayout(new BorderLayout());
-
- { // pane with all of the tabs
- masterPanel.add(this.masterPane, BorderLayout.CENTER);
-
- // update stuff
- this.masterPane.addChangeListener(e -> this.update());
-
- { // a panel for unit conversion using a selector
- final JPanel convertUnitPanel = new JPanel();
- this.masterPane.addTab("Convert Units", convertUnitPanel);
- this.masterPane.setMnemonicAt(0, KeyEvent.VK_U);
-
- convertUnitPanel.setLayout(new BorderLayout());
-
- { // panel for input part
- final JPanel inputPanel = new JPanel();
- convertUnitPanel.add(inputPanel, BorderLayout.CENTER);
-
- inputPanel.setLayout(new GridLayout(1, 3));
-
- final JComboBox<String> dimensionSelector = new JComboBox<>(
- this.presenter.dimensionNameList()
- .toArray(new String[0]));
- dimensionSelector.setSelectedItem("LENGTH");
-
- // handle dimension filter
- final MutablePredicate<String> dimensionFilter = new MutablePredicate<>(
- s -> true);
-
- // panel for From things
- inputPanel.add(this.fromSearch);
-
- this.fromSearch.addSearchFilter(dimensionFilter);
-
- { // for dimension selector and arrow that represents
- // conversion
- final JPanel inBetweenPanel = new JPanel();
- inputPanel.add(inBetweenPanel);
-
- inBetweenPanel.setLayout(new BorderLayout());
-
- { // dimension selector
- inBetweenPanel.add(dimensionSelector,
- BorderLayout.PAGE_START);
- }
-
- { // the arrow in the middle
- final JLabel arrowLabel = new JLabel("->");
- inBetweenPanel.add(arrowLabel, BorderLayout.CENTER);
- }
- }
-
- // panel for To things
-
- inputPanel.add(this.toSearch);
-
- this.toSearch.addSearchFilter(dimensionFilter);
-
- // code for dimension filter
- dimensionSelector.addItemListener(e -> {
- dimensionFilter.setPredicate(string -> View.this.presenter
- .unitMatchesDimension(string,
- (String) dimensionSelector.getSelectedItem()));
- this.fromSearch.reapplyFilter();
- this.toSearch.reapplyFilter();
- });
-
- // apply the item listener once because I have a default
- // selection
- dimensionFilter.setPredicate(string -> View.this.presenter
- .unitMatchesDimension(string,
- (String) dimensionSelector.getSelectedItem()));
- this.fromSearch.reapplyFilter();
- this.toSearch.reapplyFilter();
- }
-
- { // panel for submit and output, and also value entry
- final JPanel outputPanel = new JPanel();
- convertUnitPanel.add(outputPanel, BorderLayout.PAGE_END);
-
- outputPanel.setLayout(new GridLayout(3, 1));
-
- { // unit input
- final JPanel valueInputPanel = new JPanel();
- outputPanel.add(valueInputPanel);
-
- valueInputPanel.setLayout(new BorderLayout());
-
- { // prompt
- final JLabel valuePrompt = new JLabel(
- "Value to convert: ");
- valueInputPanel.add(valuePrompt,
- BorderLayout.LINE_START);
- }
-
- { // value to convert
- valueInputPanel.add(this.valueInput,
- BorderLayout.CENTER);
- }
- }
-
- { // button to convert
- final JButton convertButton = new JButton("Convert");
- outputPanel.add(convertButton);
-
- convertButton.addActionListener(
- e -> this.presenter.convertDimensionBased());
- convertButton.setMnemonic(KeyEvent.VK_ENTER);
- }
-
- { // output of conversion
- outputPanel.add(this.dimensionBasedOutput);
- this.dimensionBasedOutput.setEditable(false);
- }
- }
- }
-
- { // panel for unit conversion using expressions
- final JPanel convertExpressionPanel = new JPanel();
- this.masterPane.addTab("Convert Unit Expressions",
- convertExpressionPanel);
- this.masterPane.setMnemonicAt(1, KeyEvent.VK_E);
-
- convertExpressionPanel.setLayout(new GridLayout(4, 1));
-
- { // panel for units to convert from
- final JPanel fromPanel = new JPanel();
- convertExpressionPanel.add(fromPanel);
-
- fromPanel.setBorder(BorderFactory.createTitledBorder("From"));
- fromPanel.setLayout(new GridLayout(1, 1));
-
- { // entry for units
- fromPanel.add(this.fromEntry);
- }
- }
-
- { // panel for units to convert to
- final JPanel toPanel = new JPanel();
- convertExpressionPanel.add(toPanel);
-
- toPanel.setBorder(BorderFactory.createTitledBorder("To"));
- toPanel.setLayout(new GridLayout(1, 1));
-
- { // entry for units
- toPanel.add(this.toEntry);
- }
- }
-
- { // button to convert
- final JButton convertButton = new JButton("Convert");
- convertExpressionPanel.add(convertButton);
-
- convertButton.addActionListener(
- e -> this.presenter.convertExpressions());
- convertButton.setMnemonic(KeyEvent.VK_ENTER);
- }
-
- { // output of conversion
- final JPanel outputPanel = new JPanel();
- convertExpressionPanel.add(outputPanel);
-
- outputPanel
- .setBorder(BorderFactory.createTitledBorder("Output"));
- outputPanel.setLayout(new GridLayout(1, 1));
-
- { // output
- outputPanel.add(this.output);
- this.output.setEditable(false);
- }
- }
- }
-
- { // panel to look up units
- final JPanel unitLookupPanel = new JPanel();
- this.masterPane.addTab("Unit Viewer", unitLookupPanel);
- this.masterPane.setMnemonicAt(2, KeyEvent.VK_V);
-
- unitLookupPanel.setLayout(new GridLayout());
-
- { // search panel
- unitLookupPanel.add(this.unitNameList);
-
- this.unitNameList.getSearchList().addListSelectionListener(
- e -> this.presenter.unitNameSelected());
- }
-
- { // the text box for unit's toString
- unitLookupPanel.add(this.unitTextBox);
- this.unitTextBox.setEditable(false);
- this.unitTextBox.setLineWrap(true);
- }
- }
-
- { // panel to look up prefixes
- final JPanel prefixLookupPanel = new JPanel();
- this.masterPane.addTab("Prefix Viewer", prefixLookupPanel);
- this.masterPane.setMnemonicAt(3, KeyEvent.VK_P);
-
- prefixLookupPanel.setLayout(new GridLayout(1, 2));
-
- { // panel for listing and seaching
- prefixLookupPanel.add(this.prefixNameList);
-
- this.prefixNameList.getSearchList().addListSelectionListener(
- e -> this.presenter.prefixSelected());
- }
-
- { // the text box for prefix's toString
- prefixLookupPanel.add(this.prefixTextBox);
- this.prefixTextBox.setEditable(false);
- this.prefixTextBox.setLineWrap(true);
- }
- }
-
- { // Info panel
- final JPanel infoPanel = new JPanel();
- this.masterPane.addTab("\uD83D\uDEC8", // info (i) character
- new JScrollPane(infoPanel));
-
- final JTextArea infoTextArea = new JTextArea();
- infoTextArea.setEditable(false);
- infoTextArea.setOpaque(false);
- infoPanel.add(infoTextArea);
-
- // get info text
- final String infoText;
- try {
- final Path aboutFile = Path.of("src", "about.txt");
- infoText = Files.readAllLines(aboutFile).stream()
- .map(Presenter::withoutComments)
- .collect(Collectors.joining("\n"));
- } catch (final IOException e) {
- throw new AssertionError("I/O exception loading about.txt");
- }
- infoTextArea.setText(infoText);
- }
-
- { // Settings panel
- final JPanel settingsPanel = new JPanel();
- this.masterPane.addTab("\u2699", new JScrollPane(settingsPanel));
- this.masterPane.setMnemonicAt(5, KeyEvent.VK_S);
-
- settingsPanel.setLayout(
- new BoxLayout(settingsPanel, BoxLayout.PAGE_AXIS));
-
- { // rounding settings
- final JPanel roundingPanel = new JPanel();
- settingsPanel.add(roundingPanel);
- roundingPanel
- .setBorder(new TitledBorder("Rounding Settings"));
- roundingPanel.setLayout(new GridBagLayout());
-
- // rounding rule selection
- final ButtonGroup roundingRuleButtons = new ButtonGroup();
-
- final JLabel roundingRuleLabel = new JLabel("Rounding Rule:");
- roundingPanel.add(roundingRuleLabel, new GridBagBuilder(0, 0)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JRadioButton fixedPrecision = new JRadioButton(
- "Fixed Precision");
- if (this.presenter.roundingType == RoundingType.SIGNIFICANT_DIGITS) {
- fixedPrecision.setSelected(true);
- }
- fixedPrecision.addActionListener(e -> this.presenter
- .setRoundingType(RoundingType.SIGNIFICANT_DIGITS));
- roundingRuleButtons.add(fixedPrecision);
- roundingPanel.add(fixedPrecision, new GridBagBuilder(0, 1)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JRadioButton fixedDecimals = new JRadioButton(
- "Fixed Decimal Places");
- if (this.presenter.roundingType == RoundingType.DECIMAL_PLACES) {
- fixedDecimals.setSelected(true);
- }
- fixedDecimals.addActionListener(e -> this.presenter
- .setRoundingType(RoundingType.DECIMAL_PLACES));
- roundingRuleButtons.add(fixedDecimals);
- roundingPanel.add(fixedDecimals, new GridBagBuilder(0, 2)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JRadioButton relativePrecision = new JRadioButton(
- "Scientific Precision");
- if (this.presenter.roundingType == RoundingType.SCIENTIFIC) {
- relativePrecision.setSelected(true);
- }
- relativePrecision.addActionListener(e -> this.presenter
- .setRoundingType(RoundingType.SCIENTIFIC));
- roundingRuleButtons.add(relativePrecision);
- roundingPanel.add(relativePrecision, new GridBagBuilder(0, 3)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JLabel sliderLabel = new JLabel("Precision:");
- roundingPanel.add(sliderLabel, new GridBagBuilder(0, 4)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JSlider sigDigSlider = new JSlider(0, 12);
- roundingPanel.add(sigDigSlider, new GridBagBuilder(0, 5)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- sigDigSlider.setMajorTickSpacing(4);
- sigDigSlider.setMinorTickSpacing(1);
- sigDigSlider.setSnapToTicks(true);
- sigDigSlider.setPaintTicks(true);
- sigDigSlider.setPaintLabels(true);
- sigDigSlider.setValue(this.presenter.precision);
-
- sigDigSlider.addChangeListener(e -> this.presenter
- .setPrecision(sigDigSlider.getValue()));
- }
-
- { // prefix repetition settings
- final JPanel prefixRepetitionPanel = new JPanel();
- settingsPanel.add(prefixRepetitionPanel);
- prefixRepetitionPanel.setBorder(
- new TitledBorder("Prefix Repetition Settings"));
- prefixRepetitionPanel.setLayout(new GridBagLayout());
-
- // prefix rules
- final ButtonGroup prefixRuleButtons = new ButtonGroup();
-
- final JRadioButton noRepetition = new JRadioButton(
- "No Repetition");
- if (this.presenter.prefixRule == DefaultPrefixRepetitionRule.NO_REPETITION) {
- noRepetition.setSelected(true);
- }
- noRepetition.addActionListener(
- e -> this.presenter.setPrefixRepetitionRule(
- DefaultPrefixRepetitionRule.NO_REPETITION));
- prefixRuleButtons.add(noRepetition);
- prefixRepetitionPanel.add(noRepetition,
- new GridBagBuilder(0, 0)
- .setAnchor(GridBagConstraints.LINE_START)
- .build());
-
- final JRadioButton noRestriction = new JRadioButton(
- "No Restriction");
- if (this.presenter.prefixRule == DefaultPrefixRepetitionRule.NO_RESTRICTION) {
- noRestriction.setSelected(true);
- }
- noRestriction.addActionListener(
- e -> this.presenter.setPrefixRepetitionRule(
- DefaultPrefixRepetitionRule.NO_RESTRICTION));
- prefixRuleButtons.add(noRestriction);
- prefixRepetitionPanel.add(noRestriction,
- new GridBagBuilder(0, 1)
- .setAnchor(GridBagConstraints.LINE_START)
- .build());
-
- final JRadioButton customRepetition = new JRadioButton(
- "Complex Repetition");
- if (this.presenter.prefixRule == DefaultPrefixRepetitionRule.COMPLEX_REPETITION) {
- customRepetition.setSelected(true);
- }
- customRepetition.addActionListener(
- e -> this.presenter.setPrefixRepetitionRule(
- DefaultPrefixRepetitionRule.COMPLEX_REPETITION));
- prefixRuleButtons.add(customRepetition);
- prefixRepetitionPanel.add(customRepetition,
- new GridBagBuilder(0, 2)
- .setAnchor(GridBagConstraints.LINE_START)
- .build());
- }
-
- { // search settings
- final JPanel searchingPanel = new JPanel();
- settingsPanel.add(searchingPanel);
- searchingPanel.setBorder(new TitledBorder("Search Settings"));
- searchingPanel.setLayout(new GridBagLayout());
-
- // searching rules
- final ButtonGroup searchRuleButtons = new ButtonGroup();
-
- final JRadioButton noPrefixes = new JRadioButton(
- "Never Include Prefixed Units");
- noPrefixes.setEnabled(false);
- searchRuleButtons.add(noPrefixes);
- searchingPanel.add(noPrefixes, new GridBagBuilder(0, 0)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JRadioButton fixedPrefixes = new JRadioButton(
- "Include Some Prefixes");
- fixedPrefixes.setEnabled(false);
- searchRuleButtons.add(fixedPrefixes);
- searchingPanel.add(fixedPrefixes, new GridBagBuilder(0, 1)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JRadioButton explicitPrefixes = new JRadioButton(
- "Include Explicit Prefixes");
- explicitPrefixes.setEnabled(false);
- searchRuleButtons.add(explicitPrefixes);
- searchingPanel.add(explicitPrefixes, new GridBagBuilder(0, 2)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JRadioButton alwaysInclude = new JRadioButton(
- "Include All Single Prefixes");
- alwaysInclude.setEnabled(false);
- searchRuleButtons.add(alwaysInclude);
- searchingPanel.add(alwaysInclude, new GridBagBuilder(0, 3)
- .setAnchor(GridBagConstraints.LINE_START).build());
- }
-
- { // miscellaneous settings
- final JPanel miscPanel = new JPanel();
- settingsPanel.add(miscPanel);
- miscPanel
- .setBorder(new TitledBorder("Miscellaneous Settings"));
- miscPanel.setLayout(new GridBagLayout());
-
- final JCheckBox oneWay = new JCheckBox(
- "Convert One Way Only");
- oneWay.setSelected(this.presenter.oneWay);
- oneWay.addItemListener(
- e -> this.presenter.setOneWay(e.getStateChange() == 1));
- miscPanel.add(oneWay, new GridBagBuilder(0, 0)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JCheckBox showAllVariations = new JCheckBox(
- "Show Symbols in \"Convert Units\"");
- showAllVariations.setSelected(true);
- showAllVariations.setEnabled(false);
- miscPanel.add(showAllVariations, new GridBagBuilder(0, 1)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JButton unitFileButton = new JButton(
- "Manage Unit Data Files");
- unitFileButton.setEnabled(false);
- miscPanel.add(unitFileButton, new GridBagBuilder(0, 2)
- .setAnchor(GridBagConstraints.LINE_START).build());
- }
- }
- }
- }
-
- /**
- * Sets the text in the output of the dimension-based converter.
- *
- * @param text text to set
- * @since 2019-04-13
- * @since v0.2.0
- */
- public void setDimensionConverterOutputText(final String text) {
- this.dimensionBasedOutput.setText(text);
- }
-
- /**
- * Sets the text in the output of the conversion panel.
- *
- * @param text text to set
- * @since 2019-01-15
- * @since v0.1.0
- */
- public void setExpressionConverterOutputText(final String text) {
- this.output.setText(text);
- }
-
- /**
- * Sets the text of the prefix text box in the prefix viewer.
- *
- * @param text text to set
- * @since 2019-01-15
- * @since v0.1.0
- */
- public void setPrefixTextBoxText(final String text) {
- this.prefixTextBox.setText(text);
- }
-
- /**
- * Sets the text of the unit text box in the unit viewer.
- *
- * @param text text to set
- * @since 2019-01-15
- * @since v0.1.0
- */
- public void setUnitTextBoxText(final String text) {
- this.unitTextBox.setText(text);
- }
-
- /**
- * Shows an error dialog.
- *
- * @param title title of dialog
- * @param message message in dialog
- * @since 2019-01-14
- * @since v0.1.0
- */
- public void showErrorDialog(final String title, final String message) {
- JOptionPane.showMessageDialog(this.frame, message, title,
- JOptionPane.ERROR_MESSAGE);
- }
-
- public void update() {
- switch (this.getActivePane()) {
- case UNIT_CONVERTER:
- this.fromSearch.updateList();
- this.toSearch.updateList();
- break;
- default:
- // do nothing, for now
- break;
- }
- }
- }
-
- public static void main(final String[] args) {
- new View().init();
- }
-}
diff --git a/src/org/unitConverter/converterGUI/package-info.java b/src/org/unitConverter/converterGUI/package-info.java
deleted file mode 100644
index d85ecab..0000000
--- a/src/org/unitConverter/converterGUI/package-info.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-/**
- * The GUI interface of the Unit Converter.
- *
- * @author Adrien Hopkins
- * @since 2019-01-25
- * @since v0.2.0
- */
-package org.unitConverter.converterGUI; \ No newline at end of file
diff --git a/src/org/unitConverter/math/ConditionalExistenceCollections.java b/src/org/unitConverter/math/ConditionalExistenceCollections.java
deleted file mode 100644
index ac1c0cf..0000000
--- a/src/org/unitConverter/math/ConditionalExistenceCollections.java
+++ /dev/null
@@ -1,468 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.math;
-
-import java.util.AbstractCollection;
-import java.util.AbstractMap;
-import java.util.AbstractSet;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import java.util.function.Predicate;
-
-/**
- * Elements in these wrapper collections only exist if they pass a condition.
- * <p>
- * All of the collections in this class are "views" of the provided collections.
- * They are mutable if the provided collections are mutable, they allow null if
- * the provided collections allow null, they will reflect changes in the
- * provided collection, etc.
- * <p>
- * The modification operations will always run the corresponding operations,
- * even if the conditional existence collection doesn't change. For example, if
- * you have a set that ignores even numbers, add(2) will still add a 2 to the
- * backing set (but the conditional existence set will say it doesn't exist).
- * <p>
- * The returned collections do <i>not</i> pass the hashCode and equals
- * operations through to the backing collections, but rely on {@code Object}'s
- * {@code equals} and {@code hashCode} methods. This is necessary to preserve
- * the contracts of these operations in the case that the backing collections
- * are sets or lists.
- * <p>
- * Other than that, <i>the only difference between the provided collections and
- * the returned collections are that elements don't exist if they don't pass the
- * provided condition</i>.
- *
- *
- * @author Adrien Hopkins
- * @since 2019-10-17
- */
-// TODO add conditional existence Lists and Sorted/Navigable Sets/Maps
-public final class ConditionalExistenceCollections {
- /**
- * Elements in this collection only exist if they meet a condition.
- *
- * @author Adrien Hopkins
- * @since 2019-10-17
- * @param <E> type of element in collection
- */
- static final class ConditionalExistenceCollection<E>
- extends AbstractCollection<E> {
- final Collection<E> collection;
- final Predicate<E> existenceCondition;
-
- /**
- * Creates the {@code ConditionalExistenceCollection}.
- *
- * @param collection
- * @param existenceCondition
- * @since 2019-10-17
- */
- private ConditionalExistenceCollection(final Collection<E> collection,
- final Predicate<E> existenceCondition) {
- this.collection = collection;
- this.existenceCondition = existenceCondition;
- }
-
- @Override
- public boolean add(final E e) {
- return this.collection.add(e) && this.existenceCondition.test(e);
- }
-
- @Override
- public void clear() {
- this.collection.clear();
- }
-
- @Override
- public boolean contains(final Object o) {
- if (!this.collection.contains(o))
- return false;
-
- // this collection can only contain instances of E
- // since the object is in the collection, we know that it must be an
- // instance of E
- // therefore this cast will always work
- @SuppressWarnings("unchecked")
- final E e = (E) o;
-
- return this.existenceCondition.test(e);
- }
-
- @Override
- public Iterator<E> iterator() {
- return conditionalExistenceIterator(this.collection.iterator(),
- this.existenceCondition);
- }
-
- @Override
- public boolean remove(final Object o) {
- // remove() must be first in the && statement, otherwise it may not
- // execute
- final boolean containedObject = this.contains(o);
- return this.collection.remove(o) && containedObject;
- }
-
- @Override
- public int size() {
- return (int) this.collection.stream().filter(this.existenceCondition)
- .count();
- }
-
- @Override
- public Object[] toArray() {
- // ensure the toArray operation is supported
- this.collection.toArray();
-
- // if it works, do it for real
- return super.toArray();
- }
-
- @Override
- public <T> T[] toArray(T[] a) {
- // ensure the toArray operation is supported
- this.collection.toArray();
-
- // if it works, do it for real
- return super.toArray(a);
- }
- }
-
- /**
- * Elements in this wrapper iterator only exist if they pass a condition.
- *
- * @author Adrien Hopkins
- * @since 2019-10-17
- * @param <E> type of elements in iterator
- */
- static final class ConditionalExistenceIterator<E> implements Iterator<E> {
- final Iterator<E> iterator;
- final Predicate<E> existenceCondition;
- E nextElement;
- boolean hasNext;
-
- /**
- * Creates the {@code ConditionalExistenceIterator}.
- *
- * @param iterator
- * @param condition
- * @since 2019-10-17
- */
- private ConditionalExistenceIterator(final Iterator<E> iterator,
- final Predicate<E> condition) {
- this.iterator = iterator;
- this.existenceCondition = condition;
- this.getAndSetNextElement();
- }
-
- /**
- * Gets the next element, and sets nextElement and hasNext accordingly.
- *
- * @since 2019-10-17
- */
- private void getAndSetNextElement() {
- do {
- if (!this.iterator.hasNext()) {
- this.nextElement = null;
- this.hasNext = false;
- return;
- }
- this.nextElement = this.iterator.next();
- } while (!this.existenceCondition.test(this.nextElement));
- this.hasNext = true;
- }
-
- @Override
- public boolean hasNext() {
- return this.hasNext;
- }
-
- @Override
- public E next() {
- if (this.hasNext()) {
- final E next = this.nextElement;
- this.getAndSetNextElement();
- return next;
- } else
- throw new NoSuchElementException();
- }
-
- @Override
- public void remove() {
- this.iterator.remove();
- }
- }
-
- /**
- * Mappings in this map only exist if the entry passes some condition.
- *
- * @author Adrien Hopkins
- * @since 2019-10-17
- * @param <K> key type
- * @param <V> value type
- */
- static final class ConditionalExistenceMap<K, V> extends AbstractMap<K, V> {
- Map<K, V> map;
- Predicate<Entry<K, V>> entryExistenceCondition;
-
- /**
- * Creates the {@code ConditionalExistenceMap}.
- *
- * @param map
- * @param entryExistenceCondition
- * @since 2019-10-17
- */
- private ConditionalExistenceMap(final Map<K, V> map,
- final Predicate<Entry<K, V>> entryExistenceCondition) {
- this.map = map;
- this.entryExistenceCondition = entryExistenceCondition;
- }
-
- @Override
- public boolean containsKey(final Object key) {
- if (!this.map.containsKey(key))
- return false;
-
- // only instances of K have mappings in the backing map
- // since we know that key is a valid key, it must be an instance of K
- @SuppressWarnings("unchecked")
- final K keyAsK = (K) key;
-
- // get and test entry
- final V value = this.map.get(key);
- final Entry<K, V> entry = new SimpleEntry<>(keyAsK, value);
- return this.entryExistenceCondition.test(entry);
- }
-
- @Override
- public Set<Entry<K, V>> entrySet() {
- return conditionalExistenceSet(this.map.entrySet(),
- this.entryExistenceCondition);
- }
-
- @Override
- public V get(final Object key) {
- return this.containsKey(key) ? this.map.get(key) : null;
- }
-
- private final Entry<K, V> getEntry(K key) {
- return new Entry<K, V>() {
- @Override
- public K getKey() {
- return key;
- }
-
- @Override
- public V getValue() {
- return ConditionalExistenceMap.this.map.get(key);
- }
-
- @Override
- public V setValue(V value) {
- return ConditionalExistenceMap.this.map.put(key, value);
- }
- };
- }
-
- @Override
- public Set<K> keySet() {
- return conditionalExistenceSet(super.keySet(),
- k -> this.entryExistenceCondition.test(this.getEntry(k)));
- }
-
- @Override
- public V put(final K key, final V value) {
- final V oldValue = this.map.put(key, value);
-
- // get and test entry
- final Entry<K, V> entry = new SimpleEntry<>(key, oldValue);
- return this.entryExistenceCondition.test(entry) ? oldValue : null;
- }
-
- @Override
- public V remove(final Object key) {
- final V oldValue = this.map.remove(key);
- return this.containsKey(key) ? oldValue : null;
- }
-
- @Override
- public Collection<V> values() {
- // maybe change this to use ConditionalExistenceCollection
- return super.values();
- }
- }
-
- /**
- * Elements in this set only exist if a certain condition is true.
- *
- * @author Adrien Hopkins
- * @since 2019-10-17
- * @param <E> type of element in set
- */
- static final class ConditionalExistenceSet<E> extends AbstractSet<E> {
- private final Set<E> set;
- private final Predicate<E> existenceCondition;
-
- /**
- * Creates the {@code ConditionalNonexistenceSet}.
- *
- * @param set set to use
- * @param existenceCondition condition where element exists
- * @since 2019-10-17
- */
- private ConditionalExistenceSet(final Set<E> set,
- final Predicate<E> existenceCondition) {
- this.set = set;
- this.existenceCondition = existenceCondition;
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * Note that this method returns {@code false} if {@code e} does not pass
- * the existence condition.
- */
- @Override
- public boolean add(final E e) {
- return this.set.add(e) && this.existenceCondition.test(e);
- }
-
- @Override
- public void clear() {
- this.set.clear();
- }
-
- @Override
- public boolean contains(final Object o) {
- if (!this.set.contains(o))
- return false;
-
- // this set can only contain instances of E
- // since the object is in the set, we know that it must be an instance
- // of E
- // therefore this cast will always work
- @SuppressWarnings("unchecked")
- final E e = (E) o;
-
- return this.existenceCondition.test(e);
- }
-
- @Override
- public Iterator<E> iterator() {
- return conditionalExistenceIterator(this.set.iterator(),
- this.existenceCondition);
- }
-
- @Override
- public boolean remove(final Object o) {
- // remove() must be first in the && statement, otherwise it may not
- // execute
- final boolean containedObject = this.contains(o);
- return this.set.remove(o) && containedObject;
- }
-
- @Override
- public int size() {
- return (int) this.set.stream().filter(this.existenceCondition).count();
- }
-
- @Override
- public Object[] toArray() {
- // ensure the toArray operation is supported
- this.set.toArray();
-
- // if it works, do it for real
- return super.toArray();
- }
-
- @Override
- public <T> T[] toArray(T[] a) {
- // ensure the toArray operation is supported
- this.set.toArray();
-
- // if it works, do it for real
- return super.toArray(a);
- }
- }
-
- /**
- * Elements in the returned wrapper collection are ignored if they don't pass
- * a condition.
- *
- * @param <E> type of elements in collection
- * @param collection collection to wrap
- * @param existenceCondition elements only exist if this returns true
- * @return wrapper collection
- * @since 2019-10-17
- */
- public static final <E> Collection<E> conditionalExistenceCollection(
- final Collection<E> collection,
- final Predicate<E> existenceCondition) {
- return new ConditionalExistenceCollection<>(collection,
- existenceCondition);
- }
-
- /**
- * Elements in the returned wrapper iterator are ignored if they don't pass a
- * condition.
- *
- * @param <E> type of elements in iterator
- * @param iterator iterator to wrap
- * @param existenceCondition elements only exist if this returns true
- * @return wrapper iterator
- * @since 2019-10-17
- */
- public static final <E> Iterator<E> conditionalExistenceIterator(
- final Iterator<E> iterator, final Predicate<E> existenceCondition) {
- return new ConditionalExistenceIterator<>(iterator, existenceCondition);
- }
-
- /**
- * Mappings in the returned wrapper map are ignored if the corresponding
- * entry doesn't pass a condition
- *
- * @param <K> type of key in map
- * @param <V> type of value in map
- * @param map map to wrap
- * @param entryExistenceCondition mappings only exist if this returns true
- * @return wrapper map
- * @since 2019-10-17
- */
- public static final <K, V> Map<K, V> conditionalExistenceMap(
- final Map<K, V> map,
- final Predicate<Entry<K, V>> entryExistenceCondition) {
- return new ConditionalExistenceMap<>(map, entryExistenceCondition);
- }
-
- /**
- * Elements in the returned wrapper set are ignored if they don't pass a
- * condition.
- *
- * @param <E> type of elements in set
- * @param set set to wrap
- * @param existenceCondition elements only exist if this returns true
- * @return wrapper set
- * @since 2019-10-17
- */
- public static final <E> Set<E> conditionalExistenceSet(final Set<E> set,
- final Predicate<E> existenceCondition) {
- return new ConditionalExistenceSet<>(set, existenceCondition);
- }
-}
diff --git a/src/org/unitConverter/math/ConditionalExistenceCollectionsTest.java b/src/org/unitConverter/math/ConditionalExistenceCollectionsTest.java
deleted file mode 100644
index 311ace5..0000000
--- a/src/org/unitConverter/math/ConditionalExistenceCollectionsTest.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.math;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.NoSuchElementException;
-
-import org.junit.jupiter.api.Test;
-import org.unitConverter.math.ConditionalExistenceCollections.ConditionalExistenceIterator;
-
-/**
- * Tests the {@link #ConditionalExistenceCollections}.
- *
- * @author Adrien Hopkins
- * @since 2019-10-16
- */
-class ConditionalExistenceCollectionsTest {
-
- /**
- * The returned iterator ignores elements that don't start with "a".
- *
- * @return test iterator
- * @since 2019-10-17
- */
- ConditionalExistenceIterator<String> getTestIterator() {
- final List<String> items = Arrays.asList("aa", "ab", "ba");
- final Iterator<String> it = items.iterator();
- final ConditionalExistenceIterator<String> cit = (ConditionalExistenceIterator<String>) ConditionalExistenceCollections
- .conditionalExistenceIterator(it, s -> s.startsWith("a"));
- return cit;
- }
-
- /**
- * The returned map ignores mappings where the value is zero.
- *
- * @return map to be used for test data
- * @since 2019-10-16
- */
- Map<String, Integer> getTestMap() {
- final Map<String, Integer> map = new HashMap<>();
- map.put("one", 1);
- map.put("two", 2);
- map.put("zero", 0);
- map.put("ten", 10);
- final Map<String, Integer> conditionalMap = ConditionalExistenceCollections.conditionalExistenceMap(map,
- e -> !Integer.valueOf(0).equals(e.getValue()));
- return conditionalMap;
- }
-
- /**
- * Test method for {@link org.unitConverter.math.ZeroIsNullMap#containsKey(java.lang.Object)}.
- */
- @Test
- void testContainsKeyObject() {
- final Map<String, Integer> 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<String, Integer> 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<String, Integer> map = this.getTestMap();
- for (final Entry<String, Integer> e : map.entrySet()) {
- assertTrue(e.getValue() != 0);
- }
- }
-
- /**
- * Test method for {@link org.unitConverter.math.ZeroIsNullMap#get(java.lang.Object)}.
- */
- @Test
- void testGetObject() {
- final Map<String, Integer> map = this.getTestMap();
- assertEquals(1, map.get("one"));
- assertEquals(10, map.get("ten"));
- assertEquals(null, map.get("five"));
- assertEquals(null, map.get("zero"));
- }
-
- @Test
- void testIterator() {
- final ConditionalExistenceIterator<String> testIterator = this.getTestIterator();
-
- assertTrue(testIterator.hasNext);
- assertTrue(testIterator.hasNext());
- assertEquals("aa", testIterator.nextElement);
- assertEquals("aa", testIterator.next());
-
- assertTrue(testIterator.hasNext);
- assertTrue(testIterator.hasNext());
- assertEquals("ab", testIterator.nextElement);
- assertEquals("ab", testIterator.next());
-
- assertFalse(testIterator.hasNext);
- assertFalse(testIterator.hasNext());
- assertEquals(null, testIterator.nextElement);
- assertThrows(NoSuchElementException.class, testIterator::next);
- }
-
- /**
- * Test method for {@link org.unitConverter.math.ZeroIsNullMap#keySet()}.
- */
- @Test
- void testKeySet() {
- final Map<String, Integer> map = this.getTestMap();
- assertFalse(map.keySet().contains("zero"));
- }
-
- /**
- * Test method for {@link org.unitConverter.math.ZeroIsNullMap#values()}.
- */
- @Test
- void testValues() {
- final Map<String, Integer> map = this.getTestMap();
- assertFalse(map.values().contains(0));
- }
-
-}
diff --git a/src/org/unitConverter/math/DecimalComparison.java b/src/org/unitConverter/math/DecimalComparison.java
deleted file mode 100644
index 0f5b91e..0000000
--- a/src/org/unitConverter/math/DecimalComparison.java
+++ /dev/null
@@ -1,256 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.math;
-
-import java.math.BigDecimal;
-
-/**
- * A class that contains methods to compare float and double values.
- *
- * @author Adrien Hopkins
- * @since 2019-03-18
- * @since v0.2.0
- */
-public final class DecimalComparison {
- /**
- * The value used for double comparison. If two double values are within this
- * value multiplied by the larger value, they are considered equal.
- *
- * @since 2019-03-18
- * @since v0.2.0
- */
- public static final double DOUBLE_EPSILON = 1.0e-15;
-
- /**
- * The value used for float comparison. If two float values are within this
- * value multiplied by the larger value, they are considered equal.
- *
- * @since 2019-03-18
- * @since v0.2.0
- */
- public static final float FLOAT_EPSILON = 1.0e-6f;
-
- /**
- * Tests for equality of double values using {@link #DOUBLE_EPSILON}.
- * <p>
- * <strong>WARNING: </strong>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.
- * <p>
- * If this does become a concern, some ways to solve this problem:
- * <ol>
- * <li>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)
- * <li>Use {@link BigDecimal} instead of {@code double} (this will make a
- * violation of transitivity 100% impossible)
- * </ol>
- *
- * @param a first value to test
- * @param b second value to test
- * @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);
- }
-
- /**
- * Tests for double equality using a custom epsilon value.
- *
- * <p>
- * <strong>WARNING: </strong>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.
- * <p>
- * If this does become a concern, some ways to solve this problem:
- * <ol>
- * <li>Raise the value of epsilon (this does not make a violation of
- * transitivity impossible, it just significantly reduces the chances of it
- * happening)
- * <li>Use {@link BigDecimal} instead of {@code double} (this will make a
- * violation of transitivity 100% impossible)
- * </ol>
- *
- * @param a first value to test
- * @param b second value to test
- * @param epsilon allowed difference
- * @return whether they are equal
- * @since 2019-03-18
- * @since v0.2.0
- */
- public static final boolean equals(final double a, final double b,
- final double epsilon) {
- return Math.abs(a - b) <= epsilon * Math.max(Math.abs(a), Math.abs(b));
- }
-
- /**
- * Tests for equality of float values using {@link #FLOAT_EPSILON}.
- *
- * <p>
- * <strong>WARNING: </strong>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.
- * <p>
- * If this does become a concern, some ways to solve this problem:
- * <ol>
- * <li>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)
- * <li>Use {@link BigDecimal} instead of {@code float} (this will make a
- * violation of transitivity 100% impossible)
- * </ol>
- *
- * @param a first value to test
- * @param b second value to test
- * @return whether they are equal
- * @since 2019-03-18
- * @since v0.2.0
- */
- public static final boolean equals(final float a, final float b) {
- return DecimalComparison.equals(a, b, FLOAT_EPSILON);
- }
-
- /**
- * Tests for float equality using a custom epsilon value.
- *
- * <p>
- * <strong>WARNING: </strong>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.
- * <p>
- * If this does become a concern, some ways to solve this problem:
- * <ol>
- * <li>Raise the value of epsilon (this does not make a violation of
- * transitivity impossible, it just significantly reduces the chances of it
- * happening)
- * <li>Use {@link BigDecimal} instead of {@code float} (this will make a
- * violation of transitivity 100% impossible)
- * </ol>
- *
- * @param a first value to test
- * @param b second value to test
- * @param epsilon allowed difference
- * @return whether they are equal
- * @since 2019-03-18
- * @since v0.2.0
- */
- public static final boolean equals(final float a, final float b,
- final float epsilon) {
- return Math.abs(a - b) <= epsilon * Math.max(Math.abs(a), Math.abs(b));
- }
-
- /**
- * Tests for equality of {@code UncertainDouble} values using
- * {@link #DOUBLE_EPSILON}.
- * <p>
- * <strong>WARNING: </strong>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.
- * <p>
- * If this does become a concern, some ways to solve this problem:
- * <ol>
- * <li>Raise the value of epsilon using
- * {@link #equals(UncertainDouble, UncertainDouble, double)} (this does not
- * make a violation of transitivity impossible, it just significantly reduces
- * the chances of it happening)
- * <li>Use {@link BigDecimal} instead of {@code double} (this will make a
- * violation of transitivity 100% impossible)
- * </ol>
- *
- * @param a first value to test
- * @param b second value to test
- * @return whether they are equal
- * @since 2020-09-07
- * @see #hashCode(double)
- */
- public static final boolean equals(final UncertainDouble a,
- final UncertainDouble b) {
- return DecimalComparison.equals(a.value(), b.value())
- && DecimalComparison.equals(a.uncertainty(), b.uncertainty());
- }
-
- /**
- * Tests for {@code UncertainDouble} equality using a custom epsilon value.
- *
- * <p>
- * <strong>WARNING: </strong>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.
- * <p>
- * If this does become a concern, some ways to solve this problem:
- * <ol>
- * <li>Raise the value of epsilon (this does not make a violation of
- * transitivity impossible, it just significantly reduces the chances of it
- * happening)
- * <li>Use {@link BigDecimal} instead of {@code double} (this will make a
- * violation of transitivity 100% impossible)
- * </ol>
- *
- * @param a first value to test
- * @param b second value to test
- * @param epsilon allowed difference
- * @return whether they are equal
- * @since 2019-03-18
- * @since v0.2.0
- */
- public static final boolean equals(final UncertainDouble a,
- final UncertainDouble b, final double epsilon) {
- return DecimalComparison.equals(a.value(), b.value(), epsilon)
- && DecimalComparison.equals(a.uncertainty(), b.uncertainty(),
- epsilon);
- }
-
- /**
- * 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/math/ExpressionParser.java b/src/org/unitConverter/math/ExpressionParser.java
deleted file mode 100644
index 8a0e97d..0000000
--- a/src/org/unitConverter/math/ExpressionParser.java
+++ /dev/null
@@ -1,708 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.math;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.function.BinaryOperator;
-import java.util.function.Function;
-import java.util.function.UnaryOperator;
-
-/**
- * An object that can parse expressions with unary or binary operators.
- *
- * @author Adrien Hopkins
- * @param <T>
- * type of object that exists in parsed expressions
- * @since 2019-03-14
- * @since v0.2.0
- */
-public final class ExpressionParser<T> {
- /**
- * A builder that can create {@code ExpressionParser<T>} instances.
- *
- * @author Adrien Hopkins
- * @param <T>
- * type of object that exists in parsed expressions
- * @since 2019-03-17
- * @since v0.2.0
- */
- public static final class Builder<T> {
- /**
- * A function that obtains a parseable object from a string. For example, an integer {@code ExpressionParser}
- * would use {@code Integer::parseInt}.
- *
- * @since 2019-03-14
- * @since v0.2.0
- */
- private final Function<String, ? extends T> objectObtainer;
-
- /**
- * The function of the space as an operator (like 3 x y)
- *
- * @since 2019-03-22
- * @since v0.2.0
- */
- private String spaceFunction = null;
-
- /**
- * A map mapping operator strings to operator functions, for unary operators.
- *
- * @since 2019-03-14
- * @since v0.2.0
- */
- private final Map<String, PriorityUnaryOperator<T>> unaryOperators;
-
- /**
- * A map mapping operator strings to operator functions, for binary operators.
- *
- * @since 2019-03-14
- * @since v0.2.0
- */
- private final Map<String, PriorityBinaryOperator<T>> binaryOperators;
-
- /**
- * Creates the {@code Builder}.
- *
- * @param objectObtainer
- * a function that can turn strings into objects of the type handled by the parser.
- * @throws NullPointerException
- * if {@code objectObtainer} is null
- * @since 2019-03-17
- * @since v0.2.0
- */
- public Builder(final Function<String, ? extends T> objectObtainer) {
- this.objectObtainer = Objects.requireNonNull(objectObtainer, "objectObtainer must not be null.");
- this.unaryOperators = new HashMap<>();
- this.binaryOperators = new HashMap<>();
- }
-
- /**
- * Adds a binary operator to the builder.
- *
- * @param text
- * text used to reference the operator, like '+'
- * @param operator
- * operator to add
- * @param priority
- * operator's priority, which determines which operators are applied first
- * @return this builder
- * @throws NullPointerException
- * if {@code text} or {@code operator} is null
- * @since 2019-03-17
- * @since v0.2.0
- */
- public Builder<T> addBinaryOperator(final String text, final BinaryOperator<T> operator, final int priority) {
- Objects.requireNonNull(text, "text must not be null.");
- Objects.requireNonNull(operator, "operator must not be null.");
-
- // Unfortunately, I cannot use a lambda because the PriorityBinaryOperator requires arguments.
- final PriorityBinaryOperator<T> priorityOperator = new PriorityBinaryOperator<T>(priority) {
- @Override
- public T apply(final T t, final T u) {
- return operator.apply(t, u);
- }
-
- };
- this.binaryOperators.put(text, priorityOperator);
- return this;
- }
-
- /**
- * Adds a function for spaces. You must use the text of an existing binary operator.
- *
- * @param operator
- * text of operator to use
- * @return this builder
- * @since 2019-03-22
- * @since v0.2.0
- */
- public Builder<T> addSpaceFunction(final String operator) {
- Objects.requireNonNull(operator, "operator must not be null.");
-
- if (!this.binaryOperators.containsKey(operator))
- throw new IllegalArgumentException(String.format("Could not find binary operator '%s'", operator));
-
- this.spaceFunction = operator;
- return this;
- }
-
- /**
- * Adds a unary operator to the builder.
- *
- * @param text
- * text used to reference the operator, like '-'
- * @param operator
- * operator to add
- * @param priority
- * operator's priority, which determines which operators are applied first
- * @return this builder
- * @throws NullPointerException
- * if {@code text} or {@code operator} is null
- * @since 2019-03-17
- * @since v0.2.0
- */
- public Builder<T> addUnaryOperator(final String text, final UnaryOperator<T> operator, final int priority) {
- Objects.requireNonNull(text, "text must not be null.");
- Objects.requireNonNull(operator, "operator must not be null.");
-
- // Unfortunately, I cannot use a lambda because the PriorityUnaryOperator requires arguments.
- final PriorityUnaryOperator<T> priorityOperator = new PriorityUnaryOperator<T>(priority) {
- @Override
- public T apply(final T t) {
- return operator.apply(t);
- }
- };
- this.unaryOperators.put(text, priorityOperator);
- return this;
- }
-
- /**
- * @return an {@code ExpressionParser<T>} instance with the properties given to this builder
- * @since 2019-03-17
- * @since v0.2.0
- */
- public ExpressionParser<T> build() {
- return new ExpressionParser<>(this.objectObtainer, this.unaryOperators, this.binaryOperators,
- this.spaceFunction);
- }
- }
-
- /**
- * A binary operator with a priority field that determines which operators apply first.
- *
- * @author Adrien Hopkins
- * @param <T>
- * type of operand and result
- * @since 2019-03-17
- * @since v0.2.0
- */
- private static abstract class PriorityBinaryOperator<T>
- implements BinaryOperator<T>, Comparable<PriorityBinaryOperator<T>> {
- /**
- * The operator's priority. Higher-priority operators are applied before lower-priority operators
- *
- * @since 2019-03-17
- * @since v0.2.0
- */
- private final int priority;
-
- /**
- * Creates the {@code PriorityBinaryOperator}.
- *
- * @param priority
- * operator's priority
- * @since 2019-03-17
- * @since v0.2.0
- */
- public PriorityBinaryOperator(final int priority) {
- this.priority = priority;
- }
-
- /**
- * Compares this object to another by priority.
- *
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @since 2019-03-17
- * @since v0.2.0
- */
- @Override
- public int compareTo(final PriorityBinaryOperator<T> o) {
- if (this.priority < o.priority)
- return -1;
- else if (this.priority > o.priority)
- return 1;
- else
- return 0;
- }
-
- /**
- * @return priority
- * @since 2019-03-22
- * @since v0.2.0
- */
- public final int getPriority() {
- return this.priority;
- }
- }
-
- /**
- * A unary operator with a priority field that determines which operators apply first.
- *
- * @author Adrien Hopkins
- * @param <T>
- * type of operand and result
- * @since 2019-03-17
- * @since v0.2.0
- */
- private static abstract class PriorityUnaryOperator<T>
- implements UnaryOperator<T>, Comparable<PriorityUnaryOperator<T>> {
- /**
- * The operator's priority. Higher-priority operators are applied before lower-priority operators
- *
- * @since 2019-03-17
- * @since v0.2.0
- */
- private final int priority;
-
- /**
- * Creates the {@code PriorityUnaryOperator}.
- *
- * @param priority
- * operator's priority
- * @since 2019-03-17
- * @since v0.2.0
- */
- public PriorityUnaryOperator(final int priority) {
- this.priority = priority;
- }
-
- /**
- * Compares this object to another by priority.
- *
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @since 2019-03-17
- * @since v0.2.0
- */
- @Override
- public int compareTo(final PriorityUnaryOperator<T> o) {
- if (this.priority < o.priority)
- return -1;
- else if (this.priority > o.priority)
- return 1;
- else
- return 0;
- }
-
- /**
- * @return priority
- * @since 2019-03-22
- * @since v0.2.0
- */
- public final int getPriority() {
- return this.priority;
- }
- }
-
- /**
- * The types of tokens that are available.
- *
- * @author Adrien Hopkins
- * @since 2019-03-14
- * @since v0.2.0
- */
- private static enum TokenType {
- OBJECT, UNARY_OPERATOR, BINARY_OPERATOR;
- }
-
- /**
- * The opening bracket.
- *
- * @since 2019-03-22
- * @since v0.2.0
- */
- public static final char OPENING_BRACKET = '(';
-
- /**
- * The closing bracket.
- *
- * @since 2019-03-22
- * @since v0.2.0
- */
- public static final char CLOSING_BRACKET = ')';
-
- /**
- * Finds the other bracket in a pair of brackets, given the position of one.
- *
- * @param string
- * string that contains brackets
- * @param bracketPosition
- * position of first bracket
- * @return position of matching bracket
- * @throws NullPointerException
- * if string is null
- * @since 2019-03-22
- * @since v0.2.0
- */
- private static int findBracketPair(final String string, final int bracketPosition) {
- Objects.requireNonNull(string, "string must not be null.");
-
- final char openingBracket = string.charAt(bracketPosition);
-
- // figure out what closing bracket to look for
- final char closingBracket;
- switch (openingBracket) {
- case '(':
- closingBracket = ')';
- break;
- case '[':
- closingBracket = ']';
- break;
- case '{':
- closingBracket = '}';
- break;
- default:
- throw new IllegalArgumentException(String.format("Invalid bracket '%s'", openingBracket));
- }
-
- // level of brackets. every opening bracket increments this; every closing bracket decrements it
- int bracketLevel = 0;
-
- // iterate over the string to find the closing bracket
- for (int currentPosition = bracketPosition; currentPosition < string.length(); currentPosition++) {
- final char currentCharacter = string.charAt(currentPosition);
-
- if (currentCharacter == openingBracket) {
- bracketLevel++;
- } else if (currentCharacter == closingBracket) {
- bracketLevel--;
- if (bracketLevel == 0)
- return currentPosition;
- }
- }
-
- throw new IllegalArgumentException("No matching bracket found.");
- }
-
- /**
- * A function that obtains a parseable object from a string. For example, an integer {@code ExpressionParser} would
- * use {@code Integer::parseInt}.
- *
- * @since 2019-03-14
- * @since v0.2.0
- */
- private final Function<String, ? extends T> objectObtainer;
-
- /**
- * A map mapping operator strings to operator functions, for unary operators.
- *
- * @since 2019-03-14
- * @since v0.2.0
- */
- private final Map<String, PriorityUnaryOperator<T>> unaryOperators;
-
- /**
- * A map mapping operator strings to operator functions, for binary operators.
- *
- * @since 2019-03-14
- * @since v0.2.0
- */
- private final Map<String, PriorityBinaryOperator<T>> binaryOperators;
-
- /**
- * The operator for space, or null if spaces have no function.
- *
- * @since 2019-03-22
- * @since v0.2.0
- */
- private final String spaceOperator;
-
- /**
- * Creates the {@code ExpressionParser}.
- *
- * @param objectObtainer
- * function to get objects from strings
- * @param unaryOperators
- * unary operators available to the parser
- * @param binaryOperators
- * binary operators available to the parser
- * @param spaceOperator
- * operator used by spaces
- * @since 2019-03-14
- * @since v0.2.0
- */
- private ExpressionParser(final Function<String, ? extends T> objectObtainer,
- final Map<String, PriorityUnaryOperator<T>> unaryOperators,
- final Map<String, PriorityBinaryOperator<T>> binaryOperators, final String spaceOperator) {
- this.objectObtainer = objectObtainer;
- this.unaryOperators = unaryOperators;
- this.binaryOperators = binaryOperators;
- this.spaceOperator = spaceOperator;
- }
-
- /**
- * Converts a given mathematical expression to reverse Polish notation (operators after operands).
- * <p>
- * For example,<br>
- * {@code 2 * (3 + 4)}<br>
- * becomes<br>
- * {@code 2 3 4 + *}.
- *
- * @param expression
- * expression
- * @return expression in RPN
- * @since 2019-03-17
- * @since v0.2.0
- */
- private String convertExpressionToReversePolish(final String expression) {
- Objects.requireNonNull(expression, "expression must not be null.");
-
- final List<String> components = new ArrayList<>();
-
- // the part of the expression remaining to parse
- String partialExpression = expression;
-
- // find and deal with brackets
- while (partialExpression.indexOf(OPENING_BRACKET) != -1) {
- final int openingBracketPosition = partialExpression.indexOf(OPENING_BRACKET);
- final int closingBracketPosition = findBracketPair(partialExpression, openingBracketPosition);
-
- // check for function
- if (openingBracketPosition > 0 && partialExpression.charAt(openingBracketPosition - 1) != ' ') {
- // function like sin(2) or tempF(32)
- // find the position of the last space
- int spacePosition = openingBracketPosition;
- while (spacePosition >= 0 && partialExpression.charAt(spacePosition) != ' ') {
- spacePosition--;
- }
- // then split the function into pre-function and function, using the space position
- components.addAll(Arrays.asList(partialExpression.substring(0, spacePosition + 1).split(" ")));
- components.add(partialExpression.substring(spacePosition + 1, closingBracketPosition + 1));
- partialExpression = partialExpression.substring(closingBracketPosition + 1);
- } else {
- // normal brackets like (1 + 2) * (3 / 5)
- components.addAll(Arrays.asList(partialExpression.substring(0, openingBracketPosition).split(" ")));
- components.add(this.convertExpressionToReversePolish(
- partialExpression.substring(openingBracketPosition + 1, closingBracketPosition)));
- partialExpression = partialExpression.substring(closingBracketPosition + 1);
- }
- }
-
- // add everything else
- components.addAll(Arrays.asList(partialExpression.split(" ")));
-
- // remove empty entries
- while (components.contains("")) {
- components.remove("");
- }
-
- // deal with space multiplication (x y)
- if (this.spaceOperator != null) {
- for (int i = 0; i < components.size() - 1; i++) {
- if (this.getTokenType(components.get(i)) == TokenType.OBJECT
- && this.getTokenType(components.get(i + 1)) == TokenType.OBJECT) {
- components.add(++i, this.spaceOperator);
- }
- }
- }
-
- // turn the expression into reverse Polish
- while (true) {
- final int highestPriorityOperatorPosition = this.findHighestPriorityOperatorPosition(components);
- if (highestPriorityOperatorPosition == -1) {
- break;
- }
-
- // swap components based on what kind of operator there is
- // 1 + 2 becomes 2 1 +
- // - 1 becomes 1 -
- switch (this.getTokenType(components.get(highestPriorityOperatorPosition))) {
- case UNARY_OPERATOR:
- final String unaryOperator = components.remove(highestPriorityOperatorPosition);
- final String operand = components.remove(highestPriorityOperatorPosition);
- components.add(highestPriorityOperatorPosition, operand + " " + unaryOperator);
- break;
- case BINARY_OPERATOR:
- final String binaryOperator = components.remove(highestPriorityOperatorPosition);
- final String operand1 = components.remove(highestPriorityOperatorPosition - 1);
- final String operand2 = components.remove(highestPriorityOperatorPosition - 1);
- components.add(highestPriorityOperatorPosition - 1,
- operand2 + " " + operand1 + " " + binaryOperator);
- break;
- default:
- throw new AssertionError("Expected operator, found non-operator.");
- }
- }
-
- // join all of the components together, then ensure there is only one space in a row
- String expressionRPN = String.join(" ", components).replaceAll(" +", " ");
-
- while (expressionRPN.charAt(0) == ' ') {
- expressionRPN = expressionRPN.substring(1);
- }
- while (expressionRPN.charAt(expressionRPN.length() - 1) == ' ') {
- expressionRPN = expressionRPN.substring(0, expressionRPN.length() - 1);
- }
- return expressionRPN;
- }
-
- /**
- * Finds the position of the highest-priority operator in a list
- *
- * @param components
- * components to test
- * @param blacklist
- * positions of operators that should be ignored
- * @return position of highest priority, or -1 if the list contains no operators
- * @throws NullPointerException
- * if components is null
- * @since 2019-03-22
- * @since v0.2.0
- */
- private int findHighestPriorityOperatorPosition(final List<String> components) {
- Objects.requireNonNull(components, "components must not be null.");
- // find highest priority
- int maxPriority = Integer.MIN_VALUE;
- int maxPriorityPosition = -1;
-
- // go over components one by one
- // if it is an operator, test its priority to see if it's max
- // if it is, update maxPriority and maxPriorityPosition
- for (int i = 0; i < components.size(); i++) {
-
- switch (this.getTokenType(components.get(i))) {
- case UNARY_OPERATOR:
- final PriorityUnaryOperator<T> unaryOperator = this.unaryOperators.get(components.get(i));
- final int unaryPriority = unaryOperator.getPriority();
-
- if (unaryPriority > maxPriority) {
- maxPriority = unaryPriority;
- maxPriorityPosition = i;
- }
- break;
- case BINARY_OPERATOR:
- final PriorityBinaryOperator<T> binaryOperator = this.binaryOperators.get(components.get(i));
- final int binaryPriority = binaryOperator.getPriority();
-
- if (binaryPriority > maxPriority) {
- maxPriority = binaryPriority;
- maxPriorityPosition = i;
- }
- break;
- default:
- break;
- }
- }
-
- // max priority position found
- return maxPriorityPosition;
- }
-
- /**
- * Determines whether an inputted string is an object or an operator
- *
- * @param token
- * string to input
- * @return type of token it is
- * @throws NullPointerException
- * if {@code expression} is null
- * @since 2019-03-14
- * @since v0.2.0
- */
- private TokenType getTokenType(final String token) {
- Objects.requireNonNull(token, "token must not be null.");
-
- if (this.unaryOperators.containsKey(token))
- return TokenType.UNARY_OPERATOR;
- else if (this.binaryOperators.containsKey(token))
- return TokenType.BINARY_OPERATOR;
- else
- return TokenType.OBJECT;
- }
-
- /**
- * Parses an expression.
- *
- * @param expression
- * expression to parse
- * @return result
- * @throws NullPointerException
- * if {@code expression} is null
- * @since 2019-03-14
- * @since v0.2.0
- */
- public T parseExpression(final String expression) {
- return this.parseReversePolishExpression(this.convertExpressionToReversePolish(expression));
- }
-
- /**
- * Parses an expression expressed in reverse Polish notation.
- *
- * @param expression
- * expression to parse
- * @return result
- * @throws NullPointerException
- * if {@code expression} is null
- * @since 2019-03-14
- * @since v0.2.0
- */
- private T parseReversePolishExpression(final String expression) {
- Objects.requireNonNull(expression, "expression must not be null.");
-
- final Deque<T> stack = new ArrayDeque<>();
-
- // iterate over every item in the expression, then
- for (final String item : expression.split(" ")) {
- // choose a path based on what kind of thing was just read
- switch (this.getTokenType(item)) {
-
- case BINARY_OPERATOR:
- if (stack.size() < 2)
- throw new IllegalStateException(String.format(
- "Attempted to call binary operator %s with only %d arguments.", item, stack.size()));
-
- // get two arguments and operator, then apply!
- final T o1 = stack.pop();
- final T o2 = stack.pop();
- final BinaryOperator<T> binaryOperator = this.binaryOperators.get(item);
-
- stack.push(binaryOperator.apply(o1, o2));
- break;
-
- case OBJECT:
- // just add it to the stack
- stack.push(this.objectObtainer.apply(item));
- break;
-
- case UNARY_OPERATOR:
- if (stack.size() < 1)
- throw new IllegalStateException(String.format(
- "Attempted to call unary operator %s with only %d arguments.", item, stack.size()));
-
- // get one argument and operator, then apply!
- final T o = stack.pop();
- final UnaryOperator<T> unaryOperator = this.unaryOperators.get(item);
-
- stack.push(unaryOperator.apply(o));
- break;
- default:
- throw new AssertionError(
- String.format("Internal error: Invalid token type %s.", this.getTokenType(item)));
-
- }
- }
-
- // return answer, or throw an exception if I can't
- if (stack.size() > 1)
- throw new IllegalStateException("Computation ended up with more than one answer.");
- else if (stack.size() == 0)
- throw new IllegalStateException("Computation ended up without an answer.");
- return stack.pop();
- }
-}
diff --git a/src/org/unitConverter/math/ExpressionParserTest.java b/src/org/unitConverter/math/ExpressionParserTest.java
deleted file mode 100644
index f3180c1..0000000
--- a/src/org/unitConverter/math/ExpressionParserTest.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.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<Integer> 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/math/ObjectProduct.java b/src/org/unitConverter/math/ObjectProduct.java
deleted file mode 100644
index bf00647..0000000
--- a/src/org/unitConverter/math/ObjectProduct.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/**
- * Copyright (C) 2018 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.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;
-import java.util.function.Function;
-
-/**
- * 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<T> {
- /**
- * Returns an empty ObjectProduct of a certain type
- *
- * @param <T>
- * type of objects that can be multiplied
- * @return empty product
- * @since 2019-10-16
- */
- public static final <T> ObjectProduct<T> empty() {
- return new ObjectProduct<>(new HashMap<>());
- }
-
- /**
- * Gets an {@code ObjectProduct} from an object-to-integer mapping
- *
- * @param <T>
- * type of object in product
- * @param map
- * map mapping objects to exponents
- * @return object product
- * @since 2019-10-16
- */
- public static final <T> ObjectProduct<T> fromExponentMapping(final Map<T, Integer> 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 <T> ObjectProduct<T> oneOf(final T object) {
- Objects.requireNonNull(object, "object must not be null.");
- final Map<T, Integer> 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<T, Integer> exponents;
-
- /**
- * Creates the {@code ObjectProduct}.
- *
- * @param exponents
- * objects that make up this product
- * @since 2019-10-16
- */
- private ObjectProduct(final Map<T, Integer> exponents) {
- this.exponents = Collections.unmodifiableMap(ConditionalExistenceCollections.conditionalExistenceMap(exponents,
- e -> !Integer.valueOf(0).equals(e.getValue())));
- }
-
- /**
- * 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<T> dividedBy(final ObjectProduct<T> other) {
- Objects.requireNonNull(other, "other must not be null.");
- // get a list of all objects in both sets
- final Set<T> objects = new HashSet<>();
- objects.addAll(this.getBaseSet());
- objects.addAll(other.getBaseSet());
-
- // get a list of all exponents
- final Map<T, Integer> 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<T, Integer> 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<T> getBaseSet() {
- final Set<T> 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 single object, i.e. it has one exponent of one and no other nonzero exponents
- * @since 2019-10-16
- */
- public boolean isSingleObject() {
- 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<T> times(final ObjectProduct<T> other) {
- Objects.requireNonNull(other, "other must not be null.");
- // get a list of all objects in both sets
- final Set<T> objects = new HashSet<>();
- objects.addAll(this.getBaseSet());
- objects.addAll(other.getBaseSet());
-
- // get a list of all exponents
- final Map<T, Integer> 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<T> toExponent(final int exponent) {
- final Map<T, Integer> map = new HashMap<>(this.exponents);
- for (final T key : this.exponents.keySet()) {
- map.put(key, this.getExponent(key) * exponent);
- }
- return new ObjectProduct<>(map);
- }
-
- /**
- * Converts this product to a string using the objects' {@link Object#toString()} method. If objects have a long
- * toString representation, it is recommended to use {@link #toString(Function)} instead to shorten the returned
- * string.
- *
- * <p>
- * {@inheritDoc}
- */
- @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<T, String> objectToString) {
- final List<String> positiveStringComponents = new ArrayList<>();
- final List<String> negativeStringComponents = new ArrayList<>();
-
- // 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", objectToString.apply(object), exponent));
- } else if (exponent < 0) {
- negativeStringComponents.add(String.format("%s^%d", objectToString.apply(object), -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
deleted file mode 100644
index afd18b7..0000000
--- a/src/org/unitConverter/math/ObjectProductTest.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/**
- * Copyright (C) 2018 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.math;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.unitConverter.unit.SI.Dimensions.AREA;
-import static org.unitConverter.unit.SI.Dimensions.ENERGY;
-import static org.unitConverter.unit.SI.Dimensions.LENGTH;
-import static org.unitConverter.unit.SI.Dimensions.MASS;
-import static org.unitConverter.unit.SI.Dimensions.MASS_DENSITY;
-import static org.unitConverter.unit.SI.Dimensions.QUANTITY;
-import static org.unitConverter.unit.SI.Dimensions.TIME;
-import static org.unitConverter.unit.SI.Dimensions.VOLUME;
-
-import org.junit.jupiter.api.Test;
-import org.unitConverter.unit.SI;
-
-/**
- * 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(SI.BaseDimensions.LENGTH));
- assertEquals(3, VOLUME.getExponent(SI.BaseDimensions.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/UncertainDouble.java b/src/org/unitConverter/math/UncertainDouble.java
deleted file mode 100644
index 3651bd5..0000000
--- a/src/org/unitConverter/math/UncertainDouble.java
+++ /dev/null
@@ -1,419 +0,0 @@
-/**
- * Copyright (C) 2020 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.math;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.Objects;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * A double with an associated uncertainty value. For example, 3.2 ± 0.2.
- * <p>
- * All methods in this class throw a NullPointerException if any of their
- * arguments is null.
- *
- * @since 2020-09-07
- */
-public final class UncertainDouble implements Comparable<UncertainDouble> {
- /**
- * The exact value 0
- */
- public static final UncertainDouble ZERO = UncertainDouble.of(0, 0);
-
- /**
- * A regular expression that can recognize toString forms
- */
- private static final Pattern TO_STRING = Pattern
- .compile("([a-zA-Z_0-9\\.\\,]+)" // a number
- // optional "± [number]"
- + "(?:\\s*(?:±|\\+-)\\s*([a-zA-Z_0-9\\.\\,]+))?");
-
- /**
- * Parses a string in the form of {@link UncertainDouble#toString(boolean)}
- * and returns the corresponding {@code UncertainDouble} instance.
- * <p>
- * This method allows some alternative forms of the string representation,
- * such as using "+-" instead of "±".
- *
- * @param s string to parse
- * @return {@code UncertainDouble} instance
- * @throws IllegalArgumentException if the string is invalid
- * @since 2020-09-07
- */
- public static final UncertainDouble fromString(String s) {
- Objects.requireNonNull(s, "s may not be null");
- final Matcher matcher = TO_STRING.matcher(s);
-
- double value, uncertainty;
- try {
- value = Double.parseDouble(matcher.group(1));
- } catch (IllegalStateException | NumberFormatException e) {
- throw new IllegalArgumentException(
- "String " + s + " not in correct format.");
- }
-
- final String uncertaintyString = matcher.group(2);
- if (uncertaintyString == null) {
- uncertainty = 0;
- } else {
- try {
- uncertainty = Double.parseDouble(uncertaintyString);
- } catch (final NumberFormatException e) {
- throw new IllegalArgumentException(
- "String " + s + " not in correct format.");
- }
- }
-
- return UncertainDouble.of(value, uncertainty);
- }
-
- /**
- * Gets an {@code UncertainDouble} from its value and <b>absolute</b>
- * uncertainty.
- *
- * @since 2020-09-07
- */
- public static final UncertainDouble of(double value, double uncertainty) {
- return new UncertainDouble(value, uncertainty);
- }
-
- /**
- * Gets an {@code UncertainDouble} from its value and <b>relative</b>
- * uncertainty.
- *
- * @since 2020-09-07
- */
- public static final UncertainDouble ofRelative(double value,
- double relativeUncertainty) {
- return new UncertainDouble(value, value * relativeUncertainty);
- }
-
- private final double value;
-
- private final double uncertainty;
-
- /**
- * @param value
- * @param uncertainty
- * @since 2020-09-07
- */
- private UncertainDouble(double value, double uncertainty) {
- this.value = value;
- // uncertainty should only ever be positive
- this.uncertainty = Math.abs(uncertainty);
- }
-
- /**
- * Compares this {@code UncertainDouble} with another
- * {@code UncertainDouble}.
- * <p>
- * This method only compares the values, not the uncertainties. So 3.1 ± 0.5
- * is considered less than 3.2 ± 0.5, even though they are equivalent.
- * <p>
- * <b>Note:</b> The natural ordering of this class is inconsistent with
- * equals. Specifically, if two {@code UncertainDouble} instances {@code a}
- * and {@code b} have the same value but different uncertainties,
- * {@code a.compareTo(b)} will return 0 but {@code a.equals(b)} will return
- * {@code false}.
- */
- @Override
- public final int compareTo(UncertainDouble o) {
- return Double.compare(this.value, o.value);
- }
-
- /**
- * Returns the quotient of {@code this} and {@code other}.
- *
- * @since 2020-09-07
- */
- public final UncertainDouble dividedBy(UncertainDouble other) {
- Objects.requireNonNull(other, "other may not be null");
- return UncertainDouble.ofRelative(this.value / other.value, Math
- .hypot(this.relativeUncertainty(), other.relativeUncertainty()));
- }
-
- /**
- * Returns the quotient of {@code this} and the exact value {@code other}.
- *
- * @since 2020-09-07
- */
- public final UncertainDouble dividedByExact(double other) {
- return UncertainDouble.of(this.value / other, this.uncertainty / other);
- }
-
- @Override
- public final boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (!(obj instanceof UncertainDouble))
- return false;
- final UncertainDouble other = (UncertainDouble) obj;
- if (Double.compare(this.value, other.value) != 0)
- return false;
- if (Double.compare(this.uncertainty, other.uncertainty) != 0)
- return false;
- return true;
- }
-
- /**
- * @param other another {@code UncertainDouble}
- * @return true iff this and {@code other} are within each other's
- * uncertainty range.
- * @since 2020-09-07
- */
- public final boolean equivalent(UncertainDouble other) {
- Objects.requireNonNull(other, "other may not be null");
- return Math.abs(this.value - other.value) <= Math.min(this.uncertainty,
- other.uncertainty);
- }
-
- /**
- * Gets the preferred scale for rounding a value for toString.
- *
- * @since 2020-09-07
- */
- private final int getDisplayScale() {
- // round based on uncertainty
- // if uncertainty starts with 1 (ignoring zeroes and the decimal
- // point), rounds
- // so that uncertainty has 2 significant digits.
- // otherwise, rounds so that uncertainty has 1 significant digits.
- // the value is rounded to the same number of decimal places as the
- // uncertainty.
- final BigDecimal bigUncertainty = BigDecimal.valueOf(this.uncertainty);
-
- // the scale that will give the uncertainty two decimal places
- final int twoDecimalPlacesScale = bigUncertainty.scale()
- - bigUncertainty.precision() + 2;
- final BigDecimal roundedUncertainty = bigUncertainty
- .setScale(twoDecimalPlacesScale, RoundingMode.HALF_EVEN);
-
- if (roundedUncertainty.unscaledValue().intValue() >= 20)
- return twoDecimalPlacesScale - 1; // one decimal place
- else
- return twoDecimalPlacesScale;
- }
-
- @Override
- public final int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + Double.hashCode(this.value);
- result = prime * result + Double.hashCode(this.uncertainty);
- return result;
- }
-
- /**
- * @return true iff the value has no uncertainty
- *
- * @since 2020-09-07
- */
- public final boolean isExact() {
- return this.uncertainty == 0;
- }
-
- /**
- * Returns the difference of {@code this} and {@code other}.
- *
- * @since 2020-09-07
- */
- public final UncertainDouble minus(UncertainDouble other) {
- Objects.requireNonNull(other, "other may not be null");
- return UncertainDouble.of(this.value - other.value,
- Math.hypot(this.uncertainty, other.uncertainty));
- }
-
- /**
- * Returns the difference of {@code this} and the exact value {@code other}.
- *
- * @since 2020-09-07
- */
- public final UncertainDouble minusExact(double other) {
- return UncertainDouble.of(this.value - other, this.uncertainty);
- }
-
- /**
- * Returns the sum of {@code this} and {@code other}.
- *
- * @since 2020-09-07
- */
- public final UncertainDouble plus(UncertainDouble other) {
- Objects.requireNonNull(other, "other may not be null");
- return UncertainDouble.of(this.value + other.value,
- Math.hypot(this.uncertainty, other.uncertainty));
- }
-
- /**
- * Returns the sum of {@code this} and the exact value {@code other}.
- *
- * @since 2020-09-07
- */
- public final UncertainDouble plusExact(double other) {
- return UncertainDouble.of(this.value + other, this.uncertainty);
- }
-
- /**
- * @return relative uncertainty
- * @since 2020-09-07
- */
- public final double relativeUncertainty() {
- return this.uncertainty / this.value;
- }
-
- /**
- * Returns the product of {@code this} and {@code other}.
- *
- * @since 2020-09-07
- */
- public final UncertainDouble times(UncertainDouble other) {
- Objects.requireNonNull(other, "other may not be null");
- return UncertainDouble.ofRelative(this.value * other.value, Math
- .hypot(this.relativeUncertainty(), other.relativeUncertainty()));
- }
-
- /**
- * Returns the product of {@code this} and the exact value {@code other}.
- *
- * @since 2020-09-07
- */
- public final UncertainDouble timesExact(double other) {
- return UncertainDouble.of(this.value * other, this.uncertainty * other);
- }
-
- /**
- * Returns the result of {@code this} raised to the exponent {@code other}.
- *
- * @since 2020-09-07
- */
- public final UncertainDouble toExponent(UncertainDouble other) {
- Objects.requireNonNull(other, "other may not be null");
-
- final double result = Math.pow(this.value, other.value);
- final double relativeUncertainty = Math.hypot(
- other.value * this.relativeUncertainty(),
- Math.log(this.value) * other.uncertainty);
-
- return UncertainDouble.ofRelative(result, relativeUncertainty);
- }
-
- /**
- * Returns the result of {@code this} raised the exact exponent
- * {@code other}.
- *
- * @since 2020-09-07
- */
- public final UncertainDouble toExponentExact(double other) {
- return UncertainDouble.ofRelative(Math.pow(this.value, other),
- this.relativeUncertainty() * other);
- }
-
- /**
- * Returns a string representation of this {@code UncertainDouble}.
- * <p>
- * This method returns the same value as {@link #toString(boolean)}, but
- * {@code showUncertainty} is true if and only if the uncertainty is
- * non-zero.
- *
- * <p>
- * Examples:
- *
- * <pre>
- * UncertainDouble.of(3.27, 0.22).toString() = "3.3 ± 0.2"
- * UncertainDouble.of(3.27, 0.13).toString() = "3.27 ± 0.13"
- * UncertainDouble.of(-5.01, 0).toString() = "-5.01"
- * </pre>
- *
- * @since 2020-09-07
- */
- @Override
- public final String toString() {
- return this.toString(!this.isExact());
- }
-
- /**
- * Returns a string representation of this {@code UncertainDouble}.
- * <p>
- * If {@code showUncertainty} is true, the string will be of the form "VALUE
- * ± UNCERTAINTY", and if it is false the string will be of the form "VALUE"
- * <p>
- * VALUE represents a string representation of this {@code UncertainDouble}'s
- * value. If the uncertainty is non-zero, the string will be rounded to the
- * same precision as the uncertainty, otherwise it will not be rounded. The
- * string is still rounded if {@code showUncertainty} is false.<br>
- * UNCERTAINTY represents a string representation of this
- * {@code UncertainDouble}'s uncertainty. If the uncertainty ends in 1X
- * (where X represents any digit) it will be rounded to two significant
- * digits otherwise it will be rounded to one significant digit.
- * <p>
- * Examples:
- *
- * <pre>
- * UncertainDouble.of(3.27, 0.22).toString(false) = "3.3"
- * UncertainDouble.of(3.27, 0.22).toString(true) = "3.3 ± 0.2"
- * UncertainDouble.of(3.27, 0.13).toString(false) = "3.27"
- * UncertainDouble.of(3.27, 0.13).toString(true) = "3.27 ± 0.13"
- * UncertainDouble.of(-5.01, 0).toString(false) = "-5.01"
- * UncertainDouble.of(-5.01, 0).toString(true) = "-5.01 ± 0.0"
- * </pre>
- *
- * @since 2020-09-07
- */
- public final String toString(boolean showUncertainty) {
- String valueString, uncertaintyString;
-
- // generate the string representation of value and uncertainty
- if (this.isExact()) {
- uncertaintyString = "0.0";
- valueString = Double.toString(this.value);
-
- } else {
- // round the value and uncertainty according to getDisplayScale()
- final BigDecimal bigValue = BigDecimal.valueOf(this.value);
- final BigDecimal bigUncertainty = BigDecimal.valueOf(this.uncertainty);
-
- final int displayScale = this.getDisplayScale();
- final BigDecimal roundedUncertainty = bigUncertainty
- .setScale(displayScale, RoundingMode.HALF_EVEN);
- final BigDecimal roundedValue = bigValue.setScale(displayScale,
- RoundingMode.HALF_EVEN);
-
- valueString = roundedValue.toString();
- uncertaintyString = roundedUncertainty.toString();
- }
-
- // return "value" or "value ± uncertainty" depending on showUncertainty
- return valueString + (showUncertainty ? " ± " + uncertaintyString : "");
- }
-
- /**
- * @return absolute uncertainty
- * @since 2020-09-07
- */
- public final double uncertainty() {
- return this.uncertainty;
- }
-
- /**
- * @return value without uncertainty
- * @since 2020-09-07
- */
- public final double value() {
- return this.value;
- }
-}
diff --git a/src/org/unitConverter/math/package-info.java b/src/org/unitConverter/math/package-info.java
deleted file mode 100644
index 65727e4..0000000
--- a/src/org/unitConverter/math/package-info.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-/**
- * Supplementary classes that are not related to units, but are necessary for their function.
- *
- * @author Adrien Hopkins
- * @since 2019-03-14
- * @since v0.2.0
- */
-package org.unitConverter.math; \ No newline at end of file
diff --git a/src/org/unitConverter/package-info.java b/src/org/unitConverter/package-info.java
deleted file mode 100644
index 23dd165..0000000
--- a/src/org/unitConverter/package-info.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-/**
- * A program that converts units.
- *
- * @author Adrien Hopkins
- * @version v0.2.0
- * @since 2019-01-25
- */
-package org.unitConverter; \ No newline at end of file
diff --git a/src/org/unitConverter/unit/BaseDimension.java b/src/org/unitConverter/unit/BaseDimension.java
deleted file mode 100644
index 8e63a17..0000000
--- a/src/org/unitConverter/unit/BaseDimension.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import java.util.Objects;
-
-/**
- * A dimension that defines a {@code BaseUnit}
- *
- * @author Adrien Hopkins
- * @since 2019-10-16
- */
-public final class BaseDimension {
- /**
- * Gets a {@code BaseDimension} with the provided name and symbol.
- *
- * @param name
- * name of dimension
- * @param symbol
- * symbol used for dimension
- * @return dimension
- * @since 2019-10-16
- */
- public static BaseDimension valueOf(final String name, final String symbol) {
- return new BaseDimension(name, symbol);
- }
-
- /**
- * The name of the dimension.
- */
- private final String name;
- /**
- * The symbol used by the dimension. Symbols should be short, generally one or two characters.
- */
- private final String symbol;
-
- /**
- * Creates the {@code BaseDimension}.
- *
- * @param name
- * name of unit
- * @param symbol
- * symbol of unit
- * @throws NullPointerException
- * if any argument is null
- * @since 2019-10-16
- */
- private BaseDimension(final String name, final String symbol) {
- this.name = Objects.requireNonNull(name, "name must not be null.");
- this.symbol = Objects.requireNonNull(symbol, "symbol must not be null.");
- }
-
- /**
- * @return name
- * @since 2019-10-16
- */
- public final String getName() {
- return this.name;
- }
-
- /**
- * @return symbol
- * @since 2019-10-16
- */
- public final String getSymbol() {
- return this.symbol;
- }
-
- @Override
- public String toString() {
- return String.format("%s (%s)", this.getName(), this.getSymbol());
- }
-}
diff --git a/src/org/unitConverter/unit/BaseUnit.java b/src/org/unitConverter/unit/BaseUnit.java
deleted file mode 100644
index 6757bd0..0000000
--- a/src/org/unitConverter/unit/BaseUnit.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * A unit that other units are defined by.
- * <p>
- * Note that BaseUnits <b>must</b> have names and symbols. This is because they
- * are used for toString code. Therefore, the Optionals provided by
- * {@link #getPrimaryName} and {@link #getSymbol} will always contain a value.
- *
- * @author Adrien Hopkins
- * @since 2019-10-16
- */
-public final class BaseUnit extends Unit {
- /**
- * Gets a base unit from the dimension it measures, its name and its symbol.
- *
- * @param dimension dimension measured by this unit
- * @param name name of unit
- * @param symbol symbol of unit
- * @return base unit
- * @since 2019-10-16
- */
- public static BaseUnit valueOf(final BaseDimension dimension,
- final String name, final String symbol) {
- return new BaseUnit(dimension, name, symbol, new HashSet<>());
- }
-
- /**
- * Gets a base unit from the dimension it measures, its name and its symbol.
- *
- * @param dimension dimension measured by this unit
- * @param name name of unit
- * @param symbol symbol of unit
- * @return base unit
- * @since 2019-10-21
- */
- public static BaseUnit valueOf(final BaseDimension dimension,
- final String name, final String symbol, final Set<String> otherNames) {
- return new BaseUnit(dimension, name, symbol, otherNames);
- }
-
- /**
- * The dimension measured by this base unit.
- */
- private final BaseDimension dimension;
-
- /**
- * Creates the {@code BaseUnit}.
- *
- * @param dimension dimension of unit
- * @param primaryName name of unit
- * @param symbol symbol of unit
- * @throws NullPointerException if any argument is null
- * @since 2019-10-16
- */
- private BaseUnit(final BaseDimension dimension, final String primaryName,
- final String symbol, final Set<String> otherNames) {
- super(primaryName, symbol, otherNames);
- this.dimension = Objects.requireNonNull(dimension,
- "dimension must not be null.");
- }
-
- /**
- * Returns a {@code LinearUnit} with this unit as a base and a conversion
- * factor of 1. This operation must be done in order to allow units to be
- * created with operations.
- *
- * @return this unit as a {@code LinearUnit}
- * @since 2019-10-16
- */
- public LinearUnit asLinearUnit() {
- return LinearUnit.valueOf(this.getBase(), 1);
- }
-
- @Override
- protected double convertFromBase(final double value) {
- return value;
- }
-
- @Override
- protected double convertToBase(final double value) {
- return value;
- }
-
- /**
- * @return dimension
- * @since 2019-10-16
- */
- public final BaseDimension getBaseDimension() {
- return this.dimension;
- }
-
- @Override
- public String toString() {
- return this.getPrimaryName().orElse("Unnamed unit")
- + (this.getSymbol().isPresent()
- ? String.format(" (%s)", this.getSymbol().get())
- : "");
- }
-
- @Override
- public BaseUnit withName(final NameSymbol ns) {
- Objects.requireNonNull(ns, "ns must not be null.");
- if (!ns.getPrimaryName().isPresent())
- throw new IllegalArgumentException(
- "BaseUnits must have primary names.");
- if (!ns.getSymbol().isPresent())
- throw new IllegalArgumentException("BaseUnits must have symbols.");
- return BaseUnit.valueOf(this.getBaseDimension(),
- ns.getPrimaryName().get(), ns.getSymbol().get(),
- ns.getOtherNames());
- }
-}
diff --git a/src/org/unitConverter/unit/BritishImperial.java b/src/org/unitConverter/unit/BritishImperial.java
deleted file mode 100644
index ea23cd1..0000000
--- a/src/org/unitConverter/unit/BritishImperial.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-/**
- * A static utility class that contains units in the British Imperial system.
- *
- * @author Adrien Hopkins
- * @since 2019-10-21
- */
-public final class BritishImperial {
- /**
- * Imperial units that measure area
- *
- * @author Adrien Hopkins
- * @since 2019-11-08
- */
- public static final class Area {
- public static final LinearUnit SQUARE_FOOT = Length.FOOT.toExponent(2);
- public static final LinearUnit SQUARE_YARD = Length.YARD.toExponent(2);
- public static final LinearUnit SQUARE_MILE = Length.MILE.toExponent(2);
- public static final LinearUnit PERCH = Length.ROD.times(Length.ROD);
- public static final LinearUnit ROOD = Length.ROD.times(Length.FURLONG);
- public static final LinearUnit ACRE = Length.FURLONG.times(Length.CHAIN);
- }
-
- /**
- * Imperial units that measure length
- *
- * @author Adrien Hopkins
- * @since 2019-10-28
- */
- public static final class Length {
- /**
- * According to the International Yard and Pound of 1959, a yard is defined as exactly 0.9144 metres.
- */
- public static final LinearUnit YARD = SI.METRE.times(0.9144);
- public static final LinearUnit FOOT = YARD.dividedBy(3);
- public static final LinearUnit INCH = FOOT.dividedBy(12);
- public static final LinearUnit THOU = INCH.dividedBy(1000);
- public static final LinearUnit CHAIN = YARD.times(22);
- public static final LinearUnit FURLONG = CHAIN.times(10);
- public static final LinearUnit MILE = FURLONG.times(8);
- public static final LinearUnit LEAGUE = MILE.times(3);
-
- public static final LinearUnit NAUTICAL_MILE = SI.METRE.times(1852);
- public static final LinearUnit CABLE = NAUTICAL_MILE.dividedBy(10);
- public static final LinearUnit FATHOM = CABLE.dividedBy(100);
-
- public static final LinearUnit ROD = YARD.times(5.5);
- public static final LinearUnit LINK = ROD.dividedBy(25);
- }
-
- /**
- * British Imperial units that measure mass.
- *
- * @author Adrien Hopkins
- * @since 2019-11-08
- */
- public static final class Mass {
- public static final LinearUnit POUND = SI.GRAM.times(453.59237);
- public static final LinearUnit OUNCE = POUND.dividedBy(16);
- public static final LinearUnit DRACHM = POUND.dividedBy(256);
- public static final LinearUnit GRAIN = POUND.dividedBy(7000);
- public static final LinearUnit STONE = POUND.times(14);
- public static final LinearUnit QUARTER = STONE.times(2);
- public static final LinearUnit HUNDREDWEIGHT = QUARTER.times(4);
- public static final LinearUnit LONG_TON = HUNDREDWEIGHT.times(20);
- public static final LinearUnit SLUG = SI.KILOGRAM.times(14.59390294);
- }
-
- /**
- * British Imperial units that measure volume
- *
- * @author Adrien Hopkins
- * @since 2019-11-08
- */
- public static final class Volume {
- public static final LinearUnit FLUID_OUNCE = SI.LITRE.withPrefix(SI.MILLI).times(28.4130625);
- public static final LinearUnit GILL = FLUID_OUNCE.times(5);
- public static final LinearUnit PINT = FLUID_OUNCE.times(20);
- public static final LinearUnit QUART = PINT.times(2);
- public static final LinearUnit GALLON = QUART.times(4);
- public static final LinearUnit PECK = GALLON.times(2);
- public static final LinearUnit BUSHEL = PECK.times(4);
-
- public static final LinearUnit CUBIC_INCH = Length.INCH.toExponent(3);
- public static final LinearUnit CUBIC_FOOT = Length.FOOT.toExponent(3);
- public static final LinearUnit CUBIC_YARD = Length.YARD.toExponent(3);
- public static final LinearUnit ACRE_FOOT = Area.ACRE.times(Length.FOOT);
- }
-
- public static final LinearUnit OUNCE_FORCE = Mass.OUNCE.times(SI.Constants.EARTH_GRAVITY);
- public static final LinearUnit POUND_FORCE = Mass.POUND.times(SI.Constants.EARTH_GRAVITY);
-
- public static final LinearUnit BRITISH_THERMAL_UNIT = SI.JOULE.times(1055.06);
- public static final LinearUnit CALORIE = SI.JOULE.times(4.184);
- public static final LinearUnit KILOCALORIE = SI.JOULE.times(4184);
-
- public static final Unit FAHRENHEIT = Unit.fromConversionFunctions(SI.KELVIN.getBase(),
- tempK -> tempK * 1.8 - 459.67, tempF -> (tempF + 459.67) / 1.8);
-}
diff --git a/src/org/unitConverter/unit/FunctionalUnit.java b/src/org/unitConverter/unit/FunctionalUnit.java
deleted file mode 100644
index 586e0d7..0000000
--- a/src/org/unitConverter/unit/FunctionalUnit.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import java.util.Objects;
-import java.util.function.DoubleUnaryOperator;
-
-import org.unitConverter.math.ObjectProduct;
-
-/**
- * A unit that uses functional objects to convert to and from its base.
- *
- * @author Adrien Hopkins
- * @since 2019-05-22
- */
-final class FunctionalUnit extends Unit {
- /**
- * A function that accepts a value expressed in the unit's base and returns that value expressed in this unit.
- *
- * @since 2019-05-22
- */
- private final DoubleUnaryOperator converterFrom;
-
- /**
- * A function that accepts a value expressed in the unit and returns that value expressed in the unit's base.
- *
- * @since 2019-05-22
- */
- private final DoubleUnaryOperator converterTo;
-
- /**
- * Creates the {@code FunctionalUnit}.
- *
- * @param base
- * unit's base
- * @param converterFrom
- * function that accepts a value expressed in the unit's base and returns that value expressed in this
- * unit.
- * @param converterTo
- * function that accepts a value expressed in the unit and returns that value expressed in the unit's
- * base.
- * @throws NullPointerException
- * if any argument is null
- * @since 2019-05-22
- */
- public FunctionalUnit(final ObjectProduct<BaseUnit> base, final DoubleUnaryOperator converterFrom,
- final DoubleUnaryOperator converterTo) {
- super(base, NameSymbol.EMPTY);
- this.converterFrom = Objects.requireNonNull(converterFrom, "converterFrom must not be null.");
- this.converterTo = Objects.requireNonNull(converterTo, "converterTo must not be null.");
- }
-
- /**
- * Creates the {@code FunctionalUnit}.
- *
- * @param base
- * unit's base
- * @param converterFrom
- * function that accepts a value expressed in the unit's base and returns that value expressed in this
- * unit.
- * @param converterTo
- * function that accepts a value expressed in the unit and returns that value expressed in the unit's
- * base.
- * @throws NullPointerException
- * if any argument is null
- * @since 2019-05-22
- */
- public FunctionalUnit(final ObjectProduct<BaseUnit> base, final DoubleUnaryOperator converterFrom,
- final DoubleUnaryOperator converterTo, final NameSymbol ns) {
- super(base, ns);
- this.converterFrom = Objects.requireNonNull(converterFrom, "converterFrom must not be null.");
- this.converterTo = Objects.requireNonNull(converterTo, "converterTo must not be null.");
- }
-
- /**
- * {@inheritDoc}
- *
- * Uses {@code converterFrom} to convert.
- */
- @Override
- public double convertFromBase(final double value) {
- return this.converterFrom.applyAsDouble(value);
- }
-
- /**
- * {@inheritDoc}
- *
- * Uses {@code converterTo} to convert.
- */
- @Override
- public double convertToBase(final double value) {
- return this.converterTo.applyAsDouble(value);
- }
-
-}
diff --git a/src/org/unitConverter/unit/FunctionalUnitlike.java b/src/org/unitConverter/unit/FunctionalUnitlike.java
deleted file mode 100644
index 21c1fca..0000000
--- a/src/org/unitConverter/unit/FunctionalUnitlike.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/**
- * Copyright (C) 2020 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import java.util.function.DoubleFunction;
-import java.util.function.ToDoubleFunction;
-
-import org.unitConverter.math.ObjectProduct;
-
-/**
- * A unitlike form that converts using two conversion functions.
- *
- * @since 2020-09-07
- */
-final class FunctionalUnitlike<V> extends Unitlike<V> {
- /**
- * A function that accepts a value in the unitlike form's base and returns a
- * value in the unitlike form.
- *
- * @since 2020-09-07
- */
- private final DoubleFunction<V> converterFrom;
-
- /**
- * A function that accepts a value in the unitlike form and returns a value
- * in the unitlike form's base.
- */
- private final ToDoubleFunction<V> converterTo;
-
- /**
- * Creates the {@code FunctionalUnitlike}.
- *
- * @param base unitlike form's base
- * @param converterFrom function that accepts a value in the unitlike form's
- * base and returns a value in the unitlike form.
- * @param converterTo function that accepts a value in the unitlike form
- * and returns a value in the unitlike form's base.
- * @throws NullPointerException if any argument is null
- * @since 2019-05-22
- */
- protected FunctionalUnitlike(ObjectProduct<BaseUnit> unitBase, NameSymbol ns,
- DoubleFunction<V> converterFrom, ToDoubleFunction<V> converterTo) {
- super(unitBase, ns);
- this.converterFrom = converterFrom;
- this.converterTo = converterTo;
- }
-
- @Override
- protected V convertFromBase(double value) {
- return this.converterFrom.apply(value);
- }
-
- @Override
- protected double convertToBase(V value) {
- return this.converterTo.applyAsDouble(value);
- }
-
-}
diff --git a/src/org/unitConverter/unit/LinearUnit.java b/src/org/unitConverter/unit/LinearUnit.java
deleted file mode 100644
index b7f33d5..0000000
--- a/src/org/unitConverter/unit/LinearUnit.java
+++ /dev/null
@@ -1,441 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import java.util.Objects;
-
-import org.unitConverter.math.DecimalComparison;
-import org.unitConverter.math.ObjectProduct;
-import org.unitConverter.math.UncertainDouble;
-
-/**
- * A unit that can be expressed as a product of its base and a number. For
- * example, kilometres, inches and pounds.
- *
- * @author Adrien Hopkins
- * @since 2019-10-16
- */
-public final class LinearUnit extends Unit {
- /**
- * Gets a {@code LinearUnit} from a unit and a value. For example, converts
- * '59 °F' to a linear unit with the value of '288.15 K'
- *
- * @param unit unit to convert
- * @param value value to convert
- * @return value expressed as a {@code LinearUnit}
- * @since 2019-10-16
- * @throws NullPointerException if unit is null
- */
- public static LinearUnit fromUnitValue(final Unit unit, final double value) {
- return new LinearUnit(
- Objects.requireNonNull(unit, "unit must not be null.").getBase(),
- unit.convertToBase(value), NameSymbol.EMPTY);
- }
-
- /**
- * Gets a {@code LinearUnit} from a unit and a value. For example, converts
- * '59 °F' to a linear unit with the value of '288.15 K'
- *
- * @param unit unit to convert
- * @param value value to convert
- * @param ns name(s) and symbol of unit
- * @return value expressed as a {@code LinearUnit}
- * @since 2019-10-21
- * @throws NullPointerException if unit or ns is null
- */
- public static LinearUnit fromUnitValue(final Unit unit, final double value,
- final NameSymbol ns) {
- return new LinearUnit(
- Objects.requireNonNull(unit, "unit must not be null.").getBase(),
- unit.convertToBase(value), ns);
- }
-
- /**
- * @return the base unit associated with {@code unit}, as a
- * {@code LinearUnit}.
- * @since 2020-10-02
- */
- public static LinearUnit getBase(final Unit unit) {
- return new LinearUnit(unit.getBase(), 1, NameSymbol.EMPTY);
- }
-
- /**
- * @return the base unit associated with {@code unitlike}, as a
- * {@code LinearUnit}.
- * @since 2020-10-02
- */
- public static LinearUnit getBase(final Unitlike<?> unit) {
- return new LinearUnit(unit.getBase(), 1, NameSymbol.EMPTY);
- }
-
- /**
- * Gets a {@code LinearUnit} from a unit base and a conversion factor. In
- * other words, gets the product of {@code unitBase} and
- * {@code conversionFactor}, expressed as a {@code LinearUnit}.
- *
- * @param unitBase unit base to multiply by
- * @param conversionFactor number to multiply base by
- * @return product of base and conversion factor
- * @since 2019-10-16
- * @throws NullPointerException if unitBase is null
- */
- public static LinearUnit valueOf(final ObjectProduct<BaseUnit> unitBase,
- final double conversionFactor) {
- return new LinearUnit(unitBase, conversionFactor, NameSymbol.EMPTY);
- }
-
- /**
- * Gets a {@code LinearUnit} from a unit base and a conversion factor. In
- * other words, gets the product of {@code unitBase} and
- * {@code conversionFactor}, expressed as a {@code LinearUnit}.
- *
- * @param unitBase unit base to multiply by
- * @param conversionFactor number to multiply base by
- * @param ns name(s) and symbol of unit
- * @return product of base and conversion factor
- * @since 2019-10-21
- * @throws NullPointerException if unitBase is null
- */
- public static LinearUnit valueOf(final ObjectProduct<BaseUnit> unitBase,
- final double conversionFactor, final NameSymbol ns) {
- return new LinearUnit(unitBase, conversionFactor, ns);
- }
-
- /**
- * The value of this unit as represented in its base form. Mathematically,
- *
- * <pre>
- * this = conversionFactor * getBase()
- * </pre>
- *
- * @since 2019-10-16
- */
- private final double conversionFactor;
-
- /**
- * Creates the {@code LinearUnit}.
- *
- * @param unitBase base of linear unit
- * @param conversionFactor conversion factor between base and unit
- * @since 2019-10-16
- */
- private LinearUnit(final ObjectProduct<BaseUnit> unitBase,
- final double conversionFactor, final NameSymbol ns) {
- super(unitBase, ns);
- this.conversionFactor = conversionFactor;
- }
-
- /**
- * {@inheritDoc}
- *
- * Converts by dividing by {@code conversionFactor}
- */
- @Override
- protected double convertFromBase(final double value) {
- return value / this.getConversionFactor();
- }
-
- /**
- * Converts an {@code UncertainDouble} value expressed in this unit to an
- * {@code UncertainValue} value expressed in {@code other}.
- *
- * @param other unit to convert to
- * @param value value to convert
- * @return converted value
- * @since 2019-09-07
- * @throws IllegalArgumentException if {@code other} is incompatible for
- * conversion with this unit (as tested by
- * {@link Unit#canConvertTo}).
- * @throws NullPointerException if value or other is null
- */
- public UncertainDouble convertTo(LinearUnit other, UncertainDouble value) {
- Objects.requireNonNull(other, "other must not be null.");
- Objects.requireNonNull(value, "value may not be null.");
- if (this.canConvertTo(other))
- return value.timesExact(
- this.getConversionFactor() / other.getConversionFactor());
- else
- throw new IllegalArgumentException(
- String.format("Cannot convert from %s to %s.", this, other));
-
- }
-
- /**
- * {@inheritDoc}
- *
- * Converts by multiplying by {@code conversionFactor}
- */
- @Override
- protected double convertToBase(final double value) {
- return value * this.getConversionFactor();
- }
-
- /**
- * Converts an {@code UncertainDouble} to the base unit.
- *
- * @since 2020-09-07
- */
- UncertainDouble convertToBase(final UncertainDouble value) {
- return value.timesExact(this.getConversionFactor());
- }
-
- /**
- * Divides this unit by a scalar.
- *
- * @param divisor scalar to divide by
- * @return quotient
- * @since 2018-12-23
- * @since v0.1.0
- */
- public LinearUnit dividedBy(final double divisor) {
- return valueOf(this.getBase(), this.getConversionFactor() / divisor);
- }
-
- /**
- * Returns the quotient of this unit and another.
- *
- * @param divisor unit to divide by
- * @return quotient of two units
- * @throws NullPointerException if {@code divisor} is null
- * @since 2018-12-22
- * @since v0.1.0
- */
- public LinearUnit dividedBy(final LinearUnit divisor) {
- Objects.requireNonNull(divisor, "other must not be null");
-
- // divide the units
- final ObjectProduct<BaseUnit> base = this.getBase()
- .dividedBy(divisor.getBase());
- return valueOf(base,
- this.getConversionFactor() / divisor.getConversionFactor());
- }
-
- /**
- * {@inheritDoc}
- *
- * Uses the base and conversion factor of units to test for equality.
- */
- @Override
- public boolean equals(final Object obj) {
- if (!(obj instanceof LinearUnit))
- return false;
- final LinearUnit other = (LinearUnit) obj;
- return Objects.equals(this.getBase(), other.getBase())
- && DecimalComparison.equals(this.getConversionFactor(),
- other.getConversionFactor());
- }
-
- /**
- * @return conversion factor
- * @since 2019-10-16
- */
- public double getConversionFactor() {
- return this.conversionFactor;
- }
-
- /**
- * {@inheritDoc}
- *
- * Uses the base and conversion factor to compute a hash code.
- */
- @Override
- public int hashCode() {
- return 31 * this.getBase().hashCode()
- + DecimalComparison.hash(this.getConversionFactor());
- }
-
- /**
- * @return whether this unit is equivalent to a {@code BaseUnit} (i.e. there
- * is a {@code BaseUnit b} where
- * {@code b.asLinearUnit().equals(this)} returns {@code true}.)
- * @since 2019-10-16
- */
- public boolean isBase() {
- return this.isCoherent() && this.getBase().isSingleObject();
- }
-
- /**
- * @return whether this unit is coherent (i.e. has conversion factor 1)
- * @since 2019-10-16
- */
- public boolean isCoherent() {
- return this.getConversionFactor() == 1;
- }
-
- /**
- * Returns the difference of this unit and another.
- * <p>
- * Two units can be subtracted if they have the same base. Note that
- * {@link #canConvertTo} can be used to determine this. If {@code subtrahend}
- * does not meet this condition, an {@code IllegalArgumentException} will be
- * thrown.
- * </p>
- *
- * @param subtrahend unit to subtract
- * @return difference of units
- * @throws IllegalArgumentException if {@code subtrahend} is not compatible
- * for subtraction as described above
- * @throws NullPointerException if {@code subtrahend} is null
- * @since 2019-03-17
- * @since v0.2.0
- */
- public LinearUnit minus(final LinearUnit subtrahend) {
- Objects.requireNonNull(subtrahend, "addend must not be null.");
-
- // reject subtrahends that cannot be added to this unit
- if (!this.getBase().equals(subtrahend.getBase()))
- throw new IllegalArgumentException(String.format(
- "Incompatible units for subtraction \"%s\" and \"%s\".", this,
- subtrahend));
-
- // subtract the units
- return valueOf(this.getBase(),
- this.getConversionFactor() - subtrahend.getConversionFactor());
- }
-
- /**
- * Returns the sum of this unit and another.
- * <p>
- * Two units can be added if they have the same base. Note that
- * {@link #canConvertTo} can be used to determine this. If {@code addend}
- * does not meet this condition, an {@code IllegalArgumentException} will be
- * thrown.
- * </p>
- *
- * @param addend unit to add
- * @return sum of units
- * @throws IllegalArgumentException if {@code addend} is not compatible for
- * addition as described above
- * @throws NullPointerException if {@code addend} is null
- * @since 2019-03-17
- * @since v0.2.0
- */
- public LinearUnit plus(final LinearUnit addend) {
- Objects.requireNonNull(addend, "addend must not be null.");
-
- // reject addends that cannot be added to this unit
- if (!this.getBase().equals(addend.getBase()))
- throw new IllegalArgumentException(String.format(
- "Incompatible units for addition \"%s\" and \"%s\".", this,
- addend));
-
- // add the units
- return valueOf(this.getBase(),
- this.getConversionFactor() + addend.getConversionFactor());
- }
-
- /**
- * Multiplies this unit by a scalar.
- *
- * @param multiplier scalar to multiply by
- * @return product
- * @since 2018-12-23
- * @since v0.1.0
- */
- public LinearUnit times(final double multiplier) {
- return valueOf(this.getBase(), this.getConversionFactor() * multiplier);
- }
-
- /**
- * Returns the product of this unit and another.
- *
- * @param multiplier unit to multiply by
- * @return product of two units
- * @throws NullPointerException if {@code multiplier} is null
- * @since 2018-12-22
- * @since v0.1.0
- */
- public LinearUnit times(final LinearUnit multiplier) {
- Objects.requireNonNull(multiplier, "other must not be null");
-
- // multiply the units
- final ObjectProduct<BaseUnit> base = this.getBase()
- .times(multiplier.getBase());
- return valueOf(base,
- this.getConversionFactor() * multiplier.getConversionFactor());
- }
-
- /**
- * Returns this unit but to an exponent.
- *
- * @param exponent exponent to exponentiate unit to
- * @return exponentiated unit
- * @since 2019-01-15
- * @since v0.1.0
- */
- public LinearUnit toExponent(final int exponent) {
- return valueOf(this.getBase().toExponent(exponent),
- Math.pow(this.conversionFactor, exponent));
- }
-
- /**
- * @return a string providing a definition of this unit
- * @since 2019-10-21
- */
- @Override
- public String toString() {
- return this.getPrimaryName().orElse("Unnamed unit")
- + (this.getSymbol().isPresent()
- ? String.format(" (%s)", this.getSymbol().get())
- : "")
- + ", " + Double.toString(this.conversionFactor) + " * "
- + this.getBase().toString(u -> u.getSymbol().get());
- }
-
- @Override
- public LinearUnit withName(final NameSymbol ns) {
- return valueOf(this.getBase(), this.getConversionFactor(), ns);
- }
-
- /**
- * Returns the result of applying {@code prefix} to this unit.
- * <p>
- * If this unit and the provided prefix have a primary name, the returned
- * unit will have a primary name (prefix's name + unit's name). <br>
- * If this unit and the provided prefix have a symbol, the returned unit will
- * have a symbol. <br>
- * This method ignores alternate names of both this unit and the provided
- * prefix.
- *
- * @param prefix prefix to apply
- * @return unit with prefix
- * @since 2019-03-18
- * @since v0.2.0
- * @throws NullPointerException if prefix is null
- */
- public LinearUnit withPrefix(final UnitPrefix prefix) {
- final LinearUnit unit = this.times(prefix.getMultiplier());
-
- // create new name and symbol, if possible
- final String name;
- if (this.getPrimaryName().isPresent()
- && prefix.getPrimaryName().isPresent()) {
- name = prefix.getPrimaryName().get() + this.getPrimaryName().get();
- } else {
- name = null;
- }
-
- final String symbol;
- if (this.getSymbol().isPresent() && prefix.getSymbol().isPresent()) {
- symbol = prefix.getSymbol().get() + this.getSymbol().get();
- } else {
- symbol = null;
- }
-
- return unit.withName(NameSymbol.ofNullable(name, symbol));
- }
-}
diff --git a/src/org/unitConverter/unit/LinearUnitValue.java b/src/org/unitConverter/unit/LinearUnitValue.java
deleted file mode 100644
index 8de734e..0000000
--- a/src/org/unitConverter/unit/LinearUnitValue.java
+++ /dev/null
@@ -1,341 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import java.util.Objects;
-import java.util.Optional;
-
-import org.unitConverter.math.DecimalComparison;
-import org.unitConverter.math.UncertainDouble;
-
-/**
- * A possibly uncertain value expressed in a linear unit.
- *
- * Unless otherwise indicated, all methods in this class throw a
- * {@code NullPointerException} when an argument is null.
- *
- * @author Adrien Hopkins
- * @since 2020-07-26
- */
-public final class LinearUnitValue {
- public static final LinearUnitValue ONE = getExact(SI.ONE, 1);
-
- /**
- * Gets an exact {@code LinearUnitValue}
- *
- * @param unit unit to express with
- * @param value value to express
- * @return exact {@code LinearUnitValue} instance
- * @since 2020-07-26
- */
- public static final LinearUnitValue getExact(final LinearUnit unit,
- final double value) {
- return new LinearUnitValue(
- Objects.requireNonNull(unit, "unit must not be null"),
- UncertainDouble.of(value, 0));
- }
-
- /**
- * Gets an uncertain {@code LinearUnitValue}
- *
- * @param unit unit to express with
- * @param value value to express
- * @param uncertainty absolute uncertainty of value
- * @return uncertain {@code LinearUnitValue} instance
- * @since 2020-07-26
- */
- public static final LinearUnitValue of(final LinearUnit unit,
- final UncertainDouble value) {
- return new LinearUnitValue(
- Objects.requireNonNull(unit, "unit must not be null"),
- Objects.requireNonNull(value, "value may not be null"));
- }
-
- private final LinearUnit unit;
-
- private final UncertainDouble value;
-
- /**
- * @param unit unit to express as
- * @param value value to express
- * @since 2020-07-26
- */
- private LinearUnitValue(final LinearUnit unit, final UncertainDouble value) {
- this.unit = unit;
- this.value = value;
- }
-
- /**
- * @return this value as a {@code UnitValue}. All uncertainty information is
- * removed from the returned value.
- * @since 2020-08-04
- */
- public final UnitValue asUnitValue() {
- return UnitValue.of(this.unit, this.value.value());
- }
-
- /**
- * @param other a {@code LinearUnit}
- * @return true iff this value can be represented with {@code other}.
- * @since 2020-07-26
- */
- public final boolean canConvertTo(final LinearUnit other) {
- return this.unit.canConvertTo(other);
- }
-
- /**
- * Returns a LinearUnitValue that represents the same value expressed in a
- * different unit
- *
- * @param other new unit to express value in
- * @return value expressed in {@code other}
- * @since 2020-07-26
- */
- public final LinearUnitValue convertTo(final LinearUnit other) {
- return LinearUnitValue.of(other, this.unit.convertTo(other, this.value));
- }
-
- /**
- * Divides this value by a scalar
- *
- * @param divisor value to divide by
- * @return multiplied value
- * @since 2020-07-28
- */
- public LinearUnitValue dividedBy(final double divisor) {
- return LinearUnitValue.of(this.unit, this.value.dividedByExact(divisor));
- }
-
- /**
- * Divides this value by another value
- *
- * @param divisor value to multiply by
- * @return quotient
- * @since 2020-07-28
- */
- public LinearUnitValue dividedBy(final LinearUnitValue divisor) {
- return LinearUnitValue.of(this.unit.dividedBy(divisor.unit),
- this.value.dividedBy(divisor.value));
- }
-
- /**
- * Returns true if this and obj represent the same value, regardless of
- * whether or not they are expressed in the same unit. So (1000 m).equals(1
- * km) returns true.
- *
- * @since 2020-07-26
- * @see #equals(Object, boolean)
- */
- @Override
- public boolean equals(final Object obj) {
- if (!(obj instanceof LinearUnitValue))
- return false;
- final LinearUnitValue other = (LinearUnitValue) obj;
- return Objects.equals(this.unit.getBase(), other.unit.getBase())
- && this.unit.convertToBase(this.value)
- .equals(other.unit.convertToBase(other.value));
- }
-
- /**
- * Returns true if this and obj represent the same value, regardless of
- * whether or not they are expressed in the same unit. So (1000 m).equals(1
- * km) returns true.
- * <p>
- * If avoidFPErrors is true, this method will attempt to avoid floating-point
- * errors, at the cost of not always being transitive.
- *
- * @since 2020-07-28
- */
- public boolean equals(final Object obj, final boolean avoidFPErrors) {
- if (!avoidFPErrors)
- return this.equals(obj);
- if (!(obj instanceof LinearUnitValue))
- return false;
- final LinearUnitValue other = (LinearUnitValue) obj;
- return Objects.equals(this.unit.getBase(), other.unit.getBase())
- && DecimalComparison.equals(this.unit.convertToBase(this.value),
- other.unit.convertToBase(other.value));
- }
-
- /**
- * @param other another {@code LinearUnitValue}
- * @return true iff this and other are within each other's uncertainty range
- *
- * @since 2020-07-26
- */
- public boolean equivalent(final LinearUnitValue other) {
- if (other == null
- || !Objects.equals(this.unit.getBase(), other.unit.getBase()))
- return false;
- final LinearUnit base = LinearUnit.valueOf(this.unit.getBase(), 1);
- final LinearUnitValue thisBase = this.convertTo(base);
- final LinearUnitValue otherBase = other.convertTo(base);
-
- return thisBase.value.equivalent(otherBase.value);
- }
-
- /**
- * @return the unit
- * @since 2020-09-29
- */
- public final LinearUnit getUnit() {
- return this.unit;
- }
-
- /**
- * @return the value
- * @since 2020-09-29
- */
- public final UncertainDouble getValue() {
- return this.value;
- }
-
- /**
- * @return the exact value
- * @since 2020-09-07
- */
- public final double getValueExact() {
- return this.value.value();
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(this.unit.getBase(),
- this.unit.convertToBase(this.getValue()));
- }
-
- /**
- * Returns the difference of this value and another, expressed in this
- * value's unit
- *
- * @param subtrahend value to subtract
- * @return difference of values
- * @throws IllegalArgumentException if {@code subtrahend} has a unit that is
- * not compatible for addition
- * @since 2020-07-26
- */
- public LinearUnitValue minus(final LinearUnitValue subtrahend) {
- Objects.requireNonNull(subtrahend, "subtrahend may not be null");
-
- if (!this.canConvertTo(subtrahend.unit))
- throw new IllegalArgumentException(String.format(
- "Incompatible units for subtraction \"%s\" and \"%s\".",
- this.unit, subtrahend.unit));
-
- final LinearUnitValue otherConverted = subtrahend.convertTo(this.unit);
- return LinearUnitValue.of(this.unit,
- this.value.minus(otherConverted.value));
- }
-
- /**
- * Returns the sum of this value and another, expressed in this value's unit
- *
- * @param addend value to add
- * @return sum of values
- * @throws IllegalArgumentException if {@code addend} has a unit that is not
- * compatible for addition
- * @since 2020-07-26
- */
- public LinearUnitValue plus(final LinearUnitValue addend) {
- Objects.requireNonNull(addend, "addend may not be null");
-
- if (!this.canConvertTo(addend.unit))
- throw new IllegalArgumentException(String.format(
- "Incompatible units for addition \"%s\" and \"%s\".", this.unit,
- addend.unit));
-
- final LinearUnitValue otherConverted = addend.convertTo(this.unit);
- return LinearUnitValue.of(this.unit,
- this.value.plus(otherConverted.value));
- }
-
- /**
- * Multiplies this value by a scalar
- *
- * @param multiplier value to multiply by
- * @return multiplied value
- * @since 2020-07-28
- */
- public LinearUnitValue times(final double multiplier) {
- return LinearUnitValue.of(this.unit, this.value.timesExact(multiplier));
- }
-
- /**
- * Multiplies this value by another value
- *
- * @param multiplier value to multiply by
- * @return product
- * @since 2020-07-28
- */
- public LinearUnitValue times(final LinearUnitValue multiplier) {
- return LinearUnitValue.of(this.unit.times(multiplier.unit),
- this.value.times(multiplier.value));
- }
-
- /**
- * Raises a value to an exponent
- *
- * @param exponent exponent to raise to
- * @return result of exponentiation
- * @since 2020-07-28
- */
- public LinearUnitValue toExponent(final int exponent) {
- return LinearUnitValue.of(this.unit.toExponent(exponent),
- this.value.toExponentExact(exponent));
- }
-
- @Override
- public String toString() {
- return this.toString(!this.value.isExact());
- }
-
- /**
- * Returns a string representing the object. <br>
- * If the attached unit has a name or symbol, the string looks like "12 km".
- * Otherwise, it looks like "13 unnamed unit (= 2 m/s)".
- * <p>
- * If showUncertainty is true, strings like "35 ± 8" are shown instead of
- * single numbers.
- * <p>
- * Non-exact values are rounded intelligently based on their uncertainty.
- *
- * @since 2020-07-26
- */
- public String toString(final boolean showUncertainty) {
- final Optional<String> primaryName = this.unit.getPrimaryName();
- final Optional<String> symbol = this.unit.getSymbol();
- final String chosenName = symbol.orElse(primaryName.orElse(null));
-
- final UncertainDouble baseValue = this.unit.convertToBase(this.value);
-
- // get rounded strings
- // if showUncertainty is true, add brackets around the string
- final String valueString = showUncertainty ? "("
- : "" + this.value.toString(showUncertainty)
- + (showUncertainty ? ")" : "");
- final String baseValueString = showUncertainty ? "("
- : "" + baseValue.toString(showUncertainty)
- + (showUncertainty ? ")" : "");
-
- // create string
- if (primaryName.isEmpty() && symbol.isEmpty())
- return String.format("%s unnamed unit (= %s %s)", valueString,
- baseValueString, this.unit.getBase());
- else
- return String.format("%s %s", valueString, chosenName);
- }
-}
diff --git a/src/org/unitConverter/unit/MultiUnit.java b/src/org/unitConverter/unit/MultiUnit.java
deleted file mode 100644
index a1623f8..0000000
--- a/src/org/unitConverter/unit/MultiUnit.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/**
- * Copyright (C) 2020 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.unitConverter.math.ObjectProduct;
-
-/**
- * A combination of units, like "5 foot + 7 inch". All but the last units should
- * have a whole number value associated with them.
- *
- * @since 2020-10-02
- */
-public final class MultiUnit extends Unitlike<List<Double>> {
- /**
- * Creates a {@code MultiUnit} from its units. It will not have a name or
- * symbol.
- *
- * @since 2020-10-03
- */
- public static final MultiUnit of(LinearUnit... units) {
- return of(Arrays.asList(units));
- }
-
- /**
- * Creates a {@code MultiUnit} from its units. It will not have a name or
- * symbol.
- *
- * @since 2020-10-03
- */
- public static final MultiUnit of(List<LinearUnit> units) {
- if (units.size() < 1)
- throw new IllegalArgumentException("Must have at least one unit");
- final ObjectProduct<BaseUnit> unitBase = units.get(0).getBase();
- for (final LinearUnit unit : units) {
- if (!unitBase.equals(unit.getBase()))
- throw new IllegalArgumentException(
- "All units must have the same base.");
- }
- return new MultiUnit(new ArrayList<>(units), unitBase, NameSymbol.EMPTY);
- }
-
- /**
- * The units that make up this value.
- */
- private final List<LinearUnit> units;
-
- /**
- * Creates a {@code MultiUnit}.
- *
- * @since 2020-10-03
- */
- private MultiUnit(List<LinearUnit> units, ObjectProduct<BaseUnit> unitBase,
- NameSymbol ns) {
- super(unitBase, ns);
- this.units = units;
- }
-
- @Override
- protected List<Double> convertFromBase(double value) {
- final List<Double> values = new ArrayList<>(this.units.size());
- double temp = value;
-
- for (final LinearUnit unit : this.units.subList(0,
- this.units.size() - 1)) {
- values.add(Math.floor(temp / unit.getConversionFactor()));
- temp %= unit.getConversionFactor();
- }
-
- values.add(this.units.size() - 1,
- this.units.get(this.units.size() - 1).convertFromBase(temp));
-
- return values;
- }
-
- /**
- * Converts a value expressed in this unitlike form to a value expressed in
- * {@code other}.
- *
- * @implSpec If conversion is possible, this implementation returns
- * {@code other.convertFromBase(this.convertToBase(value))}.
- * Therefore, overriding either of those methods will change the
- * output of this method.
- *
- * @param other unit to convert to
- * @param value value to convert
- * @return converted value
- * @since 2020-10-03
- * @throws IllegalArgumentException if {@code other} is incompatible for
- * conversion with this unitlike form (as
- * tested by {@link Unit#canConvertTo}).
- * @throws NullPointerException if other is null
- */
- public final <U extends Unitlike<V>, V> V convertTo(U other,
- double... values) {
- final List<Double> valueList = new ArrayList<>(values.length);
- for (final double d : values) {
- valueList.add(d);
- }
-
- return this.convertTo(other, valueList);
- }
-
- /**
- * Converts a value expressed in this unitlike form to a value expressed in
- * {@code other}.
- *
- * @implSpec If conversion is possible, this implementation returns
- * {@code other.convertFromBase(this.convertToBase(value))}.
- * Therefore, overriding either of those methods will change the
- * output of this method.
- *
- * @param other unit to convert to
- * @param value value to convert
- * @return converted value
- * @since 2020-10-03
- * @throws IllegalArgumentException if {@code other} is incompatible for
- * conversion with this unitlike form (as
- * tested by {@link Unit#canConvertTo}).
- * @throws NullPointerException if other is null
- */
- public final double convertTo(Unit other, double... values) {
- final List<Double> valueList = new ArrayList<>(values.length);
- for (final double d : values) {
- valueList.add(d);
- }
-
- return this.convertTo(other, valueList);
- }
-
- @Override
- protected double convertToBase(List<Double> value) {
- if (value.size() != this.units.size())
- throw new IllegalArgumentException("Wrong number of values for "
- + this.units.size() + "-unit MultiUnit.");
-
- double baseValue = 0;
- for (int i = 0; i < this.units.size(); i++) {
- baseValue += value.get(i) * this.units.get(i).getConversionFactor();
- }
- return baseValue;
- }
-}
diff --git a/src/org/unitConverter/unit/MultiUnitTest.java b/src/org/unitConverter/unit/MultiUnitTest.java
deleted file mode 100644
index 5ea9d07..0000000
--- a/src/org/unitConverter/unit/MultiUnitTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/**
- * Copyright (C) 2020 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
-import java.util.concurrent.ThreadLocalRandom;
-
-import org.junit.jupiter.api.Test;
-
-/**
- * Tests related to the {@code MultiUnit}.
- *
- * @since 2020-10-03
- */
-class MultiUnitTest {
-
- @Test
- final void testConvert() {
- final Random rng = ThreadLocalRandom.current();
- final MultiUnit footInch = MultiUnit.of(BritishImperial.Length.FOOT,
- BritishImperial.Length.INCH);
-
- assertEquals(1702.0, footInch.convertTo(SI.METRE.withPrefix(SI.MILLI),
- Arrays.asList(5.0, 7.0)), 1.0);
-
- for (int i = 0; i < 1000; i++) {
- final double feet = rng.nextInt(1000);
- final double inches = rng.nextDouble() * 12;
- final double millimetres = feet * 304.8 + inches * 25.4;
-
- final List<Double> feetAndInches = SI.METRE.withPrefix(SI.MILLI)
- .convertTo(footInch, millimetres);
- assertEquals(feet, feetAndInches.get(0), 1e-10);
- assertEquals(inches, feetAndInches.get(1), 1e-10);
- }
- }
-
- /**
- * Test method for
- * {@link org.unitConverter.unit.MultiUnit#convertFromBase(double)}.
- */
- @Test
- final void testConvertFromBase() {
- final Random rng = ThreadLocalRandom.current();
- final MultiUnit footInch = MultiUnit.of(BritishImperial.Length.FOOT,
- BritishImperial.Length.INCH);
-
- // 1.7 m =~ 5' + 7"
- final List<Double> values = footInch.convertFromBase(1.7018);
-
- assertEquals(5, values.get(0));
- assertEquals(7, values.get(1), 1e-12);
-
- for (int i = 0; i < 1000; i++) {
- final double feet = rng.nextInt(1000);
- final double inches = rng.nextDouble() * 12;
- final double metres = feet * 0.3048 + inches * 0.0254;
-
- final List<Double> feetAndInches = footInch.convertFromBase(metres);
- assertEquals(feet, feetAndInches.get(0), 1e-10);
- assertEquals(inches, feetAndInches.get(1), 1e-10);
- }
- }
-
- /**
- * Test method for
- * {@link org.unitConverter.unit.MultiUnit#convertToBase(java.util.List)}.
- */
- @Test
- final void testConvertToBase() {
- final Random rng = ThreadLocalRandom.current();
- final MultiUnit footInch = MultiUnit.of(BritishImperial.Length.FOOT,
- BritishImperial.Length.INCH);
-
- // 1.7 m =~ 5' + 7"
- assertEquals(1.7018, footInch.convertToBase(Arrays.asList(5.0, 7.0)),
- 1e-12);
-
- for (int i = 0; i < 1000; i++) {
- final double feet = rng.nextInt(1000);
- final double inches = rng.nextDouble() * 12;
- final double metres = feet * 0.3048 + inches * 0.0254;
-
- assertEquals(metres,
- footInch.convertToBase(Arrays.asList(feet, inches)), 1e-12);
- }
- }
-}
diff --git a/src/org/unitConverter/unit/NameSymbol.java b/src/org/unitConverter/unit/NameSymbol.java
deleted file mode 100644
index 8d8302a..0000000
--- a/src/org/unitConverter/unit/NameSymbol.java
+++ /dev/null
@@ -1,280 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-
-/**
- * A class that can be used to specify names and a symbol for a unit.
- *
- * @author Adrien Hopkins
- * @since 2019-10-21
- */
-public final class NameSymbol {
- public static final NameSymbol EMPTY = new NameSymbol(Optional.empty(),
- Optional.empty(), new HashSet<>());
-
- /**
- * Creates a {@code NameSymbol}, ensuring that if primaryName is null and
- * otherNames is not empty, one name is moved from otherNames to primaryName
- *
- * Ensure that otherNames is a copy of the inputted argument.
- */
- private static final NameSymbol create(final String name,
- final String symbol, final Set<String> otherNames) {
- final Optional<String> primaryName;
-
- if (name == null && !otherNames.isEmpty()) {
- // get primary name and remove it from savedNames
- final Iterator<String> it = otherNames.iterator();
- assert it.hasNext();
- primaryName = Optional.of(it.next());
- otherNames.remove(primaryName.get());
- } else {
- primaryName = Optional.ofNullable(name);
- }
-
- return new NameSymbol(primaryName, Optional.ofNullable(symbol),
- otherNames);
- }
-
- /**
- * Gets a {@code NameSymbol} with a primary name, a symbol and no other
- * names.
- *
- * @param name name to use
- * @param symbol symbol to use
- * @return NameSymbol instance
- * @since 2019-10-21
- * @throws NullPointerException if name or symbol is null
- */
- public static final NameSymbol of(final String name, final String symbol) {
- return new NameSymbol(Optional.of(name), Optional.of(symbol),
- new HashSet<>());
- }
-
- /**
- * Gets a {@code NameSymbol} with a primary name, a symbol and additional
- * names.
- *
- * @param name name to use
- * @param symbol symbol to use
- * @param otherNames other names to use
- * @return NameSymbol instance
- * @since 2019-10-21
- * @throws NullPointerException if any argument is null
- */
- public static final NameSymbol of(final String name, final String symbol,
- final Set<String> otherNames) {
- return new NameSymbol(Optional.of(name), Optional.of(symbol),
- new HashSet<>(Objects.requireNonNull(otherNames,
- "otherNames must not be null.")));
- }
-
- /**
- * h * Gets a {@code NameSymbol} with a primary name, a symbol and additional
- * names.
- *
- * @param name name to use
- * @param symbol symbol to use
- * @param otherNames other names to use
- * @return NameSymbol instance
- * @since 2019-10-21
- * @throws NullPointerException if any argument is null
- */
- public static final NameSymbol of(final String name, final String symbol,
- final String... otherNames) {
- return new NameSymbol(Optional.of(name), Optional.of(symbol),
- new HashSet<>(Arrays.asList(Objects.requireNonNull(otherNames,
- "otherNames must not be null."))));
- }
-
- /**
- * Gets a {@code NameSymbol} with a primary name, no symbol, and no other
- * names.
- *
- * @param name name to use
- * @return NameSymbol instance
- * @since 2019-10-21
- * @throws NullPointerException if name is null
- */
- public static final NameSymbol ofName(final String name) {
- return new NameSymbol(Optional.of(name), Optional.empty(),
- new HashSet<>());
- }
-
- /**
- * Gets a {@code NameSymbol} with a primary name, a symbol and additional
- * names.
- * <p>
- * If any argument is null, this static factory replaces it with an empty
- * Optional or empty Set.
- * <p>
- * If {@code name} is null and {@code otherNames} is not empty, a primary
- * name will be picked from {@code otherNames}. This name will not appear in
- * getOtherNames().
- *
- * @param name name to use
- * @param symbol symbol to use
- * @param otherNames other names to use
- * @return NameSymbol instance
- * @since 2019-11-26
- */
- public static final NameSymbol ofNullable(final String name,
- final String symbol, final Set<String> otherNames) {
- return NameSymbol.create(name, symbol,
- otherNames == null ? new HashSet<>() : new HashSet<>(otherNames));
- }
-
- /**
- * h * Gets a {@code NameSymbol} with a primary name, a symbol and additional
- * names.
- * <p>
- * If any argument is null, this static factory replaces it with an empty
- * Optional or empty Set.
- * <p>
- * If {@code name} is null and {@code otherNames} is not empty, a primary
- * name will be picked from {@code otherNames}. This name will not appear in
- * getOtherNames().
- *
- * @param name name to use
- * @param symbol symbol to use
- * @param otherNames other names to use
- * @return NameSymbol instance
- * @since 2019-11-26
- */
- public static final NameSymbol ofNullable(final String name,
- final String symbol, final String... otherNames) {
- return create(name, symbol, otherNames == null ? new HashSet<>()
- : new HashSet<>(Arrays.asList(otherNames)));
- }
-
- /**
- * Gets a {@code NameSymbol} with a symbol and no names.
- *
- * @param symbol symbol to use
- * @return NameSymbol instance
- * @since 2019-10-21
- * @throws NullPointerException if symbol is null
- */
- public static final NameSymbol ofSymbol(final String symbol) {
- return new NameSymbol(Optional.empty(), Optional.of(symbol),
- new HashSet<>());
- }
-
- private final Optional<String> primaryName;
- private final Optional<String> symbol;
-
- private final Set<String> otherNames;
-
- /**
- * Creates the {@code NameSymbol}.
- *
- * @param primaryName primary name of unit
- * @param symbol symbol used to represent unit
- * @param otherNames other names and/or spellings, should be a mutable copy
- * of the argument
- * @since 2019-10-21
- */
- private NameSymbol(final Optional<String> primaryName,
- final Optional<String> symbol, final Set<String> otherNames) {
- this.primaryName = primaryName;
- this.symbol = symbol;
- otherNames.remove(null);
- this.otherNames = Collections.unmodifiableSet(otherNames);
-
- if (this.primaryName.isEmpty()) {
- assert this.otherNames.isEmpty();
- }
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (!(obj instanceof NameSymbol))
- return false;
- final NameSymbol other = (NameSymbol) obj;
- if (this.otherNames == null) {
- if (other.otherNames != null)
- return false;
- } else if (!this.otherNames.equals(other.otherNames))
- return false;
- if (this.primaryName == null) {
- if (other.primaryName != null)
- return false;
- } else if (!this.primaryName.equals(other.primaryName))
- return false;
- if (this.symbol == null) {
- if (other.symbol != null)
- return false;
- } else if (!this.symbol.equals(other.symbol))
- return false;
- return true;
- }
-
- /**
- * @return otherNames
- * @since 2019-10-21
- */
- public final Set<String> getOtherNames() {
- return this.otherNames;
- }
-
- /**
- * @return primaryName
- * @since 2019-10-21
- */
- public final Optional<String> getPrimaryName() {
- return this.primaryName;
- }
-
- /**
- * @return symbol
- * @since 2019-10-21
- */
- public final Optional<String> getSymbol() {
- return this.symbol;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result
- + (this.otherNames == null ? 0 : this.otherNames.hashCode());
- result = prime * result
- + (this.primaryName == null ? 0 : this.primaryName.hashCode());
- result = prime * result
- + (this.symbol == null ? 0 : this.symbol.hashCode());
- return result;
- }
-
- /**
- * @return true iff this {@code NameSymbol} contains no names or symbols.
- */
- public final boolean isEmpty() {
- // if primaryName is empty, otherNames must also be empty
- return this.primaryName.isEmpty() && this.symbol.isEmpty();
- }
-} \ No newline at end of file
diff --git a/src/org/unitConverter/unit/Nameable.java b/src/org/unitConverter/unit/Nameable.java
deleted file mode 100644
index 36740ab..0000000
--- a/src/org/unitConverter/unit/Nameable.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/**
- * Copyright (C) 2020 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import java.util.Optional;
-import java.util.Set;
-
-/**
- * An object that can hold one or more names, and possibly a symbol. The name
- * and symbol data should be immutable.
- *
- * @since 2020-09-07
- */
-public interface Nameable {
- /**
- * @return a {@code NameSymbol} that contains this object's primary name,
- * symbol and other names
- * @since 2020-09-07
- */
- NameSymbol getNameSymbol();
-
- /**
- * @return set of alternate names
- * @since 2020-09-07
- */
- default Set<String> getOtherNames() {
- return this.getNameSymbol().getOtherNames();
- }
-
- /**
- * @return preferred name of object
- * @since 2020-09-07
- */
- default Optional<String> getPrimaryName() {
- return this.getNameSymbol().getPrimaryName();
- }
-
- /**
- * @return short symbol representing object
- * @since 2020-09-07
- */
- default Optional<String> getSymbol() {
- return this.getNameSymbol().getSymbol();
- }
-}
diff --git a/src/org/unitConverter/unit/SI.java b/src/org/unitConverter/unit/SI.java
deleted file mode 100644
index c88d2bc..0000000
--- a/src/org/unitConverter/unit/SI.java
+++ /dev/null
@@ -1,479 +0,0 @@
-/**
- * Copyright (C) 2018 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import java.util.Set;
-
-import org.unitConverter.math.ObjectProduct;
-
-/**
- * All of the units, prefixes and dimensions that are used by the SI, as well as
- * some outside the SI.
- *
- * <p>
- * This class does not include prefixed units. To obtain prefixed units, use
- * {@link LinearUnit#withPrefix}:
- *
- * <pre>
- * LinearUnit KILOMETRE = SI.METRE.withPrefix(SI.KILO);
- * </pre>
- *
- *
- * @author Adrien Hopkins
- * @since 2019-10-16
- */
-public final class SI {
- /// dimensions used by SI units
- // base dimensions, as BaseDimensions
- public static final class BaseDimensions {
- public static final BaseDimension LENGTH = BaseDimension.valueOf("Length",
- "L");
- public static final BaseDimension MASS = BaseDimension.valueOf("Mass",
- "M");
- public static final BaseDimension TIME = BaseDimension.valueOf("Time",
- "T");
- public static final BaseDimension ELECTRIC_CURRENT = BaseDimension
- .valueOf("Electric Current", "I");
- public static final BaseDimension TEMPERATURE = BaseDimension
- .valueOf("Temperature", "\u0398"); // theta symbol
- public static final BaseDimension QUANTITY = BaseDimension
- .valueOf("Quantity", "N");
- public static final BaseDimension LUMINOUS_INTENSITY = BaseDimension
- .valueOf("Luminous Intensity", "J");
- public static final BaseDimension INFORMATION = BaseDimension
- .valueOf("Information", "Info"); // non-SI
- public static final BaseDimension CURRENCY = BaseDimension
- .valueOf("Currency", "$$"); // non-SI
-
- // You may NOT get SI.BaseDimensions instances!
- private BaseDimensions() {
- throw new AssertionError();
- }
- }
-
- /// base units of the SI
- // suppressing warnings since these are the same object, but in a different
- /// form (class)
- @SuppressWarnings("hiding")
- public static final class BaseUnits {
- public static final BaseUnit METRE = BaseUnit
- .valueOf(BaseDimensions.LENGTH, "metre", "m");
- public static final BaseUnit KILOGRAM = BaseUnit
- .valueOf(BaseDimensions.MASS, "kilogram", "kg");
- public static final BaseUnit SECOND = BaseUnit
- .valueOf(BaseDimensions.TIME, "second", "s");
- public static final BaseUnit AMPERE = BaseUnit
- .valueOf(BaseDimensions.ELECTRIC_CURRENT, "ampere", "A");
- public static final BaseUnit KELVIN = BaseUnit
- .valueOf(BaseDimensions.TEMPERATURE, "kelvin", "K");
- public static final BaseUnit MOLE = BaseUnit
- .valueOf(BaseDimensions.QUANTITY, "mole", "mol");
- public static final BaseUnit CANDELA = BaseUnit
- .valueOf(BaseDimensions.LUMINOUS_INTENSITY, "candela", "cd");
- public static final BaseUnit BIT = BaseUnit
- .valueOf(BaseDimensions.INFORMATION, "bit", "b");
- public static final BaseUnit DOLLAR = BaseUnit
- .valueOf(BaseDimensions.CURRENCY, "dollar", "$");
-
- public static final Set<BaseUnit> BASE_UNITS = Set.of(METRE, KILOGRAM,
- SECOND, AMPERE, KELVIN, MOLE, CANDELA, BIT);
-
- // You may NOT get SI.BaseUnits instances!
- private BaseUnits() {
- throw new AssertionError();
- }
- }
-
- /**
- * Constants that relate to the SI or other systems.
- *
- * @author Adrien Hopkins
- * @since 2019-11-08
- */
- public static final class Constants {
- public static final LinearUnit EARTH_GRAVITY = METRE.dividedBy(SECOND)
- .dividedBy(SECOND).times(9.80665);
- }
-
- // dimensions used in the SI, as ObjectProducts
- public static final class Dimensions {
- public static final ObjectProduct<BaseDimension> EMPTY = ObjectProduct
- .empty();
- public static final ObjectProduct<BaseDimension> LENGTH = ObjectProduct
- .oneOf(BaseDimensions.LENGTH);
- public static final ObjectProduct<BaseDimension> MASS = ObjectProduct
- .oneOf(BaseDimensions.MASS);
- public static final ObjectProduct<BaseDimension> TIME = ObjectProduct
- .oneOf(BaseDimensions.TIME);
- public static final ObjectProduct<BaseDimension> ELECTRIC_CURRENT = ObjectProduct
- .oneOf(BaseDimensions.ELECTRIC_CURRENT);
- public static final ObjectProduct<BaseDimension> TEMPERATURE = ObjectProduct
- .oneOf(BaseDimensions.TEMPERATURE);
- public static final ObjectProduct<BaseDimension> QUANTITY = ObjectProduct
- .oneOf(BaseDimensions.QUANTITY);
- public static final ObjectProduct<BaseDimension> LUMINOUS_INTENSITY = ObjectProduct
- .oneOf(BaseDimensions.LUMINOUS_INTENSITY);
- public static final ObjectProduct<BaseDimension> INFORMATION = ObjectProduct
- .oneOf(BaseDimensions.INFORMATION);
- public static final ObjectProduct<BaseDimension> CURRENCY = ObjectProduct
- .oneOf(BaseDimensions.CURRENCY);
-
- // derived dimensions without named SI units
- public static final ObjectProduct<BaseDimension> AREA = LENGTH
- .times(LENGTH);
- public static final ObjectProduct<BaseDimension> VOLUME = AREA
- .times(LENGTH);
- public static final ObjectProduct<BaseDimension> VELOCITY = LENGTH
- .dividedBy(TIME);
- public static final ObjectProduct<BaseDimension> ACCELERATION = VELOCITY
- .dividedBy(TIME);
- public static final ObjectProduct<BaseDimension> WAVENUMBER = EMPTY
- .dividedBy(LENGTH);
- public static final ObjectProduct<BaseDimension> MASS_DENSITY = MASS
- .dividedBy(VOLUME);
- public static final ObjectProduct<BaseDimension> SURFACE_DENSITY = MASS
- .dividedBy(AREA);
- public static final ObjectProduct<BaseDimension> SPECIFIC_VOLUME = VOLUME
- .dividedBy(MASS);
- public static final ObjectProduct<BaseDimension> CURRENT_DENSITY = ELECTRIC_CURRENT
- .dividedBy(AREA);
- public static final ObjectProduct<BaseDimension> MAGNETIC_FIELD_STRENGTH = ELECTRIC_CURRENT
- .dividedBy(LENGTH);
- public static final ObjectProduct<BaseDimension> CONCENTRATION = QUANTITY
- .dividedBy(VOLUME);
- public static final ObjectProduct<BaseDimension> MASS_CONCENTRATION = CONCENTRATION
- .times(MASS);
- public static final ObjectProduct<BaseDimension> LUMINANCE = LUMINOUS_INTENSITY
- .dividedBy(AREA);
- public static final ObjectProduct<BaseDimension> REFRACTIVE_INDEX = VELOCITY
- .dividedBy(VELOCITY);
- public static final ObjectProduct<BaseDimension> REFRACTIVE_PERMEABILITY = EMPTY
- .times(EMPTY);
- public static final ObjectProduct<BaseDimension> ANGLE = LENGTH
- .dividedBy(LENGTH);
- public static final ObjectProduct<BaseDimension> SOLID_ANGLE = AREA
- .dividedBy(AREA);
-
- // derived dimensions with named SI units
- public static final ObjectProduct<BaseDimension> FREQUENCY = EMPTY
- .dividedBy(TIME);
- public static final ObjectProduct<BaseDimension> FORCE = MASS
- .times(ACCELERATION);
- public static final ObjectProduct<BaseDimension> ENERGY = FORCE
- .times(LENGTH);
- public static final ObjectProduct<BaseDimension> POWER = ENERGY
- .dividedBy(TIME);
- public static final ObjectProduct<BaseDimension> ELECTRIC_CHARGE = ELECTRIC_CURRENT
- .times(TIME);
- public static final ObjectProduct<BaseDimension> VOLTAGE = ENERGY
- .dividedBy(ELECTRIC_CHARGE);
- public static final ObjectProduct<BaseDimension> CAPACITANCE = ELECTRIC_CHARGE
- .dividedBy(VOLTAGE);
- public static final ObjectProduct<BaseDimension> ELECTRIC_RESISTANCE = VOLTAGE
- .dividedBy(ELECTRIC_CURRENT);
- public static final ObjectProduct<BaseDimension> ELECTRIC_CONDUCTANCE = ELECTRIC_CURRENT
- .dividedBy(VOLTAGE);
- public static final ObjectProduct<BaseDimension> MAGNETIC_FLUX = VOLTAGE
- .times(TIME);
- public static final ObjectProduct<BaseDimension> MAGNETIC_FLUX_DENSITY = MAGNETIC_FLUX
- .dividedBy(AREA);
- public static final ObjectProduct<BaseDimension> INDUCTANCE = MAGNETIC_FLUX
- .dividedBy(ELECTRIC_CURRENT);
- public static final ObjectProduct<BaseDimension> LUMINOUS_FLUX = LUMINOUS_INTENSITY
- .times(SOLID_ANGLE);
- public static final ObjectProduct<BaseDimension> ILLUMINANCE = LUMINOUS_FLUX
- .dividedBy(AREA);
- public static final ObjectProduct<BaseDimension> SPECIFIC_ENERGY = ENERGY
- .dividedBy(MASS);
- public static final ObjectProduct<BaseDimension> CATALYTIC_ACTIVITY = QUANTITY
- .dividedBy(TIME);
-
- // You may NOT get SI.Dimension instances!
- private Dimensions() {
- throw new AssertionError();
- }
- }
-
- /// The units of the SI
- public static final LinearUnit ONE = LinearUnit
- .valueOf(ObjectProduct.empty(), 1);
-
- public static final LinearUnit METRE = BaseUnits.METRE.asLinearUnit()
- .withName(NameSymbol.of("metre", "m", "meter"));
- public static final LinearUnit KILOGRAM = BaseUnits.KILOGRAM.asLinearUnit()
- .withName(NameSymbol.of("kilogram", "kg"));
- public static final LinearUnit SECOND = BaseUnits.SECOND.asLinearUnit()
- .withName(NameSymbol.of("second", "s", "sec"));
- public static final LinearUnit AMPERE = BaseUnits.AMPERE.asLinearUnit()
- .withName(NameSymbol.of("ampere", "A"));
- public static final LinearUnit KELVIN = BaseUnits.KELVIN.asLinearUnit()
- .withName(NameSymbol.of("kelvin", "K"));
- public static final LinearUnit MOLE = BaseUnits.MOLE.asLinearUnit()
- .withName(NameSymbol.of("mole", "mol"));
- public static final LinearUnit CANDELA = BaseUnits.CANDELA.asLinearUnit()
- .withName(NameSymbol.of("candela", "cd"));
- public static final LinearUnit BIT = BaseUnits.BIT.asLinearUnit()
- .withName(NameSymbol.of("bit", "b"));
- public static final LinearUnit DOLLAR = BaseUnits.DOLLAR.asLinearUnit()
- .withName(NameSymbol.of("dollar", "$"));
- // Non-base units
- public static final LinearUnit RADIAN = METRE.dividedBy(METRE)
- .withName(NameSymbol.of("radian", "rad"));
-
- public static final LinearUnit STERADIAN = RADIAN.times(RADIAN)
- .withName(NameSymbol.of("steradian", "sr"));
- public static final LinearUnit HERTZ = ONE.dividedBy(SECOND)
- .withName(NameSymbol.of("hertz", "Hz"));
- // for periodic phenomena
- public static final LinearUnit NEWTON = KILOGRAM.times(METRE)
- .dividedBy(SECOND.times(SECOND))
- .withName(NameSymbol.of("newton", "N"));
- public static final LinearUnit PASCAL = NEWTON.dividedBy(METRE.times(METRE))
- .withName(NameSymbol.of("pascal", "Pa"));
- public static final LinearUnit JOULE = NEWTON.times(METRE)
- .withName(NameSymbol.of("joule", "J"));
- public static final LinearUnit WATT = JOULE.dividedBy(SECOND)
- .withName(NameSymbol.of("watt", "W"));
- public static final LinearUnit COULOMB = AMPERE.times(SECOND)
- .withName(NameSymbol.of("coulomb", "C"));
- public static final LinearUnit VOLT = JOULE.dividedBy(COULOMB)
- .withName(NameSymbol.of("volt", "V"));
- public static final LinearUnit FARAD = COULOMB.dividedBy(VOLT)
- .withName(NameSymbol.of("farad", "F"));
- public static final LinearUnit OHM = VOLT.dividedBy(AMPERE)
- .withName(NameSymbol.of("ohm", "\u03A9")); // omega
- public static final LinearUnit SIEMENS = ONE.dividedBy(OHM)
- .withName(NameSymbol.of("siemens", "S"));
- public static final LinearUnit WEBER = VOLT.times(SECOND)
- .withName(NameSymbol.of("weber", "Wb"));
- public static final LinearUnit TESLA = WEBER.dividedBy(METRE.times(METRE))
- .withName(NameSymbol.of("tesla", "T"));
- public static final LinearUnit HENRY = WEBER.dividedBy(AMPERE)
- .withName(NameSymbol.of("henry", "H"));
- public static final LinearUnit LUMEN = CANDELA.times(STERADIAN)
- .withName(NameSymbol.of("lumen", "lm"));
- public static final LinearUnit LUX = LUMEN.dividedBy(METRE.times(METRE))
- .withName(NameSymbol.of("lux", "lx"));
- public static final LinearUnit BEQUEREL = ONE.dividedBy(SECOND)
- .withName(NameSymbol.of("bequerel", "Bq"));
- // for activity referred to a nucleotide
- public static final LinearUnit GRAY = JOULE.dividedBy(KILOGRAM)
- .withName(NameSymbol.of("grey", "Gy"));
- // for absorbed dose
- public static final LinearUnit SIEVERT = JOULE.dividedBy(KILOGRAM)
- .withName(NameSymbol.of("sievert", "Sv"));
- // for dose equivalent
- public static final LinearUnit KATAL = MOLE.dividedBy(SECOND)
- .withName(NameSymbol.of("katal", "kat"));
- // common derived units included for convenience
- public static final LinearUnit GRAM = KILOGRAM.dividedBy(1000)
- .withName(NameSymbol.of("gram", "g"));
-
- public static final LinearUnit SQUARE_METRE = METRE.toExponent(2)
- .withName(NameSymbol.of("square metre", "m^2", "square meter",
- "metre squared", "meter squared"));
- public static final LinearUnit CUBIC_METRE = METRE.toExponent(3)
- .withName(NameSymbol.of("cubic metre", "m^3", "cubic meter",
- "metre cubed", "meter cubed"));
- public static final LinearUnit METRE_PER_SECOND = METRE.dividedBy(SECOND)
- .withName(
- NameSymbol.of("metre per second", "m/s", "meter per second"));
- // Non-SI units included for convenience
- public static final Unit CELSIUS = Unit
- .fromConversionFunctions(KELVIN.getBase(), tempK -> tempK - 273.15,
- tempC -> tempC + 273.15)
- .withName(NameSymbol.of("degree Celsius", "\u00B0C"));
-
- public static final LinearUnit MINUTE = SECOND.times(60)
- .withName(NameSymbol.of("minute", "min"));
- public static final LinearUnit HOUR = MINUTE.times(60)
- .withName(NameSymbol.of("hour", "h", "hr"));
- public static final LinearUnit DAY = HOUR.times(60)
- .withName(NameSymbol.of("day", "d"));
- public static final LinearUnit KILOMETRE_PER_HOUR = METRE.times(1000)
- .dividedBy(HOUR).withName(NameSymbol.of("kilometre per hour", "km/h",
- "kilometer per hour"));
- public static final LinearUnit DEGREE = RADIAN.times(360 / (2 * Math.PI))
- .withName(NameSymbol.of("degree", "\u00B0", "deg"));
- public static final LinearUnit ARCMINUTE = DEGREE.dividedBy(60)
- .withName(NameSymbol.of("arcminute", "arcmin"));
- public static final LinearUnit ARCSECOND = ARCMINUTE.dividedBy(60)
- .withName(NameSymbol.of("arcsecond", "arcsec"));
- public static final LinearUnit ASTRONOMICAL_UNIT = METRE
- .times(149597870700.0)
- .withName(NameSymbol.of("astronomical unit", "au"));
- public static final LinearUnit PARSEC = ASTRONOMICAL_UNIT
- .dividedBy(ARCSECOND).withName(NameSymbol.of("parsec", "pc"));
- public static final LinearUnit HECTARE = METRE.times(METRE).times(10000.0)
- .withName(NameSymbol.of("hectare", "ha"));
- public static final LinearUnit LITRE = METRE.times(METRE).times(METRE)
- .dividedBy(1000.0).withName(NameSymbol.of("litre", "L", "l", "liter"));
- public static final LinearUnit TONNE = KILOGRAM.times(1000.0)
- .withName(NameSymbol.of("tonne", "t", "metric ton"));
- public static final LinearUnit DALTON = KILOGRAM.times(1.660539040e-27)
- .withName(NameSymbol.of("dalton", "Da", "atomic unit", "u")); // approximate
- // value
- public static final LinearUnit ELECTRONVOLT = JOULE.times(1.602176634e-19)
- .withName(NameSymbol.of("electron volt", "eV"));
- public static final LinearUnit BYTE = BIT.times(8)
- .withName(NameSymbol.of("byte", "B"));
- public static final Unit NEPER = Unit.fromConversionFunctions(ONE.getBase(),
- pr -> 0.5 * Math.log(pr), Np -> Math.exp(2 * Np))
- .withName(NameSymbol.of("neper", "Np"));
- public static final Unit BEL = Unit.fromConversionFunctions(ONE.getBase(),
- pr -> Math.log10(pr), dB -> Math.pow(10, dB))
- .withName(NameSymbol.of("bel", "B"));
- public static final Unit DECIBEL = Unit
- .fromConversionFunctions(ONE.getBase(), pr -> 10 * Math.log10(pr),
- dB -> Math.pow(10, dB / 10))
- .withName(NameSymbol.of("decibel", "dB"));
-
- /// The prefixes of the SI
- // expanding decimal prefixes
- public static final UnitPrefix KILO = UnitPrefix.valueOf(1e3)
- .withName(NameSymbol.of("kilo", "k", "K"));
- public static final UnitPrefix MEGA = UnitPrefix.valueOf(1e6)
- .withName(NameSymbol.of("mega", "M"));
- public static final UnitPrefix GIGA = UnitPrefix.valueOf(1e9)
- .withName(NameSymbol.of("giga", "G"));
- public static final UnitPrefix TERA = UnitPrefix.valueOf(1e12)
- .withName(NameSymbol.of("tera", "T"));
- public static final UnitPrefix PETA = UnitPrefix.valueOf(1e15)
- .withName(NameSymbol.of("peta", "P"));
- public static final UnitPrefix EXA = UnitPrefix.valueOf(1e18)
- .withName(NameSymbol.of("exa", "E"));
- public static final UnitPrefix ZETTA = UnitPrefix.valueOf(1e21)
- .withName(NameSymbol.of("zetta", "Z"));
- public static final UnitPrefix YOTTA = UnitPrefix.valueOf(1e24)
- .withName(NameSymbol.of("yotta", "Y"));
-
- // contracting decimal prefixes
- public static final UnitPrefix MILLI = UnitPrefix.valueOf(1e-3)
- .withName(NameSymbol.of("milli", "m"));
- public static final UnitPrefix MICRO = UnitPrefix.valueOf(1e-6)
- .withName(NameSymbol.of("micro", "\u03BC", "u")); // mu
- public static final UnitPrefix NANO = UnitPrefix.valueOf(1e-9)
- .withName(NameSymbol.of("nano", "n"));
- public static final UnitPrefix PICO = UnitPrefix.valueOf(1e-12)
- .withName(NameSymbol.of("pico", "p"));
- public static final UnitPrefix FEMTO = UnitPrefix.valueOf(1e-15)
- .withName(NameSymbol.of("femto", "f"));
- public static final UnitPrefix ATTO = UnitPrefix.valueOf(1e-18)
- .withName(NameSymbol.of("atto", "a"));
- public static final UnitPrefix ZEPTO = UnitPrefix.valueOf(1e-21)
- .withName(NameSymbol.of("zepto", "z"));
- public static final UnitPrefix YOCTO = UnitPrefix.valueOf(1e-24)
- .withName(NameSymbol.of("yocto", "y"));
-
- // prefixes that don't match the pattern of thousands
- public static final UnitPrefix DEKA = UnitPrefix.valueOf(1e1)
- .withName(NameSymbol.of("deka", "da", "deca", "D"));
- public static final UnitPrefix HECTO = UnitPrefix.valueOf(1e2)
- .withName(NameSymbol.of("hecto", "h", "H", "hekto"));
- public static final UnitPrefix DECI = UnitPrefix.valueOf(1e-1)
- .withName(NameSymbol.of("deci", "d"));
- public static final UnitPrefix CENTI = UnitPrefix.valueOf(1e-2)
- .withName(NameSymbol.of("centi", "c"));
- public static final UnitPrefix KIBI = UnitPrefix.valueOf(1024)
- .withName(NameSymbol.of("kibi", "Ki"));
- public static final UnitPrefix MEBI = KIBI.times(1024)
- .withName(NameSymbol.of("mebi", "Mi"));
- public static final UnitPrefix GIBI = MEBI.times(1024)
- .withName(NameSymbol.of("gibi", "Gi"));
- public static final UnitPrefix TEBI = GIBI.times(1024)
- .withName(NameSymbol.of("tebi", "Ti"));
- public static final UnitPrefix PEBI = TEBI.times(1024)
- .withName(NameSymbol.of("pebi", "Pi"));
- public static final UnitPrefix EXBI = PEBI.times(1024)
- .withName(NameSymbol.of("exbi", "Ei"));
-
- // a few prefixed units
- public static final LinearUnit MICROMETRE = SI.METRE.withPrefix(SI.MICRO);
- public static final LinearUnit MILLIMETRE = SI.METRE.withPrefix(SI.MILLI);
- public static final LinearUnit KILOMETRE = SI.METRE.withPrefix(SI.KILO);
- public static final LinearUnit MEGAMETRE = SI.METRE.withPrefix(SI.MEGA);
-
- public static final LinearUnit MICROLITRE = SI.LITRE.withPrefix(SI.MICRO);
- public static final LinearUnit MILLILITRE = SI.LITRE.withPrefix(SI.MILLI);
- public static final LinearUnit KILOLITRE = SI.LITRE.withPrefix(SI.KILO);
- public static final LinearUnit MEGALITRE = SI.LITRE.withPrefix(SI.MEGA);
-
- public static final LinearUnit MICROSECOND = SI.SECOND.withPrefix(SI.MICRO);
- public static final LinearUnit MILLISECOND = SI.SECOND.withPrefix(SI.MILLI);
- public static final LinearUnit KILOSECOND = SI.SECOND.withPrefix(SI.KILO);
- public static final LinearUnit MEGASECOND = SI.SECOND.withPrefix(SI.MEGA);
-
- public static final LinearUnit MICROGRAM = SI.GRAM.withPrefix(SI.MICRO);
- public static final LinearUnit MILLIGRAM = SI.GRAM.withPrefix(SI.MILLI);
- public static final LinearUnit MEGAGRAM = SI.GRAM.withPrefix(SI.MEGA);
-
- public static final LinearUnit MICRONEWTON = SI.NEWTON.withPrefix(SI.MICRO);
- public static final LinearUnit MILLINEWTON = SI.NEWTON.withPrefix(SI.MILLI);
- public static final LinearUnit KILONEWTON = SI.NEWTON.withPrefix(SI.KILO);
- public static final LinearUnit MEGANEWTON = SI.NEWTON.withPrefix(SI.MEGA);
-
- public static final LinearUnit MICROJOULE = SI.JOULE.withPrefix(SI.MICRO);
- public static final LinearUnit MILLIJOULE = SI.JOULE.withPrefix(SI.MILLI);
- public static final LinearUnit KILOJOULE = SI.JOULE.withPrefix(SI.KILO);
- public static final LinearUnit MEGAJOULE = SI.JOULE.withPrefix(SI.MEGA);
-
- public static final LinearUnit MICROWATT = SI.WATT.withPrefix(SI.MICRO);
- public static final LinearUnit MILLIWATT = SI.WATT.withPrefix(SI.MILLI);
- public static final LinearUnit KILOWATT = SI.WATT.withPrefix(SI.KILO);
- public static final LinearUnit MEGAWATT = SI.WATT.withPrefix(SI.MEGA);
-
- public static final LinearUnit MICROCOULOMB = SI.COULOMB
- .withPrefix(SI.MICRO);
- public static final LinearUnit MILLICOULOMB = SI.COULOMB
- .withPrefix(SI.MILLI);
- public static final LinearUnit KILOCOULOMB = SI.COULOMB.withPrefix(SI.KILO);
- public static final LinearUnit MEGACOULOMB = SI.COULOMB.withPrefix(SI.MEGA);
-
- public static final LinearUnit MICROAMPERE = SI.AMPERE.withPrefix(SI.MICRO);
- public static final LinearUnit MILLIAMPERE = SI.AMPERE.withPrefix(SI.MILLI);
-
- public static final LinearUnit MICROVOLT = SI.VOLT.withPrefix(SI.MICRO);
- public static final LinearUnit MILLIVOLT = SI.VOLT.withPrefix(SI.MILLI);
- public static final LinearUnit KILOVOLT = SI.VOLT.withPrefix(SI.KILO);
- public static final LinearUnit MEGAVOLT = SI.VOLT.withPrefix(SI.MEGA);
-
- public static final LinearUnit KILOOHM = SI.OHM.withPrefix(SI.KILO);
- public static final LinearUnit MEGAOHM = SI.OHM.withPrefix(SI.MEGA);
-
- // sets of prefixes
- public static final Set<UnitPrefix> ALL_PREFIXES = Set.of(DEKA, HECTO, KILO,
- MEGA, GIGA, TERA, PETA, EXA, ZETTA, YOTTA, DECI, CENTI, MILLI, MICRO,
- NANO, PICO, FEMTO, ATTO, ZEPTO, YOCTO, KIBI, MEBI, GIBI, TEBI, PEBI,
- EXBI);
-
- public static final Set<UnitPrefix> DECIMAL_PREFIXES = Set.of(DEKA, HECTO,
- KILO, MEGA, GIGA, TERA, PETA, EXA, ZETTA, YOTTA, DECI, CENTI, MILLI,
- MICRO, NANO, PICO, FEMTO, ATTO, ZEPTO, YOCTO);
- public static final Set<UnitPrefix> THOUSAND_PREFIXES = Set.of(KILO, MEGA,
- GIGA, TERA, PETA, EXA, ZETTA, YOTTA, MILLI, MICRO, NANO, PICO, FEMTO,
- ATTO, ZEPTO, YOCTO);
- public static final Set<UnitPrefix> MAGNIFYING_PREFIXES = Set.of(DEKA, HECTO,
- KILO, MEGA, GIGA, TERA, PETA, EXA, ZETTA, YOTTA, KIBI, MEBI, GIBI,
- TEBI, PEBI, EXBI);
- public static final Set<UnitPrefix> REDUCING_PREFIXES = Set.of(DECI, CENTI,
- MILLI, MICRO, NANO, PICO, FEMTO, ATTO, ZEPTO, YOCTO);
-
- // You may NOT get SI instances!
- private SI() {
- throw new AssertionError();
- }
-}
diff --git a/src/org/unitConverter/unit/USCustomary.java b/src/org/unitConverter/unit/USCustomary.java
deleted file mode 100644
index 1c4bcfe..0000000
--- a/src/org/unitConverter/unit/USCustomary.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-/**
- * A static utility class that contains units in the US Customary system.
- *
- * @author Adrien Hopkins
- * @since 2019-10-21
- */
-public final class USCustomary {
- /**
- * US Customary units that measure area
- *
- * @author Adrien Hopkins
- * @since 2019-11-08
- */
- public static final class Area {
- public static final LinearUnit SQUARE_SURVEY_FOOT = Length.SURVEY_FOOT.times(Length.SURVEY_FOOT);
- public static final LinearUnit SQUARE_CHAIN = Length.SURVEY_CHAIN.times(Length.SURVEY_CHAIN);
- public static final LinearUnit ACRE = Length.SURVEY_CHAIN.times(Length.SURVEY_FURLONG);
- public static final LinearUnit SECTION = Length.SURVEY_MILE.times(Length.SURVEY_MILE);
- public static final LinearUnit SURVEY_TOWNSHIP = SECTION.times(36);
- }
-
- /**
- * US Customary units that measure length
- *
- * @author Adrien Hopkins
- * @since 2019-10-28
- */
- public static final class Length {
- public static final LinearUnit FOOT = BritishImperial.Length.FOOT;
- public static final LinearUnit INCH = BritishImperial.Length.INCH;
- public static final LinearUnit HAND = INCH.times(4);
- public static final LinearUnit PICA = INCH.dividedBy(6);
- public static final LinearUnit POINT = PICA.dividedBy(12);
- public static final LinearUnit YARD = BritishImperial.Length.YARD;
- public static final LinearUnit MILE = BritishImperial.Length.MILE;
-
- public static final LinearUnit SURVEY_FOOT = SI.METRE.times(1200.0 / 3937.0);
- public static final LinearUnit SURVEY_LINK = SURVEY_FOOT.times(33.0 / 50.0);
- public static final LinearUnit SURVEY_ROD = SURVEY_FOOT.times(16.5);
- public static final LinearUnit SURVEY_CHAIN = SURVEY_ROD.times(4);
- public static final LinearUnit SURVEY_FURLONG = SURVEY_CHAIN.times(10);
- public static final LinearUnit SURVEY_MILE = SURVEY_FURLONG.times(8);
- public static final LinearUnit SURVEY_LEAGUE = SURVEY_MILE.times(3);
-
- public static final LinearUnit NAUTICAL_MILE = BritishImperial.Length.NAUTICAL_MILE;
- public static final LinearUnit FATHOM = YARD.times(2);
- public static final LinearUnit CABLE = FATHOM.times(120);
- }
-
- /**
- * mass units
- *
- * @author Adrien Hopkins
- * @since 2019-11-08
- */
- public static final class Mass {
- public static final LinearUnit GRAIN = BritishImperial.Mass.GRAIN;
- public static final LinearUnit DRAM = BritishImperial.Mass.DRACHM;
- public static final LinearUnit OUNCE = BritishImperial.Mass.OUNCE;
- public static final LinearUnit POUND = BritishImperial.Mass.POUND;
- public static final LinearUnit HUNDREDWEIGHT = POUND.times(100);
- public static final LinearUnit SHORT_TON = HUNDREDWEIGHT.times(20);
-
- // troy system for precious metals
- public static final LinearUnit PENNYWEIGHT = GRAIN.times(24);
- public static final LinearUnit TROY_OUNCE = PENNYWEIGHT.times(20);
- public static final LinearUnit TROY_POUND = TROY_OUNCE.times(12);
- }
-
- /**
- * Volume units
- *
- * @author Adrien Hopkins
- * @since 2019-11-08
- */
- public static final class Volume {
- public static final LinearUnit CUBIC_INCH = Length.INCH.toExponent(3);
- public static final LinearUnit CUBIC_FOOT = Length.FOOT.toExponent(3);
- public static final LinearUnit CUBIC_YARD = Length.YARD.toExponent(3);
- public static final LinearUnit ACRE_FOOT = Area.ACRE.times(Length.FOOT);
-
- public static final LinearUnit MINIM = SI.LITRE.withPrefix(SI.MICRO).times(61.611519921875);
- public static final LinearUnit FLUID_DRAM = MINIM.times(60);
- public static final LinearUnit TEASPOON = MINIM.times(80);
- public static final LinearUnit TABLESPOON = TEASPOON.times(3);
- public static final LinearUnit FLUID_OUNCE = TABLESPOON.times(2);
- public static final LinearUnit SHOT = TABLESPOON.times(3);
- public static final LinearUnit GILL = FLUID_OUNCE.times(4);
- public static final LinearUnit CUP = GILL.times(2);
- public static final LinearUnit PINT = CUP.times(2);
- public static final LinearUnit QUART = PINT.times(2);
- public static final LinearUnit GALLON = QUART.times(4);
- public static final LinearUnit BARREL = GALLON.times(31.5);
- public static final LinearUnit OIL_BARREL = GALLON.times(42);
- public static final LinearUnit HOGSHEAD = GALLON.times(63);
-
- public static final LinearUnit DRY_PINT = SI.LITRE.times(0.5506104713575);
- public static final LinearUnit DRY_QUART = DRY_PINT.times(2);
- public static final LinearUnit DRY_GALLON = DRY_QUART.times(4);
- public static final LinearUnit PECK = DRY_GALLON.times(2);
- public static final LinearUnit BUSHEL = PECK.times(4);
- public static final LinearUnit DRY_BARREL = CUBIC_INCH.times(7056);
- }
-
- public static final LinearUnit OUNCE_FORCE = BritishImperial.OUNCE_FORCE;
- public static final LinearUnit POUND_FORCE = BritishImperial.POUND_FORCE;
-
- public static final LinearUnit BRITISH_THERMAL_UNIT = BritishImperial.BRITISH_THERMAL_UNIT;
- public static final LinearUnit CALORIE = BritishImperial.CALORIE;
- public static final LinearUnit KILOCALORIE = BritishImperial.KILOCALORIE;
- public static final LinearUnit FOOT_POUND = POUND_FORCE.times(Length.FOOT);
-
- public static final LinearUnit HORSEPOWER = Length.FOOT.times(POUND_FORCE).dividedBy(SI.MINUTE).times(33000);
- public static final LinearUnit POUND_PER_SQUARE_INCH = POUND_FORCE.dividedBy(Length.INCH.toExponent(2));
-
- public static final Unit FAHRENHEIT = BritishImperial.FAHRENHEIT;
-}
diff --git a/src/org/unitConverter/unit/Unit.java b/src/org/unitConverter/unit/Unit.java
deleted file mode 100644
index 0a3298f..0000000
--- a/src/org/unitConverter/unit/Unit.java
+++ /dev/null
@@ -1,377 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.function.DoubleUnaryOperator;
-
-import org.unitConverter.math.DecimalComparison;
-import org.unitConverter.math.ObjectProduct;
-
-/**
- * A unit that is composed of base units.
- *
- * @author Adrien Hopkins
- * @since 2019-10-16
- */
-public abstract class Unit implements Nameable {
- /**
- * Returns a unit from its base and the functions it uses to convert to and
- * from its base.
- *
- * <p>
- * For example, to get a unit representing the degree Celsius, the following
- * code can be used:
- *
- * {@code Unit.fromConversionFunctions(SI.KELVIN, tempK -> tempK - 273.15, tempC -> tempC + 273.15);}
- * </p>
- *
- * @param base unit's base
- * @param converterFrom function that accepts a value expressed in the unit's
- * base and returns that value expressed in this unit.
- * @param converterTo function that accepts a value expressed in the unit
- * and returns that value expressed in the unit's base.
- * @return a unit that uses the provided functions to convert.
- * @since 2019-05-22
- * @throws NullPointerException if any argument is null
- */
- public static final Unit fromConversionFunctions(
- final ObjectProduct<BaseUnit> base,
- final DoubleUnaryOperator converterFrom,
- final DoubleUnaryOperator converterTo) {
- return new FunctionalUnit(base, converterFrom, converterTo);
- }
-
- /**
- * Returns a unit from its base and the functions it uses to convert to and
- * from its base.
- *
- * <p>
- * For example, to get a unit representing the degree Celsius, the following
- * code can be used:
- *
- * {@code Unit.fromConversionFunctions(SI.KELVIN, tempK -> tempK - 273.15, tempC -> tempC + 273.15);}
- * </p>
- *
- * @param base unit's base
- * @param converterFrom function that accepts a value expressed in the unit's
- * base and returns that value expressed in this unit.
- * @param converterTo function that accepts a value expressed in the unit
- * and returns that value expressed in the unit's base.
- * @param ns names and symbol of unit
- * @return a unit that uses the provided functions to convert.
- * @since 2019-05-22
- * @throws NullPointerException if any argument is null
- */
- public static final Unit fromConversionFunctions(
- final ObjectProduct<BaseUnit> base,
- final DoubleUnaryOperator converterFrom,
- final DoubleUnaryOperator converterTo, final NameSymbol ns) {
- return new FunctionalUnit(base, converterFrom, converterTo, ns);
- }
-
- /**
- * The combination of units that this unit is based on.
- *
- * @since 2019-10-16
- */
- private final ObjectProduct<BaseUnit> unitBase;
-
- /**
- * This unit's name(s) and symbol
- *
- * @since 2020-09-07
- */
- private final NameSymbol nameSymbol;
-
- /**
- * Cache storing the result of getDimension()
- *
- * @since 2019-10-16
- */
- private transient ObjectProduct<BaseDimension> dimension = null;
-
- /**
- * Creates the {@code Unit}.
- *
- * @param unitBase base of unit
- * @param ns names and symbol of unit
- * @since 2019-10-16
- * @throws NullPointerException if unitBase or ns is null
- */
- Unit(ObjectProduct<BaseUnit> unitBase, NameSymbol ns) {
- this.unitBase = Objects.requireNonNull(unitBase,
- "unitBase may not be null");
- this.nameSymbol = Objects.requireNonNull(ns, "ns may not be null");
- }
-
- /**
- * A constructor that constructs {@code BaseUnit} instances.
- *
- * @since 2019-10-16
- */
- Unit(final String primaryName, final String symbol,
- final Set<String> otherNames) {
- if (this instanceof BaseUnit) {
- this.unitBase = ObjectProduct.oneOf((BaseUnit) this);
- } else
- throw new AssertionError();
- this.nameSymbol = NameSymbol.of(primaryName, symbol,
- new HashSet<>(otherNames));
- }
-
- /**
- * @return this unit as a {@link Unitlike}
- * @since 2020-09-07
- */
- public final Unitlike<Double> asUnitlike() {
- return Unitlike.fromConversionFunctions(this.getBase(),
- this::convertFromBase, this::convertToBase, this.getNameSymbol());
- }
-
- /**
- * Checks if a value expressed in this unit can be converted to a value
- * expressed in {@code other}
- *
- * @param other unit or unitlike form to test with
- * @return true if they are compatible
- * @since 2019-01-13
- * @since v0.1.0
- * @throws NullPointerException if other is null
- */
- public final boolean canConvertTo(final Unit other) {
- Objects.requireNonNull(other, "other must not be null.");
- return Objects.equals(this.getBase(), other.getBase());
- }
-
- /**
- * Checks if a value expressed in this unit can be converted to a value
- * expressed in {@code other}
- *
- * @param other unit or unitlike form to test with
- * @return true if they are compatible
- * @since 2019-01-13
- * @since v0.1.0
- * @throws NullPointerException if other is null
- */
- public final <W> boolean canConvertTo(final Unitlike<W> other) {
- Objects.requireNonNull(other, "other must not be null.");
- return Objects.equals(this.getBase(), other.getBase());
- }
-
- /**
- * Converts from a value expressed in this unit's base unit to a value
- * expressed in this unit.
- * <p>
- * This must be the inverse of {@code convertToBase}, so
- * {@code convertFromBase(convertToBase(value))} must be equal to
- * {@code value} for any value, ignoring precision loss by roundoff error.
- * </p>
- * <p>
- * If this unit <i>is</i> a base unit, this method should return
- * {@code value}.
- * </p>
- *
- * @implSpec This method is used by {@link #convertTo}, and its behaviour
- * affects the behaviour of {@code convertTo}.
- *
- * @param value value expressed in <b>base</b> unit
- * @return value expressed in <b>this</b> unit
- * @since 2018-12-22
- * @since v0.1.0
- */
- protected abstract double convertFromBase(double value);
-
- /**
- * Converts a value expressed in this unit to a value expressed in
- * {@code other}.
- *
- * @implSpec If unit conversion is possible, this implementation returns
- * {@code other.convertFromBase(this.convertToBase(value))}.
- * Therefore, overriding either of those methods will change the
- * output of this method.
- *
- * @param other unit to convert to
- * @param value value to convert
- * @return converted value
- * @since 2019-05-22
- * @throws IllegalArgumentException if {@code other} is incompatible for
- * conversion with this unit (as tested by
- * {@link Unit#canConvertTo}).
- * @throws NullPointerException if other is null
- */
- public final double convertTo(final Unit other, final double value) {
- Objects.requireNonNull(other, "other must not be null.");
- if (this.canConvertTo(other))
- return other.convertFromBase(this.convertToBase(value));
- else
- throw new IllegalArgumentException(
- String.format("Cannot convert from %s to %s.", this, other));
- }
-
- /**
- * Converts a value expressed in this unit to a value expressed in
- * {@code other}.
- *
- * @implSpec If conversion is possible, this implementation returns
- * {@code other.convertFromBase(this.convertToBase(value))}.
- * Therefore, overriding either of those methods will change the
- * output of this method.
- *
- * @param other unitlike form to convert to
- * @param value value to convert
- * @param <W> type of value to convert to
- * @return converted value
- * @since 2020-09-07
- * @throws IllegalArgumentException if {@code other} is incompatible for
- * conversion with this unit (as tested by
- * {@link Unit#canConvertTo}).
- * @throws NullPointerException if other is null
- */
- public final <W> W convertTo(final Unitlike<W> other, final double value) {
- Objects.requireNonNull(other, "other must not be null.");
- if (this.canConvertTo(other))
- return other.convertFromBase(this.convertToBase(value));
- else
- throw new IllegalArgumentException(
- String.format("Cannot convert from %s to %s.", this, other));
- }
-
- /**
- * Converts from a value expressed in this unit to a value expressed in this
- * unit's base unit.
- * <p>
- * This must be the inverse of {@code convertFromBase}, so
- * {@code convertToBase(convertFromBase(value))} must be equal to
- * {@code value} for any value, ignoring precision loss by roundoff error.
- * </p>
- * <p>
- * If this unit <i>is</i> a base unit, this method should return
- * {@code value}.
- * </p>
- *
- * @implSpec This method is used by {@link #convertTo}, and its behaviour
- * affects the behaviour of {@code convertTo}.
- *
- * @param value value expressed in <b>this</b> unit
- * @return value expressed in <b>base</b> unit
- * @since 2018-12-22
- * @since v0.1.0
- */
- protected abstract double convertToBase(double value);
-
- /**
- * @return combination of units that this unit is based on
- * @since 2018-12-22
- * @since v0.1.0
- */
- public final ObjectProduct<BaseUnit> getBase() {
- return this.unitBase;
- }
-
- /**
- * @return dimension measured by this unit
- * @since 2018-12-22
- * @since v0.1.0
- */
- public final ObjectProduct<BaseDimension> getDimension() {
- if (this.dimension == null) {
- final Map<BaseUnit, Integer> mapping = this.unitBase.exponentMap();
- final Map<BaseDimension, Integer> dimensionMap = new HashMap<>();
-
- for (final BaseUnit key : mapping.keySet()) {
- dimensionMap.put(key.getBaseDimension(), mapping.get(key));
- }
-
- this.dimension = ObjectProduct.fromExponentMapping(dimensionMap);
- }
- return this.dimension;
- }
-
- /**
- * @return the nameSymbol
- * @since 2020-09-07
- */
- @Override
- public final NameSymbol getNameSymbol() {
- return this.nameSymbol;
- }
-
- /**
- * Returns true iff this unit is metric.
- * <p>
- * "Metric" is defined by three conditions:
- * <ul>
- * <li>Must be an instance of {@link LinearUnit}.</li>
- * <li>Must be based on the SI base units (as determined by getBase())</li>
- * <li>The conversion factor must be a power of 10.</li>
- * </ul>
- * <p>
- * Note that this definition excludes some units that many would consider
- * "metric", such as the degree Celsius (fails the first condition),
- * calories, minutes and hours (fail the third condition).
- * <p>
- * All SI units (as designated by the BIPM) except the degree Celsius are
- * considered "metric" by this definition.
- *
- * @since 2020-08-27
- */
- public final boolean isMetric() {
- // first condition - check that it is a linear unit
- if (!(this instanceof LinearUnit))
- return false;
- final LinearUnit linear = (LinearUnit) this;
-
- // second condition - check that
- for (final BaseUnit b : linear.getBase().getBaseSet()) {
- if (!SI.BaseUnits.BASE_UNITS.contains(b))
- return false;
- }
-
- // third condition - check that conversion factor is a power of 10
- return DecimalComparison
- .equals(Math.log10(linear.getConversionFactor()) % 1.0, 0);
- }
-
- @Override
- public String toString() {
- return this.getPrimaryName().orElse("Unnamed unit")
- + (this.getSymbol().isPresent()
- ? String.format(" (%s)", this.getSymbol().get())
- : "")
- + ", derived from "
- + this.getBase().toString(u -> u.getSymbol().get())
- + (this.getOtherNames().isEmpty() ? ""
- : ", also called " + String.join(", ", this.getOtherNames()));
- }
-
- /**
- * @param ns name(s) and symbol to use
- * @return a copy of this unit with provided name(s) and symbol
- * @since 2019-10-21
- * @throws NullPointerException if ns is null
- */
- public Unit withName(final NameSymbol ns) {
- return fromConversionFunctions(this.getBase(), this::convertFromBase,
- this::convertToBase,
- Objects.requireNonNull(ns, "ns must not be null."));
- }
-}
diff --git a/src/org/unitConverter/unit/UnitDatabase.java b/src/org/unitConverter/unit/UnitDatabase.java
deleted file mode 100644
index 000acf5..0000000
--- a/src/org/unitConverter/unit/UnitDatabase.java
+++ /dev/null
@@ -1,1991 +0,0 @@
-/**
- * Copyright (C) 2018 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.math.BigDecimal;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.AbstractSet;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.NoSuchElementException;
-import java.util.Objects;
-import java.util.Set;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.unitConverter.math.ConditionalExistenceCollections;
-import org.unitConverter.math.DecimalComparison;
-import org.unitConverter.math.ExpressionParser;
-import org.unitConverter.math.ObjectProduct;
-import org.unitConverter.math.UncertainDouble;
-
-/**
- * A database of units, prefixes and dimensions, and their names.
- *
- * @author Adrien Hopkins
- * @since 2019-01-07
- * @since v0.1.0
- */
-public final class UnitDatabase {
- /**
- * A map for units that allows the use of prefixes.
- * <p>
- * As this map implementation is intended to be used as a sort of "augmented
- * view" of a unit and prefix map, it is unmodifiable but instead reflects
- * the changes to the maps passed into it. Do not edit this map, instead edit
- * the maps that were passed in during construction.
- * </p>
- * <p>
- * The rules for applying prefixes onto units are the following:
- * <ul>
- * <li>Prefixes can only be applied to linear units.</li>
- * <li>Before attempting to search for prefixes in a unit name, this map will
- * first search for a unit name. So, if there are two units, "B" and "AB",
- * and a prefix "A", this map will favour the unit "AB" over the unit "B"
- * with the prefix "A", even though they have the same string.</li>
- * <li>Longer prefixes are preferred to shorter prefixes. So, if you have
- * units "BC" and "C", and prefixes "AB" and "A", inputting "ABC" will return
- * the unit "C" with the prefix "AB", not "BC" with the prefix "A".</li>
- * </ul>
- * </p>
- * <p>
- * This map is infinite in size if there is at least one unit and at least
- * one prefix. If it is infinite, some operations that only work with finite
- * collections, like converting name/entry sets to arrays, will throw an
- * {@code IllegalStateException}.
- * </p>
- * <p>
- * Because of ambiguities between prefixes (i.e. kilokilo = mega),
- * {@link #containsValue} and {@link #values()} currently ignore prefixes.
- * </p>
- *
- * @author Adrien Hopkins
- * @since 2019-04-13
- * @since v0.2.0
- */
- private static final class PrefixedUnitMap implements Map<String, Unit> {
- /**
- * The class used for entry sets.
- *
- * <p>
- * If the map that created this set is infinite in size (has at least one
- * unit and at least one prefix), this set is infinite as well. If this
- * set is infinite in size, {@link #toArray} will fail with a
- * {@code IllegalStateException} instead of creating an infinite-sized
- * array.
- * </p>
- *
- * @author Adrien Hopkins
- * @since 2019-04-13
- * @since v0.2.0
- */
- private static final class PrefixedUnitEntrySet
- extends AbstractSet<Map.Entry<String, Unit>> {
- /**
- * The entry for this set.
- *
- * @author Adrien Hopkins
- * @since 2019-04-14
- * @since v0.2.0
- */
- private static final class PrefixedUnitEntry
- implements Entry<String, Unit> {
- private final String key;
- private final Unit value;
-
- /**
- * Creates the {@code PrefixedUnitEntry}.
- *
- * @param key key
- * @param value value
- * @since 2019-04-14
- * @since v0.2.0
- */
- public PrefixedUnitEntry(final String key, final Unit value) {
- this.key = key;
- this.value = value;
- }
-
- /**
- * @since 2019-05-03
- */
- @Override
- public boolean equals(final Object o) {
- if (!(o instanceof Map.Entry))
- return false;
- final Map.Entry<?, ?> other = (Map.Entry<?, ?>) o;
- return Objects.equals(this.getKey(), other.getKey())
- && Objects.equals(this.getValue(), other.getValue());
- }
-
- @Override
- public String getKey() {
- return this.key;
- }
-
- @Override
- public Unit getValue() {
- return this.value;
- }
-
- /**
- * @since 2019-05-03
- */
- @Override
- public int hashCode() {
- return (this.getKey() == null ? 0 : this.getKey().hashCode())
- ^ (this.getValue() == null ? 0
- : this.getValue().hashCode());
- }
-
- @Override
- public Unit setValue(final Unit value) {
- throw new UnsupportedOperationException(
- "Cannot set value in an immutable entry");
- }
-
- /**
- * Returns a string representation of the entry. The format of the
- * string is the string representation of the key, then the equals
- * ({@code =}) character, then the string representation of the
- * value.
- *
- * @since 2019-05-03
- */
- @Override
- public String toString() {
- return this.getKey() + "=" + this.getValue();
- }
- }
-
- /**
- * An iterator that iterates over the units of a
- * {@code PrefixedUnitNameSet}.
- *
- * @author Adrien Hopkins
- * @since 2019-04-14
- * @since v0.2.0
- */
- private static final class PrefixedUnitEntryIterator
- implements Iterator<Entry<String, Unit>> {
- // position in the unit list
- private int unitNamePosition = 0;
- // the indices of the prefixes attached to the current unit
- private final List<Integer> prefixCoordinates = new ArrayList<>();
-
- // values from the unit entry set
- private final Map<String, Unit> map;
- private transient final List<String> unitNames;
- private transient final List<String> prefixNames;
-
- /**
- * Creates the
- * {@code UnitsDatabase.PrefixedUnitMap.PrefixedUnitNameSet.PrefixedUnitNameIterator}.
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- public PrefixedUnitEntryIterator(final PrefixedUnitMap map) {
- this.map = map;
- this.unitNames = new ArrayList<>(map.units.keySet());
- this.prefixNames = new ArrayList<>(map.prefixes.keySet());
- }
-
- /**
- * @return current unit name
- * @since 2019-04-14
- * @since v0.2.0
- */
- private String getCurrentUnitName() {
- final StringBuilder unitName = new StringBuilder();
- for (final int i : this.prefixCoordinates) {
- unitName.append(this.prefixNames.get(i));
- }
- unitName.append(this.unitNames.get(this.unitNamePosition));
-
- return unitName.toString();
- }
-
- @Override
- public boolean hasNext() {
- if (this.unitNames.isEmpty())
- return false;
- else {
- if (this.prefixNames.isEmpty())
- return this.unitNamePosition >= this.unitNames.size() - 1;
- else
- return true;
- }
- }
-
- /**
- * Changes this iterator's position to the next available one.
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- private void incrementPosition() {
- this.unitNamePosition++;
-
- if (this.unitNamePosition >= this.unitNames.size()) {
- // we have used all of our units, go to a different prefix
- this.unitNamePosition = 0;
-
- // if the prefix coordinates are empty, then set it to [0]
- if (this.prefixCoordinates.isEmpty()) {
- this.prefixCoordinates.add(0, 0);
- } else {
- // get the prefix coordinate to increment, then increment
- int i = this.prefixCoordinates.size() - 1;
- this.prefixCoordinates.set(i,
- this.prefixCoordinates.get(i) + 1);
-
- // fix any carrying errors
- while (i >= 0 && this.prefixCoordinates
- .get(i) >= this.prefixNames.size()) {
- // carry over
- this.prefixCoordinates.set(i--, 0); // null and
- // decrement at the
- // same time
-
- if (i < 0) { // we need to add a new coordinate
- this.prefixCoordinates.add(0, 0);
- } else { // increment an existing one
- this.prefixCoordinates.set(i,
- this.prefixCoordinates.get(i) + 1);
- }
- }
- }
- }
- }
-
- @Override
- public Entry<String, Unit> next() {
- // get next element
- final Entry<String, Unit> nextEntry = this.peek();
-
- // iterate to next position
- this.incrementPosition();
-
- return nextEntry;
- }
-
- /**
- * @return the next element in the iterator, without iterating over
- * it
- * @since 2019-05-03
- */
- private Entry<String, Unit> peek() {
- if (!this.hasNext())
- throw new NoSuchElementException("No units left!");
-
- // if I have prefixes, ensure I'm not using a nonlinear unit
- // since all of the unprefixed stuff is done, just remove
- // nonlinear units
- if (!this.prefixCoordinates.isEmpty()) {
- while (this.unitNamePosition < this.unitNames.size()
- && !(this.map.get(this.unitNames.get(
- this.unitNamePosition)) instanceof LinearUnit)) {
- this.unitNames.remove(this.unitNamePosition);
- }
- }
-
- final String nextName = this.getCurrentUnitName();
-
- return new PrefixedUnitEntry(nextName, this.map.get(nextName));
- }
-
- /**
- * Returns a string representation of the object. The exact details
- * of the representation are unspecified and subject to change.
- *
- * @since 2019-05-03
- */
- @Override
- public String toString() {
- return String.format(
- "Iterator iterating over name-unit entries; next value is \"%s\"",
- this.peek());
- }
- }
-
- // the map that created this set
- private final PrefixedUnitMap map;
-
- /**
- * Creates the {@code PrefixedUnitNameSet}.
- *
- * @param map map that created this set
- * @since 2019-04-13
- * @since v0.2.0
- */
- public PrefixedUnitEntrySet(final PrefixedUnitMap map) {
- this.map = map;
- }
-
- @Override
- public boolean add(final Map.Entry<String, Unit> e) {
- throw new UnsupportedOperationException(
- "Cannot add to an immutable set");
- }
-
- @Override
- public boolean addAll(
- final Collection<? extends Map.Entry<String, Unit>> c) {
- throw new UnsupportedOperationException(
- "Cannot add to an immutable set");
- }
-
- @Override
- public void clear() {
- throw new UnsupportedOperationException(
- "Cannot clear an immutable set");
- }
-
- @Override
- public boolean contains(final Object o) {
- // get the entry
- final Entry<String, Unit> entry;
-
- try {
- // This is OK because I'm in a try-catch block, catching the
- // exact exception that would be thrown.
- @SuppressWarnings("unchecked")
- final Entry<String, Unit> tempEntry = (Entry<String, Unit>) o;
- entry = tempEntry;
- } catch (final ClassCastException e) {
- throw new IllegalArgumentException(
- "Attempted to test for an entry using a non-entry.");
- }
-
- return this.map.containsKey(entry.getKey())
- && this.map.get(entry.getKey()).equals(entry.getValue());
- }
-
- @Override
- public boolean containsAll(final Collection<?> c) {
- for (final Object o : c)
- if (!this.contains(o))
- return false;
- return true;
- }
-
- @Override
- public boolean isEmpty() {
- return this.map.isEmpty();
- }
-
- @Override
- public Iterator<Entry<String, Unit>> iterator() {
- return new PrefixedUnitEntryIterator(this.map);
- }
-
- @Override
- public boolean remove(final Object o) {
- throw new UnsupportedOperationException(
- "Cannot remove from an immutable set");
- }
-
- @Override
- public boolean removeAll(final Collection<?> c) {
- throw new UnsupportedOperationException(
- "Cannot remove from an immutable set");
- }
-
- @Override
- public boolean removeIf(
- final Predicate<? super Entry<String, Unit>> filter) {
- throw new UnsupportedOperationException(
- "Cannot remove from an immutable set");
- }
-
- @Override
- public boolean retainAll(final Collection<?> c) {
- throw new UnsupportedOperationException(
- "Cannot remove from an immutable set");
- }
-
- @Override
- public int size() {
- if (this.map.units.isEmpty())
- return 0;
- else {
- if (this.map.prefixes.isEmpty())
- return this.map.units.size();
- else
- // infinite set
- return Integer.MAX_VALUE;
- }
- }
-
- /**
- * @throws IllegalStateException if the set is infinite in size
- */
- @Override
- public Object[] toArray() {
- if (this.map.units.isEmpty() || this.map.prefixes.isEmpty())
- return super.toArray();
- else
- // infinite set
- throw new IllegalStateException(
- "Cannot make an infinite set into an array.");
- }
-
- /**
- * @throws IllegalStateException if the set is infinite in size
- */
- @Override
- public <T> T[] toArray(final T[] a) {
- if (this.map.units.isEmpty() || this.map.prefixes.isEmpty())
- return super.toArray(a);
- else
- // infinite set
- throw new IllegalStateException(
- "Cannot make an infinite set into an array.");
- }
-
- @Override
- public String toString() {
- if (this.map.units.isEmpty() || this.map.prefixes.isEmpty())
- return super.toString();
- else
- return String.format(
- "Infinite set of name-unit entries created from units %s and prefixes %s",
- this.map.units, this.map.prefixes);
- }
- }
-
- /**
- * The class used for unit name sets.
- *
- * <p>
- * If the map that created this set is infinite in size (has at least one
- * unit and at least one prefix), this set is infinite as well. If this
- * set is infinite in size, {@link #toArray} will fail with a
- * {@code IllegalStateException} instead of creating an infinite-sized
- * array.
- * </p>
- *
- * @author Adrien Hopkins
- * @since 2019-04-13
- * @since v0.2.0
- */
- private static final class PrefixedUnitNameSet
- extends AbstractSet<String> {
- /**
- * An iterator that iterates over the units of a
- * {@code PrefixedUnitNameSet}.
- *
- * @author Adrien Hopkins
- * @since 2019-04-14
- * @since v0.2.0
- */
- private static final class PrefixedUnitNameIterator
- implements Iterator<String> {
- // position in the unit list
- private int unitNamePosition = 0;
- // the indices of the prefixes attached to the current unit
- private final List<Integer> prefixCoordinates = new ArrayList<>();
-
- // values from the unit name set
- private final Map<String, Unit> map;
- private transient final List<String> unitNames;
- private transient final List<String> prefixNames;
-
- /**
- * Creates the
- * {@code UnitsDatabase.PrefixedUnitMap.PrefixedUnitNameSet.PrefixedUnitNameIterator}.
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- public PrefixedUnitNameIterator(final PrefixedUnitMap map) {
- this.map = map;
- this.unitNames = new ArrayList<>(map.units.keySet());
- this.prefixNames = new ArrayList<>(map.prefixes.keySet());
- }
-
- /**
- * @return current unit name
- * @since 2019-04-14
- * @since v0.2.0
- */
- private String getCurrentUnitName() {
- final StringBuilder unitName = new StringBuilder();
- for (final int i : this.prefixCoordinates) {
- unitName.append(this.prefixNames.get(i));
- }
- unitName.append(this.unitNames.get(this.unitNamePosition));
-
- return unitName.toString();
- }
-
- @Override
- public boolean hasNext() {
- if (this.unitNames.isEmpty())
- return false;
- else {
- if (this.prefixNames.isEmpty())
- return this.unitNamePosition >= this.unitNames.size() - 1;
- else
- return true;
- }
- }
-
- /**
- * Changes this iterator's position to the next available one.
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- private void incrementPosition() {
- this.unitNamePosition++;
-
- if (this.unitNamePosition >= this.unitNames.size()) {
- // we have used all of our units, go to a different prefix
- this.unitNamePosition = 0;
-
- // if the prefix coordinates are empty, then set it to [0]
- if (this.prefixCoordinates.isEmpty()) {
- this.prefixCoordinates.add(0, 0);
- } else {
- // get the prefix coordinate to increment, then increment
- int i = this.prefixCoordinates.size() - 1;
- this.prefixCoordinates.set(i,
- this.prefixCoordinates.get(i) + 1);
-
- // fix any carrying errors
- while (i >= 0 && this.prefixCoordinates
- .get(i) >= this.prefixNames.size()) {
- // carry over
- this.prefixCoordinates.set(i--, 0); // null and
- // decrement at the
- // same time
-
- if (i < 0) { // we need to add a new coordinate
- this.prefixCoordinates.add(0, 0);
- } else { // increment an existing one
- this.prefixCoordinates.set(i,
- this.prefixCoordinates.get(i) + 1);
- }
- }
- }
- }
- }
-
- @Override
- public String next() {
- final String nextName = this.peek();
-
- this.incrementPosition();
-
- return nextName;
- }
-
- /**
- * @return the next element in the iterator, without iterating over
- * it
- * @since 2019-05-03
- */
- private String peek() {
- if (!this.hasNext())
- throw new NoSuchElementException("No units left!");
- // if I have prefixes, ensure I'm not using a nonlinear unit
- // since all of the unprefixed stuff is done, just remove
- // nonlinear units
- if (!this.prefixCoordinates.isEmpty()) {
- while (this.unitNamePosition < this.unitNames.size()
- && !(this.map.get(this.unitNames.get(
- this.unitNamePosition)) instanceof LinearUnit)) {
- this.unitNames.remove(this.unitNamePosition);
- }
- }
-
- return this.getCurrentUnitName();
- }
-
- /**
- * Returns a string representation of the object. The exact details
- * of the representation are unspecified and subject to change.
- *
- * @since 2019-05-03
- */
- @Override
- public String toString() {
- return String.format(
- "Iterator iterating over unit names; next value is \"%s\"",
- this.peek());
- }
- }
-
- // the map that created this set
- private final PrefixedUnitMap map;
-
- /**
- * Creates the {@code PrefixedUnitNameSet}.
- *
- * @param map map that created this set
- * @since 2019-04-13
- * @since v0.2.0
- */
- public PrefixedUnitNameSet(final PrefixedUnitMap map) {
- this.map = map;
- }
-
- @Override
- public boolean add(final String e) {
- throw new UnsupportedOperationException(
- "Cannot add to an immutable set");
- }
-
- @Override
- public boolean addAll(final Collection<? extends String> c) {
- throw new UnsupportedOperationException(
- "Cannot add to an immutable set");
- }
-
- @Override
- public void clear() {
- throw new UnsupportedOperationException(
- "Cannot clear an immutable set");
- }
-
- @Override
- public boolean contains(final Object o) {
- return this.map.containsKey(o);
- }
-
- @Override
- public boolean containsAll(final Collection<?> c) {
- for (final Object o : c)
- if (!this.contains(o))
- return false;
- return true;
- }
-
- @Override
- public boolean isEmpty() {
- return this.map.isEmpty();
- }
-
- @Override
- public Iterator<String> iterator() {
- return new PrefixedUnitNameIterator(this.map);
- }
-
- @Override
- public boolean remove(final Object o) {
- throw new UnsupportedOperationException(
- "Cannot remove from an immutable set");
- }
-
- @Override
- public boolean removeAll(final Collection<?> c) {
- throw new UnsupportedOperationException(
- "Cannot remove from an immutable set");
- }
-
- @Override
- public boolean removeIf(final Predicate<? super String> filter) {
- throw new UnsupportedOperationException(
- "Cannot remove from an immutable set");
- }
-
- @Override
- public boolean retainAll(final Collection<?> c) {
- throw new UnsupportedOperationException(
- "Cannot remove from an immutable set");
- }
-
- @Override
- public int size() {
- if (this.map.units.isEmpty())
- return 0;
- else {
- if (this.map.prefixes.isEmpty())
- return this.map.units.size();
- else
- // infinite set
- return Integer.MAX_VALUE;
- }
- }
-
- /**
- * @throws IllegalStateException if the set is infinite in size
- */
- @Override
- public Object[] toArray() {
- if (this.map.units.isEmpty() || this.map.prefixes.isEmpty())
- return super.toArray();
- else
- // infinite set
- throw new IllegalStateException(
- "Cannot make an infinite set into an array.");
-
- }
-
- /**
- * @throws IllegalStateException if the set is infinite in size
- */
- @Override
- public <T> T[] toArray(final T[] a) {
- if (this.map.units.isEmpty() || this.map.prefixes.isEmpty())
- return super.toArray(a);
- else
- // infinite set
- throw new IllegalStateException(
- "Cannot make an infinite set into an array.");
- }
-
- @Override
- public String toString() {
- if (this.map.units.isEmpty() || this.map.prefixes.isEmpty())
- return super.toString();
- else
- return String.format(
- "Infinite set of name-unit entries created from units %s and prefixes %s",
- this.map.units, this.map.prefixes);
- }
- }
-
- /**
- * The units stored in this collection, without prefixes.
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- private final Map<String, Unit> units;
-
- /**
- * The available prefixes for use.
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- private final Map<String, UnitPrefix> prefixes;
-
- // caches
- private transient Collection<Unit> values = null;
- private transient Set<String> keySet = null;
- private transient Set<Entry<String, Unit>> entrySet = null;
-
- /**
- * Creates the {@code PrefixedUnitMap}.
- *
- * @param units map mapping unit names to units
- * @param prefixes map mapping prefix names to prefixes
- * @since 2019-04-13
- * @since v0.2.0
- */
- public PrefixedUnitMap(final Map<String, Unit> units,
- final Map<String, UnitPrefix> prefixes) {
- // I am making unmodifiable maps to ensure I don't accidentally make
- // changes.
- this.units = Collections.unmodifiableMap(units);
- this.prefixes = Collections.unmodifiableMap(prefixes);
- }
-
- @Override
- public void clear() {
- throw new UnsupportedOperationException(
- "Cannot clear an immutable map");
- }
-
- @Override
- public Unit compute(final String key,
- final BiFunction<? super String, ? super Unit, ? extends Unit> remappingFunction) {
- throw new UnsupportedOperationException(
- "Cannot edit an immutable map");
- }
-
- @Override
- public Unit computeIfAbsent(final String key,
- final Function<? super String, ? extends Unit> mappingFunction) {
- throw new UnsupportedOperationException(
- "Cannot edit an immutable map");
- }
-
- @Override
- public Unit computeIfPresent(final String key,
- final BiFunction<? super String, ? super Unit, ? extends Unit> remappingFunction) {
- throw new UnsupportedOperationException(
- "Cannot edit an immutable map");
- }
-
- @Override
- public boolean containsKey(final Object key) {
- // First, test if there is a unit with the key
- if (this.units.containsKey(key))
- return true;
-
- // Next, try to cast it to String
- if (!(key instanceof String))
- throw new IllegalArgumentException(
- "Attempted to test for a unit using a non-string name.");
- final String unitName = (String) key;
-
- // Then, look for the longest prefix that is attached to a valid unit
- String longestPrefix = null;
- int longestLength = 0;
-
- for (final String prefixName : this.prefixes.keySet()) {
- // a prefix name is valid if:
- // - it is prefixed (i.e. the unit name starts with it)
- // - it is longer than the existing largest prefix (since I am
- // looking for the longest valid prefix)
- // - the part after the prefix is a valid unit name
- // - the unit described that name is a linear unit (since only
- // linear units can have prefixes)
- if (unitName.startsWith(prefixName)
- && prefixName.length() > longestLength) {
- final String rest = unitName.substring(prefixName.length());
- if (this.containsKey(rest)
- && this.get(rest) instanceof LinearUnit) {
- longestPrefix = prefixName;
- longestLength = prefixName.length();
- }
- }
- }
-
- return longestPrefix != null;
- }
-
- /**
- * {@inheritDoc}
- *
- * <p>
- * Because of ambiguities between prefixes (i.e. kilokilo = mega), this
- * method only tests for prefixless units.
- * </p>
- */
- @Override
- public boolean containsValue(final Object value) {
- return this.units.containsValue(value);
- }
-
- @Override
- public Set<Entry<String, Unit>> entrySet() {
- if (this.entrySet == null) {
- this.entrySet = new PrefixedUnitEntrySet(this);
- }
- return this.entrySet;
- }
-
- @Override
- public Unit get(final Object key) {
- // First, test if there is a unit with the key
- if (this.units.containsKey(key))
- return this.units.get(key);
-
- // Next, try to cast it to String
- if (!(key instanceof String))
- throw new IllegalArgumentException(
- "Attempted to obtain a unit using a non-string name.");
- final String unitName = (String) key;
-
- // Then, look for the longest prefix that is attached to a valid unit
- String longestPrefix = null;
- int longestLength = 0;
-
- for (final String prefixName : this.prefixes.keySet()) {
- // a prefix name is valid if:
- // - it is prefixed (i.e. the unit name starts with it)
- // - it is longer than the existing largest prefix (since I am
- // looking for the longest valid prefix)
- // - the part after the prefix is a valid unit name
- // - the unit described that name is a linear unit (since only
- // linear units can have prefixes)
- if (unitName.startsWith(prefixName)
- && prefixName.length() > longestLength) {
- final String rest = unitName.substring(prefixName.length());
- if (this.containsKey(rest)
- && this.get(rest) instanceof LinearUnit) {
- longestPrefix = prefixName;
- longestLength = prefixName.length();
- }
- }
- }
-
- // if none found, returns null
- if (longestPrefix == null)
- return null;
- else {
- // get necessary data
- final String rest = unitName.substring(longestLength);
- // this cast will not fail because I verified that it would work
- // before selecting this prefix
- final LinearUnit unit = (LinearUnit) this.get(rest);
- final UnitPrefix prefix = this.prefixes.get(longestPrefix);
-
- return unit.withPrefix(prefix);
- }
- }
-
- @Override
- public boolean isEmpty() {
- return this.units.isEmpty();
- }
-
- @Override
- public Set<String> keySet() {
- if (this.keySet == null) {
- this.keySet = new PrefixedUnitNameSet(this);
- }
- return this.keySet;
- }
-
- @Override
- public Unit merge(final String key, final Unit value,
- final BiFunction<? super Unit, ? super Unit, ? extends Unit> remappingFunction) {
- throw new UnsupportedOperationException(
- "Cannot merge into an immutable map");
- }
-
- @Override
- public Unit put(final String key, final Unit value) {
- throw new UnsupportedOperationException(
- "Cannot add entries to an immutable map");
- }
-
- @Override
- public void putAll(final Map<? extends String, ? extends Unit> m) {
- throw new UnsupportedOperationException(
- "Cannot add entries to an immutable map");
- }
-
- @Override
- public Unit putIfAbsent(final String key, final Unit value) {
- throw new UnsupportedOperationException(
- "Cannot add entries to an immutable map");
- }
-
- @Override
- public Unit remove(final Object key) {
- throw new UnsupportedOperationException(
- "Cannot remove entries from an immutable map");
- }
-
- @Override
- public boolean remove(final Object key, final Object value) {
- throw new UnsupportedOperationException(
- "Cannot remove entries from an immutable map");
- }
-
- @Override
- public Unit replace(final String key, final Unit value) {
- throw new UnsupportedOperationException(
- "Cannot replace entries in an immutable map");
- }
-
- @Override
- public boolean replace(final String key, final Unit oldValue,
- final Unit newValue) {
- throw new UnsupportedOperationException(
- "Cannot replace entries in an immutable map");
- }
-
- @Override
- public void replaceAll(
- final BiFunction<? super String, ? super Unit, ? extends Unit> function) {
- throw new UnsupportedOperationException(
- "Cannot replace entries in an immutable map");
- }
-
- @Override
- public int size() {
- if (this.units.isEmpty())
- return 0;
- else {
- if (this.prefixes.isEmpty())
- return this.units.size();
- else
- // infinite set
- return Integer.MAX_VALUE;
- }
- }
-
- @Override
- public String toString() {
- if (this.units.isEmpty() || this.prefixes.isEmpty())
- return super.toString();
- else
- return String.format(
- "Infinite map of name-unit entries created from units %s and prefixes %s",
- this.units, this.prefixes);
- }
-
- /**
- * {@inheritDoc}
- *
- * <p>
- * Because of ambiguities between prefixes (i.e. kilokilo = mega), this
- * method ignores prefixes.
- * </p>
- */
- @Override
- public Collection<Unit> values() {
- if (this.values == null) {
- this.values = Collections
- .unmodifiableCollection(this.units.values());
- }
- return this.values;
- }
- }
-
- /**
- * Replacements done to *all* expression types
- */
- private static final Map<Pattern, String> EXPRESSION_REPLACEMENTS = new HashMap<>();
-
- // add data to expression replacements
- static {
- // add spaces around operators
- for (final String operator : Arrays.asList("\\*", "/", "\\^")) {
- EXPRESSION_REPLACEMENTS.put(Pattern.compile(operator),
- " " + operator + " ");
- }
-
- // replace multiple spaces with a single space
- EXPRESSION_REPLACEMENTS.put(Pattern.compile(" +"), " ");
- // place brackets around any expression of the form "number unit", with or
- // without the space
- EXPRESSION_REPLACEMENTS.put(Pattern.compile("((?:-?[1-9]\\d*|0)" // integer
- + "(?:\\.\\d+(?:[eE]\\d+))?)" // optional decimal point with numbers
- // after it
- + "\\s*" // optional space(s)
- + "([a-zA-Z]+(?:\\^\\d+)?" // any string of letters
- + "(?:\\s+[a-zA-Z]+(?:\\^\\d+)?))" // optional other letters
- + "(?!-?\\d)" // no number directly afterwards (avoids matching
- // "1e3")
- ), "\\($1 $2\\)");
- }
-
- /**
- * A regular expression that separates names and expressions in unit files.
- */
- private static final Pattern NAME_EXPRESSION = Pattern
- .compile("(\\S+)\\s+(\\S.*)");
-
- /**
- * The exponent operator
- *
- * @param base base of exponentiation
- * @param exponentUnit exponent
- * @return result
- * @since 2019-04-10
- * @since v0.2.0
- */
- private static final LinearUnit exponentiateUnits(final LinearUnit base,
- final LinearUnit exponentUnit) {
- // exponent function - first check if o2 is a number,
- if (exponentUnit.getBase().equals(SI.ONE.getBase())) {
- // then check if it is an integer,
- final double exponent = exponentUnit.getConversionFactor();
- if (DecimalComparison.equals(exponent % 1, 0))
- // then exponentiate
- return base.toExponent((int) (exponent + 0.5));
- else
- // not an integer
- throw new UnsupportedOperationException(
- "Decimal exponents are currently not supported.");
- } else
- // not a number
- throw new IllegalArgumentException("Exponents must be numbers.");
- }
-
- /**
- * The exponent operator
- *
- * @param base base of exponentiation
- * @param exponentUnit exponent
- * @return result
- * @since 2020-08-04
- */
- private static final LinearUnitValue exponentiateUnitValues(
- final LinearUnitValue base, final LinearUnitValue exponentValue) {
- // exponent function - first check if o2 is a number,
- if (exponentValue.canConvertTo(SI.ONE)) {
- // then check if it is an integer,
- final double exponent = exponentValue.getValueExact();
- if (DecimalComparison.equals(exponent % 1, 0))
- // then exponentiate
- return base.toExponent((int) (exponent + 0.5));
- else
- // not an integer
- throw new UnsupportedOperationException(
- "Decimal exponents are currently not supported.");
- } else
- // not a number
- throw new IllegalArgumentException("Exponents must be numbers.");
- }
-
- /**
- * The units in this system, excluding prefixes.
- *
- * @since 2019-01-07
- * @since v0.1.0
- */
- private final Map<String, Unit> prefixlessUnits;
-
- /**
- * The unit prefixes in this system.
- *
- * @since 2019-01-14
- * @since v0.1.0
- */
- private final Map<String, UnitPrefix> prefixes;
-
- /**
- * The dimensions in this system.
- *
- * @since 2019-03-14
- * @since v0.2.0
- */
- private final Map<String, ObjectProduct<BaseDimension>> dimensions;
-
- /**
- * A map mapping strings to units (including prefixes)
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- private final Map<String, Unit> units;
-
- /**
- * The rule that specifies when prefix repetition is allowed. It takes in one
- * argument: a list of the prefixes being applied to the unit
- * <p>
- * The prefixes are inputted in <em>application order</em>. This means that
- * testing whether "kilomegagigametre" is a valid unit is equivalent to
- * running the following code (assuming all variables are defined correctly):
- * <br>
- * {@code prefixRepetitionRule.test(Arrays.asList(giga, mega, kilo))}
- */
- private Predicate<List<UnitPrefix>> prefixRepetitionRule;
-
- /**
- * A parser that can parse unit expressions.
- *
- * @since 2019-03-22
- * @since v0.2.0
- */
- private final ExpressionParser<LinearUnit> unitExpressionParser = new ExpressionParser.Builder<>(
- this::getLinearUnit).addBinaryOperator("+", (o1, o2) -> o1.plus(o2), 0)
- .addBinaryOperator("-", (o1, o2) -> o1.minus(o2), 0)
- .addBinaryOperator("*", (o1, o2) -> o1.times(o2), 1)
- .addSpaceFunction("*")
- .addBinaryOperator("/", (o1, o2) -> o1.dividedBy(o2), 1)
- .addBinaryOperator("^", UnitDatabase::exponentiateUnits, 2)
- .build();
-
- /**
- * A parser that can parse unit value expressions.
- *
- * @since 2020-08-04
- */
- private final ExpressionParser<LinearUnitValue> unitValueExpressionParser = new ExpressionParser.Builder<>(
- this::getLinearUnitValue)
- .addBinaryOperator("+", (o1, o2) -> o1.plus(o2), 0)
- .addBinaryOperator("-", (o1, o2) -> o1.minus(o2), 0)
- .addBinaryOperator("*", (o1, o2) -> o1.times(o2), 1)
- .addSpaceFunction("*")
- .addBinaryOperator("/", (o1, o2) -> o1.dividedBy(o2), 1)
- .addBinaryOperator("^", UnitDatabase::exponentiateUnitValues, 2)
- .build();
-
- /**
- * A parser that can parse unit prefix expressions
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- private final ExpressionParser<UnitPrefix> prefixExpressionParser = new ExpressionParser.Builder<>(
- this::getPrefix).addBinaryOperator("*", (o1, o2) -> o1.times(o2), 0)
- .addSpaceFunction("*")
- .addBinaryOperator("/", (o1, o2) -> o1.dividedBy(o2), 0)
- .addBinaryOperator("^",
- (o1, o2) -> o1.toExponent(o2.getMultiplier()), 1)
- .build();
-
- /**
- * A parser that can parse unit dimension expressions.
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- private final ExpressionParser<ObjectProduct<BaseDimension>> unitDimensionParser = new ExpressionParser.Builder<>(
- this::getDimension).addBinaryOperator("*", (o1, o2) -> o1.times(o2), 0)
- .addSpaceFunction("*")
- .addBinaryOperator("/", (o1, o2) -> o1.dividedBy(o2), 0).build();
-
- /**
- * Creates the {@code UnitsDatabase}.
- *
- * @since 2019-01-10
- * @since v0.1.0
- */
- public UnitDatabase() {
- this(prefixes -> true);
- }
-
- /**
- * Creates the {@code UnitsDatabase}
- *
- * @param prefixRepetitionRule the rule that determines when prefix
- * repetition is allowed
- * @since 2020-08-26
- */
- public UnitDatabase(Predicate<List<UnitPrefix>> prefixRepetitionRule) {
- this.prefixlessUnits = new HashMap<>();
- this.prefixes = new HashMap<>();
- this.dimensions = new HashMap<>();
- this.prefixRepetitionRule = prefixRepetitionRule;
- this.units = ConditionalExistenceCollections.conditionalExistenceMap(
- new PrefixedUnitMap(this.prefixlessUnits, this.prefixes),
- entry -> this.prefixRepetitionRule
- .test(this.getPrefixesFromName(entry.getKey())));
- }
-
- /**
- * Adds a unit dimension to the database.
- *
- * @param name dimension's name
- * @param dimension dimension to add
- * @throws NullPointerException if name or dimension is null
- * @since 2019-03-14
- * @since v0.2.0
- */
- public void addDimension(final String name,
- final ObjectProduct<BaseDimension> dimension) {
- this.dimensions.put(
- Objects.requireNonNull(name, "name must not be null."),
- Objects.requireNonNull(dimension, "dimension must not be null."));
- }
-
- /**
- * Adds to the list from a line in a unit dimension file.
- *
- * @param line line to look at
- * @param lineCounter number of line, for error messages
- * @since 2019-04-10
- * @since v0.2.0
- */
- private void addDimensionFromLine(final String line,
- final long lineCounter) {
- // ignore lines that start with a # sign - they're comments
- if (line.isEmpty())
- return;
- if (line.contains("#")) {
- this.addDimensionFromLine(line.substring(0, line.indexOf("#")),
- lineCounter);
- return;
- }
-
- // divide line into name and expression
- final Matcher lineMatcher = NAME_EXPRESSION.matcher(line);
- if (!lineMatcher.matches())
- throw new IllegalArgumentException(String.format(
- "Error at line %d: Lines of a dimension file must consist of a dimension name, then spaces or tabs, then a dimension expression.",
- lineCounter));
- final String name = lineMatcher.group(1);
- final String expression = lineMatcher.group(2);
-
- if (name.endsWith(" ")) {
- System.err.printf("Warning - line %d's dimension name ends in a space",
- lineCounter);
- }
-
- // if expression is "!", search for an existing dimension
- // if no unit found, throw an error
- if (expression.equals("!")) {
- if (!this.containsDimensionName(name))
- throw new IllegalArgumentException(String.format(
- "! used but no dimension found (line %d).", lineCounter));
- } else {
- // it's a unit, get the unit
- final ObjectProduct<BaseDimension> dimension;
- try {
- dimension = this.getDimensionFromExpression(expression);
- } catch (final IllegalArgumentException e) {
- System.err.printf("Parsing error on line %d:%n", lineCounter);
- throw e;
- }
-
- this.addDimension(name, dimension);
- }
- }
-
- /**
- * Adds a unit prefix to the database.
- *
- * @param name prefix's name
- * @param prefix prefix to add
- * @throws NullPointerException if name or prefix is null
- * @since 2019-01-14
- * @since v0.1.0
- */
- public void addPrefix(final String name, final UnitPrefix prefix) {
- this.prefixes.put(Objects.requireNonNull(name, "name must not be null."),
- Objects.requireNonNull(prefix, "prefix must not be null."));
- }
-
- /**
- * Adds a unit to the database.
- *
- * @param name unit's name
- * @param unit unit to add
- * @throws NullPointerException if unit is null
- * @since 2019-01-10
- * @since v0.1.0
- */
- public void addUnit(final String name, final Unit unit) {
- this.prefixlessUnits.put(
- Objects.requireNonNull(name, "name must not be null."),
- Objects.requireNonNull(unit, "unit must not be null."));
- }
-
- /**
- * Adds to the list from a line in a unit file.
- *
- * @param line line to look at
- * @param lineCounter number of line, for error messages
- * @since 2019-04-10
- * @since v0.2.0
- */
- private void addUnitOrPrefixFromLine(final String line,
- final long lineCounter) {
- // ignore lines that start with a # sign - they're comments
- if (line.isEmpty())
- return;
- if (line.contains("#")) {
- this.addUnitOrPrefixFromLine(line.substring(0, line.indexOf("#")),
- lineCounter);
- return;
- }
-
- // divide line into name and expression
- final Matcher lineMatcher = NAME_EXPRESSION.matcher(line);
- if (!lineMatcher.matches())
- throw new IllegalArgumentException(String.format(
- "Error at line %d: Lines of a unit file must consist of a unit name, then spaces or tabs, then a unit expression.",
- lineCounter));
- final String name = lineMatcher.group(1);
-
- final String expression = lineMatcher.group(2);
-
- if (name.endsWith(" ")) {
- System.err.printf("Warning - line %d's unit name ends in a space",
- lineCounter);
- }
-
- // if expression is "!", search for an existing unit
- // if no unit found, throw an error
- if (expression.equals("!")) {
- if (!this.containsUnitName(name))
- throw new IllegalArgumentException(String
- .format("! used but no unit found (line %d).", lineCounter));
- } else {
- if (name.endsWith("-")) {
- final UnitPrefix prefix;
- try {
- prefix = this.getPrefixFromExpression(expression);
- } catch (final IllegalArgumentException e) {
- System.err.printf("Parsing error on line %d:%n", lineCounter);
- throw e;
- }
- this.addPrefix(name.substring(0, name.length() - 1), prefix);
- } else {
- // it's a unit, get the unit
- final Unit unit;
- try {
- unit = this.getUnitFromExpression(expression);
- } catch (final IllegalArgumentException e) {
- System.err.printf("Parsing error on line %d:%n", lineCounter);
- throw e;
- }
-
- this.addUnit(name, unit);
- }
- }
- }
-
- /**
- * Tests if the database has a unit dimension with this name.
- *
- * @param name name to test
- * @return if database contains name
- * @since 2019-03-14
- * @since v0.2.0
- */
- public boolean containsDimensionName(final String name) {
- return this.dimensions.containsKey(name);
- }
-
- /**
- * Tests if the database has a unit prefix with this name.
- *
- * @param name name to test
- * @return if database contains name
- * @since 2019-01-13
- * @since v0.1.0
- */
- public boolean containsPrefixName(final String name) {
- return this.prefixes.containsKey(name);
- }
-
- /**
- * Tests if the database has a unit with this name, taking prefixes into
- * consideration
- *
- * @param name name to test
- * @return if database contains name
- * @since 2019-01-13
- * @since v0.1.0
- */
- public boolean containsUnitName(final String name) {
- return this.units.containsKey(name);
- }
-
- /**
- * @return a map mapping dimension names to dimensions
- * @since 2019-04-13
- * @since v0.2.0
- */
- public Map<String, ObjectProduct<BaseDimension>> dimensionMap() {
- return Collections.unmodifiableMap(this.dimensions);
- }
-
- /**
- * Evaluates a unit expression, following the same rules as
- * {@link #getUnitFromExpression}.
- *
- * @param expression expression to parse
- * @return {@code LinearUnitValue} representing value of expression
- * @since 2020-08-04
- */
- public LinearUnitValue evaluateUnitExpression(final String expression) {
- Objects.requireNonNull(expression, "expression must not be null.");
-
- // attempt to get a unit as an alias, or a number with precision first
- if (this.containsUnitName(expression))
- return this.getLinearUnitValue(expression);
-
- // force operators to have spaces
- String modifiedExpression = expression;
- modifiedExpression = modifiedExpression.replaceAll("\\+", " \\+ ");
- modifiedExpression = modifiedExpression.replaceAll("-", " - ");
-
- // format expression
- for (final Entry<Pattern, String> replacement : EXPRESSION_REPLACEMENTS
- .entrySet()) {
- modifiedExpression = replacement.getKey().matcher(modifiedExpression)
- .replaceAll(replacement.getValue());
- }
-
- // the previous operation breaks negative numbers, fix them!
- // (i.e. -2 becomes - 2)
- // FIXME the previous operaton also breaks stuff like "1e-5"
- for (int i = 0; i < modifiedExpression.length(); i++) {
- if (modifiedExpression.charAt(i) == '-'
- && (i < 2 || Arrays.asList('+', '-', '*', '/', '^')
- .contains(modifiedExpression.charAt(i - 2)))) {
- // found a broken negative number
- modifiedExpression = modifiedExpression.substring(0, i + 1)
- + modifiedExpression.substring(i + 2);
- }
- }
-
- return this.unitValueExpressionParser.parseExpression(modifiedExpression);
- }
-
- /**
- * Gets a unit dimension from the database using its name.
- *
- * <p>
- * This method accepts exponents, like "L^3"
- * </p>
- *
- * @param name dimension's name
- * @return dimension
- * @since 2019-03-14
- * @since v0.2.0
- */
- public ObjectProduct<BaseDimension> getDimension(final String name) {
- Objects.requireNonNull(name, "name must not be null.");
- if (name.contains("^")) {
- final String[] baseAndExponent = name.split("\\^");
-
- final ObjectProduct<BaseDimension> base = this
- .getDimension(baseAndExponent[0]);
-
- final int exponent;
- try {
- exponent = Integer
- .parseInt(baseAndExponent[baseAndExponent.length - 1]);
- } catch (final NumberFormatException e2) {
- throw new IllegalArgumentException("Exponent must be an integer.");
- }
-
- return base.toExponent(exponent);
- }
- return this.dimensions.get(name);
- }
-
- /**
- * Uses the database's data to parse an expression into a unit dimension
- * <p>
- * The expression is a series of any of the following:
- * <ul>
- * <li>The name of a unit dimension, which multiplies or divides the result
- * based on preceding operators</li>
- * <li>The operators '*' and '/', which multiply and divide (note that just
- * putting two unit dimensions next to each other is equivalent to
- * multiplication)</li>
- * <li>The operator '^' which exponentiates. Exponents must be integers.</li>
- * </ul>
- *
- * @param expression expression to parse
- * @throws IllegalArgumentException if the expression cannot be parsed
- * @throws NullPointerException if expression is null
- * @since 2019-04-13
- * @since v0.2.0
- */
- public ObjectProduct<BaseDimension> getDimensionFromExpression(
- final String expression) {
- Objects.requireNonNull(expression, "expression must not be null.");
-
- // attempt to get a dimension as an alias first
- if (this.containsDimensionName(expression))
- return this.getDimension(expression);
-
- // force operators to have spaces
- String modifiedExpression = expression;
-
- // format expression
- for (final Entry<Pattern, String> replacement : EXPRESSION_REPLACEMENTS
- .entrySet()) {
- modifiedExpression = replacement.getKey().matcher(modifiedExpression)
- .replaceAll(replacement.getValue());
- }
- modifiedExpression = modifiedExpression.replaceAll(" *\\^ *", "\\^");
-
- return this.unitDimensionParser.parseExpression(modifiedExpression);
- }
-
- /**
- * Gets a unit. If it is linear, cast it to a LinearUnit and return it.
- * Otherwise, throw an {@code IllegalArgumentException}.
- *
- * @param name unit's name
- * @return unit
- * @since 2019-03-22
- * @since v0.2.0
- */
- private LinearUnit getLinearUnit(final String name) {
- // see if I am using a function-unit like tempC(100)
- Objects.requireNonNull(name, "name may not be null");
- if (name.contains("(") && name.contains(")")) {
- // break it into function name and value
- final List<String> parts = Arrays.asList(name.split("\\("));
- if (parts.size() != 2)
- throw new IllegalArgumentException(
- "Format nonlinear units like: unit(value).");
-
- // solve the function
- final Unit unit = this.getUnit(parts.get(0));
- final double value = Double.parseDouble(
- parts.get(1).substring(0, parts.get(1).length() - 1));
- return LinearUnit.fromUnitValue(unit, value);
- } else {
- // get a linear unit
- final Unit unit = this.getUnit(name);
-
- if (unit instanceof LinearUnit)
- return (LinearUnit) unit;
- else
- throw new IllegalArgumentException(
- String.format("%s is not a linear unit.", name));
- }
- }
-
- /**
- * Gets a {@code LinearUnitValue} from a unit name. Nonlinear units will be
- * converted to their base units.
- *
- * @param name name of unit
- * @return {@code LinearUnitValue} instance
- * @since 2020-08-04
- */
- private LinearUnitValue getLinearUnitValue(final String name) {
- try {
- // try to parse it as a number - otherwise it is not a number!
- final BigDecimal number = new BigDecimal(name);
-
- final double uncertainty = Math.pow(10, -number.scale());
- return LinearUnitValue.of(SI.ONE,
- UncertainDouble.of(number.doubleValue(), uncertainty));
- } catch (final NumberFormatException e) {
- return LinearUnitValue.getExact(this.getLinearUnit(name), 1);
- }
- }
-
- /**
- * Gets a unit prefix from the database from its name
- *
- * @param name prefix's name
- * @return prefix
- * @since 2019-01-10
- * @since v0.1.0
- */
- public UnitPrefix getPrefix(final String name) {
- try {
- return UnitPrefix.valueOf(Double.parseDouble(name));
- } catch (final NumberFormatException e) {
- return this.prefixes.get(name);
- }
- }
-
- /**
- * Gets all of the prefixes that are on a unit name, in application order.
- *
- * @param unitName name of unit
- * @return prefixes
- * @since 2020-08-26
- */
- List<UnitPrefix> getPrefixesFromName(final String unitName) {
- final List<UnitPrefix> prefixes = new ArrayList<>();
- String name = unitName;
-
- while (!this.prefixlessUnits.containsKey(name)) {
- // find the longest prefix
- String longestPrefixName = null;
- int longestLength = name.length();
-
- while (longestPrefixName == null) {
- longestLength--;
- if (longestLength <= 0)
- throw new AssertionError(
- "No prefix found in " + name + ", but it is not a unit!");
- if (this.prefixes.containsKey(name.substring(0, longestLength))) {
- longestPrefixName = name.substring(0, longestLength);
- }
- }
-
- // longest prefix found!
- final UnitPrefix prefix = this.getPrefix(longestPrefixName);
- prefixes.add(0, prefix);
- name = name.substring(longestLength);
- }
- return prefixes;
- }
-
- /**
- * Gets a unit prefix from a prefix expression
- * <p>
- * Currently, prefix expressions are much simpler than unit expressions: They
- * are either a number or the name of another prefix
- * </p>
- *
- * @param expression expression to input
- * @return prefix
- * @throws IllegalArgumentException if expression cannot be parsed
- * @throws NullPointerException if any argument is null
- * @since 2019-01-14
- * @since v0.1.0
- */
- public UnitPrefix getPrefixFromExpression(final String expression) {
- Objects.requireNonNull(expression, "expression must not be null.");
-
- // attempt to get a unit as an alias first
- if (this.containsUnitName(expression))
- return this.getPrefix(expression);
-
- // force operators to have spaces
- String modifiedExpression = expression;
-
- // format expression
- for (final Entry<Pattern, String> replacement : EXPRESSION_REPLACEMENTS
- .entrySet()) {
- modifiedExpression = replacement.getKey().matcher(modifiedExpression)
- .replaceAll(replacement.getValue());
- }
-
- return this.prefixExpressionParser.parseExpression(modifiedExpression);
- }
-
- /**
- * @return the prefixRepetitionRule
- * @since 2020-08-26
- */
- public final Predicate<List<UnitPrefix>> getPrefixRepetitionRule() {
- return this.prefixRepetitionRule;
- }
-
- /**
- * Gets a unit from the database from its name, looking for prefixes.
- *
- * @param name unit's name
- * @return unit
- * @since 2019-01-10
- * @since v0.1.0
- */
- public Unit getUnit(final String name) {
- try {
- final double value = Double.parseDouble(name);
- return SI.ONE.times(value);
- } catch (final NumberFormatException e) {
- final Unit unit = this.units.get(name);
- if (unit == null)
- throw new NoSuchElementException("No unit " + name);
- else if (unit.getPrimaryName().isEmpty())
- return unit.withName(NameSymbol.ofName(name));
- else if (!unit.getPrimaryName().get().equals(name)) {
- final Set<String> otherNames = new HashSet<>(unit.getOtherNames());
- otherNames.add(unit.getPrimaryName().get());
- return unit.withName(NameSymbol.ofNullable(name,
- unit.getSymbol().orElse(null), otherNames));
- } else if (!unit.getOtherNames().contains(name)) {
- final Set<String> otherNames = new HashSet<>(unit.getOtherNames());
- otherNames.add(name);
- return unit.withName(
- NameSymbol.ofNullable(unit.getPrimaryName().orElse(null),
- unit.getSymbol().orElse(null), otherNames));
- } else
- return unit;
- }
-
- }
-
- /**
- * Uses the database's unit data to parse an expression into a unit
- * <p>
- * The expression is a series of any of the following:
- * <ul>
- * <li>The name of a unit, which multiplies or divides the result based on
- * preceding operators</li>
- * <li>The operators '*' and '/', which multiply and divide (note that just
- * putting two units or values next to each other is equivalent to
- * multiplication)</li>
- * <li>The operator '^' which exponentiates. Exponents must be integers.</li>
- * <li>A number which is multiplied or divided</li>
- * </ul>
- * This method only works with linear units.
- *
- * @param expression expression to parse
- * @throws IllegalArgumentException if the expression cannot be parsed
- * @throws NullPointerException if expression is null
- * @since 2019-01-07
- * @since v0.1.0
- */
- public Unit getUnitFromExpression(final String expression) {
- Objects.requireNonNull(expression, "expression must not be null.");
-
- // attempt to get a unit as an alias first
- if (this.containsUnitName(expression))
- return this.getUnit(expression);
-
- // force operators to have spaces
- String modifiedExpression = expression;
- modifiedExpression = modifiedExpression.replaceAll("\\+", " \\+ ");
- modifiedExpression = modifiedExpression.replaceAll("-", " - ");
-
- // format expression
- for (final Entry<Pattern, String> replacement : EXPRESSION_REPLACEMENTS
- .entrySet()) {
- modifiedExpression = replacement.getKey().matcher(modifiedExpression)
- .replaceAll(replacement.getValue());
- }
-
- // the previous operation breaks negative numbers, fix them!
- // (i.e. -2 becomes - 2)
- for (int i = 0; i < modifiedExpression.length(); i++) {
- if (modifiedExpression.charAt(i) == '-'
- && (i < 2 || Arrays.asList('+', '-', '*', '/', '^')
- .contains(modifiedExpression.charAt(i - 2)))) {
- // found a broken negative number
- modifiedExpression = modifiedExpression.substring(0, i + 1)
- + modifiedExpression.substring(i + 2);
- }
- }
-
- return this.unitExpressionParser.parseExpression(modifiedExpression);
- }
-
- /**
- * Adds all dimensions from a file, using data from the database to parse
- * them.
- * <p>
- * Each line in the file should consist of a name and an expression (parsed
- * by getDimensionFromExpression) separated by any number of tab characters.
- * <p>
- * <p>
- * Allowed exceptions:
- * <ul>
- * <li>Anything after a '#' character is considered a comment and
- * ignored.</li>
- * <li>Blank lines are also ignored</li>
- * <li>If an expression consists of a single exclamation point, instead of
- * parsing it, this method will search the database for an existing unit. If
- * no unit is found, an IllegalArgumentException is thrown. This is used to
- * define initial units and ensure that the database contains them.</li>
- * </ul>
- *
- * @param file file to read
- * @throws IllegalArgumentException if the file cannot be parsed, found or
- * read
- * @throws NullPointerException if file is null
- * @since 2019-01-13
- * @since v0.1.0
- */
- public void loadDimensionFile(final Path file) {
- Objects.requireNonNull(file, "file must not be null.");
- try {
- long lineCounter = 0;
- for (final String line : Files.readAllLines(file)) {
- this.addDimensionFromLine(line, ++lineCounter);
- }
- } catch (final FileNotFoundException e) {
- throw new IllegalArgumentException("Could not find file " + file, e);
- } catch (final IOException e) {
- throw new IllegalArgumentException("Could not read file " + file, e);
- }
- }
-
- /**
- * Adds all units from a file, using data from the database to parse them.
- * <p>
- * Each line in the file should consist of a name and an expression (parsed
- * by getUnitFromExpression) separated by any number of tab characters.
- * <p>
- * <p>
- * Allowed exceptions:
- * <ul>
- * <li>Anything after a '#' character is considered a comment and
- * ignored.</li>
- * <li>Blank lines are also ignored</li>
- * <li>If an expression consists of a single exclamation point, instead of
- * parsing it, this method will search the database for an existing unit. If
- * no unit is found, an IllegalArgumentException is thrown. This is used to
- * define initial units and ensure that the database contains them.</li>
- * </ul>
- *
- * @param file file to read
- * @throws IllegalArgumentException if the file cannot be parsed, found or
- * read
- * @throws NullPointerException if file is null
- * @since 2019-01-13
- * @since v0.1.0
- */
- public void loadUnitsFile(final Path file) {
- Objects.requireNonNull(file, "file must not be null.");
- try {
- long lineCounter = 0;
- for (final String line : Files.readAllLines(file)) {
- this.addUnitOrPrefixFromLine(line, ++lineCounter);
- }
- } catch (final FileNotFoundException e) {
- throw new IllegalArgumentException("Could not find file " + file, e);
- } catch (final IOException e) {
- throw new IllegalArgumentException("Could not read file " + file, e);
- }
- }
-
- /**
- * @return a map mapping prefix names to prefixes
- * @since 2019-04-13
- * @since v0.2.0
- */
- public Map<String, UnitPrefix> prefixMap() {
- return Collections.unmodifiableMap(this.prefixes);
- }
-
- /**
- * @param prefixRepetitionRule the prefixRepetitionRule to set
- * @since 2020-08-26
- */
- public final void setPrefixRepetitionRule(
- Predicate<List<UnitPrefix>> prefixRepetitionRule) {
- this.prefixRepetitionRule = prefixRepetitionRule;
- }
-
- /**
- * @return a string stating the number of units, prefixes and dimensions in
- * the database
- */
- @Override
- public String toString() {
- return String.format(
- "Unit Database with %d units, %d unit prefixes and %d dimensions",
- this.prefixlessUnits.size(), this.prefixes.size(),
- this.dimensions.size());
- }
-
- /**
- * Returns a map mapping unit names to units, including units with prefixes.
- * <p>
- * The returned map is infinite in size if there is at least one unit and at
- * least one prefix. If it is infinite, some operations that only work with
- * finite collections, like converting name/entry sets to arrays, will throw
- * an {@code IllegalStateException}.
- * </p>
- * <p>
- * Specifically, the operations that will throw an IllegalStateException if
- * the map is infinite in size are:
- * <ul>
- * <li>{@code unitMap.entrySet().toArray()} (either overloading)</li>
- * <li>{@code unitMap.keySet().toArray()} (either overloading)</li>
- * </ul>
- * </p>
- * <p>
- * Because of ambiguities between prefixes (i.e. kilokilo = mega), the map's
- * {@link PrefixedUnitMap#containsValue containsValue} and
- * {@link PrefixedUnitMap#values() values()} methods currently ignore
- * prefixes.
- * </p>
- *
- * @return a map mapping unit names to units, including prefixed names
- * @since 2019-04-13
- * @since v0.2.0
- */
- public Map<String, Unit> unitMap() {
- return this.units; // PrefixedUnitMap is immutable so I don't need to make
- // an unmodifiable map.
- }
-
- /**
- * @return a map mapping unit names to units, ignoring prefixes
- * @since 2019-04-13
- * @since v0.2.0
- */
- public Map<String, Unit> unitMapPrefixless() {
- return Collections.unmodifiableMap(this.prefixlessUnits);
- }
-}
diff --git a/src/org/unitConverter/unit/UnitDatabaseTest.java b/src/org/unitConverter/unit/UnitDatabaseTest.java
deleted file mode 100644
index 2b981b6..0000000
--- a/src/org/unitConverter/unit/UnitDatabaseTest.java
+++ /dev/null
@@ -1,309 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.NoSuchElementException;
-import java.util.Set;
-
-import org.junit.jupiter.api.Test;
-
-/**
- * A test for the {@link UnitDatabase} class. This is NOT part of this program's
- * public API.
- *
- * @author Adrien Hopkins
- * @since 2019-04-14
- * @since v0.2.0
- */
-class UnitDatabaseTest {
- // some linear units and one nonlinear
- private static final Unit U = SI.METRE;
- private static final Unit V = SI.KILOGRAM;
- private static final Unit W = SI.SECOND;
-
- // used for testing expressions
- // J = U^2 * V / W^2
- private static final LinearUnit J = SI.KILOGRAM.times(SI.METRE.toExponent(2))
- .dividedBy(SI.SECOND.toExponent(2));
- private static final LinearUnit K = SI.KELVIN;
-
- private static final Unit NONLINEAR = Unit
- .fromConversionFunctions(SI.METRE.getBase(), o -> o + 1, o -> o - 1);
-
- // make the prefix values prime so I can tell which multiplications were made
- private static final UnitPrefix A = UnitPrefix.valueOf(2)
- .withName(NameSymbol.ofName("A"));
- private static final UnitPrefix B = UnitPrefix.valueOf(3)
- .withName(NameSymbol.ofName("B"));
- private static final UnitPrefix C = UnitPrefix.valueOf(5)
- .withName(NameSymbol.ofName("C"));
- private static final UnitPrefix AB = UnitPrefix.valueOf(7);
- private static final UnitPrefix BC = UnitPrefix.valueOf(11);
-
- /**
- * Confirms that operations that shouldn't function for infinite databases
- * throw an {@code IllegalStateException}.
- *
- * @since 2019-05-03
- */
- @Test
- // @Timeout(value = 5, unit = TimeUnit.SECONDS)
- public void testInfiniteSetExceptions() {
- // load units
- final UnitDatabase infiniteDatabase = new UnitDatabase();
-
- infiniteDatabase.addUnit("J", J);
- infiniteDatabase.addUnit("K", K);
-
- infiniteDatabase.addPrefix("A", A);
- infiniteDatabase.addPrefix("B", B);
- infiniteDatabase.addPrefix("C", C);
-
- final Set<Entry<String, Unit>> entrySet = infiniteDatabase.unitMap()
- .entrySet();
- final Set<String> keySet = infiniteDatabase.unitMap().keySet();
- assertThrows(IllegalStateException.class, () -> entrySet.toArray());
- assertThrows(IllegalStateException.class, () -> keySet.toArray());
- }
-
- /**
- * Test that prefixes correctly apply to units.
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- @Test
- public void testPrefixes() {
- final UnitDatabase database = new UnitDatabase();
-
- database.addUnit("U", U);
- database.addUnit("V", V);
- database.addUnit("W", W);
-
- database.addPrefix("A", A);
- database.addPrefix("B", B);
- database.addPrefix("C", C);
-
- // test the getPrefixesFromName method
- final List<UnitPrefix> expected = Arrays.asList(C, B, A);
- assertEquals(expected, database.getPrefixesFromName("ABCU"));
-
- // get the product
- final Unit abcuNonlinear = database.getUnit("ABCU");
- assert abcuNonlinear instanceof LinearUnit;
-
- final LinearUnit abcu = (LinearUnit) abcuNonlinear;
- assertEquals(A.getMultiplier() * B.getMultiplier() * C.getMultiplier(),
- abcu.getConversionFactor(), 1e-15);
- }
-
- /**
- * Tests the functionnalites of the prefixless unit map.
- *
- * <p>
- * The map should be an auto-updating view of the units in the database.
- * </p>
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- @Test
- public void testPrefixlessUnitMap() {
- final UnitDatabase database = new UnitDatabase();
- final Map<String, Unit> prefixlessUnits = database.unitMapPrefixless();
-
- database.addUnit("U", U);
- database.addUnit("V", V);
- database.addUnit("W", W);
-
- // this should work because the map should be an auto-updating view
- assertTrue(prefixlessUnits.containsKey("U"));
- assertFalse(prefixlessUnits.containsKey("Z"));
-
- assertTrue(prefixlessUnits.containsValue(U));
- assertFalse(prefixlessUnits.containsValue(NONLINEAR));
- }
-
- /**
- * Tests that the database correctly stores and retrieves units, ignoring
- * prefixes.
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- @Test
- public void testPrefixlessUnits() {
- final UnitDatabase database = new UnitDatabase();
-
- database.addUnit("U", U);
- database.addUnit("V", V);
- database.addUnit("W", W);
-
- assertTrue(database.containsUnitName("U"));
- assertFalse(database.containsUnitName("Z"));
-
- assertEquals(U, database.getUnit("U"));
- assertThrows(NoSuchElementException.class, () -> database.getUnit("Z"));
- }
-
- /**
- * Test that unit expressions return the correct value.
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- @Test
- public void testUnitExpressions() {
- // load units
- final UnitDatabase database = new UnitDatabase();
-
- database.addUnit("U", U);
- database.addUnit("V", V);
- database.addUnit("W", W);
- database.addUnit("fj", J.times(5));
- database.addUnit("ej", J.times(8));
-
- database.addPrefix("A", A);
- database.addPrefix("B", B);
- database.addPrefix("C", C);
-
- // first test - test prefixes and operations
- final Unit expected1 = J.withPrefix(A).withPrefix(B).withPrefix(C)
- .withPrefix(C);
- final Unit actual1 = database.getUnitFromExpression("ABV * CU^2 / W / W");
-
- assertEquals(expected1, actual1);
-
- // second test - test addition and subtraction
- final Unit expected2 = J.times(58);
- final Unit actual2 = database.getUnitFromExpression("2 fj + 6 ej");
-
- assertEquals(expected2, actual2);
- }
-
- /**
- * Tests both the unit name iterator and the name-unit entry iterator
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- @Test
- public void testUnitIterator() {
- // load units
- final UnitDatabase database = new UnitDatabase();
-
- database.addUnit("J", J);
- database.addUnit("K", K);
-
- database.addPrefix("A", A);
- database.addPrefix("B", B);
- database.addPrefix("C", C);
-
- final int NUM_UNITS = database.unitMapPrefixless().size();
- final int NUM_PREFIXES = database.prefixMap().size();
-
- final Iterator<String> nameIterator = database.unitMap().keySet()
- .iterator();
- final Iterator<Entry<String, Unit>> entryIterator = database.unitMap()
- .entrySet().iterator();
-
- int expectedLength = 1;
- int unitsWithThisLengthSoFar = 0;
-
- // loop 1000 times
- for (int i = 0; i < 1000; i++) {
- // expected length of next
- if (unitsWithThisLengthSoFar >= NUM_UNITS
- * (int) Math.pow(NUM_PREFIXES, expectedLength - 1)) {
- expectedLength++;
- unitsWithThisLengthSoFar = 0;
- }
-
- // test that stuff is valid
- final String nextName = nameIterator.next();
- final Unit nextUnit = database.getUnit(nextName);
- final Entry<String, Unit> nextEntry = entryIterator.next();
-
- assertEquals(expectedLength, nextName.length());
- assertEquals(nextName, nextEntry.getKey());
- assertEquals(nextUnit, nextEntry.getValue());
-
- unitsWithThisLengthSoFar++;
- }
-
- // test toString for consistency
- final String entryIteratorString = entryIterator.toString();
- for (int i = 0; i < 3; i++) {
- assertEquals(entryIteratorString, entryIterator.toString());
- }
-
- final String nameIteratorString = nameIterator.toString();
- for (int i = 0; i < 3; i++) {
- assertEquals(nameIteratorString, nameIterator.toString());
- }
- }
-
- /**
- * Determine, given a unit name that could mean multiple things, which
- * meaning is chosen.
- * <p>
- * For example, "ABCU" could mean "A-B-C-U", "AB-C-U", or "A-BC-U". In this
- * case, "AB-C-U" is the correct choice.
- * </p>
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- @Test
- public void testUnitPrefixCombinations() {
- // load units
- final UnitDatabase database = new UnitDatabase();
-
- database.addUnit("J", J);
-
- database.addPrefix("A", A);
- database.addPrefix("B", B);
- database.addPrefix("C", C);
- database.addPrefix("AB", AB);
- database.addPrefix("BC", BC);
-
- // test 1 - AB-C-J vs A-BC-J vs A-B-C-J
- final Unit expected1 = J.withPrefix(AB).withPrefix(C);
- final Unit actual1 = database.getUnit("ABCJ");
-
- assertEquals(expected1, actual1);
-
- // test 2 - ABC-J vs AB-CJ vs AB-C-J
- database.addUnit("CJ", J.times(13));
- database.addPrefix("ABC", UnitPrefix.valueOf(17));
-
- final Unit expected2 = J.times(17);
- final Unit actual2 = database.getUnit("ABCJ");
-
- assertEquals(expected2, actual2);
- }
-}
diff --git a/src/org/unitConverter/unit/UnitPrefix.java b/src/org/unitConverter/unit/UnitPrefix.java
deleted file mode 100644
index 31cc0b3..0000000
--- a/src/org/unitConverter/unit/UnitPrefix.java
+++ /dev/null
@@ -1,242 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-
-import org.unitConverter.math.DecimalComparison;
-
-/**
- * A prefix that can be applied to a {@code LinearUnit} to multiply it by some value
- *
- * @author Adrien Hopkins
- * @since 2019-10-16
- */
-public final class UnitPrefix {
- /**
- * Gets a {@code UnitPrefix} from a multiplier
- *
- * @param multiplier
- * multiplier of prefix
- * @return prefix
- * @since 2019-10-16
- */
- public static UnitPrefix valueOf(final double multiplier) {
- return new UnitPrefix(multiplier, NameSymbol.EMPTY);
- }
-
- /**
- * Gets a {@code UnitPrefix} from a multiplier and a name
- *
- * @param multiplier
- * multiplier of prefix
- * @param ns
- * name(s) and symbol of prefix
- * @return prefix
- * @since 2019-10-16
- * @throws NullPointerException
- * if ns is null
- */
- public static UnitPrefix valueOf(final double multiplier, final NameSymbol ns) {
- return new UnitPrefix(multiplier, Objects.requireNonNull(ns, "ns must not be null."));
- }
-
- /**
- * This prefix's primary name
- */
- private final Optional<String> primaryName;
-
- /**
- * This prefix's symbol
- */
- private final Optional<String> symbol;
-
- /**
- * Other names and symbols used by this prefix
- */
- private final Set<String> otherNames;
-
- /**
- * The number that this prefix multiplies units by
- *
- * @since 2019-10-16
- */
- private final double multiplier;
-
- /**
- * Creates the {@code DefaultUnitPrefix}.
- *
- * @param multiplier
- * @since 2019-01-14
- * @since v0.2.0
- */
- private UnitPrefix(final double multiplier, final NameSymbol ns) {
- this.multiplier = multiplier;
- this.primaryName = ns.getPrimaryName();
- this.symbol = ns.getSymbol();
- this.otherNames = ns.getOtherNames();
- }
-
- /**
- * Divides this prefix by a scalar
- *
- * @param divisor
- * number to divide by
- * @return quotient of prefix and scalar
- * @since 2019-10-16
- */
- public UnitPrefix dividedBy(final double divisor) {
- return valueOf(this.getMultiplier() / divisor);
- }
-
- /**
- * Divides this prefix by {@code other}.
- *
- * @param other
- * prefix to divide by
- * @return quotient of prefixes
- * @since 2019-04-13
- * @since v0.2.0
- */
- public UnitPrefix dividedBy(final UnitPrefix other) {
- return valueOf(this.getMultiplier() / other.getMultiplier());
- }
-
- /**
- * {@inheritDoc}
- *
- * Uses the prefix's multiplier to determine equality.
- */
- @Override
- public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (!(obj instanceof UnitPrefix))
- return false;
- final UnitPrefix other = (UnitPrefix) obj;
- return DecimalComparison.equals(this.getMultiplier(), other.getMultiplier());
- }
-
- /**
- * @return prefix's multiplier
- * @since 2019-11-26
- */
- public double getMultiplier() {
- return this.multiplier;
- }
-
- /**
- * @return other names
- * @since 2019-11-26
- */
- public final Set<String> getOtherNames() {
- return this.otherNames;
- }
-
- /**
- * @return primary name
- * @since 2019-11-26
- */
- public final Optional<String> getPrimaryName() {
- return this.primaryName;
- }
-
- /**
- * @return symbol
- * @since 2019-11-26
- */
- public final Optional<String> getSymbol() {
- return this.symbol;
- }
-
- /**
- * {@inheritDoc}
- *
- * Uses the prefix's multiplier to determine a hash code.
- */
- @Override
- public int hashCode() {
- return DecimalComparison.hash(this.getMultiplier());
- }
-
- /**
- * Multiplies this prefix by a scalar
- *
- * @param multiplicand
- * number to multiply by
- * @return product of prefix and scalar
- * @since 2019-10-16
- */
- public UnitPrefix times(final double multiplicand) {
- return valueOf(this.getMultiplier() * multiplicand);
- }
-
- /**
- * Multiplies this prefix by {@code other}.
- *
- * @param other
- * prefix to multiply by
- * @return product of prefixes
- * @since 2019-04-13
- * @since v0.2.0
- */
- public UnitPrefix times(final UnitPrefix other) {
- return valueOf(this.getMultiplier() * other.getMultiplier());
- }
-
- /**
- * Raises this prefix to an exponent.
- *
- * @param exponent
- * exponent to raise to
- * @return result of exponentiation.
- * @since 2019-04-13
- * @since v0.2.0
- */
- public UnitPrefix toExponent(final double exponent) {
- return valueOf(Math.pow(this.getMultiplier(), exponent));
- }
-
- /**
- * @return a string describing the prefix and its multiplier
- */
- @Override
- public String toString() {
- if (this.primaryName.isPresent())
- return String.format("%s (\u00D7 %s)", this.primaryName.get(), this.multiplier);
- else if (this.symbol.isPresent())
- return String.format("%s (\u00D7 %s)", this.symbol.get(), this.multiplier);
- else
- return String.format("Unit Prefix (\u00D7 %s)", this.multiplier);
- }
-
- /**
- * @param ns
- * name(s) and symbol to use
- * @return copy of this prefix with provided name(s) and symbol
- * @since 2019-11-26
- * @throws NullPointerException
- * if ns is null
- */
- public UnitPrefix withName(final NameSymbol ns) {
- return valueOf(this.multiplier, ns);
- }
-}
diff --git a/src/org/unitConverter/unit/UnitTest.java b/src/org/unitConverter/unit/UnitTest.java
deleted file mode 100644
index c0711dc..0000000
--- a/src/org/unitConverter/unit/UnitTest.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/**
- * Copyright (C) 2018 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.util.Random;
-import java.util.concurrent.ThreadLocalRandom;
-
-import org.junit.jupiter.api.Test;
-import org.unitConverter.math.DecimalComparison;
-
-/**
- * Testing the various Unit classes. This is NOT part of this program's public
- * API.
- *
- * @author Adrien Hopkins
- * @since 2018-12-22
- * @since v0.1.0
- */
-class UnitTest {
- /** A random number generator */
- private static final Random rng = ThreadLocalRandom.current();
-
- @Test
- public void testAdditionAndSubtraction() {
- final LinearUnit inch = SI.METRE.times(0.0254)
- .withName(NameSymbol.of("inch", "in"));
- final LinearUnit foot = SI.METRE.times(0.3048)
- .withName(NameSymbol.of("foot", "ft"));
-
- assertEquals(inch.plus(foot), SI.METRE.times(0.3302));
- assertEquals(foot.minus(inch), SI.METRE.times(0.2794));
-
- // test with LinearUnitValue
- final LinearUnitValue value1 = LinearUnitValue.getExact(SI.METRE, 15);
- final LinearUnitValue value2 = LinearUnitValue.getExact(foot, 120);
- final LinearUnitValue value3 = LinearUnitValue.getExact(SI.METRE, 0.5);
- final LinearUnitValue value4 = LinearUnitValue.getExact(SI.KILOGRAM, 60);
-
- // make sure addition is done correctly
- assertEquals(51.576, value1.plus(value2).getValueExact(), 0.001);
- assertEquals(15.5, value1.plus(value3).getValueExact());
- assertEquals(52.076, value1.plus(value2).plus(value3).getValueExact(),
- 0.001);
-
- // make sure addition uses the correct unit, and is still associative
- // (ignoring floating-point rounding errors)
- assertEquals(SI.METRE, value1.plus(value2).getUnit());
- assertEquals(SI.METRE, value1.plus(value2).plus(value3).getUnit());
- assertEquals(foot, value2.plus(value1).getUnit());
- assertTrue(value1.plus(value2).equals(value2.plus(value1), true));
-
- // make sure errors happen when they should
- assertThrows(IllegalArgumentException.class, () -> value1.plus(value4));
- }
-
- @Test
- public void testConversion() {
- final LinearUnit metre = SI.METRE;
- final Unit inch = metre.times(0.0254);
-
- final UnitValue value = UnitValue.of(inch, 75);
-
- assertEquals(1.9, inch.convertTo(metre, 75), 0.01);
- assertEquals(1.9, value.convertTo(metre).getValue(), 0.01);
-
- // try random stuff
- for (int i = 0; i < 1000; i++) {
- // initiate random values
- final double conversionFactor = UnitTest.rng.nextDouble() * 1000000;
- final double testValue = UnitTest.rng.nextDouble() * 1000000;
- final double expected = testValue * conversionFactor;
-
- // test
- final Unit unit = SI.METRE.times(conversionFactor);
- final double actual = unit.convertToBase(testValue);
-
- assertEquals(actual, expected,
- expected * DecimalComparison.DOUBLE_EPSILON);
- }
- }
-
- @Test
- public void testEquals() {
- final LinearUnit metre = SI.METRE;
- final Unit meter = SI.BaseUnits.METRE.asLinearUnit();
-
- assertEquals(metre, meter);
- }
-
- @Test
- public void testIsMetric() {
- final Unit metre = SI.METRE;
- final Unit megasecond = SI.SECOND.withPrefix(SI.MEGA);
- final Unit hour = SI.HOUR;
-
- assertTrue(metre.isMetric());
- assertTrue(megasecond.isMetric());
- assertFalse(hour.isMetric());
- }
-
- @Test
- public void testMultiplicationAndDivision() {
- // test unit-times-unit multiplication
- final LinearUnit generatedJoule = SI.KILOGRAM
- .times(SI.METRE.toExponent(2)).dividedBy(SI.SECOND.toExponent(2));
- final LinearUnit actualJoule = SI.JOULE;
-
- assertEquals(generatedJoule, actualJoule);
-
- // test multiplication by conversion factors
- final LinearUnit kilometre = SI.METRE.times(1000);
- final LinearUnit hour = SI.SECOND.times(3600);
- final LinearUnit generatedKPH = kilometre.dividedBy(hour);
-
- final LinearUnit actualKPH = SI.METRE.dividedBy(SI.SECOND).dividedBy(3.6);
-
- assertEquals(generatedKPH, actualKPH);
- }
-
- @Test
- public void testPrefixes() {
- final LinearUnit generatedKilometre = SI.METRE.withPrefix(SI.KILO);
- final LinearUnit actualKilometre = SI.METRE.times(1000);
-
- assertEquals(generatedKilometre, actualKilometre);
- }
-}
diff --git a/src/org/unitConverter/unit/UnitValue.java b/src/org/unitConverter/unit/UnitValue.java
deleted file mode 100644
index c138332..0000000
--- a/src/org/unitConverter/unit/UnitValue.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import java.util.Objects;
-import java.util.Optional;
-
-/**
- * A value expressed in a unit.
- *
- * Unless otherwise indicated, all methods in this class throw a
- * {@code NullPointerException} when an argument is null.
- *
- * @author Adrien Hopkins
- * @since 2020-07-26
- */
-public final class UnitValue {
- /**
- * Creates a {@code UnitValue} from a unit and the associated value.
- *
- * @param unit unit to use
- * @param value value to use
- * @return {@code UnitValue} instance
- */
- public static UnitValue of(Unit unit, double value) {
- return new UnitValue(
- Objects.requireNonNull(unit, "unit must not be null"), value);
- }
-
- private final Unit unit;
- private final double value;
-
- /**
- * @param unit the unit being used
- * @param value the value being represented
- */
- private UnitValue(Unit unit, Double value) {
- this.unit = unit;
- this.value = value;
- }
-
- /**
- * @return true if this value can be converted to {@code other}.
- * @since 2020-10-01
- */
- public final boolean canConvertTo(Unit other) {
- return this.unit.canConvertTo(other);
- }
-
- /**
- * @return true if this value can be converted to {@code other}.
- * @since 2020-10-01
- */
- public final <W> boolean canConvertTo(Unitlike<W> other) {
- return this.unit.canConvertTo(other);
- }
-
- /**
- * Returns a UnitlikeValue that represents the same value expressed in a
- * different unitlike form.
- *
- * @param other new unit to express value in
- * @return value expressed in {@code other}
- */
- public final <U extends Unitlike<W>, W> UnitlikeValue<U, W> convertTo(
- U other) {
- return UnitlikeValue.of(other,
- this.unit.convertTo(other, this.getValue()));
- }
-
- /**
- * Returns a UnitValue that represents the same value expressed in a
- * different unit
- *
- * @param other new unit to express value in
- * @return value expressed in {@code other}
- */
- public final UnitValue convertTo(Unit other) {
- return UnitValue.of(other,
- this.getUnit().convertTo(other, this.getValue()));
- }
-
- /**
- * Returns this unit value represented as a {@code LinearUnitValue} with this
- * unit's base unit as the base.
- *
- * @param ns name and symbol for the base unit, use NameSymbol.EMPTY if not
- * needed.
- * @since 2020-09-29
- */
- public final LinearUnitValue convertToBase(NameSymbol ns) {
- final LinearUnit base = LinearUnit.getBase(this.unit).withName(ns);
- return this.convertToLinear(base);
- }
-
- /**
- * @return a {@code LinearUnitValue} that is equivalent to this value. It
- * will have zero uncertainty.
- * @since 2020-09-29
- */
- public final LinearUnitValue convertToLinear(LinearUnit other) {
- return LinearUnitValue.getExact(other,
- this.getUnit().convertTo(other, this.getValue()));
- }
-
- /**
- * Returns true if this and obj represent the same value, regardless of
- * whether or not they are expressed in the same unit. So (1000 m).equals(1
- * km) returns true.
- */
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof UnitValue))
- return false;
- final UnitValue other = (UnitValue) obj;
- return Objects.equals(this.getUnit().getBase(), other.getUnit().getBase())
- && Double.doubleToLongBits(
- this.getUnit().convertToBase(this.getValue())) == Double
- .doubleToLongBits(
- other.getUnit().convertToBase(other.getValue()));
- }
-
- /**
- * @return the unit
- * @since 2020-09-29
- */
- public final Unit getUnit() {
- return this.unit;
- }
-
- /**
- * @return the value
- * @since 2020-09-29
- */
- public final double getValue() {
- return this.value;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(this.getUnit().getBase(),
- this.getUnit().convertFromBase(this.getValue()));
- }
-
- @Override
- public String toString() {
- final Optional<String> primaryName = this.getUnit().getPrimaryName();
- final Optional<String> symbol = this.getUnit().getSymbol();
- if (primaryName.isEmpty() && symbol.isEmpty()) {
- final double baseValue = this.getUnit().convertToBase(this.getValue());
- return String.format("%s unnamed unit (= %s %s)", this.getValue(),
- baseValue, this.getUnit().getBase());
- } else {
- final String unitName = symbol.orElse(primaryName.get());
- return this.getValue() + " " + unitName;
- }
- }
-}
diff --git a/src/org/unitConverter/unit/Unitlike.java b/src/org/unitConverter/unit/Unitlike.java
deleted file mode 100644
index 8077771..0000000
--- a/src/org/unitConverter/unit/Unitlike.java
+++ /dev/null
@@ -1,260 +0,0 @@
-/**
- * Copyright (C) 2020 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-import java.util.function.DoubleFunction;
-import java.util.function.ToDoubleFunction;
-
-import org.unitConverter.math.ObjectProduct;
-
-/**
- * An object that can convert a value between multiple forms (instances of the
- * object); like a unit but the "converted value" can be any type.
- *
- * @since 2020-09-07
- */
-public abstract class Unitlike<V> implements Nameable {
- /**
- * Returns a unitlike form from its base and the functions it uses to convert
- * to and from its base.
- *
- * @param base unitlike form's base
- * @param converterFrom function that accepts a value expressed in the
- * unitlike form's base and returns that value expressed
- * in this unitlike form.
- * @param converterTo function that accepts a value expressed in the
- * unitlike form and returns that value expressed in the
- * unit's base.
- * @return a unitlike form that uses the provided functions to convert.
- * @since 2020-09-07
- * @throws NullPointerException if any argument is null
- */
- public static final <W> Unitlike<W> fromConversionFunctions(
- final ObjectProduct<BaseUnit> base,
- final DoubleFunction<W> converterFrom,
- final ToDoubleFunction<W> converterTo) {
- return new FunctionalUnitlike<>(base, NameSymbol.EMPTY, converterFrom,
- converterTo);
- }
-
- /**
- * Returns a unitlike form from its base and the functions it uses to convert
- * to and from its base.
- *
- * @param base unitlike form's base
- * @param converterFrom function that accepts a value expressed in the
- * unitlike form's base and returns that value expressed
- * in this unitlike form.
- * @param converterTo function that accepts a value expressed in the
- * unitlike form and returns that value expressed in the
- * unit's base.
- * @param ns names and symbol of unit
- * @return a unitlike form that uses the provided functions to convert.
- * @since 2020-09-07
- * @throws NullPointerException if any argument is null
- */
- public static final <W> Unitlike<W> fromConversionFunctions(
- final ObjectProduct<BaseUnit> base,
- final DoubleFunction<W> converterFrom,
- final ToDoubleFunction<W> converterTo, final NameSymbol ns) {
- return new FunctionalUnitlike<>(base, ns, converterFrom, converterTo);
- }
-
- /**
- * The combination of units that this unit is based on.
- *
- * @since 2019-10-16
- */
- private final ObjectProduct<BaseUnit> unitBase;
-
- /**
- * This unit's name(s) and symbol
- *
- * @since 2020-09-07
- */
- private final NameSymbol nameSymbol;
-
- /**
- * Cache storing the result of getDimension()
- *
- * @since 2019-10-16
- */
- private transient ObjectProduct<BaseDimension> dimension = null;
-
- /**
- * @param unitBase
- * @since 2020-09-07
- */
- protected Unitlike(ObjectProduct<BaseUnit> unitBase, NameSymbol ns) {
- this.unitBase = Objects.requireNonNull(unitBase,
- "unitBase may not be null");
- this.nameSymbol = Objects.requireNonNull(ns, "ns may not be null");
- }
-
- /**
- * Checks if a value expressed in this unitlike form can be converted to a
- * value expressed in {@code other}
- *
- * @param other unit or unitlike form to test with
- * @return true if they are compatible
- * @since 2019-01-13
- * @since v0.1.0
- * @throws NullPointerException if other is null
- */
- public final boolean canConvertTo(final Unit other) {
- Objects.requireNonNull(other, "other must not be null.");
- return Objects.equals(this.getBase(), other.getBase());
- }
-
- /**
- * Checks if a value expressed in this unitlike form can be converted to a
- * value expressed in {@code other}
- *
- * @param other unit or unitlike form to test with
- * @return true if they are compatible
- * @since 2019-01-13
- * @since v0.1.0
- * @throws NullPointerException if other is null
- */
- public final <W> boolean canConvertTo(final Unitlike<W> other) {
- Objects.requireNonNull(other, "other must not be null.");
- return Objects.equals(this.getBase(), other.getBase());
- }
-
- protected abstract V convertFromBase(double value);
-
- /**
- * Converts a value expressed in this unitlike form to a value expressed in
- * {@code other}.
- *
- * @implSpec If conversion is possible, this implementation returns
- * {@code other.convertFromBase(this.convertToBase(value))}.
- * Therefore, overriding either of those methods will change the
- * output of this method.
- *
- * @param other unit to convert to
- * @param value value to convert
- * @return converted value
- * @since 2019-05-22
- * @throws IllegalArgumentException if {@code other} is incompatible for
- * conversion with this unitlike form (as
- * tested by {@link Unit#canConvertTo}).
- * @throws NullPointerException if other is null
- */
- public final double convertTo(final Unit other, final V value) {
- Objects.requireNonNull(other, "other must not be null.");
- if (this.canConvertTo(other))
- return other.convertFromBase(this.convertToBase(value));
- else
- throw new IllegalArgumentException(
- String.format("Cannot convert from %s to %s.", this, other));
- }
-
- /**
- * Converts a value expressed in this unitlike form to a value expressed in
- * {@code other}.
- *
- * @implSpec If conversion is possible, this implementation returns
- * {@code other.convertFromBase(this.convertToBase(value))}.
- * Therefore, overriding either of those methods will change the
- * output of this method.
- *
- * @param other unitlike form to convert to
- * @param value value to convert
- * @param <W> type of value to convert to
- * @return converted value
- * @since 2020-09-07
- * @throws IllegalArgumentException if {@code other} is incompatible for
- * conversion with this unitlike form (as
- * tested by {@link Unit#canConvertTo}).
- * @throws NullPointerException if other is null
- */
- public final <W> W convertTo(final Unitlike<W> other, final V value) {
- Objects.requireNonNull(other, "other must not be null.");
- if (this.canConvertTo(other))
- return other.convertFromBase(this.convertToBase(value));
- else
- throw new IllegalArgumentException(
- String.format("Cannot convert from %s to %s.", this, other));
- }
-
- protected abstract double convertToBase(V value);
-
- /**
- * @return combination of units that this unit is based on
- * @since 2018-12-22
- * @since v0.1.0
- */
- public final ObjectProduct<BaseUnit> getBase() {
- return this.unitBase;
- }
-
- /**
- * @return dimension measured by this unit
- * @since 2018-12-22
- * @since v0.1.0
- */
- public final ObjectProduct<BaseDimension> getDimension() {
- if (this.dimension == null) {
- final Map<BaseUnit, Integer> mapping = this.unitBase.exponentMap();
- final Map<BaseDimension, Integer> dimensionMap = new HashMap<>();
-
- for (final BaseUnit key : mapping.keySet()) {
- dimensionMap.put(key.getBaseDimension(), mapping.get(key));
- }
-
- this.dimension = ObjectProduct.fromExponentMapping(dimensionMap);
- }
- return this.dimension;
- }
-
- /**
- * @return the nameSymbol
- * @since 2020-09-07
- */
- @Override
- public final NameSymbol getNameSymbol() {
- return this.nameSymbol;
- }
-
- @Override
- public String toString() {
- return this.getPrimaryName().orElse("Unnamed unitlike form")
- + (this.getSymbol().isPresent()
- ? String.format(" (%s)", this.getSymbol().get())
- : "")
- + ", derived from "
- + this.getBase().toString(u -> u.getSymbol().get())
- + (this.getOtherNames().isEmpty() ? ""
- : ", also called " + String.join(", ", this.getOtherNames()));
- }
-
- /**
- * @param ns name(s) and symbol to use
- * @return a copy of this unitlike form with provided name(s) and symbol
- * @since 2020-09-07
- * @throws NullPointerException if ns is null
- */
- public Unitlike<V> withName(final NameSymbol ns) {
- return fromConversionFunctions(this.getBase(), this::convertFromBase,
- this::convertToBase,
- Objects.requireNonNull(ns, "ns must not be null."));
- }
-}
diff --git a/src/org/unitConverter/unit/UnitlikeValue.java b/src/org/unitConverter/unit/UnitlikeValue.java
deleted file mode 100644
index 79201c4..0000000
--- a/src/org/unitConverter/unit/UnitlikeValue.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/**
- * Copyright (C) 2020 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package org.unitConverter.unit;
-
-import java.util.Optional;
-
-/**
- *
- * @since 2020-09-07
- */
-final class UnitlikeValue<T extends Unitlike<V>, V> {
- /**
- * Gets a {@code UnitlikeValue<V>}.
- *
- * @since 2020-10-02
- */
- public static <T extends Unitlike<V>, V> UnitlikeValue<T, V> of(T unitlike,
- V value) {
- return new UnitlikeValue<>(unitlike, value);
- }
-
- private final T unitlike;
- private final V value;
-
- /**
- * @param unitlike
- * @param value
- * @since 2020-09-07
- */
- private UnitlikeValue(T unitlike, V value) {
- this.unitlike = unitlike;
- this.value = value;
- }
-
- /**
- * @return true if this value can be converted to {@code other}.
- * @since 2020-10-01
- */
- public final boolean canConvertTo(Unit other) {
- return this.unitlike.canConvertTo(other);
- }
-
- /**
- * @return true if this value can be converted to {@code other}.
- * @since 2020-10-01
- */
- public final <W> boolean canConvertTo(Unitlike<W> other) {
- return this.unitlike.canConvertTo(other);
- }
-
- /**
- * Returns a UnitlikeValue that represents the same value expressed in a
- * different unitlike form.
- *
- * @param other new unit to express value in
- * @return value expressed in {@code other}
- */
- public final <U extends Unitlike<W>, W> UnitlikeValue<U, W> convertTo(
- U other) {
- return UnitlikeValue.of(other,
- this.unitlike.convertTo(other, this.getValue()));
- }
-
- /**
- * Returns a UnitValue that represents the same value expressed in a
- * different unit
- *
- * @param other new unit to express value in
- * @return value expressed in {@code other}
- */
- public final UnitValue convertTo(Unit other) {
- return UnitValue.of(other,
- this.unitlike.convertTo(other, this.getValue()));
- }
-
- /**
- * Returns this unit value represented as a {@code LinearUnitValue} with this
- * unit's base unit as the base.
- *
- * @param ns name and symbol for the base unit, use NameSymbol.EMPTY if not
- * needed.
- * @since 2020-09-29
- */
- public final LinearUnitValue convertToBase(NameSymbol ns) {
- final LinearUnit base = LinearUnit.getBase(this.unitlike).withName(ns);
- return this.convertToLinear(base);
- }
-
- /**
- * @return a {@code LinearUnitValue} that is equivalent to this value. It
- * will have zero uncertainty.
- * @since 2020-09-29
- */
- public final LinearUnitValue convertToLinear(LinearUnit other) {
- return LinearUnitValue.getExact(other,
- this.getUnitlike().convertTo(other, this.getValue()));
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (!(obj instanceof UnitlikeValue))
- return false;
- final UnitlikeValue<?, ?> other = (UnitlikeValue<?, ?>) obj;
- if (this.getUnitlike() == null) {
- if (other.getUnitlike() != null)
- return false;
- } else if (!this.getUnitlike().equals(other.getUnitlike()))
- return false;
- if (this.getValue() == null) {
- if (other.getValue() != null)
- return false;
- } else if (!this.getValue().equals(other.getValue()))
- return false;
- return true;
- }
-
- /**
- * @return the unitlike
- * @since 2020-09-29
- */
- public final Unitlike<V> getUnitlike() {
- return this.unitlike;
- }
-
- /**
- * @return the value
- * @since 2020-09-29
- */
- public final V getValue() {
- return this.value;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result
- + (this.getUnitlike() == null ? 0 : this.getUnitlike().hashCode());
- result = prime * result
- + (this.getValue() == null ? 0 : this.getValue().hashCode());
- return result;
- }
-
- @Override
- public String toString() {
- final Optional<String> primaryName = this.getUnitlike().getPrimaryName();
- final Optional<String> symbol = this.getUnitlike().getSymbol();
- if (primaryName.isEmpty() && symbol.isEmpty()) {
- final double baseValue = this.getUnitlike()
- .convertToBase(this.getValue());
- return String.format("%s unnamed unit (= %s %s)", this.getValue(),
- baseValue, this.getUnitlike().getBase());
- } else {
- final String unitName = symbol.orElse(primaryName.get());
- return this.getValue() + " " + unitName;
- }
- }
-}
diff --git a/src/org/unitConverter/unit/package-info.java b/src/org/unitConverter/unit/package-info.java
deleted file mode 100644
index 2f0e097..0000000
--- a/src/org/unitConverter/unit/package-info.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Copyright (C) 2019 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-/**
- * Everything to do with the units that make up Unit Converter.
- *
- * @author Adrien Hopkins
- * @since 2019-10-16
- * @since v0.1.0
- */
-package org.unitConverter.unit; \ No newline at end of file