From f21b9678ddc7bb6b59f0f076bb978a6993890548 Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Wed, 20 Feb 2019 20:57:06 -0500 Subject: Created a GUI for a planned dimension-based converter. The GUI does not do anything right now, but the idea is that the user will select a dimension (length, mass, energy) and then select units to convert from a searchable list. --- .../converterGUI/UnitConverterGUI.java | 124 +++++++++++++++++++-- 1 file changed, 115 insertions(+), 9 deletions(-) (limited to 'src/unitConverter') diff --git a/src/unitConverter/converterGUI/UnitConverterGUI.java b/src/unitConverter/converterGUI/UnitConverterGUI.java index b54e0da..cf78ea8 100755 --- a/src/unitConverter/converterGUI/UnitConverterGUI.java +++ b/src/unitConverter/converterGUI/UnitConverterGUI.java @@ -21,6 +21,7 @@ import java.awt.GridLayout; import java.io.File; import java.math.BigDecimal; import java.math.MathContext; +import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -28,7 +29,10 @@ import java.util.function.Predicate; import javax.swing.BorderFactory; import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFormattedTextField; import javax.swing.JFrame; +import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; @@ -476,15 +480,117 @@ final class UnitConverterGUI { final JTabbedPane masterPane = new JTabbedPane(); masterPanel.add(masterPane, BorderLayout.CENTER); - { // panel for unit conversion - final JPanel convertPanel = new JPanel(); - masterPane.addTab("Convert Units", convertPanel); + { // a panel for unit conversion using a selector + final JPanel convertUnitPanel = new JPanel(); + masterPane.addTab("Convert Units", convertUnitPanel); - convertPanel.setLayout(new GridLayout(5, 1)); + convertUnitPanel.setLayout(new BorderLayout()); + + { // panel for input part + final JPanel inputPanel = new JPanel(); + convertUnitPanel.add(inputPanel, BorderLayout.CENTER); + + inputPanel.setLayout(new GridLayout(1, 3)); + + { // panel for From things + final JPanel fromPanel = new JPanel(); + inputPanel.add(fromPanel); + + fromPanel.setLayout(new BorderLayout()); + + { // search box for from + final JTextField fromSearch = new JTextField("Search..."); + fromPanel.add(fromSearch, BorderLayout.PAGE_START); + } + + { // list for From units + final JList fromList = new JList<>(); + fromPanel.add(fromList, BorderLayout.CENTER); + } + } + + { // for dimension selector and arrow that represents conversion + final JPanel inBetweenPanel = new JPanel(); + inputPanel.add(inBetweenPanel); + + inBetweenPanel.setLayout(new BorderLayout()); + + { // dimension selector + final JComboBox dimensionSelector = new JComboBox<>( + new String[] {"Select dimension..."}); + 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 + final JPanel toPanel = new JPanel(); + inputPanel.add(toPanel); + + toPanel.setLayout(new BorderLayout()); + + { // search box for to + final JTextField toSearch = new JTextField("Search..."); + toPanel.add(toSearch, BorderLayout.PAGE_START); + } + + { // list for To units + final JList toList = new JList<>(); + toPanel.add(toList, BorderLayout.CENTER); + } + } + + } + + { // 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 + final JTextField valueInput = new JFormattedTextField( + new DecimalFormat("###############0.################")); + valueInputPanel.add(valueInput, BorderLayout.CENTER); + } + } + + { // button to convert + final JButton convertButton = new JButton("Convert"); + outputPanel.add(convertButton); + } + + { // output of conversion + final JLabel outputLabel = new JLabel(); + outputPanel.add(outputLabel); + } + } + } + + { // panel for unit conversion using expressions + final JPanel convertExpressionPanel = new JPanel(); + masterPane.addTab("Convert Unit Expressions", convertExpressionPanel); + + convertExpressionPanel.setLayout(new GridLayout(5, 1)); { // panel for units to convert from final JPanel fromPanel = new JPanel(); - convertPanel.add(fromPanel); + convertExpressionPanel.add(fromPanel); fromPanel.setBorder(BorderFactory.createTitledBorder("From")); fromPanel.setLayout(new GridLayout(1, 1)); @@ -496,7 +602,7 @@ final class UnitConverterGUI { { // panel for units to convert to final JPanel toPanel = new JPanel(); - convertPanel.add(toPanel); + convertExpressionPanel.add(toPanel); toPanel.setBorder(BorderFactory.createTitledBorder("To")); toPanel.setLayout(new GridLayout(1, 1)); @@ -508,14 +614,14 @@ final class UnitConverterGUI { { // button to convert final JButton convertButton = new JButton("Convert!"); - convertPanel.add(convertButton); + convertExpressionPanel.add(convertButton); convertButton.addActionListener(e -> this.presenter.convert()); } { // output of conversion final JPanel outputPanel = new JPanel(); - convertPanel.add(outputPanel); + convertExpressionPanel.add(outputPanel); outputPanel.setBorder(BorderFactory.createTitledBorder("Output")); outputPanel.setLayout(new GridLayout(1, 1)); @@ -528,7 +634,7 @@ final class UnitConverterGUI { { // panel for specifying precision final JPanel sigDigPanel = new JPanel(); - convertPanel.add(sigDigPanel); + convertExpressionPanel.add(sigDigPanel); sigDigPanel.setBorder(BorderFactory.createTitledBorder("Significant Digits")); -- cgit v1.2.3 From 5c4cd6d206e195d0c5efce747e8670f8e77cb59c Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Thu, 14 Mar 2019 18:07:12 -0400 Subject: Added unit dimensions to the unit database. --- CHANGELOG.org | 4 +++ src/unitConverter/UnitsDatabase.java | 69 ++++++++++++++++++++++++++++++++---- 2 files changed, 67 insertions(+), 6 deletions(-) (limited to 'src/unitConverter') diff --git a/CHANGELOG.org b/CHANGELOG.org index 1dbe268..280ceb3 100644 --- a/CHANGELOG.org +++ b/CHANGELOG.org @@ -1,6 +1,10 @@ * Changelog All notable changes in this project will be shown in this file. +** Unreleased +*** Added + - GUI for a selection-based unit converter + - The UnitDatabase now stores dimensions. ** v0.1.0 NOTE: At this stage, the API is subject to significant change. *** Added diff --git a/src/unitConverter/UnitsDatabase.java b/src/unitConverter/UnitsDatabase.java index 4816db1..9f5a6a2 100755 --- a/src/unitConverter/UnitsDatabase.java +++ b/src/unitConverter/UnitsDatabase.java @@ -37,7 +37,7 @@ import unitConverter.unit.Unit; import unitConverter.unit.UnitPrefix; /** - * A database of units. + * A database of units and prefixes, and their names. * * @author Adrien Hopkins * @since 2019-01-07 @@ -60,6 +60,13 @@ public final class UnitsDatabase { */ private final Map prefixes; + /** + * The dimensions in this system. + * + * @since 2019-03-14 + */ + private final Map dimensions; + /** * Creates the {@code UnitsDatabase}. * @@ -69,6 +76,7 @@ public final class UnitsDatabase { public UnitsDatabase() { this.units = new HashMap<>(); this.prefixes = new HashMap<>(); + this.dimensions = new HashMap<>(); } /** @@ -164,14 +172,30 @@ public final class UnitsDatabase { } /** - * Adds a unit prefix to the database using a custom name + * 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 + */ + public void addDimension(final String name, final UnitDimension dimension) { + this.dimensions.put(Objects.requireNonNull(name, "name must not be null."), + Objects.requireNonNull(dimension, "dimension must not be null.")); + } + + /** + * Adds a unit prefix to the database. * * @param name * prefix's name * @param prefix * prefix to add * @throws NullPointerException - * if name or unit is null + * if name or prefix is null * @since 2019-01-14 * @since v0.1.0 */ @@ -181,7 +205,7 @@ public final class UnitsDatabase { } /** - * Adds a unit to the database using a custom name + * Adds a unit to the database. * * @param name * unit's name @@ -193,7 +217,20 @@ public final class UnitsDatabase { * @since v0.1.0 */ public void addUnit(final String name, final Unit unit) { - this.units.put(name, Objects.requireNonNull(unit, "unit must not be null.")); + this.units.put(Objects.requireNonNull(name, "name must not be null."), + Objects.requireNonNull(unit, "unit must not be null.")); + } + + /** + * 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 + */ + public boolean containsDimensionName(final String name) { + return this.dimensions.containsKey(name); } /** @@ -210,7 +247,7 @@ public final class UnitsDatabase { } /** - * Tests if the database has a unit prefix with this name + * Tests if the database has a unit prefix with this name. * * @param name * name to test @@ -241,6 +278,26 @@ public final class UnitsDatabase { return this.units.containsKey(name); } + /** + * @return an immutable set of all of the dimension names in this database. + * @since 2019-03-14 + */ + public Set dimensionNameSet() { + return Collections.unmodifiableSet(this.dimensions.keySet()); + } + + /** + * Gets a unit dimension from the database using its name. + * + * @param name + * dimension's name + * @return dimension + * @since 2019-03-14 + */ + public UnitDimension getDimension(final String name) { + return this.dimensions.get(name); + } + /** * Gets a unit prefix from the database from its name * -- cgit v1.2.3 From b20cd4223b4ffc03e334627a82ca4eff9738912c Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Sat, 16 Mar 2019 14:55:07 -0400 Subject: Moved project to Maven. --- .classpath | 22 +- .gitlab-ci.yml | 14 + .project | 6 + .settings/org.eclipse.jdt.core.prefs | 2 + .settings/org.eclipse.m2e.core.prefs | 4 + pom.xml | 37 + src/org/unitConverter/UnitsDatabase.java | 641 +++++++++++++++++ .../converterGUI/DelegateListModel.java | 232 ++++++ .../converterGUI/FilterComparator.java | 93 +++ .../unitConverter/converterGUI/GridBagBuilder.java | 479 +++++++++++++ .../converterGUI/UnitConverterGUI.java | 777 +++++++++++++++++++++ .../unitConverter/converterGUI/package-info.java | 23 + src/org/unitConverter/dimension/BaseDimension.java | 40 ++ .../dimension/OtherBaseDimension.java | 55 ++ .../unitConverter/dimension/SIBaseDimension.java | 57 ++ .../dimension/StandardDimensions.java | 80 +++ src/org/unitConverter/dimension/UnitDimension.java | 241 +++++++ .../unitConverter/dimension/UnitDimensionTest.java | 77 ++ src/org/unitConverter/dimension/package-info.java | 23 + src/org/unitConverter/package-info.java | 23 + src/org/unitConverter/unit/AbstractUnit.java | 172 +++++ src/org/unitConverter/unit/BaseUnit.java | 180 +++++ src/org/unitConverter/unit/DefaultUnitPrefix.java | 67 ++ src/org/unitConverter/unit/LinearUnit.java | 184 +++++ src/org/unitConverter/unit/NonlinearUnits.java | 57 ++ src/org/unitConverter/unit/SI.java | 74 ++ src/org/unitConverter/unit/SIPrefix.java | 54 ++ src/org/unitConverter/unit/Unit.java | 110 +++ src/org/unitConverter/unit/UnitPrefix.java | 33 + src/org/unitConverter/unit/UnitSystem.java | 53 ++ src/org/unitConverter/unit/UnitTest.java | 46 ++ src/org/unitConverter/unit/package-info.java | 23 + src/unitConverter/UnitsDatabase.java | 641 ----------------- .../converterGUI/DelegateListModel.java | 232 ------ .../converterGUI/FilterComparator.java | 93 --- src/unitConverter/converterGUI/GridBagBuilder.java | 479 ------------- .../converterGUI/UnitConverterGUI.java | 777 --------------------- src/unitConverter/converterGUI/package-info.java | 23 - src/unitConverter/dimension/BaseDimension.java | 40 -- .../dimension/OtherBaseDimension.java | 55 -- src/unitConverter/dimension/SIBaseDimension.java | 57 -- .../dimension/StandardDimensions.java | 80 --- src/unitConverter/dimension/UnitDimension.java | 241 ------- src/unitConverter/dimension/UnitDimensionTest.java | 77 -- src/unitConverter/dimension/package-info.java | 23 - src/unitConverter/package-info.java | 23 - src/unitConverter/unit/AbstractUnit.java | 172 ----- src/unitConverter/unit/BaseUnit.java | 180 ----- src/unitConverter/unit/DefaultUnitPrefix.java | 67 -- src/unitConverter/unit/LinearUnit.java | 184 ----- src/unitConverter/unit/NonlinearUnits.java | 57 -- src/unitConverter/unit/SI.java | 74 -- src/unitConverter/unit/SIPrefix.java | 54 -- src/unitConverter/unit/Unit.java | 110 --- src/unitConverter/unit/UnitPrefix.java | 33 - src/unitConverter/unit/UnitSystem.java | 53 -- src/unitConverter/unit/UnitTest.java | 47 -- src/unitConverter/unit/package-info.java | 23 - 58 files changed, 3975 insertions(+), 3899 deletions(-) create mode 100644 .gitlab-ci.yml create mode 100644 .settings/org.eclipse.m2e.core.prefs create mode 100644 pom.xml create mode 100755 src/org/unitConverter/UnitsDatabase.java create mode 100755 src/org/unitConverter/converterGUI/DelegateListModel.java create mode 100755 src/org/unitConverter/converterGUI/FilterComparator.java create mode 100755 src/org/unitConverter/converterGUI/GridBagBuilder.java create mode 100755 src/org/unitConverter/converterGUI/UnitConverterGUI.java create mode 100644 src/org/unitConverter/converterGUI/package-info.java create mode 100755 src/org/unitConverter/dimension/BaseDimension.java create mode 100755 src/org/unitConverter/dimension/OtherBaseDimension.java create mode 100755 src/org/unitConverter/dimension/SIBaseDimension.java create mode 100755 src/org/unitConverter/dimension/StandardDimensions.java create mode 100755 src/org/unitConverter/dimension/UnitDimension.java create mode 100755 src/org/unitConverter/dimension/UnitDimensionTest.java create mode 100755 src/org/unitConverter/dimension/package-info.java create mode 100644 src/org/unitConverter/package-info.java create mode 100644 src/org/unitConverter/unit/AbstractUnit.java create mode 100755 src/org/unitConverter/unit/BaseUnit.java create mode 100755 src/org/unitConverter/unit/DefaultUnitPrefix.java create mode 100644 src/org/unitConverter/unit/LinearUnit.java create mode 100755 src/org/unitConverter/unit/NonlinearUnits.java create mode 100644 src/org/unitConverter/unit/SI.java create mode 100755 src/org/unitConverter/unit/SIPrefix.java create mode 100755 src/org/unitConverter/unit/Unit.java create mode 100755 src/org/unitConverter/unit/UnitPrefix.java create mode 100755 src/org/unitConverter/unit/UnitSystem.java create mode 100755 src/org/unitConverter/unit/UnitTest.java create mode 100644 src/org/unitConverter/unit/package-info.java delete mode 100755 src/unitConverter/UnitsDatabase.java delete mode 100755 src/unitConverter/converterGUI/DelegateListModel.java delete mode 100755 src/unitConverter/converterGUI/FilterComparator.java delete mode 100755 src/unitConverter/converterGUI/GridBagBuilder.java delete mode 100755 src/unitConverter/converterGUI/UnitConverterGUI.java delete mode 100644 src/unitConverter/converterGUI/package-info.java delete mode 100755 src/unitConverter/dimension/BaseDimension.java delete mode 100755 src/unitConverter/dimension/OtherBaseDimension.java delete mode 100755 src/unitConverter/dimension/SIBaseDimension.java delete mode 100755 src/unitConverter/dimension/StandardDimensions.java delete mode 100755 src/unitConverter/dimension/UnitDimension.java delete mode 100755 src/unitConverter/dimension/UnitDimensionTest.java delete mode 100755 src/unitConverter/dimension/package-info.java delete mode 100644 src/unitConverter/package-info.java delete mode 100644 src/unitConverter/unit/AbstractUnit.java delete mode 100755 src/unitConverter/unit/BaseUnit.java delete mode 100755 src/unitConverter/unit/DefaultUnitPrefix.java delete mode 100644 src/unitConverter/unit/LinearUnit.java delete mode 100755 src/unitConverter/unit/NonlinearUnits.java delete mode 100644 src/unitConverter/unit/SI.java delete mode 100755 src/unitConverter/unit/SIPrefix.java delete mode 100755 src/unitConverter/unit/Unit.java delete mode 100755 src/unitConverter/unit/UnitPrefix.java delete mode 100755 src/unitConverter/unit/UnitSystem.java delete mode 100755 src/unitConverter/unit/UnitTest.java delete mode 100644 src/unitConverter/unit/package-info.java (limited to 'src/unitConverter') diff --git a/.classpath b/.classpath index 145a671..72d7394 100644 --- a/.classpath +++ b/.classpath @@ -1,7 +1,21 @@ - - - - + + + + + + + + + + + + + + + + + + diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..7c2efb0 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,14 @@ +# attempt at ci + +image: maven:3-jdk-8 + + +build: + stage: build + script: "mvn clean -B" + script: "mvn compile -B" + + +test: + stage: test + script: "mvn verify" diff --git a/.project b/.project index 656186b..56f686e 100644 --- a/.project +++ b/.project @@ -10,8 +10,14 @@ + + org.eclipse.m2e.core.maven2Builder + + + + org.eclipse.m2e.core.maven2Nature org.eclipse.jdt.core.javanature diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index 3a21537..8445b6b 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -8,4 +8,6 @@ org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.release=disabled org.eclipse.jdt.core.compiler.source=1.8 diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3aede3b --- /dev/null +++ b/pom.xml @@ -0,0 +1,37 @@ + + 4.0.0 + org.unitConverter + unitConverter + 0.1.0 + Unit Converter + A Java unit converter inspired by GNU Units + + src + + + maven-compiler-plugin + 3.8.0 + + 1.8 + 1.8 + + + + org.codehaus.mojo + exec-maven-plugin + + org.unitConverter.converterGUI.UnitConverterGUI + + + + + + + junit + junit + 4.11 + + + \ No newline at end of file diff --git a/src/org/unitConverter/UnitsDatabase.java b/src/org/unitConverter/UnitsDatabase.java new file mode 100755 index 0000000..4d41735 --- /dev/null +++ b/src/org/unitConverter/UnitsDatabase.java @@ -0,0 +1,641 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import org.unitConverter.dimension.UnitDimension; +import org.unitConverter.unit.AbstractUnit; +import org.unitConverter.unit.BaseUnit; +import org.unitConverter.unit.DefaultUnitPrefix; +import org.unitConverter.unit.LinearUnit; +import org.unitConverter.unit.SI; +import org.unitConverter.unit.Unit; +import org.unitConverter.unit.UnitPrefix; + +/** + * A database of units and prefixes, and their names. + * + * @author Adrien Hopkins + * @since 2019-01-07 + * @since v0.1.0 + */ +public final class UnitsDatabase { + /** + * The units in this system. + * + * @since 2019-01-07 + * @since v0.1.0 + */ + private final Map units; + + /** + * The unit prefixes in this system. + * + * @since 2019-01-14 + * @since v0.1.0 + */ + private final Map prefixes; + + /** + * The dimensions in this system. + * + * @since 2019-03-14 + */ + private final Map dimensions; + + /** + * Creates the {@code UnitsDatabase}. + * + * @since 2019-01-10 + * @since v0.1.0 + */ + public UnitsDatabase() { + this.units = new HashMap<>(); + this.prefixes = new HashMap<>(); + this.dimensions = new HashMap<>(); + } + + /** + * Adds all units from a file, using data from the database to parse them. + *

+ * Each line in the file should consist of a name and an expression (parsed by getUnitFromExpression) separated by + * any number of tab characters. + *

+ *

+ * Allowed exceptions: + *

    + *
  • Any line that begins with the '#' character is considered a comment and ignored.
  • + *
  • Blank lines are also ignored
  • + *
  • 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.
  • + *
+ * + * @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 addAllFromFile(final File file) { + Objects.requireNonNull(file, "file must not be null."); + try (FileReader fileReader = new FileReader(file); BufferedReader reader = new BufferedReader(fileReader)) { + // while the reader has lines to read, read a line, then parse it, then add it + long lineCounter = 0; + while (reader.ready()) { + final String line = reader.readLine(); + lineCounter++; + + // ignore lines that start with a # sign - they're comments + if (line.startsWith("#") || line.isEmpty()) { + continue; + } + + // divide line into name and expression + final String[] parts = line.split("\t"); + if (parts.length < 2) + throw new IllegalArgumentException(String.format( + "Lines must consist of a unit name and its definition, separated by tab(s) (line %d).", + lineCounter)); + final String name = parts[0]; + final String expression = parts[parts.length - 1]; + + if (name.endsWith(" ")) { + System.err.printf("Warning - line %d's unit name ends in a space", lineCounter); + } + + // 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; + } + AbstractUnit.incrementUnitCounter(); + if (unit instanceof BaseUnit) { + AbstractUnit.incrementBaseUnitCounter(); + } + this.addUnit(name, unit); + } + } + } + } 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 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 + */ + public void addDimension(final String name, final UnitDimension dimension) { + this.dimensions.put(Objects.requireNonNull(name, "name must not be null."), + Objects.requireNonNull(dimension, "dimension must not be null.")); + } + + /** + * 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.units.put(Objects.requireNonNull(name, "name must not be null."), + Objects.requireNonNull(unit, "unit must not be null.")); + } + + /** + * 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 + */ + public boolean containsDimensionName(final String name) { + return this.dimensions.containsKey(name); + } + + /** + * Tests if the database has a unit with this name, ignoring prefixes + * + * @param name + * name to test + * @return if database contains name + * @since 2019-01-13 + * @since v0.1.0 + */ + public boolean containsPrefixlessUnitName(final String name) { + return this.units.containsKey(name); + } + + /** + * Tests if the database has a unit prefix with this name. + * + * @param name + * 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) { + // check for prefixes + for (final String prefixName : this.prefixNameSet()) { + if (name.startsWith(prefixName)) + if (this.containsUnitName(name.substring(prefixName.length()))) + return true; + } + return this.units.containsKey(name); + } + + /** + * @return an immutable set of all of the dimension names in this database. + * @since 2019-03-14 + */ + public Set dimensionNameSet() { + return Collections.unmodifiableSet(this.dimensions.keySet()); + } + + /** + * Gets a unit dimension from the database using its name. + * + * @param name + * dimension's name + * @return dimension + * @since 2019-03-14 + */ + public UnitDimension getDimension(final String name) { + return this.dimensions.get(name); + } + + /** + * 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) { + return this.prefixes.get(name); + } + + /** + * Gets a unit prefix from a prefix expression + *

+ * Currently, prefix expressions are much simpler than unit expressions: They are either a number or the name of + * another prefix + *

+ * + * @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."); + + try { + return new DefaultUnitPrefix(Double.parseDouble(expression)); + } catch (final NumberFormatException e) { + if (expression.contains("^")) { + final String[] baseAndExponent = expression.split("\\^"); + + final double base; + try { + base = Double.parseDouble(baseAndExponent[0]); + } catch (final NumberFormatException e2) { + throw new IllegalArgumentException("Base of exponientation must be a number."); + } + + final int exponent; + try { + exponent = Integer.parseInt(baseAndExponent[baseAndExponent.length - 1]); + } catch (final NumberFormatException e2) { + throw new IllegalArgumentException("Exponent must be an integer."); + } + + return new DefaultUnitPrefix(Math.pow(base, exponent)); + } else { + if (!this.containsPrefixName(expression)) + throw new IllegalArgumentException("Unrecognized prefix name \"" + expression + "\"."); + return this.getPrefix(expression); + } + } + } + + /** + * Gets a unit from the database from its name, ignoring prefixes. + * + * @param name + * unit's name + * @return unit + * @since 2019-01-10 + * @since v0.1.0 + */ + public Unit getPrefixlessUnit(final String name) { + return this.units.get(name); + } + + /** + * Gets a unit from the database from its name, looking for prefixes. + * + * @param name + * unit's name + * @return unit + * @since 2019-01-10 + * @since v0.1.0 + */ + public Unit getUnit(final String name) { + if (name.contains("^")) { + final String[] baseAndExponent = name.split("\\^"); + + LinearUnit base; + try { + base = SI.SI.getBaseUnit(UnitDimension.EMPTY).times(Double.parseDouble(baseAndExponent[0])); + } catch (final NumberFormatException e2) { + final Unit unit = this.getUnit(baseAndExponent[0]); + if (unit instanceof LinearUnit) { + base = (LinearUnit) unit; + } else if (unit instanceof BaseUnit) { + base = ((BaseUnit) unit).asLinearUnit(); + } else + throw new IllegalArgumentException("Base of exponientation must be a linear or base unit."); + } + + final int exponent; + try { + exponent = Integer.parseInt(baseAndExponent[baseAndExponent.length - 1]); + } catch (final NumberFormatException e2) { + throw new IllegalArgumentException("Exponent must be an integer."); + } + + final LinearUnit exponentiated = base.toExponent(exponent); + if (exponentiated.getConversionFactor() == 1) + return exponentiated.getSystem().getBaseUnit(exponentiated.getDimension()); + else + return exponentiated; + } else { + for (final String prefixName : this.prefixNameSet()) { + // check for a prefix + if (name.startsWith(prefixName)) { + // prefix found! Make sure what comes after it is actually a unit! + final String prefixless = name.substring(prefixName.length()); + if (this.containsUnitName(prefixless)) { + // yep, it's a proper prefix! Get the unit! + final Unit unit = this.getUnit(prefixless); + final UnitPrefix prefix = this.getPrefix(prefixName); + + // Prefixes only work with linear and base units, so make sure it's one of those + if (unit instanceof LinearUnit) { + final LinearUnit linearUnit = (LinearUnit) unit; + return linearUnit.times(prefix.getMultiplier()); + } else if (unit instanceof BaseUnit) { + final BaseUnit baseUnit = (BaseUnit) unit; + return baseUnit.times(prefix.getMultiplier()); + } + } + } + } + return this.units.get(name); + } + } + + /** + * Uses the database's unit data to parse an expression into a unit + *

+ * The expression is a series of any of the following: + *

    + *
  • The name of a unit, which multiplies or divides the result based on preceding operators
  • + *
  • The operators '*' and '/', which multiply and divide (note that just putting two units or values next to each + * other is equivalent to multiplication)
  • + *
  • The operator '^' which exponentiates. Exponents must be integers.
  • + *
  • A number which is multiplied or divided
  • + *
+ * This method only works with linear units. + * + * @param expression + * expression to parse + * @throws IllegalArgumentException + * if the expression cannot be parsed + * @throws NullPointerException + * if any argument is null + * @since 2019-01-07 + * @since v0.1.0 + */ + public Unit getUnitFromExpression(final String expression) { + Objects.requireNonNull(expression, "expression must not be null."); + + // parse the expression + // start with an "empty" unit then apply operations on it + LinearUnit unit = SI.SI.getBaseUnit(UnitDimension.EMPTY).asLinearUnit(); + boolean dividing = false; + + // if I'm just creating an alias, just create one instead of going through the parsing process + if (!expression.contains(" ") && !expression.contains("*") && !expression.contains("/") + && !expression.contains("(") && !expression.contains(")") && !expression.contains("^")) { + try { + return SI.SI.getBaseUnit(UnitDimension.EMPTY).times(Double.parseDouble(expression)); + } catch (final NumberFormatException e) { + if (!this.containsUnitName(expression)) + throw new IllegalArgumentException("Unrecognized unit name \"" + expression + "\"."); + return this.getUnit(expression); + } + } + + // \\* means "asterisk", * is reserved + for (final String part : expression.replaceAll("\\*", " \\* ").replaceAll("/", " / ").replaceAll(" \\^", "\\^") + .replaceAll("\\^ ", "\\^").split(" ")) { + if ("".equals(part)) { + continue; + } + // "unit1 unit2" is the same as "unit1 * unit2", so multiplication signs do nothing + if ("*".equals(part)) { + continue; + } + // When I reach a division sign, don't parse a unit, instead tell myself I'm going to divide the next + // thing + if ("/".equals(part) || "per".equals(part)) { + dividing = true; + continue; + } + + try { + final double partAsNumber = Double.parseDouble(part); // if this works, it's a number + // this code should not throw any exceptions, so I'm going to throw AssertionErrors if it does + try { + if (dividing) { + unit = unit.dividedBy(partAsNumber); + dividing = false; + } else { + unit = unit.times(partAsNumber); + } + } catch (final Exception e) { + throw new AssertionError(e); + } + } catch (final NumberFormatException e) { + // it's a unit, try that + + if (part.contains("(") && part.endsWith(")")) { + // the unitsfile is looking for a nonlinear unit + final String[] unitAndValue = part.split("\\("); + + // this will work because I've checked that it contains a ( + final String unitName = unitAndValue[0]; + final String valueStringWithBracket = unitAndValue[unitAndValue.length - 1]; + final String valueString = valueStringWithBracket.substring(0, valueStringWithBracket.length() - 1); + final double value; + + // try to get the value - else throw an error + try { + value = Double.parseDouble(valueString); + } catch (final NumberFormatException e2) { + throw new IllegalArgumentException("Unparseable value " + valueString); + } + + // get this unit in a linear form + if (!this.containsPrefixlessUnitName(unitName)) + throw new IllegalArgumentException("Unrecognized unit name \"" + part + "\"."); + final Unit partUnit = this.getPrefixlessUnit(unitName); + final LinearUnit multiplier = partUnit.getBase().times(partUnit.convertToBase(value)); + + // finally, add it to the expression + if (dividing) { + unit = unit.dividedBy(multiplier); + dividing = false; + } else { + unit = unit.times(multiplier); + } + } else { + // check for exponientation + if (part.contains("^")) { + final String[] valueAndExponent = part.split("\\^"); + // this will always work because of the contains check + final String valueString = valueAndExponent[0]; + final String exponentString = valueAndExponent[valueAndExponent.length - 1]; + + LinearUnit value; + + // first, try to get the value + try { + final double valueAsNumber = Double.parseDouble(valueString); + + value = SI.SI.getBaseUnit(UnitDimension.EMPTY).times(valueAsNumber); + } catch (final NumberFormatException e2) { + + // look for a unit + if (!this.containsUnitName(valueString)) + throw new IllegalArgumentException("Unrecognized unit name \"" + valueString + "\"."); + final Unit valueUnit = this.getUnit(valueString); + + // try to turn the value into a linear unit + if (valueUnit instanceof LinearUnit) { + value = (LinearUnit) valueUnit; + } else if (valueUnit instanceof BaseUnit) { + value = ((BaseUnit) valueUnit).asLinearUnit(); + } else + throw new IllegalArgumentException("Only linear and base units can be exponientated."); + } + + // now, try to get the exponent + final int exponent; + try { + exponent = Integer.parseInt(exponentString); + } catch (final NumberFormatException e2) { + throw new IllegalArgumentException("Exponents must be integers."); + } + + final LinearUnit exponientated = value.toExponent(exponent); + + if (dividing) { + unit = unit.dividedBy(exponientated); + dividing = false; + } else { + unit = unit.times(exponientated); + } + } else { + // no exponent - look for a unit + // the unitsfile is looking for a linear unit + if (!this.containsUnitName(part)) + throw new IllegalArgumentException("Unrecognized unit name \"" + part + "\"."); + Unit other = this.getUnit(part); + if (other instanceof BaseUnit) { + other = ((BaseUnit) other).asLinearUnit(); + } + if (other instanceof LinearUnit) { + if (dividing) { + unit = unit.dividedBy((LinearUnit) other); + dividing = false; + } else { + unit = unit.times((LinearUnit) other); + } + } else + throw new IllegalArgumentException( + "Only linear or base units can be multiplied and divided. If you want to use a non-linear unit, try the format UNITNAME(VALUE)."); + } + } + } + } + + // replace conversion-factor-1 linear units with base units + // this improves the autogenerated names, allowing them to use derived SI names + if (unit != null && unit.getConversionFactor() == 1) + return unit.getSystem().getBaseUnit(unit.getDimension()); + else + return unit; + } + + /** + * @return an immutable set of all of the unit names in this database, ignoring prefixes + * @since 2019-01-14 + * @since v0.1.0 + */ + public Set prefixlessUnitNameSet() { + return Collections.unmodifiableSet(this.units.keySet()); + } + + /** + * @return an immutable set of all of the prefix names in this database + * @since 2019-01-14 + * @since v0.1.0 + */ + public Set prefixNameSet() { + return Collections.unmodifiableSet(this.prefixes.keySet()); + } +} diff --git a/src/org/unitConverter/converterGUI/DelegateListModel.java b/src/org/unitConverter/converterGUI/DelegateListModel.java new file mode 100755 index 0000000..e375126 --- /dev/null +++ b/src/org/unitConverter/converterGUI/DelegateListModel.java @@ -0,0 +1,232 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.converterGUI; + +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. + *

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

+ * + * @author Adrien Hopkins + * @since 2019-01-14 + * @since v0.1.0 + */ +final class DelegateListModel extends AbstractListModel implements List { + /** + * @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 delegate; + + /** + * Creates the {@code DelegateListModel}. + * + * @param delegate + * list to delegate + * @since 2019-01-14 + * @since v0.1.0 + */ + public DelegateListModel(final List 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 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 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 iterator() { + return this.delegate.iterator(); + } + + @Override + public int lastIndexOf(final Object elem) { + return this.delegate.lastIndexOf(elem); + } + + @Override + public ListIterator listIterator() { + return this.delegate.listIterator(); + } + + @Override + public ListIterator 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 subList(final int fromIndex, final int toIndex) { + return this.delegate.subList(fromIndex, toIndex); + } + + @Override + public Object[] toArray() { + return this.delegate.toArray(); + } + + @Override + public 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 new file mode 100755 index 0000000..bebc2df --- /dev/null +++ b/src/org/unitConverter/converterGUI/FilterComparator.java @@ -0,0 +1,93 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.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 + */ +public final class FilterComparator implements Comparator { + /** + * 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 comparator; + + /** + * 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. + * @since 2019-01-15 + * @since v0.1.0 + * @throws NullPointerException + * if filter is null + */ + public FilterComparator(final String filter, final Comparator comparator) { + this.filter = Objects.requireNonNull(filter, "filter must not be null."); + this.comparator = comparator; + } + + @Override + public int compare(final String arg0, final String arg1) { + // elements that start with the filter always go first + if (arg0.startsWith(this.filter) && !arg1.startsWith(this.filter)) + return -1; + else if (!arg0.startsWith(this.filter) && arg1.startsWith(this.filter)) + return 1; + + // elements that contain the filter but don't start with them go next + if (arg0.contains(this.filter) && !arg1.contains(this.filter)) + return -1; + else if (!arg0.contains(this.filter) && !arg1.contains(this.filter)) + return 1; + + // other elements go last + if (this.comparator == null) + return arg0.compareTo(arg1); + else + return this.comparator.compare(arg0, arg1); + } +} diff --git a/src/org/unitConverter/converterGUI/GridBagBuilder.java b/src/org/unitConverter/converterGUI/GridBagBuilder.java new file mode 100755 index 0000000..f1229b2 --- /dev/null +++ b/src/org/unitConverter/converterGUI/GridBagBuilder.java @@ -0,0 +1,479 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.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. + *

+ * Specifies the cell containing the leading edge of the component's display area, where the first cell in a row has + * gridx=0. 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 + * RELATIVE specifies that the component be placed immediately following the component that was added + * to the container just before this component was added. + *

+ * The default value is RELATIVE. gridx 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. + *

+ * Specifies the cell at the top of the component's display area, where the topmost cell has gridy=0. + * The value RELATIVE specifies that the component be placed just below the component that was added to + * the container just before this component was added. + *

+ * The default value is RELATIVE. gridy 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. + *

+ * Specifies the number of cells in a row for the component's display area. + *

+ * Use REMAINDER to specify that the component's display area will be from gridx to the + * last cell in the row. Use RELATIVE to specify that the component's display area will be from + * gridx to the next to the last one in its row. + *

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

+ * Specifies the number of cells in a column for the component's display area. + *

+ * Use REMAINDER to specify that the component's display area will be from gridy to the + * last cell in the column. Use RELATIVE to specify that the component's display area will be from + * gridy to the next to the last one in its column. + *

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

+ * Specifies how to distribute extra horizontal space. + *

+ * The grid bag layout manager calculates the weight of a column to be the maximum weightx 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. + *

+ * If all the weights are zero, all the extra space appears between the grids of the cell and the left and right + * edges. + *

+ * The default value of this field is 0. weightx 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. + *

+ * Specifies how to distribute extra vertical space. + *

+ * The grid bag layout manager calculates the weight of a row to be the maximum weighty 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. + *

+ * If all the weights are zero, all the extra space appears between the grids of the cell and the top and bottom + * edges. + *

+ * The default value of this field is 0. weighty 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. + *

+ * This field is used when the component is smaller than its display area. It determines where, within the display + * area, to place the component. + *

+ * 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: + * CENTER, NORTH, NORTHEAST, EAST, SOUTHEAST, + * SOUTH, SOUTHWEST, WEST, and NORTHWEST. The orientation + * relative values are: PAGE_START, PAGE_END, LINE_START, + * LINE_END, FIRST_LINE_START, FIRST_LINE_END, LAST_LINE_START + * and LAST_LINE_END. The baseline relative values are: BASELINE, + * BASELINE_LEADING, BASELINE_TRAILING, ABOVE_BASELINE, + * ABOVE_BASELINE_LEADING, ABOVE_BASELINE_TRAILING, BELOW_BASELINE, + * BELOW_BASELINE_LEADING, and BELOW_BASELINE_TRAILING. The default value is + * CENTER. + * + * @serial + * @see #clone() + * @see java.awt.ComponentOrientation + */ + private int anchor; + + /** + * The built {@code GridBagConstraints}'s {@code fill} property. + *

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

+ * The following values are valid for fill: + * + *

    + *
  • NONE: Do not resize the component. + *
  • HORIZONTAL: Make the component wide enough to fill its display area horizontally, but do not + * change its height. + *
  • VERTICAL: Make the component tall enough to fill its display area vertically, but do not change + * its width. + *
  • BOTH: Make the component fill its display area entirely. + *
+ *

+ * The default value is NONE. + * + * @serial + * @see #clone() + */ + private int fill; + + /** + * The built {@code GridBagConstraints}'s {@code insets} property. + *

+ * This field specifies the external padding of the component, the minimum amount of space between the component and + * the edges of its display area. + *

+ * The default value is new Insets(0, 0, 0, 0). + * + * @serial + * @see #clone() + */ + private Insets insets; + + /** + * The built {@code GridBagConstraints}'s {@code ipadx} property. + *

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

+ * The default value is 0. + * + * @serial + * @see #clone() + * @see java.awt.GridBagConstraints#ipady + */ + private int ipadx; + + /** + * The built {@code GridBagConstraints}'s {@code ipady} property. + *

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

+ * 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/UnitConverterGUI.java b/src/org/unitConverter/converterGUI/UnitConverterGUI.java new file mode 100755 index 0000000..a70e971 --- /dev/null +++ b/src/org/unitConverter/converterGUI/UnitConverterGUI.java @@ -0,0 +1,777 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.converterGUI; + +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.io.File; +import java.math.BigDecimal; +import java.math.MathContext; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.function.Predicate; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFormattedTextField; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSlider; +import javax.swing.JTabbedPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.ListModel; +import javax.swing.ListSelectionModel; + +import org.unitConverter.UnitsDatabase; +import org.unitConverter.dimension.StandardDimensions; +import org.unitConverter.dimension.UnitDimension; +import org.unitConverter.unit.AbstractUnit; +import org.unitConverter.unit.NonlinearUnits; +import org.unitConverter.unit.SI; +import org.unitConverter.unit.Unit; +import org.unitConverter.unit.UnitPrefix; + +/** + * @author Adrien Hopkins + * @since 2018-12-27 + * @since v0.1.0 + */ +final class UnitConverterGUI { + private static class Presenter { + /** The presenter's associated view. */ + private final View view; + + /** The units known by the program. */ + private final UnitsDatabase units; + + /** The names of all of the units */ + private final List unitNames; + + /** The names of all of the units, but filtered */ + private final DelegateListModel unitNamesFiltered; + + /** The names of all of the prefixes */ + private final List prefixNames; + + /** The names of all of the prefixes */ + private final DelegateListModel prefixNamesFiltered; + + private final Comparator prefixNameComparator; + + private int significantFigures = 6; + + /** + * 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.units = new UnitsDatabase(); + this.units.addUnit("metre", SI.METRE); + this.units.addUnit("kilogram", SI.KILOGRAM); + this.units.addUnit("gram", SI.KILOGRAM.dividedBy(1000)); + this.units.addUnit("second", SI.SECOND); + this.units.addUnit("ampere", SI.AMPERE); + this.units.addUnit("kelvin", SI.KELVIN); + this.units.addUnit("mole", SI.MOLE); + this.units.addUnit("candela", SI.CANDELA); + this.units.addUnit("bit", SI.SI.getBaseUnit(StandardDimensions.INFORMATION)); + this.units.addUnit("unit", SI.SI.getBaseUnit(UnitDimension.EMPTY)); + // nonlinear units - must be loaded manually + this.units.addUnit("tempCelsius", NonlinearUnits.CELSIUS); + this.units.addUnit("tempFahrenheit", NonlinearUnits.FAHRENHEIT); + + this.units.addAllFromFile(new File("unitsfile.txt")); + + // 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.units.containsPrefixName(o1)) + return -1; + else if (!Presenter.this.units.containsPrefixName(o2)) + return 1; + + final UnitPrefix p1 = Presenter.this.units.getPrefix(o1); + final UnitPrefix p2 = Presenter.this.units.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.units.prefixlessUnitNameSet()); + this.unitNames.sort(null); // sorts it using Comparable + + this.unitNamesFiltered = new DelegateListModel<>(new ArrayList<>(this.units.prefixlessUnitNameSet())); + this.unitNamesFiltered.sort(null); // sorts it using Comparable + + this.prefixNames = new ArrayList<>(this.units.prefixNameSet()); + this.prefixNames.sort(this.prefixNameComparator); // sorts it using my comparator + + this.prefixNamesFiltered = new DelegateListModel<>(new ArrayList<>(this.units.prefixNameSet())); + this.prefixNamesFiltered.sort(this.prefixNameComparator); // sorts it using my comparator + + System.out.printf("Successfully loaded %d units (%d base units)", AbstractUnit.getUnitCount(), + AbstractUnit.getBaseUnitCount()); + } + + /** + * Runs whenever the convert button is pressed. + * + *

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

+ * + * @since 2019-01-26 + * @since v0.1.0 + */ + public final void convert() { + final String fromUnitString = this.view.getFromText(); + final String toUnitString = this.view.getToText(); + + // try to parse from + final Unit from; + try { + from = this.units.getUnitFromExpression(fromUnitString); + } catch (final IllegalArgumentException e) { + this.view.showErrorDialog("Parse Error", "Could not recognize text in From entry: " + e.getMessage()); + return; + } + + final double value; + // try to parse to + final Unit to; + try { + to = this.units.getUnitFromExpression(toUnitString); + } catch (final IllegalArgumentException e) { + this.view.showErrorDialog("Parse Error", "Could not recognize text in To entry: " + e.getMessage()); + return; + } + + // if I can't convert, leave + if (!from.canConvertTo(to)) { + this.view.showErrorDialog("Conversion Error", + String.format("Cannot convert between %s and %s", fromUnitString, toUnitString)); + return; + } + + value = to.convertFromBase(from.convertToBase(1)); + + // round value + final BigDecimal bigValue = new BigDecimal(value).round(new MathContext(this.significantFigures)); + String output = bigValue.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); + } + } + + this.view.setOutputText(String.format("%s = %s %s", fromUnitString, output, toUnitString)); + } + + /** + * Filters the filtered model for units + * + * @param filter + * filter to use + * @since 2019-01-15 + * @since v0.1.0 + */ + private final void filterFilteredPrefixModel(final Predicate filter) { + this.prefixNamesFiltered.clear(); + for (final String prefixName : this.prefixNames) { + if (filter.test(prefixName)) { + this.prefixNamesFiltered.add(prefixName); + } + } + } + + /** + * Filters the filtered model for units + * + * @param filter + * filter to use + * @since 2019-01-15 + * @since v0.1.0 + */ + private final void filterFilteredUnitModel(final Predicate filter) { + this.unitNamesFiltered.clear(); + for (final String unitName : this.unitNames) { + if (filter.test(unitName)) { + this.unitNamesFiltered.add(unitName); + } + } + } + + /** + * @return a list model of all of the unit keys + * @since 2019-01-14 + * @since v0.1.0 + */ + public final ListModel keyListModel() { + return this.unitNamesFiltered; + } + + /** + * Runs whenever the prefix filter is changed. + *

+ * Filters the prefix list then sorts it using a {@code FilterComparator}. + *

+ * + * @since 2019-01-15 + * @since v0.1.0 + */ + public final void prefixFilterUpdated() { + final String filter = this.view.getPrefixFilterText(); + if (filter.equals("")) { + this.filterFilteredPrefixModel(t -> true); + } else { + this.filterFilteredPrefixModel(t -> t.contains(filter)); + } + this.prefixNamesFiltered.sort(new FilterComparator(filter)); + } + + /** + * @return a list model of all of the prefix names + * @since 2019-01-15 + */ + public final ListModel prefixNameListModel() { + return this.prefixNamesFiltered; + } + + /** + * Runs whenever a prefix is selected in the viewer. + *

+ * Shows its information in the text box to the right. + *

+ * + * @since 2019-01-15 + * @since v0.1.0 + */ + public final void prefixSelected() { + final int index = this.view.getPrefixListSelection(); + if (index == -1) + return; + else { + final String prefixName = this.prefixNamesFiltered.get(index); + final UnitPrefix prefix = this.units.getPrefix(prefixName); + + this.view.setPrefixTextBoxText(String.format("%s%nMultiplier: %s", prefixName, prefix.getMultiplier())); + } + } + + /** + * @param significantFigures + * new value of significantFigures + * @since 2019-01-15 + */ + public final void setSignificantFigures(final int significantFigures) { + this.significantFigures = significantFigures; + } + + /** + * Runs whenever the unit filter is changed. + *

+ * Filters the unit list then sorts it using a {@code FilterComparator}. + *

+ * + * @since 2019-01-15 + * @since v0.1.0 + */ + public final void unitFilterUpdated() { + final String filter = this.view.getUnitFilterText(); + if (filter.equals("")) { + this.filterFilteredUnitModel(t -> true); + } else { + this.filterFilteredUnitModel(t -> t.contains(filter)); + } + this.unitNamesFiltered.sort(new FilterComparator(filter)); + } + + /** + * Runs whenever a unit is selected in the viewer. + *

+ * Shows its information in the text box to the right. + *

+ * + * @since 2019-01-15 + * @since v0.1.0 + */ + public void unitNameSelected() { + final int index = this.view.getUnitListSelection(); + if (index == -1) + return; + else { + final String unitName = this.unitNamesFiltered.get(index); + final Unit unit = this.units.getUnit(unitName); + + this.view.setUnitTextBoxText(unit.toString()); + } + } + } + + private static class View { + /** The view's frame. */ + private final JFrame frame; + /** The view's associated presenter. */ + private final Presenter presenter; + + /** The list of unit names in the unit viewer */ + private final JList unitNameList; + /** The list of prefix names in the prefix viewer */ + private final JList prefixNameList; + /** The unit search box in the unit viewer */ + private final JTextField unitFilterEntry; + /** The text box for unit data in the unit viewer */ + private final JTextArea unitTextBox; + /** The prefix search box in the prefix viewer */ + private final JTextField prefixFilterEntry; + /** The text box for prefix data in the prefix viewer */ + private final JTextArea prefixTextBox; + /** 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; + + /** + * 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(JFrame.EXIT_ON_CLOSE); + + // create the components + this.unitNameList = new JList<>(this.presenter.keyListModel()); + this.prefixNameList = new JList<>(this.presenter.prefixNameListModel()); + this.unitFilterEntry = new JTextField(); + this.unitTextBox = new JTextArea(); + this.prefixFilterEntry = new JTextField(); + this.prefixTextBox = new JTextArea(); + this.fromEntry = new JTextField(); + this.toEntry = new JTextField(); + this.output = new JTextArea(2, 32); + + // create more components + this.initComponents(); + + this.frame.pack(); + } + + /** + * @return text in "From" box in converter panel + * @since 2019-01-15 + * @since v0.1.0 + */ + public String getFromText() { + return this.fromEntry.getText(); + } + + /** + * @return text in prefix filter + * @since 2019-01-15 + * @since v0.1.0 + */ + public String getPrefixFilterText() { + return this.prefixFilterEntry.getText(); + } + + /** + * @return index of selected prefix + * @since 2019-01-15 + * @since v0.1.0 + */ + public int getPrefixListSelection() { + return this.prefixNameList.getSelectedIndex(); + } + + /** + * @return text in "To" box in converter panel + * @since 2019-01-26 + * @since v0.1.0 + */ + public String getToText() { + return this.toEntry.getText(); + } + + /** + * @return text in unit filter + * @see javax.swing.text.JTextComponent#getText() + */ + public String getUnitFilterText() { + return this.unitFilterEntry.getText(); + } + + /** + * @return index of selected unit + * @since 2019-01-15 + * @since v0.1.0 + */ + public int getUnitListSelection() { + return this.unitNameList.getSelectedIndex(); + } + + /** + * 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 + final JTabbedPane masterPane = new JTabbedPane(); + masterPanel.add(masterPane, BorderLayout.CENTER); + + { // a panel for unit conversion using a selector + final JPanel convertUnitPanel = new JPanel(); + masterPane.addTab("Convert Units", convertUnitPanel); + + convertUnitPanel.setLayout(new BorderLayout()); + + { // panel for input part + final JPanel inputPanel = new JPanel(); + convertUnitPanel.add(inputPanel, BorderLayout.CENTER); + + inputPanel.setLayout(new GridLayout(1, 3)); + + { // panel for From things + final JPanel fromPanel = new JPanel(); + inputPanel.add(fromPanel); + + fromPanel.setLayout(new BorderLayout()); + + { // search box for from + final JTextField fromSearch = new JTextField("Search..."); + fromPanel.add(fromSearch, BorderLayout.PAGE_START); + } + + { // list for From units + final JList fromList = new JList<>(); + fromPanel.add(fromList, BorderLayout.CENTER); + } + } + + { // for dimension selector and arrow that represents conversion + final JPanel inBetweenPanel = new JPanel(); + inputPanel.add(inBetweenPanel); + + inBetweenPanel.setLayout(new BorderLayout()); + + { // dimension selector + final JComboBox dimensionSelector = new JComboBox<>( + new String[] {"Select dimension..."}); + 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 + final JPanel toPanel = new JPanel(); + inputPanel.add(toPanel); + + toPanel.setLayout(new BorderLayout()); + + { // search box for to + final JTextField toSearch = new JTextField("Search..."); + toPanel.add(toSearch, BorderLayout.PAGE_START); + } + + { // list for To units + final JList toList = new JList<>(); + toPanel.add(toList, BorderLayout.CENTER); + } + } + + } + + { // 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 + final JTextField valueInput = new JFormattedTextField( + new DecimalFormat("###############0.################")); + valueInputPanel.add(valueInput, BorderLayout.CENTER); + } + } + + { // button to convert + final JButton convertButton = new JButton("Convert"); + outputPanel.add(convertButton); + } + + { // output of conversion + final JLabel outputLabel = new JLabel(); + outputPanel.add(outputLabel); + } + } + } + + { // panel for unit conversion using expressions + final JPanel convertExpressionPanel = new JPanel(); + masterPane.addTab("Convert Unit Expressions", convertExpressionPanel); + + convertExpressionPanel.setLayout(new GridLayout(5, 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.convert()); + } + + { // 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 for specifying precision + final JPanel sigDigPanel = new JPanel(); + convertExpressionPanel.add(sigDigPanel); + + sigDigPanel.setBorder(BorderFactory.createTitledBorder("Significant Digits")); + + { // slider + final JSlider sigDigSlider = new JSlider(0, 12); + sigDigPanel.add(sigDigSlider); + + sigDigSlider.setMajorTickSpacing(4); + sigDigSlider.setMinorTickSpacing(1); + sigDigSlider.setSnapToTicks(true); + sigDigSlider.setPaintTicks(true); + sigDigSlider.setPaintLabels(true); + + sigDigSlider.addChangeListener( + e -> this.presenter.setSignificantFigures(sigDigSlider.getValue())); + } + } + } + + { // panel to look up units + final JPanel unitLookupPanel = new JPanel(); + masterPane.addTab("Unit Viewer", unitLookupPanel); + + unitLookupPanel.setLayout(new GridLayout()); + + { // panel for listing and searching + final JPanel listPanel = new JPanel(); + unitLookupPanel.add(listPanel); + + listPanel.setLayout(new BorderLayout()); + + { // unit search box + listPanel.add(this.unitFilterEntry, BorderLayout.PAGE_START); + this.unitFilterEntry.addCaretListener(e -> this.presenter.unitFilterUpdated()); + } + + { // a list of units + listPanel.add(new JScrollPane(this.unitNameList), BorderLayout.CENTER); + this.unitNameList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // temp + this.unitNameList.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(); + masterPane.addTab("Prefix Viewer", prefixLookupPanel); + + prefixLookupPanel.setLayout(new GridLayout(1, 2)); + + { // panel for listing and seaching + final JPanel prefixListPanel = new JPanel(); + prefixLookupPanel.add(prefixListPanel); + + prefixListPanel.setLayout(new BorderLayout()); + + { // prefix search box + prefixListPanel.add(this.prefixFilterEntry, BorderLayout.PAGE_START); + this.prefixFilterEntry.addCaretListener(e -> this.presenter.prefixFilterUpdated()); + } + + { // a list of prefixes + prefixListPanel.add(new JScrollPane(this.prefixNameList), BorderLayout.CENTER); + this.prefixNameList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // temp + this.prefixNameList.addListSelectionListener(e -> { + this.presenter.prefixSelected(); + }); + } + } + + { // the text box for prefix's toString + prefixLookupPanel.add(this.prefixTextBox); + this.unitTextBox.setEditable(false); + } + } + } + } + + /** + * 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 setOutputText(final String text) { + this.output.setText(text); + } + + /** + * Sets the text of the prefix text box. + * + * @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. + * + * @param t + * text to set + * @see javax.swing.text.JTextComponent#setText(java.lang.String) + */ + public void setUnitTextBoxText(final String t) { + this.unitTextBox.setText(t); + } + + /** + * 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 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 new file mode 100644 index 0000000..d899f97 --- /dev/null +++ b/src/org/unitConverter/converterGUI/package-info.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2019 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +/** + * All classes that work to convert units. + * + * @author Adrien Hopkins + * @since 2019-01-25 + */ +package org.unitConverter.converterGUI; \ No newline at end of file diff --git a/src/org/unitConverter/dimension/BaseDimension.java b/src/org/unitConverter/dimension/BaseDimension.java new file mode 100755 index 0000000..5e3ddad --- /dev/null +++ b/src/org/unitConverter/dimension/BaseDimension.java @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.dimension; + +/** + * A base dimension that makes up {@code UnitDimension} objects. + * + * @author Adrien Hopkins + * @since 2018-12-22 + * @since v0.1.0 + */ +public interface BaseDimension { + /** + * @return the dimension's name + * @since 2018-12-22 + * @since v0.1.0 + */ + String getName(); + + /** + * @return a short string (usually one character) that represents this base dimension + * @since 2018-12-22 + * @since v0.1.0 + */ + String getSymbol(); +} diff --git a/src/org/unitConverter/dimension/OtherBaseDimension.java b/src/org/unitConverter/dimension/OtherBaseDimension.java new file mode 100755 index 0000000..8aea2b9 --- /dev/null +++ b/src/org/unitConverter/dimension/OtherBaseDimension.java @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.dimension; + +import java.util.Objects; + +/** + * Non-SI base dimensions. + * + * @author Adrien Hopkins + * @since 2019-01-14 + * @since v0.1.0 + */ +public enum OtherBaseDimension implements BaseDimension { + INFORMATION("Info"), CURRENCY("$$"); + + /** The dimension's symbol */ + private final String symbol; + + /** + * Creates the {@code SIBaseDimension}. + * + * @param symbol + * dimension's symbol + * @since 2018-12-11 + * @since v0.1.0 + */ + private OtherBaseDimension(final String symbol) { + this.symbol = Objects.requireNonNull(symbol, "symbol must not be null."); + } + + @Override + public String getName() { + return this.toString(); + } + + @Override + public String getSymbol() { + return this.symbol; + } +} diff --git a/src/org/unitConverter/dimension/SIBaseDimension.java b/src/org/unitConverter/dimension/SIBaseDimension.java new file mode 100755 index 0000000..c459963 --- /dev/null +++ b/src/org/unitConverter/dimension/SIBaseDimension.java @@ -0,0 +1,57 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.dimension; + +import java.util.Objects; + +/** + * The seven base dimensions that make up the SI. + * + * @author Adrien Hopkins + * @since 2018-12-11 + * @since v0.1.0 + */ +public enum SIBaseDimension implements BaseDimension { + LENGTH("L"), MASS("M"), TIME("T"), ELECTRIC_CURRENT("I"), TEMPERATURE("\u0398"), // u0398 is the theta symbol + QUANTITY("N"), LUMINOUS_INTENSITY("J"); + + /** The dimension's symbol */ + private final String symbol; + + /** + * Creates the {@code SIBaseDimension}. + * + * @param symbol + * dimension's symbol + * @since 2018-12-11 + * @since v0.1.0 + */ + private SIBaseDimension(final String symbol) { + this.symbol = Objects.requireNonNull(symbol, "symbol must not be null."); + } + + @Override + public String getName() { + return this.toString(); + } + + @Override + public String getSymbol() { + return this.symbol; + } + +} diff --git a/src/org/unitConverter/dimension/StandardDimensions.java b/src/org/unitConverter/dimension/StandardDimensions.java new file mode 100755 index 0000000..4b1b814 --- /dev/null +++ b/src/org/unitConverter/dimension/StandardDimensions.java @@ -0,0 +1,80 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.dimension; + +/** + * All of the dimensions that are used by the SI. + * + * @author Adrien Hopkins + * @since 2018-12-11 + * @since v0.1.0 + */ +public final class StandardDimensions { + // base dimensions + public static final UnitDimension EMPTY = UnitDimension.EMPTY; + public static final UnitDimension LENGTH = UnitDimension.getBase(SIBaseDimension.LENGTH); + public static final UnitDimension MASS = UnitDimension.getBase(SIBaseDimension.MASS); + public static final UnitDimension TIME = UnitDimension.getBase(SIBaseDimension.TIME); + public static final UnitDimension ELECTRIC_CURRENT = UnitDimension.getBase(SIBaseDimension.ELECTRIC_CURRENT); + public static final UnitDimension TEMPERATURE = UnitDimension.getBase(SIBaseDimension.TEMPERATURE); + public static final UnitDimension QUANTITY = UnitDimension.getBase(SIBaseDimension.QUANTITY); + public static final UnitDimension LUMINOUS_INTENSITY = UnitDimension.getBase(SIBaseDimension.LUMINOUS_INTENSITY); + public static final UnitDimension INFORMATION = UnitDimension.getBase(OtherBaseDimension.INFORMATION); + public static final UnitDimension CURRENCY = UnitDimension.getBase(OtherBaseDimension.CURRENCY); + // derived dimensions without named SI units + public static final UnitDimension AREA = LENGTH.times(LENGTH); + + public static final UnitDimension VOLUME = AREA.times(LENGTH); + public static final UnitDimension VELOCITY = LENGTH.dividedBy(TIME); + public static final UnitDimension ACCELERATION = VELOCITY.dividedBy(TIME); + public static final UnitDimension WAVENUMBER = EMPTY.dividedBy(LENGTH); + public static final UnitDimension MASS_DENSITY = MASS.dividedBy(VOLUME); + public static final UnitDimension SURFACE_DENSITY = MASS.dividedBy(AREA); + public static final UnitDimension SPECIFIC_VOLUME = VOLUME.dividedBy(MASS); + public static final UnitDimension CURRENT_DENSITY = ELECTRIC_CURRENT.dividedBy(AREA); + public static final UnitDimension MAGNETIC_FIELD_STRENGTH = ELECTRIC_CURRENT.dividedBy(LENGTH); + public static final UnitDimension CONCENTRATION = QUANTITY.dividedBy(VOLUME); + public static final UnitDimension MASS_CONCENTRATION = CONCENTRATION.times(MASS); + public static final UnitDimension LUMINANCE = LUMINOUS_INTENSITY.dividedBy(AREA); + public static final UnitDimension REFRACTIVE_INDEX = VELOCITY.dividedBy(VELOCITY); + public static final UnitDimension REFLACTIVE_PERMEABILITY = EMPTY.times(EMPTY); + public static final UnitDimension ANGLE = LENGTH.dividedBy(LENGTH); + public static final UnitDimension SOLID_ANGLE = AREA.dividedBy(AREA); + // derived dimensions with named SI units + public static final UnitDimension FREQUENCY = EMPTY.dividedBy(TIME); + + public static final UnitDimension FORCE = MASS.times(ACCELERATION); + public static final UnitDimension ENERGY = FORCE.times(LENGTH); + public static final UnitDimension POWER = ENERGY.dividedBy(TIME); + public static final UnitDimension ELECTRIC_CHARGE = ELECTRIC_CURRENT.times(TIME); + public static final UnitDimension VOLTAGE = ENERGY.dividedBy(ELECTRIC_CHARGE); + public static final UnitDimension CAPACITANCE = ELECTRIC_CHARGE.dividedBy(VOLTAGE); + public static final UnitDimension ELECTRIC_RESISTANCE = VOLTAGE.dividedBy(ELECTRIC_CURRENT); + public static final UnitDimension ELECTRIC_CONDUCTANCE = ELECTRIC_CURRENT.dividedBy(VOLTAGE); + public static final UnitDimension MAGNETIC_FLUX = VOLTAGE.times(TIME); + public static final UnitDimension MAGNETIC_FLUX_DENSITY = MAGNETIC_FLUX.dividedBy(AREA); + public static final UnitDimension INDUCTANCE = MAGNETIC_FLUX.dividedBy(ELECTRIC_CURRENT); + public static final UnitDimension LUMINOUS_FLUX = LUMINOUS_INTENSITY.times(SOLID_ANGLE); + public static final UnitDimension ILLUMINANCE = LUMINOUS_FLUX.dividedBy(AREA); + public static final UnitDimension SPECIFIC_ENERGY = ENERGY.dividedBy(MASS); + public static final UnitDimension CATALYTIC_ACTIVITY = QUANTITY.dividedBy(TIME); + + // You may NOT get StandardDimensions instances! + private StandardDimensions() { + throw new AssertionError(); + } +} diff --git a/src/org/unitConverter/dimension/UnitDimension.java b/src/org/unitConverter/dimension/UnitDimension.java new file mode 100755 index 0000000..dbeaeff --- /dev/null +++ b/src/org/unitConverter/dimension/UnitDimension.java @@ -0,0 +1,241 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.dimension; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * An object that represents what a unit measures, like length, mass, area, energy, etc. + * + * @author Adrien Hopkins + * @since 2018-12-11 + * @since v0.1.0 + */ +public final class UnitDimension { + /** + * The unit dimension where every exponent is zero + * + * @since 2018-12-12 + * @since v0.1.0 + */ + public static final UnitDimension EMPTY = new UnitDimension(new HashMap<>()); + + /** + * Gets an UnitDimension that has 1 of a certain dimension and nothing else + * + * @param dimension + * dimension to get + * @return unit dimension + * @since 2018-12-11 + * @since v0.1.0 + */ + public static final UnitDimension getBase(final BaseDimension dimension) { + final Map map = new HashMap<>(); + map.put(dimension, 1); + return new UnitDimension(map); + } + + /** + * The base dimensions that make up this dimension. + * + * @since 2018-12-11 + * @since v0.1.0 + */ + final Map exponents; + + /** + * Creates the {@code UnitDimension}. + * + * @param exponents + * base dimensions that make up this dimension + * @since 2018-12-11 + * @since v0.1.0 + */ + private UnitDimension(final Map exponents) { + this.exponents = new HashMap<>(exponents); + } + + /** + * Divides this dimension by another + * + * @param other + * other dimension + * @return quotient of two dimensions + * @since 2018-12-11 + * @since v0.1.0 + */ + public UnitDimension dividedBy(final UnitDimension other) { + final Map map = new HashMap<>(this.exponents); + + for (final BaseDimension key : other.exponents.keySet()) { + if (map.containsKey(key)) { + // add the dimensions + map.put(key, map.get(key) - other.exponents.get(key)); + } else { + map.put(key, -other.exponents.get(key)); + } + } + return new UnitDimension(map); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof UnitDimension)) + return false; + final UnitDimension other = (UnitDimension) obj; + + // anything with a value of 0 is equal to a nonexistent value + for (final BaseDimension b : this.getBaseSet()) { + if (this.exponents.get(b) != other.exponents.get(b)) + if (!(this.exponents.get(b) == 0 && !other.exponents.containsKey(b))) + return false; + } + for (final BaseDimension b : other.getBaseSet()) { + if (this.exponents.get(b) != other.exponents.get(b)) + if (!(other.exponents.get(b) == 0 && !this.exponents.containsKey(b))) + return false; + } + return true; + } + + /** + * @return a set of all of the base dimensions with non-zero exponents that make up this dimension. + * @since 2018-12-12 + * @since v0.1.0 + */ + public final Set getBaseSet() { + final Set dimensions = new HashSet<>(); + + // add all dimensions with a nonzero exponent - they shouldn't be there in the first place + for (final BaseDimension 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 BaseDimension dimension) { + return this.exponents.getOrDefault(dimension, 0); + } + + @Override + public int hashCode() { + return Objects.hash(this.exponents); + } + + /** + * @return true if this dimension is a base, i.e. it has one exponent of one and no other nonzero exponents + * @since 2019-01-15 + * @since v0.1.0 + */ + public boolean isBase() { + int oneCount = 0; + boolean twoOrMore = false; // has exponents of 2 or more + for (final BaseDimension b : this.getBaseSet()) { + if (this.exponents.get(b) == 1) { + oneCount++; + } else if (this.exponents.get(b) != 0) { + twoOrMore = true; + } + } + return (oneCount == 0 || oneCount == 1) && !twoOrMore; + } + + /** + * Multiplies this dimension by another + * + * @param other + * other dimension + * @return product of two dimensions + * @since 2018-12-11 + * @since v0.1.0 + */ + public UnitDimension times(final UnitDimension other) { + final Map map = new HashMap<>(this.exponents); + + for (final BaseDimension key : other.exponents.keySet()) { + if (map.containsKey(key)) { + // add the dimensions + map.put(key, map.get(key) + other.exponents.get(key)); + } else { + map.put(key, other.exponents.get(key)); + } + } + return new UnitDimension(map); + } + + /** + * Returns this dimension, but to an exponent + * + * @param exp + * exponent + * @return result of exponientation + * @since 2019-01-15 + * @since v0.1.0 + */ + public UnitDimension toExponent(final int exp) { + final Map map = new HashMap<>(this.exponents); + for (final BaseDimension key : this.exponents.keySet()) { + map.put(key, this.getExponent(key) * exp); + } + return new UnitDimension(map); + } + + @Override + public String toString() { + final List positiveStringComponents = new ArrayList<>(); + final List negativeStringComponents = new ArrayList<>(); + + // for each base dimension that makes up this dimension, add it and its exponent + for (final BaseDimension dimension : this.getBaseSet()) { + final int exponent = this.exponents.get(dimension); + if (exponent > 0) { + positiveStringComponents.add(String.format("%s^%d", dimension.getSymbol(), exponent)); + } else if (exponent < 0) { + negativeStringComponents.add(String.format("%s^%d", dimension.getSymbol(), -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/dimension/UnitDimensionTest.java b/src/org/unitConverter/dimension/UnitDimensionTest.java new file mode 100755 index 0000000..3b09610 --- /dev/null +++ b/src/org/unitConverter/dimension/UnitDimensionTest.java @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.dimension; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.unitConverter.dimension.StandardDimensions.AREA; +import static org.unitConverter.dimension.StandardDimensions.ENERGY; +import static org.unitConverter.dimension.StandardDimensions.LENGTH; +import static org.unitConverter.dimension.StandardDimensions.MASS; +import static org.unitConverter.dimension.StandardDimensions.MASS_DENSITY; +import static org.unitConverter.dimension.StandardDimensions.QUANTITY; +import static org.unitConverter.dimension.StandardDimensions.TIME; +import static org.unitConverter.dimension.StandardDimensions.VOLUME; + +import org.junit.Test; + +/** + * Tests for {@link UnitDimension}. + * + * @author Adrien Hopkins + * @since 2018-12-12 + * @since v0.1.0 + */ +class UnitDimensionTest { + /** + * Tests {@link UnitDimension#equals} + * + * @since 2018-12-12 + * @since v0.1.0 + */ + @Test + void testEquals() { + assertEquals(LENGTH, LENGTH); + assertFalse(LENGTH.equals(QUANTITY)); + } + + /** + * Tests {@code UnitDimension}'s exponentiation + * + * @since 2019-01-15 + * @since v0.1.0 + */ + @Test + void testExponents() { + assertEquals(1, LENGTH.getExponent(SIBaseDimension.LENGTH)); + assertEquals(3, VOLUME.getExponent(SIBaseDimension.LENGTH)); + } + + /** + * Tests {@code UnitDimension}'s multiplication and division. + * + * @since 2018-12-12 + * @since v0.1.0 + */ + @Test + 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/dimension/package-info.java b/src/org/unitConverter/dimension/package-info.java new file mode 100755 index 0000000..db363df --- /dev/null +++ b/src/org/unitConverter/dimension/package-info.java @@ -0,0 +1,23 @@ +/** + * 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 . + */ +/** + * Everything to do with what a unit measures, or its dimension. + * + * @author Adrien Hopkins + * @since 2018-12-22 + */ +package org.unitConverter.dimension; \ No newline at end of file diff --git a/src/org/unitConverter/package-info.java b/src/org/unitConverter/package-info.java new file mode 100644 index 0000000..4f51ad0 --- /dev/null +++ b/src/org/unitConverter/package-info.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2019 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +/** + * A program that converts units. + * + * @author Adrien Hopkins + * @since 2019-01-25 + */ +package org.unitConverter; \ No newline at end of file diff --git a/src/org/unitConverter/unit/AbstractUnit.java b/src/org/unitConverter/unit/AbstractUnit.java new file mode 100644 index 0000000..6088960 --- /dev/null +++ b/src/org/unitConverter/unit/AbstractUnit.java @@ -0,0 +1,172 @@ +/** + * Copyright (C) 2019 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.unit; + +import java.util.Objects; + +import org.unitConverter.dimension.UnitDimension; + +/** + * The default abstract implementation of the {@code Unit} interface. + * + * @author Adrien Hopkins + * @since 2018-12-22 + * @since v0.1.0 + */ +public abstract class AbstractUnit implements Unit { + /** + * The number of units created, including base units. + * + * @since 2019-01-02 + * @since v0.1.0 + */ + private static long unitCount = 0; + + /** + * The number of base units created. + * + * @since 2019-01-02 + * @since v0.1.0 + */ + private static long baseUnitCount = 0; + + /** + * @return number of base units created + * @since 2019-01-02 + * @since v0.1.0 + */ + public static final long getBaseUnitCount() { + return baseUnitCount; + } + + /** + * @return number of units created + * @since 2019-01-02 + * @since v0.1.0 + */ + public static final long getUnitCount() { + return unitCount; + } + + /** + * Increments the number of base units. + * + * @since 2019-01-15 + * @since v0.1.0 + */ + public static final void incrementBaseUnitCounter() { + baseUnitCount++; + } + + /** + * Increments the number of units. + * + * @since 2019-01-15 + * @since v0.1.0 + */ + public static final void incrementUnitCounter() { + unitCount++; + } + + /** + * The dimension, or what the unit measures. + * + * @since 2018-12-22 + * @since v0.1.0 + */ + private final UnitDimension dimension; + + /** + * The unit's base unit. Values converted by {@code convertFromBase} and {@code convertToBase} are expressed in this + * unit. + * + * @since 2018-12-22 + * @since v0.1.0 + */ + private final BaseUnit base; + + /** + * The system that this unit is a part of. + * + * @since 2018-12-23 + * @since v0.1.0 + */ + private final UnitSystem system; + + /** + * Creates the {@code AbstractUnit}. + * + * @param base + * unit's base + * @throws NullPointerException + * if name, symbol or base is null + * @since 2018-12-22 + * @since v0.1.0 + */ + public AbstractUnit(final BaseUnit base) { + this.base = Objects.requireNonNull(base, "base must not be null."); + this.dimension = this.base.getDimension(); + this.system = this.base.getSystem(); + } + + /** + * Creates the {@code AbstractUnit} using a unique dimension. This constructor is for making base units and should + * only be used by {@code BaseUnit}. + * + * @param dimension + * dimension measured by unit + * @param system + * system that unit is a part of + * @throws AssertionError + * if this constructor is not run by {@code BaseUnit} or a subclass + * @throws NullPointerException + * if name, symbol or dimension is null + * @since 2018-12-23 + * @since v0.1.0 + */ + AbstractUnit(final UnitDimension dimension, final UnitSystem system) { + // try to set this as a base unit + if (this instanceof BaseUnit) { + this.base = (BaseUnit) this; + } else + throw new AssertionError(); + + this.dimension = Objects.requireNonNull(dimension, "dimension must not be null."); + this.system = Objects.requireNonNull(system, "system must not be null."); + } + + @Override + public final BaseUnit getBase() { + return this.base; + } + + @Override + public final UnitDimension getDimension() { + return this.dimension; + } + + @Override + public final UnitSystem getSystem() { + return this.system; + } + + // TODO document and revise units' toString methods + @Override + public String toString() { + return String.format("%s-derived unit of dimension %s", this.getSystem(), this.getDimension()); + } +} diff --git a/src/org/unitConverter/unit/BaseUnit.java b/src/org/unitConverter/unit/BaseUnit.java new file mode 100755 index 0000000..1f0c825 --- /dev/null +++ b/src/org/unitConverter/unit/BaseUnit.java @@ -0,0 +1,180 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.unit; + +import java.util.Objects; + +import org.unitConverter.dimension.UnitDimension; + +/** + * A unit that is the base for its dimension. It does not have to be for a base dimension, so units like the Newton and + * Joule are still base units. + * + * @author Adrien Hopkins + * @since 2018-12-23 + * @since v0.1.0 + */ +public final class BaseUnit extends AbstractUnit { + /** + * Is this unit a full base (i.e. m, s, ... but not N, J, ...) + * + * @since 2019-01-15 + * @since v0.1.0 + */ + private final boolean isFullBase; + + /** + * Creates the {@code BaseUnit}. + * + * @param dimension + * dimension measured by unit + * @param system + * system that unit is a part of + * @param name + * name of unit + * @param symbol + * symbol of unit + * @since 2018-12-23 + * @since v0.1.0 + */ + BaseUnit(final UnitDimension dimension, final UnitSystem system) { + super(dimension, system); + this.isFullBase = dimension.isBase(); + } + + /** + * @return this unit as a {@code LinearUnit} + * @since 2019-01-25 + * @since v0.1.0 + */ + public LinearUnit asLinearUnit() { + return this.times(1); + } + + @Override + public double convertFromBase(final double value) { + return value; + } + + @Override + public double convertToBase(final double value) { + return value; + } + + /** + * Divides this unit by another unit. + * + * @param other + * unit to divide by + * @return quotient of two units + * @throws IllegalArgumentException + * if this unit's system is not other's system + * @throws NullPointerException + * if other is null + * @since 2018-12-22 + * @since v0.1.0 + */ + public BaseUnit dividedBy(final BaseUnit other) { + Objects.requireNonNull(other, "other must not be null."); + if (!this.getSystem().equals(other.getSystem())) + throw new IllegalArgumentException("Incompatible base units for division."); + return new BaseUnit(this.getDimension().dividedBy(other.getDimension()), this.getSystem()); + } + + /** + * Divides this unit by a divisor + * + * @param divisor + * amount to divide by + * @return quotient + * @since 2018-12-23 + * @since v0.1.0 + */ + public LinearUnit dividedBy(final double divisor) { + return new LinearUnit(this, 1 / divisor); + } + + @Override + public boolean equals(final Object obj) { + if (!(obj instanceof BaseUnit)) + return false; + final BaseUnit other = (BaseUnit) obj; + return Objects.equals(this.getSystem(), other.getSystem()) + && Objects.equals(this.getDimension(), other.getDimension()); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = result * prime + this.getSystem().hashCode(); + result = result * prime + this.getDimension().hashCode(); + return result; + } + + /** + * Multiplies this unit by another unit. + * + * @param other + * unit to multiply by + * @return product of two units + * @throws IllegalArgumentException + * if this unit's system is not other's system + * @throws NullPointerException + * if other is null + * @since 2018-12-22 + * @since v0.1.0 + */ + public BaseUnit times(final BaseUnit other) { + Objects.requireNonNull(other, "other must not be null."); + if (!this.getSystem().equals(other.getSystem())) + throw new IllegalArgumentException("Incompatible base units for multiplication."); + return new BaseUnit(this.getDimension().times(other.getDimension()), this.getSystem()); + } + + /** + * Multiplies this unit by a multiplier. + * + * @param multiplier + * amount to multiply by + * @return product + * @since 2018-12-23 + * @since v0.1.0 + */ + public LinearUnit times(final double multiplier) { + return new LinearUnit(this, multiplier); + } + + /** + * Returns this unit, but to an exponent. + * + * @param exponent + * exponent + * @return result of exponentiation + * @since 2019-01-15 + * @since v0.1.0 + */ + public BaseUnit toExponent(final int exponent) { + return this.getSystem().getBaseUnit(this.getDimension().toExponent(exponent)); + } + + @Override + public String toString() { + return String.format("%s base unit of%s dimension %s", this.getSystem(), this.isFullBase ? " base" : "", + this.getDimension()); + } +} diff --git a/src/org/unitConverter/unit/DefaultUnitPrefix.java b/src/org/unitConverter/unit/DefaultUnitPrefix.java new file mode 100755 index 0000000..c0e8dcc --- /dev/null +++ b/src/org/unitConverter/unit/DefaultUnitPrefix.java @@ -0,0 +1,67 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.unit; + +import java.util.Objects; + +/** + * The default implementation of {@code UnitPrefix}, which contains a multiplier and nothing else. + * + * @author Adrien Hopkins + * @since 2019-01-14 + * @since v0.1.0 + */ +public final class DefaultUnitPrefix implements UnitPrefix { + private final double multiplier; + + /** + * Creates the {@code DefaultUnitPrefix}. + * + * @param multiplier + * @since 2019-01-14 + */ + public DefaultUnitPrefix(final double multiplier) { + this.multiplier = multiplier; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof DefaultUnitPrefix)) + return false; + final DefaultUnitPrefix other = (DefaultUnitPrefix) obj; + return Double.doubleToLongBits(this.multiplier) == Double.doubleToLongBits(other.multiplier); + } + + @Override + public double getMultiplier() { + return this.multiplier; + } + + @Override + public int hashCode() { + return Objects.hash(this.multiplier); + } + + @Override + public String toString() { + return String.format("Unit prefix equal to %s", this.multiplier); + } +} diff --git a/src/org/unitConverter/unit/LinearUnit.java b/src/org/unitConverter/unit/LinearUnit.java new file mode 100644 index 0000000..ab46f1e --- /dev/null +++ b/src/org/unitConverter/unit/LinearUnit.java @@ -0,0 +1,184 @@ +/** + * Copyright (C) 2019 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.unit; + +import java.util.Objects; + +import org.unitConverter.dimension.UnitDimension; + +/** + * A unit that is equal to a certain number multiplied by its base. + * + * @author Adrien Hopkins + * @since 2018-12-22 + * @since v0.1.0 + */ +public final class LinearUnit extends AbstractUnit { + /** + * The value of one of this unit in this unit's base unit + * + * @since 2018-12-22 + * @since v0.1.0 + */ + private final double conversionFactor; + + /** + * + * Creates the {@code LinearUnit}. + * + * @param base + * unit's base + * @param conversionFactor + * value of one of this unit in its base + * @since 2018-12-23 + * @since v0.1.0 + */ + LinearUnit(final BaseUnit base, final double conversionFactor) { + super(base); + this.conversionFactor = conversionFactor; + } + + /** + * Creates the {@code LinearUnit} as a base unit. + * + * @param dimension + * dimension measured by unit + * @param system + * system unit is part of + * @since 2019-01-25 + * @since v0.1.0 + */ + LinearUnit(final UnitDimension dimension, final UnitSystem system, final double conversionFactor) { + super(dimension, system); + this.conversionFactor = conversionFactor; + } + + @Override + public double convertFromBase(final double value) { + return value / this.getConversionFactor(); + } + + @Override + public double convertToBase(final double value) { + return value * this.getConversionFactor(); + } + + /** + * Divides this unit by a scalar. + * + * @param divisor + * scalar to divide by + * @return quotient + * @since 2018-12-23 + * @since v0.1.0 + */ + public LinearUnit dividedBy(final double divisor) { + return new LinearUnit(this.getBase(), this.getConversionFactor() / divisor); + } + + /** + * Divides this unit by another unit. + * + * @param other + * unit to divide by + * @return quotient of two units + * @throws NullPointerException + * if other is null + * @since 2018-12-22 + * @since v0.1.0 + */ + public LinearUnit dividedBy(final LinearUnit other) { + Objects.requireNonNull(other, "other must not be null"); + final BaseUnit base = this.getBase().dividedBy(other.getBase()); + return new LinearUnit(base, this.getConversionFactor() / other.getConversionFactor()); + } + + @Override + public boolean equals(final Object obj) { + if (!(obj instanceof LinearUnit)) + return false; + final LinearUnit other = (LinearUnit) obj; + return Objects.equals(this.getBase(), other.getBase()) + && Objects.equals(this.getConversionFactor(), other.getConversionFactor()); + } + + /** + * @return conversionFactor + * @since 2018-12-22 + * @since v0.1.0 + */ + public final double getConversionFactor() { + return this.conversionFactor; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = result * prime + this.getBase().hashCode(); + result = result * prime + Double.hashCode(this.getConversionFactor()); + return result; + } + + /** + * 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 new LinearUnit(this.getBase(), this.getConversionFactor() * multiplier); + } + + /** + * Multiplies this unit by another unit. + * + * @param other + * unit to multiply by= + * @return product of two units + * @throws NullPointerException + * if other is null + * @since 2018-12-22 + * @since v0.1.0 + */ + public LinearUnit times(final LinearUnit other) { + Objects.requireNonNull(other, "other must not be null"); + final BaseUnit base = this.getBase().times(other.getBase()); + return new LinearUnit(base, this.getConversionFactor() * other.getConversionFactor()); + } + + /** + * Returns this unit but to an exponent. + * + * @param exponent + * exponent to exponientate unit to + * @return exponientated unit + * @since 2019-01-15 + * @since v0.1.0 + */ + public LinearUnit toExponent(final int exponent) { + return new LinearUnit(this.getBase().toExponent(exponent), Math.pow(this.conversionFactor, exponent)); + } + + @Override + public String toString() { + return super.toString() + String.format(" (equal to %s * base)", this.getConversionFactor()); + } +} diff --git a/src/org/unitConverter/unit/NonlinearUnits.java b/src/org/unitConverter/unit/NonlinearUnits.java new file mode 100755 index 0000000..e47c28f --- /dev/null +++ b/src/org/unitConverter/unit/NonlinearUnits.java @@ -0,0 +1,57 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.unit; + +/** + * Some major nonlinear units. + * + * @author Adrien Hopkins + * @since 2019-01-14 + * @since v0.1.0 + */ +public final class NonlinearUnits { + public static final Unit CELSIUS = new AbstractUnit(SI.KELVIN) { + + @Override + public double convertFromBase(final double value) { + return value - 273.15; + } + + @Override + public double convertToBase(final double value) { + return value + 273.15; + } + }; + + public static final Unit FAHRENHEIT = new AbstractUnit(SI.KELVIN) { + + @Override + public double convertFromBase(final double value) { + return 1.8 * value - 459.67; + } + + @Override + public double convertToBase(final double value) { + return (value + 459.67) / 1.8; + } + }; + + // You may NOT get a NonlinearUnits instance. + private NonlinearUnits() { + throw new AssertionError(); + } +} diff --git a/src/org/unitConverter/unit/SI.java b/src/org/unitConverter/unit/SI.java new file mode 100644 index 0000000..46e6ff1 --- /dev/null +++ b/src/org/unitConverter/unit/SI.java @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.unit; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import org.unitConverter.dimension.StandardDimensions; +import org.unitConverter.dimension.UnitDimension; + +/** + * The SI, which holds all SI units + * + * @author Adrien Hopkins + * @since 2018-12-23 + * @since v0.1.0 + */ +public enum SI implements UnitSystem { + SI; + + /** + * This system's base units. + * + * @since 2019-01-25 + * @since v0.1.0 + */ + private static final Set baseUnits = new HashSet<>(); + + // base units + public static final BaseUnit METRE = SI.getBaseUnit(StandardDimensions.LENGTH); + public static final BaseUnit KILOGRAM = SI.getBaseUnit(StandardDimensions.MASS); + public static final BaseUnit SECOND = SI.getBaseUnit(StandardDimensions.TIME); + public static final BaseUnit AMPERE = SI.getBaseUnit(StandardDimensions.ELECTRIC_CURRENT); + public static final BaseUnit KELVIN = SI.getBaseUnit(StandardDimensions.TEMPERATURE); + public static final BaseUnit MOLE = SI.getBaseUnit(StandardDimensions.QUANTITY); + public static final BaseUnit CANDELA = SI.getBaseUnit(StandardDimensions.LUMINOUS_INTENSITY); + + @Override + public BaseUnit getBaseUnit(final UnitDimension dimension) { + // try to find an existing unit before creating a new one + + Objects.requireNonNull(dimension, "dimension must not be null."); + for (final BaseUnit unit : baseUnits) { + // it will be equal since the conditions for equality are dimension and system, + // and system is always SI. + if (unit.getDimension().equals(dimension)) + return unit; + } + // could not find an existing base unit + final BaseUnit unit = new BaseUnit(dimension, this); + baseUnits.add(unit); + return unit; + } + + @Override + public String getName() { + return "SI"; + } +} diff --git a/src/org/unitConverter/unit/SIPrefix.java b/src/org/unitConverter/unit/SIPrefix.java new file mode 100755 index 0000000..31d7ff2 --- /dev/null +++ b/src/org/unitConverter/unit/SIPrefix.java @@ -0,0 +1,54 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.unit; + +/** + * The SI prefixes. + * + * @author Adrien Hopkins + * @since 2019-01-14 + * @since v0.1.0 + */ +public enum SIPrefix implements UnitPrefix { + DECA(10), HECTO(100), KILO(1e3), MEGA(1e6), GIGA(1e9), TERA(1e12), PETA(1e15), EXA(1e18), ZETTA(1e21), YOTTA( + 1e24), DECI(0.1), CENTI(0.01), MILLI( + 1e-3), MICRO(1e-6), NANO(1e-9), PICO(1e-12), FEMTO(1e-15), ATTO(1e-18), ZEPTO(1e-21), YOCTO(1e-24); + + private final double multiplier; + + /** + * Creates the {@code SIPrefix}. + * + * @param multiplier + * prefix's multiplier + * @since 2019-01-14 + * @since v0.1.0 + */ + private SIPrefix(final double multiplier) { + this.multiplier = multiplier; + } + + /** + * @return value + * @since 2019-01-14 + * @since v0.1.0 + */ + @Override + public final double getMultiplier() { + return this.multiplier; + } +} diff --git a/src/org/unitConverter/unit/Unit.java b/src/org/unitConverter/unit/Unit.java new file mode 100755 index 0000000..86fc5a2 --- /dev/null +++ b/src/org/unitConverter/unit/Unit.java @@ -0,0 +1,110 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.unit; + +import java.util.Objects; + +import org.unitConverter.dimension.UnitDimension; + +/** + * A unit that has an associated base unit, and can convert a value expressed in it to and from that base. + * + * @author Adrien Hopkins + * @since 2018-12-22 + * @since v0.1.0 + */ +public interface Unit { + /** + * Checks if a value expressed in this unit can be converted to a value expressed in {@code other} + * + * @param other + * unit to test with + * @return true if the units are compatible + * @since 2019-01-13 + * @since v0.1.0 + */ + default boolean canConvertTo(final Unit other) { + return Objects.equals(this.getBase(), other.getBase()); + } + + /** + * Converts from a value expressed in this unit's base unit to a value expressed in this unit. + *

+ * This must be the inverse of {@code convertToBase}, so {@code convertFromBase(convertToBase(value))} must be equal + * to {@code value} for any value, ignoring precision loss by roundoff error. + *

+ *

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

+ * + * @param value + * value expressed in base unit + * @return value expressed in this unit + * @since 2018-12-22 + * @since v0.1.0 + */ + double convertFromBase(double value); + + /** + * Converts from a value expressed in this unit to a value expressed in this unit's base unit. + *

+ * This must be the inverse of {@code convertFromBase}, so {@code convertToBase(convertFromBase(value))} must be + * equal to {@code value} for any value, ignoring precision loss by roundoff error. + *

+ *

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

+ * + * @param value + * value expressed in this unit + * @return value expressed in base unit + * @since 2018-12-22 + * @since v0.1.0 + */ + double convertToBase(double value); + + /** + *

+ * Returns the base unit associated with this unit. + *

+ *

+ * The dimension of this unit must be equal to the dimension of the returned unit. + *

+ *

+ * If this unit is a base unit, this method should return this unit.\ + *

+ * + * @return base unit associated with this unit + * @since 2018-12-22 + * @since v0.1.0 + */ + BaseUnit getBase(); + + /** + * @return dimension measured by this unit + * @since 2018-12-22 + * @since v0.1.0 + */ + UnitDimension getDimension(); + + /** + * @return system that this unit is a part of + * @since 2018-12-23 + * @since v0.1.0 + */ + UnitSystem getSystem(); +} diff --git a/src/org/unitConverter/unit/UnitPrefix.java b/src/org/unitConverter/unit/UnitPrefix.java new file mode 100755 index 0000000..289e60f --- /dev/null +++ b/src/org/unitConverter/unit/UnitPrefix.java @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.unit; + +/** + * A prefix that can be attached onto the front of any unit, which multiplies it by a certain value + * + * @author Adrien Hopkins + * @since 2019-01-14 + * @since v0.1.0 + */ +public interface UnitPrefix { + /** + * @return this prefix's multiplier + * @since 2019-01-14 + * @since v0.1.0 + */ + double getMultiplier(); +} diff --git a/src/org/unitConverter/unit/UnitSystem.java b/src/org/unitConverter/unit/UnitSystem.java new file mode 100755 index 0000000..550eff6 --- /dev/null +++ b/src/org/unitConverter/unit/UnitSystem.java @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.unit; + +import java.util.Objects; + +import org.unitConverter.dimension.UnitDimension; + +/** + * A system of units. Each unit should be aware of its system. + * + * @author Adrien Hopkins + * @since 2018-12-23 + * @since v0.1.0 + */ +public interface UnitSystem { + /** + * Gets a base unit for this system and the provided dimension. + * + * @param dimension + * dimension used by base unit + * @return base unit + * @throws NullPointerException + * if dimension is null + * @since 2019-01-25 + * @since v0.1.0 + */ + default BaseUnit getBaseUnit(final UnitDimension dimension) { + Objects.requireNonNull(dimension, "dimension must not be null."); + return new BaseUnit(dimension, this); + } + + /** + * @return name of system + * @since 2018-12-23 + * @since v0.1.0 + */ + String getName(); +} diff --git a/src/org/unitConverter/unit/UnitTest.java b/src/org/unitConverter/unit/UnitTest.java new file mode 100755 index 0000000..931cc57 --- /dev/null +++ b/src/org/unitConverter/unit/UnitTest.java @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.unitConverter.unit; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.unitConverter.dimension.StandardDimensions; + +/** + * Testing the various Unit classes + * + * @author Adrien Hopkins + * @since 2018-12-22 + */ +class UnitTest { + @Test + void testConversion() { + final BaseUnit metre = SI.METRE; + final Unit inch = metre.times(0.0254); + + assertEquals(1.9, inch.convertToBase(75), 0.01); + } + + @Test + void testEquals() { + final BaseUnit metre = SI.METRE; + final Unit meter = SI.SI.getBaseUnit(StandardDimensions.LENGTH); + + assertEquals(metre, meter); + } +} diff --git a/src/org/unitConverter/unit/package-info.java b/src/org/unitConverter/unit/package-info.java new file mode 100644 index 0000000..c4493ae --- /dev/null +++ b/src/org/unitConverter/unit/package-info.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2019 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +/** + * All of the classes that correspond to the units being converted. + * + * @author Adrien Hopkins + * @since 2019-01-25 + */ +package org.unitConverter.unit; \ No newline at end of file diff --git a/src/unitConverter/UnitsDatabase.java b/src/unitConverter/UnitsDatabase.java deleted file mode 100755 index 9f5a6a2..0000000 --- a/src/unitConverter/UnitsDatabase.java +++ /dev/null @@ -1,641 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -import unitConverter.dimension.UnitDimension; -import unitConverter.unit.AbstractUnit; -import unitConverter.unit.BaseUnit; -import unitConverter.unit.DefaultUnitPrefix; -import unitConverter.unit.LinearUnit; -import unitConverter.unit.SI; -import unitConverter.unit.Unit; -import unitConverter.unit.UnitPrefix; - -/** - * A database of units and prefixes, and their names. - * - * @author Adrien Hopkins - * @since 2019-01-07 - * @since v0.1.0 - */ -public final class UnitsDatabase { - /** - * The units in this system. - * - * @since 2019-01-07 - * @since v0.1.0 - */ - private final Map units; - - /** - * The unit prefixes in this system. - * - * @since 2019-01-14 - * @since v0.1.0 - */ - private final Map prefixes; - - /** - * The dimensions in this system. - * - * @since 2019-03-14 - */ - private final Map dimensions; - - /** - * Creates the {@code UnitsDatabase}. - * - * @since 2019-01-10 - * @since v0.1.0 - */ - public UnitsDatabase() { - this.units = new HashMap<>(); - this.prefixes = new HashMap<>(); - this.dimensions = new HashMap<>(); - } - - /** - * Adds all units from a file, using data from the database to parse them. - *

- * Each line in the file should consist of a name and an expression (parsed by getUnitFromExpression) separated by - * any number of tab characters. - *

- *

- * Allowed exceptions: - *

    - *
  • Any line that begins with the '#' character is considered a comment and ignored.
  • - *
  • Blank lines are also ignored
  • - *
  • 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.
  • - *
- * - * @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 addAllFromFile(final File file) { - Objects.requireNonNull(file, "file must not be null."); - try (FileReader fileReader = new FileReader(file); BufferedReader reader = new BufferedReader(fileReader)) { - // while the reader has lines to read, read a line, then parse it, then add it - long lineCounter = 0; - while (reader.ready()) { - final String line = reader.readLine(); - lineCounter++; - - // ignore lines that start with a # sign - they're comments - if (line.startsWith("#") || line.isEmpty()) { - continue; - } - - // divide line into name and expression - final String[] parts = line.split("\t"); - if (parts.length < 2) - throw new IllegalArgumentException(String.format( - "Lines must consist of a unit name and its definition, separated by tab(s) (line %d).", - lineCounter)); - final String name = parts[0]; - final String expression = parts[parts.length - 1]; - - if (name.endsWith(" ")) { - System.err.printf("Warning - line %d's unit name ends in a space", lineCounter); - } - - // 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; - } - AbstractUnit.incrementUnitCounter(); - if (unit instanceof BaseUnit) { - AbstractUnit.incrementBaseUnitCounter(); - } - this.addUnit(name, unit); - } - } - } - } 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 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 - */ - public void addDimension(final String name, final UnitDimension dimension) { - this.dimensions.put(Objects.requireNonNull(name, "name must not be null."), - Objects.requireNonNull(dimension, "dimension must not be null.")); - } - - /** - * 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.units.put(Objects.requireNonNull(name, "name must not be null."), - Objects.requireNonNull(unit, "unit must not be null.")); - } - - /** - * 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 - */ - public boolean containsDimensionName(final String name) { - return this.dimensions.containsKey(name); - } - - /** - * Tests if the database has a unit with this name, ignoring prefixes - * - * @param name - * name to test - * @return if database contains name - * @since 2019-01-13 - * @since v0.1.0 - */ - public boolean containsPrefixlessUnitName(final String name) { - return this.units.containsKey(name); - } - - /** - * Tests if the database has a unit prefix with this name. - * - * @param name - * 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) { - // check for prefixes - for (final String prefixName : this.prefixNameSet()) { - if (name.startsWith(prefixName)) - if (this.containsUnitName(name.substring(prefixName.length()))) - return true; - } - return this.units.containsKey(name); - } - - /** - * @return an immutable set of all of the dimension names in this database. - * @since 2019-03-14 - */ - public Set dimensionNameSet() { - return Collections.unmodifiableSet(this.dimensions.keySet()); - } - - /** - * Gets a unit dimension from the database using its name. - * - * @param name - * dimension's name - * @return dimension - * @since 2019-03-14 - */ - public UnitDimension getDimension(final String name) { - return this.dimensions.get(name); - } - - /** - * 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) { - return this.prefixes.get(name); - } - - /** - * Gets a unit prefix from a prefix expression - *

- * Currently, prefix expressions are much simpler than unit expressions: They are either a number or the name of - * another prefix - *

- * - * @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."); - - try { - return new DefaultUnitPrefix(Double.parseDouble(expression)); - } catch (final NumberFormatException e) { - if (expression.contains("^")) { - final String[] baseAndExponent = expression.split("\\^"); - - final double base; - try { - base = Double.parseDouble(baseAndExponent[0]); - } catch (final NumberFormatException e2) { - throw new IllegalArgumentException("Base of exponientation must be a number."); - } - - final int exponent; - try { - exponent = Integer.parseInt(baseAndExponent[baseAndExponent.length - 1]); - } catch (final NumberFormatException e2) { - throw new IllegalArgumentException("Exponent must be an integer."); - } - - return new DefaultUnitPrefix(Math.pow(base, exponent)); - } else { - if (!this.containsPrefixName(expression)) - throw new IllegalArgumentException("Unrecognized prefix name \"" + expression + "\"."); - return this.getPrefix(expression); - } - } - } - - /** - * Gets a unit from the database from its name, ignoring prefixes. - * - * @param name - * unit's name - * @return unit - * @since 2019-01-10 - * @since v0.1.0 - */ - public Unit getPrefixlessUnit(final String name) { - return this.units.get(name); - } - - /** - * Gets a unit from the database from its name, looking for prefixes. - * - * @param name - * unit's name - * @return unit - * @since 2019-01-10 - * @since v0.1.0 - */ - public Unit getUnit(final String name) { - if (name.contains("^")) { - final String[] baseAndExponent = name.split("\\^"); - - LinearUnit base; - try { - base = SI.SI.getBaseUnit(UnitDimension.EMPTY).times(Double.parseDouble(baseAndExponent[0])); - } catch (final NumberFormatException e2) { - final Unit unit = this.getUnit(baseAndExponent[0]); - if (unit instanceof LinearUnit) { - base = (LinearUnit) unit; - } else if (unit instanceof BaseUnit) { - base = ((BaseUnit) unit).asLinearUnit(); - } else - throw new IllegalArgumentException("Base of exponientation must be a linear or base unit."); - } - - final int exponent; - try { - exponent = Integer.parseInt(baseAndExponent[baseAndExponent.length - 1]); - } catch (final NumberFormatException e2) { - throw new IllegalArgumentException("Exponent must be an integer."); - } - - final LinearUnit exponentiated = base.toExponent(exponent); - if (exponentiated.getConversionFactor() == 1) - return exponentiated.getSystem().getBaseUnit(exponentiated.getDimension()); - else - return exponentiated; - } else { - for (final String prefixName : this.prefixNameSet()) { - // check for a prefix - if (name.startsWith(prefixName)) { - // prefix found! Make sure what comes after it is actually a unit! - final String prefixless = name.substring(prefixName.length()); - if (this.containsUnitName(prefixless)) { - // yep, it's a proper prefix! Get the unit! - final Unit unit = this.getUnit(prefixless); - final UnitPrefix prefix = this.getPrefix(prefixName); - - // Prefixes only work with linear and base units, so make sure it's one of those - if (unit instanceof LinearUnit) { - final LinearUnit linearUnit = (LinearUnit) unit; - return linearUnit.times(prefix.getMultiplier()); - } else if (unit instanceof BaseUnit) { - final BaseUnit baseUnit = (BaseUnit) unit; - return baseUnit.times(prefix.getMultiplier()); - } - } - } - } - return this.units.get(name); - } - } - - /** - * Uses the database's unit data to parse an expression into a unit - *

- * The expression is a series of any of the following: - *

    - *
  • The name of a unit, which multiplies or divides the result based on preceding operators
  • - *
  • The operators '*' and '/', which multiply and divide (note that just putting two units or values next to each - * other is equivalent to multiplication)
  • - *
  • The operator '^' which exponentiates. Exponents must be integers.
  • - *
  • A number which is multiplied or divided
  • - *
- * This method only works with linear units. - * - * @param expression - * expression to parse - * @throws IllegalArgumentException - * if the expression cannot be parsed - * @throws NullPointerException - * if any argument is null - * @since 2019-01-07 - * @since v0.1.0 - */ - public Unit getUnitFromExpression(final String expression) { - Objects.requireNonNull(expression, "expression must not be null."); - - // parse the expression - // start with an "empty" unit then apply operations on it - LinearUnit unit = SI.SI.getBaseUnit(UnitDimension.EMPTY).asLinearUnit(); - boolean dividing = false; - - // if I'm just creating an alias, just create one instead of going through the parsing process - if (!expression.contains(" ") && !expression.contains("*") && !expression.contains("/") - && !expression.contains("(") && !expression.contains(")") && !expression.contains("^")) { - try { - return SI.SI.getBaseUnit(UnitDimension.EMPTY).times(Double.parseDouble(expression)); - } catch (final NumberFormatException e) { - if (!this.containsUnitName(expression)) - throw new IllegalArgumentException("Unrecognized unit name \"" + expression + "\"."); - return this.getUnit(expression); - } - } - - // \\* means "asterisk", * is reserved - for (final String part : expression.replaceAll("\\*", " \\* ").replaceAll("/", " / ").replaceAll(" \\^", "\\^") - .replaceAll("\\^ ", "\\^").split(" ")) { - if ("".equals(part)) { - continue; - } - // "unit1 unit2" is the same as "unit1 * unit2", so multiplication signs do nothing - if ("*".equals(part)) { - continue; - } - // When I reach a division sign, don't parse a unit, instead tell myself I'm going to divide the next - // thing - if ("/".equals(part) || "per".equals(part)) { - dividing = true; - continue; - } - - try { - final double partAsNumber = Double.parseDouble(part); // if this works, it's a number - // this code should not throw any exceptions, so I'm going to throw AssertionErrors if it does - try { - if (dividing) { - unit = unit.dividedBy(partAsNumber); - dividing = false; - } else { - unit = unit.times(partAsNumber); - } - } catch (final Exception e) { - throw new AssertionError(e); - } - } catch (final NumberFormatException e) { - // it's a unit, try that - - if (part.contains("(") && part.endsWith(")")) { - // the unitsfile is looking for a nonlinear unit - final String[] unitAndValue = part.split("\\("); - - // this will work because I've checked that it contains a ( - final String unitName = unitAndValue[0]; - final String valueStringWithBracket = unitAndValue[unitAndValue.length - 1]; - final String valueString = valueStringWithBracket.substring(0, valueStringWithBracket.length() - 1); - final double value; - - // try to get the value - else throw an error - try { - value = Double.parseDouble(valueString); - } catch (final NumberFormatException e2) { - throw new IllegalArgumentException("Unparseable value " + valueString); - } - - // get this unit in a linear form - if (!this.containsPrefixlessUnitName(unitName)) - throw new IllegalArgumentException("Unrecognized unit name \"" + part + "\"."); - final Unit partUnit = this.getPrefixlessUnit(unitName); - final LinearUnit multiplier = partUnit.getBase().times(partUnit.convertToBase(value)); - - // finally, add it to the expression - if (dividing) { - unit = unit.dividedBy(multiplier); - dividing = false; - } else { - unit = unit.times(multiplier); - } - } else { - // check for exponientation - if (part.contains("^")) { - final String[] valueAndExponent = part.split("\\^"); - // this will always work because of the contains check - final String valueString = valueAndExponent[0]; - final String exponentString = valueAndExponent[valueAndExponent.length - 1]; - - LinearUnit value; - - // first, try to get the value - try { - final double valueAsNumber = Double.parseDouble(valueString); - - value = SI.SI.getBaseUnit(UnitDimension.EMPTY).times(valueAsNumber); - } catch (final NumberFormatException e2) { - - // look for a unit - if (!this.containsUnitName(valueString)) - throw new IllegalArgumentException("Unrecognized unit name \"" + valueString + "\"."); - final Unit valueUnit = this.getUnit(valueString); - - // try to turn the value into a linear unit - if (valueUnit instanceof LinearUnit) { - value = (LinearUnit) valueUnit; - } else if (valueUnit instanceof BaseUnit) { - value = ((BaseUnit) valueUnit).asLinearUnit(); - } else - throw new IllegalArgumentException("Only linear and base units can be exponientated."); - } - - // now, try to get the exponent - final int exponent; - try { - exponent = Integer.parseInt(exponentString); - } catch (final NumberFormatException e2) { - throw new IllegalArgumentException("Exponents must be integers."); - } - - final LinearUnit exponientated = value.toExponent(exponent); - - if (dividing) { - unit = unit.dividedBy(exponientated); - dividing = false; - } else { - unit = unit.times(exponientated); - } - } else { - // no exponent - look for a unit - // the unitsfile is looking for a linear unit - if (!this.containsUnitName(part)) - throw new IllegalArgumentException("Unrecognized unit name \"" + part + "\"."); - Unit other = this.getUnit(part); - if (other instanceof BaseUnit) { - other = ((BaseUnit) other).asLinearUnit(); - } - if (other instanceof LinearUnit) { - if (dividing) { - unit = unit.dividedBy((LinearUnit) other); - dividing = false; - } else { - unit = unit.times((LinearUnit) other); - } - } else - throw new IllegalArgumentException( - "Only linear or base units can be multiplied and divided. If you want to use a non-linear unit, try the format UNITNAME(VALUE)."); - } - } - } - } - - // replace conversion-factor-1 linear units with base units - // this improves the autogenerated names, allowing them to use derived SI names - if (unit != null && unit.getConversionFactor() == 1) - return unit.getSystem().getBaseUnit(unit.getDimension()); - else - return unit; - } - - /** - * @return an immutable set of all of the unit names in this database, ignoring prefixes - * @since 2019-01-14 - * @since v0.1.0 - */ - public Set prefixlessUnitNameSet() { - return Collections.unmodifiableSet(this.units.keySet()); - } - - /** - * @return an immutable set of all of the prefix names in this database - * @since 2019-01-14 - * @since v0.1.0 - */ - public Set prefixNameSet() { - return Collections.unmodifiableSet(this.prefixes.keySet()); - } -} diff --git a/src/unitConverter/converterGUI/DelegateListModel.java b/src/unitConverter/converterGUI/DelegateListModel.java deleted file mode 100755 index 0e9b342..0000000 --- a/src/unitConverter/converterGUI/DelegateListModel.java +++ /dev/null @@ -1,232 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter.converterGUI; - -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. - *

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

- * - * @author Adrien Hopkins - * @since 2019-01-14 - * @since v0.1.0 - */ -final class DelegateListModel extends AbstractListModel implements List { - /** - * @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 delegate; - - /** - * Creates the {@code DelegateListModel}. - * - * @param delegate - * list to delegate - * @since 2019-01-14 - * @since v0.1.0 - */ - public DelegateListModel(final List 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 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 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 iterator() { - return this.delegate.iterator(); - } - - @Override - public int lastIndexOf(final Object elem) { - return this.delegate.lastIndexOf(elem); - } - - @Override - public ListIterator listIterator() { - return this.delegate.listIterator(); - } - - @Override - public ListIterator 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 subList(final int fromIndex, final int toIndex) { - return this.delegate.subList(fromIndex, toIndex); - } - - @Override - public Object[] toArray() { - return this.delegate.toArray(); - } - - @Override - public T[] toArray(final T[] a) { - return this.delegate.toArray(a); - } - - @Override - public String toString() { - return this.delegate.toString(); - } -} diff --git a/src/unitConverter/converterGUI/FilterComparator.java b/src/unitConverter/converterGUI/FilterComparator.java deleted file mode 100755 index 27ec3ab..0000000 --- a/src/unitConverter/converterGUI/FilterComparator.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package 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 - */ -public final class FilterComparator implements Comparator { - /** - * 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 comparator; - - /** - * 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. - * @since 2019-01-15 - * @since v0.1.0 - * @throws NullPointerException - * if filter is null - */ - public FilterComparator(final String filter, final Comparator comparator) { - this.filter = Objects.requireNonNull(filter, "filter must not be null."); - this.comparator = comparator; - } - - @Override - public int compare(final String arg0, final String arg1) { - // elements that start with the filter always go first - if (arg0.startsWith(this.filter) && !arg1.startsWith(this.filter)) - return -1; - else if (!arg0.startsWith(this.filter) && arg1.startsWith(this.filter)) - return 1; - - // elements that contain the filter but don't start with them go next - if (arg0.contains(this.filter) && !arg1.contains(this.filter)) - return -1; - else if (!arg0.contains(this.filter) && !arg1.contains(this.filter)) - return 1; - - // other elements go last - if (this.comparator == null) - return arg0.compareTo(arg1); - else - return this.comparator.compare(arg0, arg1); - } -} diff --git a/src/unitConverter/converterGUI/GridBagBuilder.java b/src/unitConverter/converterGUI/GridBagBuilder.java deleted file mode 100755 index e036677..0000000 --- a/src/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 . - */ -package 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. - *

- * Specifies the cell containing the leading edge of the component's display area, where the first cell in a row has - * gridx=0. 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 - * RELATIVE specifies that the component be placed immediately following the component that was added - * to the container just before this component was added. - *

- * The default value is RELATIVE. gridx 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. - *

- * Specifies the cell at the top of the component's display area, where the topmost cell has gridy=0. - * The value RELATIVE specifies that the component be placed just below the component that was added to - * the container just before this component was added. - *

- * The default value is RELATIVE. gridy 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. - *

- * Specifies the number of cells in a row for the component's display area. - *

- * Use REMAINDER to specify that the component's display area will be from gridx to the - * last cell in the row. Use RELATIVE to specify that the component's display area will be from - * gridx to the next to the last one in its row. - *

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

- * Specifies the number of cells in a column for the component's display area. - *

- * Use REMAINDER to specify that the component's display area will be from gridy to the - * last cell in the column. Use RELATIVE to specify that the component's display area will be from - * gridy to the next to the last one in its column. - *

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

- * Specifies how to distribute extra horizontal space. - *

- * The grid bag layout manager calculates the weight of a column to be the maximum weightx 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. - *

- * If all the weights are zero, all the extra space appears between the grids of the cell and the left and right - * edges. - *

- * The default value of this field is 0. weightx 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. - *

- * Specifies how to distribute extra vertical space. - *

- * The grid bag layout manager calculates the weight of a row to be the maximum weighty 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. - *

- * If all the weights are zero, all the extra space appears between the grids of the cell and the top and bottom - * edges. - *

- * The default value of this field is 0. weighty 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. - *

- * This field is used when the component is smaller than its display area. It determines where, within the display - * area, to place the component. - *

- * 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: - * CENTER, NORTH, NORTHEAST, EAST, SOUTHEAST, - * SOUTH, SOUTHWEST, WEST, and NORTHWEST. The orientation - * relative values are: PAGE_START, PAGE_END, LINE_START, - * LINE_END, FIRST_LINE_START, FIRST_LINE_END, LAST_LINE_START - * and LAST_LINE_END. The baseline relative values are: BASELINE, - * BASELINE_LEADING, BASELINE_TRAILING, ABOVE_BASELINE, - * ABOVE_BASELINE_LEADING, ABOVE_BASELINE_TRAILING, BELOW_BASELINE, - * BELOW_BASELINE_LEADING, and BELOW_BASELINE_TRAILING. The default value is - * CENTER. - * - * @serial - * @see #clone() - * @see java.awt.ComponentOrientation - */ - private int anchor; - - /** - * The built {@code GridBagConstraints}'s {@code fill} property. - *

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

- * The following values are valid for fill: - * - *

    - *
  • NONE: Do not resize the component. - *
  • HORIZONTAL: Make the component wide enough to fill its display area horizontally, but do not - * change its height. - *
  • VERTICAL: Make the component tall enough to fill its display area vertically, but do not change - * its width. - *
  • BOTH: Make the component fill its display area entirely. - *
- *

- * The default value is NONE. - * - * @serial - * @see #clone() - */ - private int fill; - - /** - * The built {@code GridBagConstraints}'s {@code insets} property. - *

- * This field specifies the external padding of the component, the minimum amount of space between the component and - * the edges of its display area. - *

- * The default value is new Insets(0, 0, 0, 0). - * - * @serial - * @see #clone() - */ - private Insets insets; - - /** - * The built {@code GridBagConstraints}'s {@code ipadx} property. - *

- * 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 ipadx pixels. - *

- * The default value is 0. - * - * @serial - * @see #clone() - * @see java.awt.GridBagConstraints#ipady - */ - private int ipadx; - - /** - * The built {@code GridBagConstraints}'s {@code ipady} property. - *

- * 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 ipady pixels. - *

- * 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/unitConverter/converterGUI/UnitConverterGUI.java b/src/unitConverter/converterGUI/UnitConverterGUI.java deleted file mode 100755 index cf78ea8..0000000 --- a/src/unitConverter/converterGUI/UnitConverterGUI.java +++ /dev/null @@ -1,777 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter.converterGUI; - -import java.awt.BorderLayout; -import java.awt.GridLayout; -import java.io.File; -import java.math.BigDecimal; -import java.math.MathContext; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.function.Predicate; - -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JFormattedTextField; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSlider; -import javax.swing.JTabbedPane; -import javax.swing.JTextArea; -import javax.swing.JTextField; -import javax.swing.ListModel; -import javax.swing.ListSelectionModel; - -import unitConverter.UnitsDatabase; -import unitConverter.dimension.StandardDimensions; -import unitConverter.dimension.UnitDimension; -import unitConverter.unit.AbstractUnit; -import unitConverter.unit.NonlinearUnits; -import unitConverter.unit.SI; -import unitConverter.unit.Unit; -import unitConverter.unit.UnitPrefix; - -/** - * @author Adrien Hopkins - * @since 2018-12-27 - * @since v0.1.0 - */ -final class UnitConverterGUI { - private static class Presenter { - /** The presenter's associated view. */ - private final View view; - - /** The units known by the program. */ - private final UnitsDatabase units; - - /** The names of all of the units */ - private final List unitNames; - - /** The names of all of the units, but filtered */ - private final DelegateListModel unitNamesFiltered; - - /** The names of all of the prefixes */ - private final List prefixNames; - - /** The names of all of the prefixes */ - private final DelegateListModel prefixNamesFiltered; - - private final Comparator prefixNameComparator; - - private int significantFigures = 6; - - /** - * 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.units = new UnitsDatabase(); - this.units.addUnit("metre", SI.METRE); - this.units.addUnit("kilogram", SI.KILOGRAM); - this.units.addUnit("gram", SI.KILOGRAM.dividedBy(1000)); - this.units.addUnit("second", SI.SECOND); - this.units.addUnit("ampere", SI.AMPERE); - this.units.addUnit("kelvin", SI.KELVIN); - this.units.addUnit("mole", SI.MOLE); - this.units.addUnit("candela", SI.CANDELA); - this.units.addUnit("bit", SI.SI.getBaseUnit(StandardDimensions.INFORMATION)); - this.units.addUnit("unit", SI.SI.getBaseUnit(UnitDimension.EMPTY)); - // nonlinear units - must be loaded manually - this.units.addUnit("tempCelsius", NonlinearUnits.CELSIUS); - this.units.addUnit("tempFahrenheit", NonlinearUnits.FAHRENHEIT); - - this.units.addAllFromFile(new File("unitsfile.txt")); - - // 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.units.containsPrefixName(o1)) - return -1; - else if (!Presenter.this.units.containsPrefixName(o2)) - return 1; - - final UnitPrefix p1 = Presenter.this.units.getPrefix(o1); - final UnitPrefix p2 = Presenter.this.units.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.units.prefixlessUnitNameSet()); - this.unitNames.sort(null); // sorts it using Comparable - - this.unitNamesFiltered = new DelegateListModel<>(new ArrayList<>(this.units.prefixlessUnitNameSet())); - this.unitNamesFiltered.sort(null); // sorts it using Comparable - - this.prefixNames = new ArrayList<>(this.units.prefixNameSet()); - this.prefixNames.sort(this.prefixNameComparator); // sorts it using my comparator - - this.prefixNamesFiltered = new DelegateListModel<>(new ArrayList<>(this.units.prefixNameSet())); - this.prefixNamesFiltered.sort(this.prefixNameComparator); // sorts it using my comparator - - System.out.printf("Successfully loaded %d units (%d base units)", AbstractUnit.getUnitCount(), - AbstractUnit.getBaseUnitCount()); - } - - /** - * Runs whenever the convert button is pressed. - * - *

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

- * - * @since 2019-01-26 - * @since v0.1.0 - */ - public final void convert() { - final String fromUnitString = this.view.getFromText(); - final String toUnitString = this.view.getToText(); - - // try to parse from - final Unit from; - try { - from = this.units.getUnitFromExpression(fromUnitString); - } catch (final IllegalArgumentException e) { - this.view.showErrorDialog("Parse Error", "Could not recognize text in From entry: " + e.getMessage()); - return; - } - - final double value; - // try to parse to - final Unit to; - try { - to = this.units.getUnitFromExpression(toUnitString); - } catch (final IllegalArgumentException e) { - this.view.showErrorDialog("Parse Error", "Could not recognize text in To entry: " + e.getMessage()); - return; - } - - // if I can't convert, leave - if (!from.canConvertTo(to)) { - this.view.showErrorDialog("Conversion Error", - String.format("Cannot convert between %s and %s", fromUnitString, toUnitString)); - return; - } - - value = to.convertFromBase(from.convertToBase(1)); - - // round value - final BigDecimal bigValue = new BigDecimal(value).round(new MathContext(this.significantFigures)); - String output = bigValue.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); - } - } - - this.view.setOutputText(String.format("%s = %s %s", fromUnitString, output, toUnitString)); - } - - /** - * Filters the filtered model for units - * - * @param filter - * filter to use - * @since 2019-01-15 - * @since v0.1.0 - */ - private final void filterFilteredPrefixModel(final Predicate filter) { - this.prefixNamesFiltered.clear(); - for (final String prefixName : this.prefixNames) { - if (filter.test(prefixName)) { - this.prefixNamesFiltered.add(prefixName); - } - } - } - - /** - * Filters the filtered model for units - * - * @param filter - * filter to use - * @since 2019-01-15 - * @since v0.1.0 - */ - private final void filterFilteredUnitModel(final Predicate filter) { - this.unitNamesFiltered.clear(); - for (final String unitName : this.unitNames) { - if (filter.test(unitName)) { - this.unitNamesFiltered.add(unitName); - } - } - } - - /** - * @return a list model of all of the unit keys - * @since 2019-01-14 - * @since v0.1.0 - */ - public final ListModel keyListModel() { - return this.unitNamesFiltered; - } - - /** - * Runs whenever the prefix filter is changed. - *

- * Filters the prefix list then sorts it using a {@code FilterComparator}. - *

- * - * @since 2019-01-15 - * @since v0.1.0 - */ - public final void prefixFilterUpdated() { - final String filter = this.view.getPrefixFilterText(); - if (filter.equals("")) { - this.filterFilteredPrefixModel(t -> true); - } else { - this.filterFilteredPrefixModel(t -> t.contains(filter)); - } - this.prefixNamesFiltered.sort(new FilterComparator(filter)); - } - - /** - * @return a list model of all of the prefix names - * @since 2019-01-15 - */ - public final ListModel prefixNameListModel() { - return this.prefixNamesFiltered; - } - - /** - * Runs whenever a prefix is selected in the viewer. - *

- * Shows its information in the text box to the right. - *

- * - * @since 2019-01-15 - * @since v0.1.0 - */ - public final void prefixSelected() { - final int index = this.view.getPrefixListSelection(); - if (index == -1) - return; - else { - final String prefixName = this.prefixNamesFiltered.get(index); - final UnitPrefix prefix = this.units.getPrefix(prefixName); - - this.view.setPrefixTextBoxText(String.format("%s%nMultiplier: %s", prefixName, prefix.getMultiplier())); - } - } - - /** - * @param significantFigures - * new value of significantFigures - * @since 2019-01-15 - */ - public final void setSignificantFigures(final int significantFigures) { - this.significantFigures = significantFigures; - } - - /** - * Runs whenever the unit filter is changed. - *

- * Filters the unit list then sorts it using a {@code FilterComparator}. - *

- * - * @since 2019-01-15 - * @since v0.1.0 - */ - public final void unitFilterUpdated() { - final String filter = this.view.getUnitFilterText(); - if (filter.equals("")) { - this.filterFilteredUnitModel(t -> true); - } else { - this.filterFilteredUnitModel(t -> t.contains(filter)); - } - this.unitNamesFiltered.sort(new FilterComparator(filter)); - } - - /** - * Runs whenever a unit is selected in the viewer. - *

- * Shows its information in the text box to the right. - *

- * - * @since 2019-01-15 - * @since v0.1.0 - */ - public void unitNameSelected() { - final int index = this.view.getUnitListSelection(); - if (index == -1) - return; - else { - final String unitName = this.unitNamesFiltered.get(index); - final Unit unit = this.units.getUnit(unitName); - - this.view.setUnitTextBoxText(unit.toString()); - } - } - } - - private static class View { - /** The view's frame. */ - private final JFrame frame; - /** The view's associated presenter. */ - private final Presenter presenter; - - /** The list of unit names in the unit viewer */ - private final JList unitNameList; - /** The list of prefix names in the prefix viewer */ - private final JList prefixNameList; - /** The unit search box in the unit viewer */ - private final JTextField unitFilterEntry; - /** The text box for unit data in the unit viewer */ - private final JTextArea unitTextBox; - /** The prefix search box in the prefix viewer */ - private final JTextField prefixFilterEntry; - /** The text box for prefix data in the prefix viewer */ - private final JTextArea prefixTextBox; - /** 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; - - /** - * 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(JFrame.EXIT_ON_CLOSE); - - // create the components - this.unitNameList = new JList<>(this.presenter.keyListModel()); - this.prefixNameList = new JList<>(this.presenter.prefixNameListModel()); - this.unitFilterEntry = new JTextField(); - this.unitTextBox = new JTextArea(); - this.prefixFilterEntry = new JTextField(); - this.prefixTextBox = new JTextArea(); - this.fromEntry = new JTextField(); - this.toEntry = new JTextField(); - this.output = new JTextArea(2, 32); - - // create more components - this.initComponents(); - - this.frame.pack(); - } - - /** - * @return text in "From" box in converter panel - * @since 2019-01-15 - * @since v0.1.0 - */ - public String getFromText() { - return this.fromEntry.getText(); - } - - /** - * @return text in prefix filter - * @since 2019-01-15 - * @since v0.1.0 - */ - public String getPrefixFilterText() { - return this.prefixFilterEntry.getText(); - } - - /** - * @return index of selected prefix - * @since 2019-01-15 - * @since v0.1.0 - */ - public int getPrefixListSelection() { - return this.prefixNameList.getSelectedIndex(); - } - - /** - * @return text in "To" box in converter panel - * @since 2019-01-26 - * @since v0.1.0 - */ - public String getToText() { - return this.toEntry.getText(); - } - - /** - * @return text in unit filter - * @see javax.swing.text.JTextComponent#getText() - */ - public String getUnitFilterText() { - return this.unitFilterEntry.getText(); - } - - /** - * @return index of selected unit - * @since 2019-01-15 - * @since v0.1.0 - */ - public int getUnitListSelection() { - return this.unitNameList.getSelectedIndex(); - } - - /** - * 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 - final JTabbedPane masterPane = new JTabbedPane(); - masterPanel.add(masterPane, BorderLayout.CENTER); - - { // a panel for unit conversion using a selector - final JPanel convertUnitPanel = new JPanel(); - masterPane.addTab("Convert Units", convertUnitPanel); - - convertUnitPanel.setLayout(new BorderLayout()); - - { // panel for input part - final JPanel inputPanel = new JPanel(); - convertUnitPanel.add(inputPanel, BorderLayout.CENTER); - - inputPanel.setLayout(new GridLayout(1, 3)); - - { // panel for From things - final JPanel fromPanel = new JPanel(); - inputPanel.add(fromPanel); - - fromPanel.setLayout(new BorderLayout()); - - { // search box for from - final JTextField fromSearch = new JTextField("Search..."); - fromPanel.add(fromSearch, BorderLayout.PAGE_START); - } - - { // list for From units - final JList fromList = new JList<>(); - fromPanel.add(fromList, BorderLayout.CENTER); - } - } - - { // for dimension selector and arrow that represents conversion - final JPanel inBetweenPanel = new JPanel(); - inputPanel.add(inBetweenPanel); - - inBetweenPanel.setLayout(new BorderLayout()); - - { // dimension selector - final JComboBox dimensionSelector = new JComboBox<>( - new String[] {"Select dimension..."}); - 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 - final JPanel toPanel = new JPanel(); - inputPanel.add(toPanel); - - toPanel.setLayout(new BorderLayout()); - - { // search box for to - final JTextField toSearch = new JTextField("Search..."); - toPanel.add(toSearch, BorderLayout.PAGE_START); - } - - { // list for To units - final JList toList = new JList<>(); - toPanel.add(toList, BorderLayout.CENTER); - } - } - - } - - { // 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 - final JTextField valueInput = new JFormattedTextField( - new DecimalFormat("###############0.################")); - valueInputPanel.add(valueInput, BorderLayout.CENTER); - } - } - - { // button to convert - final JButton convertButton = new JButton("Convert"); - outputPanel.add(convertButton); - } - - { // output of conversion - final JLabel outputLabel = new JLabel(); - outputPanel.add(outputLabel); - } - } - } - - { // panel for unit conversion using expressions - final JPanel convertExpressionPanel = new JPanel(); - masterPane.addTab("Convert Unit Expressions", convertExpressionPanel); - - convertExpressionPanel.setLayout(new GridLayout(5, 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.convert()); - } - - { // 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 for specifying precision - final JPanel sigDigPanel = new JPanel(); - convertExpressionPanel.add(sigDigPanel); - - sigDigPanel.setBorder(BorderFactory.createTitledBorder("Significant Digits")); - - { // slider - final JSlider sigDigSlider = new JSlider(0, 12); - sigDigPanel.add(sigDigSlider); - - sigDigSlider.setMajorTickSpacing(4); - sigDigSlider.setMinorTickSpacing(1); - sigDigSlider.setSnapToTicks(true); - sigDigSlider.setPaintTicks(true); - sigDigSlider.setPaintLabels(true); - - sigDigSlider.addChangeListener( - e -> this.presenter.setSignificantFigures(sigDigSlider.getValue())); - } - } - } - - { // panel to look up units - final JPanel unitLookupPanel = new JPanel(); - masterPane.addTab("Unit Viewer", unitLookupPanel); - - unitLookupPanel.setLayout(new GridLayout()); - - { // panel for listing and searching - final JPanel listPanel = new JPanel(); - unitLookupPanel.add(listPanel); - - listPanel.setLayout(new BorderLayout()); - - { // unit search box - listPanel.add(this.unitFilterEntry, BorderLayout.PAGE_START); - this.unitFilterEntry.addCaretListener(e -> this.presenter.unitFilterUpdated()); - } - - { // a list of units - listPanel.add(new JScrollPane(this.unitNameList), BorderLayout.CENTER); - this.unitNameList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // temp - this.unitNameList.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(); - masterPane.addTab("Prefix Viewer", prefixLookupPanel); - - prefixLookupPanel.setLayout(new GridLayout(1, 2)); - - { // panel for listing and seaching - final JPanel prefixListPanel = new JPanel(); - prefixLookupPanel.add(prefixListPanel); - - prefixListPanel.setLayout(new BorderLayout()); - - { // prefix search box - prefixListPanel.add(this.prefixFilterEntry, BorderLayout.PAGE_START); - this.prefixFilterEntry.addCaretListener(e -> this.presenter.prefixFilterUpdated()); - } - - { // a list of prefixes - prefixListPanel.add(new JScrollPane(this.prefixNameList), BorderLayout.CENTER); - this.prefixNameList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // temp - this.prefixNameList.addListSelectionListener(e -> { - this.presenter.prefixSelected(); - }); - } - } - - { // the text box for prefix's toString - prefixLookupPanel.add(this.prefixTextBox); - this.unitTextBox.setEditable(false); - } - } - } - } - - /** - * 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 setOutputText(final String text) { - this.output.setText(text); - } - - /** - * Sets the text of the prefix text box. - * - * @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. - * - * @param t - * text to set - * @see javax.swing.text.JTextComponent#setText(java.lang.String) - */ - public void setUnitTextBoxText(final String t) { - this.unitTextBox.setText(t); - } - - /** - * 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 static void main(final String[] args) { - new View().init(); - } -} diff --git a/src/unitConverter/converterGUI/package-info.java b/src/unitConverter/converterGUI/package-info.java deleted file mode 100644 index 9f7fa57..0000000 --- a/src/unitConverter/converterGUI/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (C) 2019 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -/** - * All classes that work to convert units. - * - * @author Adrien Hopkins - * @since 2019-01-25 - */ -package unitConverter.converterGUI; \ No newline at end of file diff --git a/src/unitConverter/dimension/BaseDimension.java b/src/unitConverter/dimension/BaseDimension.java deleted file mode 100755 index 0c09dce..0000000 --- a/src/unitConverter/dimension/BaseDimension.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter.dimension; - -/** - * A base dimension that makes up {@code UnitDimension} objects. - * - * @author Adrien Hopkins - * @since 2018-12-22 - * @since v0.1.0 - */ -public interface BaseDimension { - /** - * @return the dimension's name - * @since 2018-12-22 - * @since v0.1.0 - */ - String getName(); - - /** - * @return a short string (usually one character) that represents this base dimension - * @since 2018-12-22 - * @since v0.1.0 - */ - String getSymbol(); -} diff --git a/src/unitConverter/dimension/OtherBaseDimension.java b/src/unitConverter/dimension/OtherBaseDimension.java deleted file mode 100755 index 8c6d25d..0000000 --- a/src/unitConverter/dimension/OtherBaseDimension.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter.dimension; - -import java.util.Objects; - -/** - * Non-SI base dimensions. - * - * @author Adrien Hopkins - * @since 2019-01-14 - * @since v0.1.0 - */ -public enum OtherBaseDimension implements BaseDimension { - INFORMATION("Info"), CURRENCY("$$"); - - /** The dimension's symbol */ - private final String symbol; - - /** - * Creates the {@code SIBaseDimension}. - * - * @param symbol - * dimension's symbol - * @since 2018-12-11 - * @since v0.1.0 - */ - private OtherBaseDimension(final String symbol) { - this.symbol = Objects.requireNonNull(symbol, "symbol must not be null."); - } - - @Override - public String getName() { - return this.toString(); - } - - @Override - public String getSymbol() { - return this.symbol; - } -} diff --git a/src/unitConverter/dimension/SIBaseDimension.java b/src/unitConverter/dimension/SIBaseDimension.java deleted file mode 100755 index 928d8d6..0000000 --- a/src/unitConverter/dimension/SIBaseDimension.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter.dimension; - -import java.util.Objects; - -/** - * The seven base dimensions that make up the SI. - * - * @author Adrien Hopkins - * @since 2018-12-11 - * @since v0.1.0 - */ -public enum SIBaseDimension implements BaseDimension { - LENGTH("L"), MASS("M"), TIME("T"), ELECTRIC_CURRENT("I"), TEMPERATURE("\u0398"), // u0398 is the theta symbol - QUANTITY("N"), LUMINOUS_INTENSITY("J"); - - /** The dimension's symbol */ - private final String symbol; - - /** - * Creates the {@code SIBaseDimension}. - * - * @param symbol - * dimension's symbol - * @since 2018-12-11 - * @since v0.1.0 - */ - private SIBaseDimension(final String symbol) { - this.symbol = Objects.requireNonNull(symbol, "symbol must not be null."); - } - - @Override - public String getName() { - return this.toString(); - } - - @Override - public String getSymbol() { - return this.symbol; - } - -} diff --git a/src/unitConverter/dimension/StandardDimensions.java b/src/unitConverter/dimension/StandardDimensions.java deleted file mode 100755 index b3edb7d..0000000 --- a/src/unitConverter/dimension/StandardDimensions.java +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter.dimension; - -/** - * All of the dimensions that are used by the SI. - * - * @author Adrien Hopkins - * @since 2018-12-11 - * @since v0.1.0 - */ -public final class StandardDimensions { - // base dimensions - public static final UnitDimension EMPTY = UnitDimension.EMPTY; - public static final UnitDimension LENGTH = UnitDimension.getBase(SIBaseDimension.LENGTH); - public static final UnitDimension MASS = UnitDimension.getBase(SIBaseDimension.MASS); - public static final UnitDimension TIME = UnitDimension.getBase(SIBaseDimension.TIME); - public static final UnitDimension ELECTRIC_CURRENT = UnitDimension.getBase(SIBaseDimension.ELECTRIC_CURRENT); - public static final UnitDimension TEMPERATURE = UnitDimension.getBase(SIBaseDimension.TEMPERATURE); - public static final UnitDimension QUANTITY = UnitDimension.getBase(SIBaseDimension.QUANTITY); - public static final UnitDimension LUMINOUS_INTENSITY = UnitDimension.getBase(SIBaseDimension.LUMINOUS_INTENSITY); - public static final UnitDimension INFORMATION = UnitDimension.getBase(OtherBaseDimension.INFORMATION); - public static final UnitDimension CURRENCY = UnitDimension.getBase(OtherBaseDimension.CURRENCY); - // derived dimensions without named SI units - public static final UnitDimension AREA = LENGTH.times(LENGTH); - - public static final UnitDimension VOLUME = AREA.times(LENGTH); - public static final UnitDimension VELOCITY = LENGTH.dividedBy(TIME); - public static final UnitDimension ACCELERATION = VELOCITY.dividedBy(TIME); - public static final UnitDimension WAVENUMBER = EMPTY.dividedBy(LENGTH); - public static final UnitDimension MASS_DENSITY = MASS.dividedBy(VOLUME); - public static final UnitDimension SURFACE_DENSITY = MASS.dividedBy(AREA); - public static final UnitDimension SPECIFIC_VOLUME = VOLUME.dividedBy(MASS); - public static final UnitDimension CURRENT_DENSITY = ELECTRIC_CURRENT.dividedBy(AREA); - public static final UnitDimension MAGNETIC_FIELD_STRENGTH = ELECTRIC_CURRENT.dividedBy(LENGTH); - public static final UnitDimension CONCENTRATION = QUANTITY.dividedBy(VOLUME); - public static final UnitDimension MASS_CONCENTRATION = CONCENTRATION.times(MASS); - public static final UnitDimension LUMINANCE = LUMINOUS_INTENSITY.dividedBy(AREA); - public static final UnitDimension REFRACTIVE_INDEX = VELOCITY.dividedBy(VELOCITY); - public static final UnitDimension REFLACTIVE_PERMEABILITY = EMPTY.times(EMPTY); - public static final UnitDimension ANGLE = LENGTH.dividedBy(LENGTH); - public static final UnitDimension SOLID_ANGLE = AREA.dividedBy(AREA); - // derived dimensions with named SI units - public static final UnitDimension FREQUENCY = EMPTY.dividedBy(TIME); - - public static final UnitDimension FORCE = MASS.times(ACCELERATION); - public static final UnitDimension ENERGY = FORCE.times(LENGTH); - public static final UnitDimension POWER = ENERGY.dividedBy(TIME); - public static final UnitDimension ELECTRIC_CHARGE = ELECTRIC_CURRENT.times(TIME); - public static final UnitDimension VOLTAGE = ENERGY.dividedBy(ELECTRIC_CHARGE); - public static final UnitDimension CAPACITANCE = ELECTRIC_CHARGE.dividedBy(VOLTAGE); - public static final UnitDimension ELECTRIC_RESISTANCE = VOLTAGE.dividedBy(ELECTRIC_CURRENT); - public static final UnitDimension ELECTRIC_CONDUCTANCE = ELECTRIC_CURRENT.dividedBy(VOLTAGE); - public static final UnitDimension MAGNETIC_FLUX = VOLTAGE.times(TIME); - public static final UnitDimension MAGNETIC_FLUX_DENSITY = MAGNETIC_FLUX.dividedBy(AREA); - public static final UnitDimension INDUCTANCE = MAGNETIC_FLUX.dividedBy(ELECTRIC_CURRENT); - public static final UnitDimension LUMINOUS_FLUX = LUMINOUS_INTENSITY.times(SOLID_ANGLE); - public static final UnitDimension ILLUMINANCE = LUMINOUS_FLUX.dividedBy(AREA); - public static final UnitDimension SPECIFIC_ENERGY = ENERGY.dividedBy(MASS); - public static final UnitDimension CATALYTIC_ACTIVITY = QUANTITY.dividedBy(TIME); - - // You may NOT get StandardDimensions instances! - private StandardDimensions() { - throw new AssertionError(); - } -} diff --git a/src/unitConverter/dimension/UnitDimension.java b/src/unitConverter/dimension/UnitDimension.java deleted file mode 100755 index 40e5bbc..0000000 --- a/src/unitConverter/dimension/UnitDimension.java +++ /dev/null @@ -1,241 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter.dimension; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -/** - * An object that represents what a unit measures, like length, mass, area, energy, etc. - * - * @author Adrien Hopkins - * @since 2018-12-11 - * @since v0.1.0 - */ -public final class UnitDimension { - /** - * The unit dimension where every exponent is zero - * - * @since 2018-12-12 - * @since v0.1.0 - */ - public static final UnitDimension EMPTY = new UnitDimension(new HashMap<>()); - - /** - * Gets an UnitDimension that has 1 of a certain dimension and nothing else - * - * @param dimension - * dimension to get - * @return unit dimension - * @since 2018-12-11 - * @since v0.1.0 - */ - public static final UnitDimension getBase(final BaseDimension dimension) { - final Map map = new HashMap<>(); - map.put(dimension, 1); - return new UnitDimension(map); - } - - /** - * The base dimensions that make up this dimension. - * - * @since 2018-12-11 - * @since v0.1.0 - */ - final Map exponents; - - /** - * Creates the {@code UnitDimension}. - * - * @param exponents - * base dimensions that make up this dimension - * @since 2018-12-11 - * @since v0.1.0 - */ - private UnitDimension(final Map exponents) { - this.exponents = new HashMap<>(exponents); - } - - /** - * Divides this dimension by another - * - * @param other - * other dimension - * @return quotient of two dimensions - * @since 2018-12-11 - * @since v0.1.0 - */ - public UnitDimension dividedBy(final UnitDimension other) { - final Map map = new HashMap<>(this.exponents); - - for (final BaseDimension key : other.exponents.keySet()) { - if (map.containsKey(key)) { - // add the dimensions - map.put(key, map.get(key) - other.exponents.get(key)); - } else { - map.put(key, -other.exponents.get(key)); - } - } - return new UnitDimension(map); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof UnitDimension)) - return false; - final UnitDimension other = (UnitDimension) obj; - - // anything with a value of 0 is equal to a nonexistent value - for (final BaseDimension b : this.getBaseSet()) { - if (this.exponents.get(b) != other.exponents.get(b)) - if (!(this.exponents.get(b) == 0 && !other.exponents.containsKey(b))) - return false; - } - for (final BaseDimension b : other.getBaseSet()) { - if (this.exponents.get(b) != other.exponents.get(b)) - if (!(other.exponents.get(b) == 0 && !this.exponents.containsKey(b))) - return false; - } - return true; - } - - /** - * @return a set of all of the base dimensions with non-zero exponents that make up this dimension. - * @since 2018-12-12 - * @since v0.1.0 - */ - public final Set getBaseSet() { - final Set dimensions = new HashSet<>(); - - // add all dimensions with a nonzero exponent - they shouldn't be there in the first place - for (final BaseDimension 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 BaseDimension dimension) { - return this.exponents.getOrDefault(dimension, 0); - } - - @Override - public int hashCode() { - return Objects.hash(this.exponents); - } - - /** - * @return true if this dimension is a base, i.e. it has one exponent of one and no other nonzero exponents - * @since 2019-01-15 - * @since v0.1.0 - */ - public boolean isBase() { - int oneCount = 0; - boolean twoOrMore = false; // has exponents of 2 or more - for (final BaseDimension b : this.getBaseSet()) { - if (this.exponents.get(b) == 1) { - oneCount++; - } else if (this.exponents.get(b) != 0) { - twoOrMore = true; - } - } - return (oneCount == 0 || oneCount == 1) && !twoOrMore; - } - - /** - * Multiplies this dimension by another - * - * @param other - * other dimension - * @return product of two dimensions - * @since 2018-12-11 - * @since v0.1.0 - */ - public UnitDimension times(final UnitDimension other) { - final Map map = new HashMap<>(this.exponents); - - for (final BaseDimension key : other.exponents.keySet()) { - if (map.containsKey(key)) { - // add the dimensions - map.put(key, map.get(key) + other.exponents.get(key)); - } else { - map.put(key, other.exponents.get(key)); - } - } - return new UnitDimension(map); - } - - /** - * Returns this dimension, but to an exponent - * - * @param exp - * exponent - * @return result of exponientation - * @since 2019-01-15 - * @since v0.1.0 - */ - public UnitDimension toExponent(final int exp) { - final Map map = new HashMap<>(this.exponents); - for (final BaseDimension key : this.exponents.keySet()) { - map.put(key, this.getExponent(key) * exp); - } - return new UnitDimension(map); - } - - @Override - public String toString() { - final List positiveStringComponents = new ArrayList<>(); - final List negativeStringComponents = new ArrayList<>(); - - // for each base dimension that makes up this dimension, add it and its exponent - for (final BaseDimension dimension : this.getBaseSet()) { - final int exponent = this.exponents.get(dimension); - if (exponent > 0) { - positiveStringComponents.add(String.format("%s^%d", dimension.getSymbol(), exponent)); - } else if (exponent < 0) { - negativeStringComponents.add(String.format("%s^%d", dimension.getSymbol(), -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/unitConverter/dimension/UnitDimensionTest.java b/src/unitConverter/dimension/UnitDimensionTest.java deleted file mode 100755 index 86db1b8..0000000 --- a/src/unitConverter/dimension/UnitDimensionTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter.dimension; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static unitConverter.dimension.StandardDimensions.AREA; -import static unitConverter.dimension.StandardDimensions.ENERGY; -import static unitConverter.dimension.StandardDimensions.LENGTH; -import static unitConverter.dimension.StandardDimensions.MASS; -import static unitConverter.dimension.StandardDimensions.MASS_DENSITY; -import static unitConverter.dimension.StandardDimensions.QUANTITY; -import static unitConverter.dimension.StandardDimensions.TIME; -import static unitConverter.dimension.StandardDimensions.VOLUME; - -import org.junit.jupiter.api.Test; - -/** - * Tests for {@link UnitDimension}. - * - * @author Adrien Hopkins - * @since 2018-12-12 - * @since v0.1.0 - */ -class UnitDimensionTest { - /** - * Tests {@link UnitDimension#equals} - * - * @since 2018-12-12 - * @since v0.1.0 - */ - @Test - void testEquals() { - assertEquals(LENGTH, LENGTH); - assertFalse(LENGTH.equals(QUANTITY)); - } - - /** - * Tests {@code UnitDimension}'s exponentiation - * - * @since 2019-01-15 - * @since v0.1.0 - */ - @Test - void testExponents() { - assertEquals(1, LENGTH.getExponent(SIBaseDimension.LENGTH)); - assertEquals(3, VOLUME.getExponent(SIBaseDimension.LENGTH)); - } - - /** - * Tests {@code UnitDimension}'s multiplication and division. - * - * @since 2018-12-12 - * @since v0.1.0 - */ - @Test - 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/unitConverter/dimension/package-info.java b/src/unitConverter/dimension/package-info.java deleted file mode 100755 index 74895ce..0000000 --- a/src/unitConverter/dimension/package-info.java +++ /dev/null @@ -1,23 +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 . - */ -/** - * Everything to do with what a unit measures, or its dimension. - * - * @author Adrien Hopkins - * @since 2018-12-22 - */ -package unitConverter.dimension; \ No newline at end of file diff --git a/src/unitConverter/package-info.java b/src/unitConverter/package-info.java deleted file mode 100644 index e2f7ff7..0000000 --- a/src/unitConverter/package-info.java +++ /dev/null @@ -1,23 +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 . - */ -/** - * A program that converts units. - * - * @author Adrien Hopkins - * @since 2019-01-25 - */ -package unitConverter; \ No newline at end of file diff --git a/src/unitConverter/unit/AbstractUnit.java b/src/unitConverter/unit/AbstractUnit.java deleted file mode 100644 index 24814e8..0000000 --- a/src/unitConverter/unit/AbstractUnit.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 . - */ -package unitConverter.unit; - -import java.util.Objects; - -import unitConverter.dimension.UnitDimension; - -/** - * The default abstract implementation of the {@code Unit} interface. - * - * @author Adrien Hopkins - * @since 2018-12-22 - * @since v0.1.0 - */ -public abstract class AbstractUnit implements Unit { - /** - * The number of units created, including base units. - * - * @since 2019-01-02 - * @since v0.1.0 - */ - private static long unitCount = 0; - - /** - * The number of base units created. - * - * @since 2019-01-02 - * @since v0.1.0 - */ - private static long baseUnitCount = 0; - - /** - * @return number of base units created - * @since 2019-01-02 - * @since v0.1.0 - */ - public static final long getBaseUnitCount() { - return baseUnitCount; - } - - /** - * @return number of units created - * @since 2019-01-02 - * @since v0.1.0 - */ - public static final long getUnitCount() { - return unitCount; - } - - /** - * Increments the number of base units. - * - * @since 2019-01-15 - * @since v0.1.0 - */ - public static final void incrementBaseUnitCounter() { - baseUnitCount++; - } - - /** - * Increments the number of units. - * - * @since 2019-01-15 - * @since v0.1.0 - */ - public static final void incrementUnitCounter() { - unitCount++; - } - - /** - * The dimension, or what the unit measures. - * - * @since 2018-12-22 - * @since v0.1.0 - */ - private final UnitDimension dimension; - - /** - * The unit's base unit. Values converted by {@code convertFromBase} and {@code convertToBase} are expressed in this - * unit. - * - * @since 2018-12-22 - * @since v0.1.0 - */ - private final BaseUnit base; - - /** - * The system that this unit is a part of. - * - * @since 2018-12-23 - * @since v0.1.0 - */ - private final UnitSystem system; - - /** - * Creates the {@code AbstractUnit}. - * - * @param base - * unit's base - * @throws NullPointerException - * if name, symbol or base is null - * @since 2018-12-22 - * @since v0.1.0 - */ - public AbstractUnit(final BaseUnit base) { - this.base = Objects.requireNonNull(base, "base must not be null."); - this.dimension = this.base.getDimension(); - this.system = this.base.getSystem(); - } - - /** - * Creates the {@code AbstractUnit} using a unique dimension. This constructor is for making base units and should - * only be used by {@code BaseUnit}. - * - * @param dimension - * dimension measured by unit - * @param system - * system that unit is a part of - * @throws AssertionError - * if this constructor is not run by {@code BaseUnit} or a subclass - * @throws NullPointerException - * if name, symbol or dimension is null - * @since 2018-12-23 - * @since v0.1.0 - */ - AbstractUnit(final UnitDimension dimension, final UnitSystem system) { - // try to set this as a base unit - if (this instanceof BaseUnit) { - this.base = (BaseUnit) this; - } else - throw new AssertionError(); - - this.dimension = Objects.requireNonNull(dimension, "dimension must not be null."); - this.system = Objects.requireNonNull(system, "system must not be null."); - } - - @Override - public final BaseUnit getBase() { - return this.base; - } - - @Override - public final UnitDimension getDimension() { - return this.dimension; - } - - @Override - public final UnitSystem getSystem() { - return this.system; - } - - // TODO document and revise units' toString methods - @Override - public String toString() { - return String.format("%s-derived unit of dimension %s", this.getSystem(), this.getDimension()); - } -} diff --git a/src/unitConverter/unit/BaseUnit.java b/src/unitConverter/unit/BaseUnit.java deleted file mode 100755 index fe36c45..0000000 --- a/src/unitConverter/unit/BaseUnit.java +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter.unit; - -import java.util.Objects; - -import unitConverter.dimension.UnitDimension; - -/** - * A unit that is the base for its dimension. It does not have to be for a base dimension, so units like the Newton and - * Joule are still base units. - * - * @author Adrien Hopkins - * @since 2018-12-23 - * @since v0.1.0 - */ -public final class BaseUnit extends AbstractUnit { - /** - * Is this unit a full base (i.e. m, s, ... but not N, J, ...) - * - * @since 2019-01-15 - * @since v0.1.0 - */ - private final boolean isFullBase; - - /** - * Creates the {@code BaseUnit}. - * - * @param dimension - * dimension measured by unit - * @param system - * system that unit is a part of - * @param name - * name of unit - * @param symbol - * symbol of unit - * @since 2018-12-23 - * @since v0.1.0 - */ - BaseUnit(final UnitDimension dimension, final UnitSystem system) { - super(dimension, system); - this.isFullBase = dimension.isBase(); - } - - /** - * @return this unit as a {@code LinearUnit} - * @since 2019-01-25 - * @since v0.1.0 - */ - public LinearUnit asLinearUnit() { - return this.times(1); - } - - @Override - public double convertFromBase(final double value) { - return value; - } - - @Override - public double convertToBase(final double value) { - return value; - } - - /** - * Divides this unit by another unit. - * - * @param other - * unit to divide by - * @return quotient of two units - * @throws IllegalArgumentException - * if this unit's system is not other's system - * @throws NullPointerException - * if other is null - * @since 2018-12-22 - * @since v0.1.0 - */ - public BaseUnit dividedBy(final BaseUnit other) { - Objects.requireNonNull(other, "other must not be null."); - if (!this.getSystem().equals(other.getSystem())) - throw new IllegalArgumentException("Incompatible base units for division."); - return new BaseUnit(this.getDimension().dividedBy(other.getDimension()), this.getSystem()); - } - - /** - * Divides this unit by a divisor - * - * @param divisor - * amount to divide by - * @return quotient - * @since 2018-12-23 - * @since v0.1.0 - */ - public LinearUnit dividedBy(final double divisor) { - return new LinearUnit(this, 1 / divisor); - } - - @Override - public boolean equals(final Object obj) { - if (!(obj instanceof BaseUnit)) - return false; - final BaseUnit other = (BaseUnit) obj; - return Objects.equals(this.getSystem(), other.getSystem()) - && Objects.equals(this.getDimension(), other.getDimension()); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = result * prime + this.getSystem().hashCode(); - result = result * prime + this.getDimension().hashCode(); - return result; - } - - /** - * Multiplies this unit by another unit. - * - * @param other - * unit to multiply by - * @return product of two units - * @throws IllegalArgumentException - * if this unit's system is not other's system - * @throws NullPointerException - * if other is null - * @since 2018-12-22 - * @since v0.1.0 - */ - public BaseUnit times(final BaseUnit other) { - Objects.requireNonNull(other, "other must not be null."); - if (!this.getSystem().equals(other.getSystem())) - throw new IllegalArgumentException("Incompatible base units for multiplication."); - return new BaseUnit(this.getDimension().times(other.getDimension()), this.getSystem()); - } - - /** - * Multiplies this unit by a multiplier. - * - * @param multiplier - * amount to multiply by - * @return product - * @since 2018-12-23 - * @since v0.1.0 - */ - public LinearUnit times(final double multiplier) { - return new LinearUnit(this, multiplier); - } - - /** - * Returns this unit, but to an exponent. - * - * @param exponent - * exponent - * @return result of exponentiation - * @since 2019-01-15 - * @since v0.1.0 - */ - public BaseUnit toExponent(final int exponent) { - return this.getSystem().getBaseUnit(this.getDimension().toExponent(exponent)); - } - - @Override - public String toString() { - return String.format("%s base unit of%s dimension %s", this.getSystem(), this.isFullBase ? " base" : "", - this.getDimension()); - } -} diff --git a/src/unitConverter/unit/DefaultUnitPrefix.java b/src/unitConverter/unit/DefaultUnitPrefix.java deleted file mode 100755 index d19161b..0000000 --- a/src/unitConverter/unit/DefaultUnitPrefix.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter.unit; - -import java.util.Objects; - -/** - * The default implementation of {@code UnitPrefix}, which contains a multiplier and nothing else. - * - * @author Adrien Hopkins - * @since 2019-01-14 - * @since v0.1.0 - */ -public final class DefaultUnitPrefix implements UnitPrefix { - private final double multiplier; - - /** - * Creates the {@code DefaultUnitPrefix}. - * - * @param multiplier - * @since 2019-01-14 - */ - public DefaultUnitPrefix(final double multiplier) { - this.multiplier = multiplier; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof DefaultUnitPrefix)) - return false; - final DefaultUnitPrefix other = (DefaultUnitPrefix) obj; - return Double.doubleToLongBits(this.multiplier) == Double.doubleToLongBits(other.multiplier); - } - - @Override - public double getMultiplier() { - return this.multiplier; - } - - @Override - public int hashCode() { - return Objects.hash(this.multiplier); - } - - @Override - public String toString() { - return String.format("Unit prefix equal to %s", this.multiplier); - } -} diff --git a/src/unitConverter/unit/LinearUnit.java b/src/unitConverter/unit/LinearUnit.java deleted file mode 100644 index b786b3b..0000000 --- a/src/unitConverter/unit/LinearUnit.java +++ /dev/null @@ -1,184 +0,0 @@ -/** - * Copyright (C) 2019 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter.unit; - -import java.util.Objects; - -import unitConverter.dimension.UnitDimension; - -/** - * A unit that is equal to a certain number multiplied by its base. - * - * @author Adrien Hopkins - * @since 2018-12-22 - * @since v0.1.0 - */ -public final class LinearUnit extends AbstractUnit { - /** - * The value of one of this unit in this unit's base unit - * - * @since 2018-12-22 - * @since v0.1.0 - */ - private final double conversionFactor; - - /** - * - * Creates the {@code LinearUnit}. - * - * @param base - * unit's base - * @param conversionFactor - * value of one of this unit in its base - * @since 2018-12-23 - * @since v0.1.0 - */ - LinearUnit(final BaseUnit base, final double conversionFactor) { - super(base); - this.conversionFactor = conversionFactor; - } - - /** - * Creates the {@code LinearUnit} as a base unit. - * - * @param dimension - * dimension measured by unit - * @param system - * system unit is part of - * @since 2019-01-25 - * @since v0.1.0 - */ - LinearUnit(final UnitDimension dimension, final UnitSystem system, final double conversionFactor) { - super(dimension, system); - this.conversionFactor = conversionFactor; - } - - @Override - public double convertFromBase(final double value) { - return value / this.getConversionFactor(); - } - - @Override - public double convertToBase(final double value) { - return value * this.getConversionFactor(); - } - - /** - * Divides this unit by a scalar. - * - * @param divisor - * scalar to divide by - * @return quotient - * @since 2018-12-23 - * @since v0.1.0 - */ - public LinearUnit dividedBy(final double divisor) { - return new LinearUnit(this.getBase(), this.getConversionFactor() / divisor); - } - - /** - * Divides this unit by another unit. - * - * @param other - * unit to divide by - * @return quotient of two units - * @throws NullPointerException - * if other is null - * @since 2018-12-22 - * @since v0.1.0 - */ - public LinearUnit dividedBy(final LinearUnit other) { - Objects.requireNonNull(other, "other must not be null"); - final BaseUnit base = this.getBase().dividedBy(other.getBase()); - return new LinearUnit(base, this.getConversionFactor() / other.getConversionFactor()); - } - - @Override - public boolean equals(final Object obj) { - if (!(obj instanceof LinearUnit)) - return false; - final LinearUnit other = (LinearUnit) obj; - return Objects.equals(this.getBase(), other.getBase()) - && Objects.equals(this.getConversionFactor(), other.getConversionFactor()); - } - - /** - * @return conversionFactor - * @since 2018-12-22 - * @since v0.1.0 - */ - public final double getConversionFactor() { - return this.conversionFactor; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = result * prime + this.getBase().hashCode(); - result = result * prime + Double.hashCode(this.getConversionFactor()); - return result; - } - - /** - * 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 new LinearUnit(this.getBase(), this.getConversionFactor() * multiplier); - } - - /** - * Multiplies this unit by another unit. - * - * @param other - * unit to multiply by= - * @return product of two units - * @throws NullPointerException - * if other is null - * @since 2018-12-22 - * @since v0.1.0 - */ - public LinearUnit times(final LinearUnit other) { - Objects.requireNonNull(other, "other must not be null"); - final BaseUnit base = this.getBase().times(other.getBase()); - return new LinearUnit(base, this.getConversionFactor() * other.getConversionFactor()); - } - - /** - * Returns this unit but to an exponent. - * - * @param exponent - * exponent to exponientate unit to - * @return exponientated unit - * @since 2019-01-15 - * @since v0.1.0 - */ - public LinearUnit toExponent(final int exponent) { - return new LinearUnit(this.getBase().toExponent(exponent), Math.pow(this.conversionFactor, exponent)); - } - - @Override - public String toString() { - return super.toString() + String.format(" (equal to %s * base)", this.getConversionFactor()); - } -} diff --git a/src/unitConverter/unit/NonlinearUnits.java b/src/unitConverter/unit/NonlinearUnits.java deleted file mode 100755 index ec1874c..0000000 --- a/src/unitConverter/unit/NonlinearUnits.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter.unit; - -/** - * Some major nonlinear units. - * - * @author Adrien Hopkins - * @since 2019-01-14 - * @since v0.1.0 - */ -public final class NonlinearUnits { - public static final Unit CELSIUS = new AbstractUnit(SI.KELVIN) { - - @Override - public double convertFromBase(final double value) { - return value - 273.15; - } - - @Override - public double convertToBase(final double value) { - return value + 273.15; - } - }; - - public static final Unit FAHRENHEIT = new AbstractUnit(SI.KELVIN) { - - @Override - public double convertFromBase(final double value) { - return 1.8 * value - 459.67; - } - - @Override - public double convertToBase(final double value) { - return (value + 459.67) / 1.8; - } - }; - - // You may NOT get a NonlinearUnits instance. - private NonlinearUnits() { - throw new AssertionError(); - } -} diff --git a/src/unitConverter/unit/SI.java b/src/unitConverter/unit/SI.java deleted file mode 100644 index 54eb4c5..0000000 --- a/src/unitConverter/unit/SI.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter.unit; - -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import unitConverter.dimension.StandardDimensions; -import unitConverter.dimension.UnitDimension; - -/** - * The SI, which holds all SI units - * - * @author Adrien Hopkins - * @since 2018-12-23 - * @since v0.1.0 - */ -public enum SI implements UnitSystem { - SI; - - /** - * This system's base units. - * - * @since 2019-01-25 - * @since v0.1.0 - */ - private static final Set baseUnits = new HashSet<>(); - - // base units - public static final BaseUnit METRE = SI.getBaseUnit(StandardDimensions.LENGTH); - public static final BaseUnit KILOGRAM = SI.getBaseUnit(StandardDimensions.MASS); - public static final BaseUnit SECOND = SI.getBaseUnit(StandardDimensions.TIME); - public static final BaseUnit AMPERE = SI.getBaseUnit(StandardDimensions.ELECTRIC_CURRENT); - public static final BaseUnit KELVIN = SI.getBaseUnit(StandardDimensions.TEMPERATURE); - public static final BaseUnit MOLE = SI.getBaseUnit(StandardDimensions.QUANTITY); - public static final BaseUnit CANDELA = SI.getBaseUnit(StandardDimensions.LUMINOUS_INTENSITY); - - @Override - public BaseUnit getBaseUnit(final UnitDimension dimension) { - // try to find an existing unit before creating a new one - - Objects.requireNonNull(dimension, "dimension must not be null."); - for (final BaseUnit unit : baseUnits) { - // it will be equal since the conditions for equality are dimension and system, - // and system is always SI. - if (unit.getDimension().equals(dimension)) - return unit; - } - // could not find an existing base unit - final BaseUnit unit = new BaseUnit(dimension, this); - baseUnits.add(unit); - return unit; - } - - @Override - public String getName() { - return "SI"; - } -} diff --git a/src/unitConverter/unit/SIPrefix.java b/src/unitConverter/unit/SIPrefix.java deleted file mode 100755 index 54625fb..0000000 --- a/src/unitConverter/unit/SIPrefix.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter.unit; - -/** - * The SI prefixes. - * - * @author Adrien Hopkins - * @since 2019-01-14 - * @since v0.1.0 - */ -public enum SIPrefix implements UnitPrefix { - DECA(10), HECTO(100), KILO(1e3), MEGA(1e6), GIGA(1e9), TERA(1e12), PETA(1e15), EXA(1e18), ZETTA(1e21), YOTTA( - 1e24), DECI(0.1), CENTI(0.01), MILLI( - 1e-3), MICRO(1e-6), NANO(1e-9), PICO(1e-12), FEMTO(1e-15), ATTO(1e-18), ZEPTO(1e-21), YOCTO(1e-24); - - private final double multiplier; - - /** - * Creates the {@code SIPrefix}. - * - * @param multiplier - * prefix's multiplier - * @since 2019-01-14 - * @since v0.1.0 - */ - private SIPrefix(final double multiplier) { - this.multiplier = multiplier; - } - - /** - * @return value - * @since 2019-01-14 - * @since v0.1.0 - */ - @Override - public final double getMultiplier() { - return this.multiplier; - } -} diff --git a/src/unitConverter/unit/Unit.java b/src/unitConverter/unit/Unit.java deleted file mode 100755 index 54f1423..0000000 --- a/src/unitConverter/unit/Unit.java +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter.unit; - -import java.util.Objects; - -import unitConverter.dimension.UnitDimension; - -/** - * A unit that has an associated base unit, and can convert a value expressed in it to and from that base. - * - * @author Adrien Hopkins - * @since 2018-12-22 - * @since v0.1.0 - */ -public interface Unit { - /** - * Checks if a value expressed in this unit can be converted to a value expressed in {@code other} - * - * @param other - * unit to test with - * @return true if the units are compatible - * @since 2019-01-13 - * @since v0.1.0 - */ - default boolean canConvertTo(final Unit other) { - return Objects.equals(this.getBase(), other.getBase()); - } - - /** - * Converts from a value expressed in this unit's base unit to a value expressed in this unit. - *

- * This must be the inverse of {@code convertToBase}, so {@code convertFromBase(convertToBase(value))} must be equal - * to {@code value} for any value, ignoring precision loss by roundoff error. - *

- *

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

- * - * @param value - * value expressed in base unit - * @return value expressed in this unit - * @since 2018-12-22 - * @since v0.1.0 - */ - double convertFromBase(double value); - - /** - * Converts from a value expressed in this unit to a value expressed in this unit's base unit. - *

- * This must be the inverse of {@code convertFromBase}, so {@code convertToBase(convertFromBase(value))} must be - * equal to {@code value} for any value, ignoring precision loss by roundoff error. - *

- *

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

- * - * @param value - * value expressed in this unit - * @return value expressed in base unit - * @since 2018-12-22 - * @since v0.1.0 - */ - double convertToBase(double value); - - /** - *

- * Returns the base unit associated with this unit. - *

- *

- * The dimension of this unit must be equal to the dimension of the returned unit. - *

- *

- * If this unit is a base unit, this method should return this unit.\ - *

- * - * @return base unit associated with this unit - * @since 2018-12-22 - * @since v0.1.0 - */ - BaseUnit getBase(); - - /** - * @return dimension measured by this unit - * @since 2018-12-22 - * @since v0.1.0 - */ - UnitDimension getDimension(); - - /** - * @return system that this unit is a part of - * @since 2018-12-23 - * @since v0.1.0 - */ - UnitSystem getSystem(); -} diff --git a/src/unitConverter/unit/UnitPrefix.java b/src/unitConverter/unit/UnitPrefix.java deleted file mode 100755 index a0c1b7c..0000000 --- a/src/unitConverter/unit/UnitPrefix.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter.unit; - -/** - * A prefix that can be attached onto the front of any unit, which multiplies it by a certain value - * - * @author Adrien Hopkins - * @since 2019-01-14 - * @since v0.1.0 - */ -public interface UnitPrefix { - /** - * @return this prefix's multiplier - * @since 2019-01-14 - * @since v0.1.0 - */ - double getMultiplier(); -} diff --git a/src/unitConverter/unit/UnitSystem.java b/src/unitConverter/unit/UnitSystem.java deleted file mode 100755 index ce8c249..0000000 --- a/src/unitConverter/unit/UnitSystem.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter.unit; - -import java.util.Objects; - -import unitConverter.dimension.UnitDimension; - -/** - * A system of units. Each unit should be aware of its system. - * - * @author Adrien Hopkins - * @since 2018-12-23 - * @since v0.1.0 - */ -public interface UnitSystem { - /** - * Gets a base unit for this system and the provided dimension. - * - * @param dimension - * dimension used by base unit - * @return base unit - * @throws NullPointerException - * if dimension is null - * @since 2019-01-25 - * @since v0.1.0 - */ - default BaseUnit getBaseUnit(final UnitDimension dimension) { - Objects.requireNonNull(dimension, "dimension must not be null."); - return new BaseUnit(dimension, this); - } - - /** - * @return name of system - * @since 2018-12-23 - * @since v0.1.0 - */ - String getName(); -} diff --git a/src/unitConverter/unit/UnitTest.java b/src/unitConverter/unit/UnitTest.java deleted file mode 100755 index c3237eb..0000000 --- a/src/unitConverter/unit/UnitTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (C) 2018 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package unitConverter.unit; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -import unitConverter.dimension.StandardDimensions; - -/** - * Testing the various Unit classes - * - * @author Adrien Hopkins - * @since 2018-12-22 - */ -class UnitTest { - @Test - void testConversion() { - final BaseUnit metre = SI.METRE; - final Unit inch = metre.times(0.0254); - - assertEquals(1.9, inch.convertToBase(75), 0.01); - } - - @Test - void testEquals() { - final BaseUnit metre = SI.METRE; - final Unit meter = SI.SI.getBaseUnit(StandardDimensions.LENGTH); - - assertEquals(metre, meter); - } -} diff --git a/src/unitConverter/unit/package-info.java b/src/unitConverter/unit/package-info.java deleted file mode 100644 index b5deb0c..0000000 --- a/src/unitConverter/unit/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (C) 2019 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -/** - * All of the classes that correspond to the units being converted. - * - * @author Adrien Hopkins - * @since 2019-01-25 - */ -package unitConverter.unit; \ No newline at end of file -- cgit v1.2.3