diff options
Diffstat (limited to 'src/org')
40 files changed, 0 insertions, 11410 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 f36cf28..0000000 --- a/src/org/unitConverter/unit/SI.java +++ /dev/null @@ -1,434 +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.Arrays;
 -import java.util.Collections;
 -import java.util.HashSet;
 -import java.util.Set;
 -
 -import org.unitConverter.math.ObjectProduct;
 -
 -/**
 - * All of the units, prefixes and dimensions that are used by the SI, as well as
 - * some outside the SI.
 - * 
 - * <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 = setOf(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> REFLACTIVE_PERMEABILITY = EMPTY
 -				.times(EMPTY);
 -		public static final ObjectProduct<BaseDimension> ANGLE = LENGTH
 -				.dividedBy(LENGTH);
 -		public static final ObjectProduct<BaseDimension> SOLID_ANGLE = AREA
 -				.dividedBy(AREA);
 -		
 -		// derived dimensions with named SI units
 -		public static final ObjectProduct<BaseDimension> FREQUENCY = EMPTY
 -				.dividedBy(TIME);
 -		public static final ObjectProduct<BaseDimension> FORCE = MASS
 -				.times(ACCELERATION);
 -		public static final ObjectProduct<BaseDimension> ENERGY = FORCE
 -				.times(LENGTH);
 -		public static final ObjectProduct<BaseDimension> POWER = ENERGY
 -				.dividedBy(TIME);
 -		public static final ObjectProduct<BaseDimension> ELECTRIC_CHARGE = ELECTRIC_CURRENT
 -				.times(TIME);
 -		public static final ObjectProduct<BaseDimension> VOLTAGE = ENERGY
 -				.dividedBy(ELECTRIC_CHARGE);
 -		public static final ObjectProduct<BaseDimension> CAPACITANCE = ELECTRIC_CHARGE
 -				.dividedBy(VOLTAGE);
 -		public static final ObjectProduct<BaseDimension> ELECTRIC_RESISTANCE = VOLTAGE
 -				.dividedBy(ELECTRIC_CURRENT);
 -		public static final ObjectProduct<BaseDimension> ELECTRIC_CONDUCTANCE = ELECTRIC_CURRENT
 -				.dividedBy(VOLTAGE);
 -		public static final ObjectProduct<BaseDimension> MAGNETIC_FLUX = VOLTAGE
 -				.times(TIME);
 -		public static final ObjectProduct<BaseDimension> MAGNETIC_FLUX_DENSITY = MAGNETIC_FLUX
 -				.dividedBy(AREA);
 -		public static final ObjectProduct<BaseDimension> INDUCTANCE = MAGNETIC_FLUX
 -				.dividedBy(ELECTRIC_CURRENT);
 -		public static final ObjectProduct<BaseDimension> LUMINOUS_FLUX = LUMINOUS_INTENSITY
 -				.times(SOLID_ANGLE);
 -		public static final ObjectProduct<BaseDimension> ILLUMINANCE = LUMINOUS_FLUX
 -				.dividedBy(AREA);
 -		public static final ObjectProduct<BaseDimension> SPECIFIC_ENERGY = ENERGY
 -				.dividedBy(MASS);
 -		public static final ObjectProduct<BaseDimension> CATALYTIC_ACTIVITY = QUANTITY
 -				.dividedBy(TIME);
 -		
 -		// 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"));
 -	// sets of prefixes
 -	public static final Set<UnitPrefix> ALL_PREFIXES = setOf(DEKA, HECTO, KILO,
 -			MEGA, GIGA, TERA, PETA, EXA, ZETTA, YOTTA, DECI, CENTI, MILLI, MICRO,
 -			NANO, PICO, FEMTO, ATTO, ZEPTO, YOCTO, KIBI, MEBI, GIBI, TEBI, PEBI,
 -			EXBI);
 -	
 -	public static final Set<UnitPrefix> DECIMAL_PREFIXES = setOf(DEKA, HECTO,
 -			KILO, MEGA, GIGA, TERA, PETA, EXA, ZETTA, YOTTA, DECI, CENTI, MILLI,
 -			MICRO, NANO, PICO, FEMTO, ATTO, ZEPTO, YOCTO);
 -	public static final Set<UnitPrefix> THOUSAND_PREFIXES = setOf(KILO, MEGA,
 -			GIGA, TERA, PETA, EXA, ZETTA, YOTTA, MILLI, MICRO, NANO, PICO, FEMTO,
 -			ATTO, ZEPTO, YOCTO);
 -	public static final Set<UnitPrefix> MAGNIFYING_PREFIXES = setOf(DEKA, HECTO,
 -			KILO, MEGA, GIGA, TERA, PETA, EXA, ZETTA, YOTTA, KIBI, MEBI, GIBI,
 -			TEBI, PEBI, EXBI);
 -	public static final Set<UnitPrefix> REDUCING_PREFIXES = setOf(DECI, CENTI,
 -			MILLI, MICRO, NANO, PICO, FEMTO, ATTO, ZEPTO, YOCTO);
 -	
 -	// this method just calls Arrays.asList, which is itself safe.
 -	@SafeVarargs
 -	private static final <T> Set<T> setOf(T... args) {
 -		return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(args)));
 -	}
 -	
 -	// 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 | 
