From bfe1f266922bffd3c0c8d8906535be7621217e7a Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Fri, 22 Mar 2019 19:35:30 -0400 Subject: Unit Expressions are now parsed with the expression parser. Addition and subtraction are now possible. --- unitsfile.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'unitsfile.txt') diff --git a/unitsfile.txt b/unitsfile.txt index 78e51f7..2455c8a 100755 --- a/unitsfile.txt +++ b/unitsfile.txt @@ -136,7 +136,7 @@ steradian m^2 / m^2 sr steradian degree 360 / tau * radian deg degree -° degree +° degree # Nonlinear units, which are not supported by the file reader and must be defined manually # Use tempC(100) for 100 degrees Celsius -- cgit v1.2.3 From e0c5021a9ba85debf0c0722d78f75a0dbcc8376b Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Sat, 13 Apr 2019 12:17:14 -0400 Subject: Implemented the dimension-based converter. Also added a search box list, and fixed a bug with dimension exponentiation. --- dimensionfile.txt | 10 +- src/org/unitConverter/UnitsDatabase.java | 2 +- .../converterGUI/DelegateListModel.java | 10 + .../converterGUI/MutablePredicate.java | 60 ++++++ .../unitConverter/converterGUI/SearchBoxList.java | 205 +++++++++++++++++++++ .../converterGUI/UnitConverterGUI.java | 180 ++++++++++++++---- unitsfile.txt | 20 +- 7 files changed, 444 insertions(+), 43 deletions(-) create mode 100644 src/org/unitConverter/converterGUI/MutablePredicate.java create mode 100644 src/org/unitConverter/converterGUI/SearchBoxList.java (limited to 'unitsfile.txt') diff --git a/dimensionfile.txt b/dimensionfile.txt index d3c068c..7a1da10 100644 --- a/dimensionfile.txt +++ b/dimensionfile.txt @@ -4,10 +4,14 @@ # ! means "look for an existing dimension which I will load at the start" # This is necessary because every dimension must be defined by others, and I need somewhere to start. +# I have excluded electric current and quantity since their units are exclusively SI. + LENGTH ! MASS ! TIME ! -ELECTRIC_CURRENT ! TEMPERATURE ! -QUANTITY ! -LUMINOUS_INTENSITY ! \ No newline at end of file +LUMINOUS_INTENSITY ! + +# Derived Dimensions +VELOCITY LENGTH / TIME +ENERGY MASS * VELOCITY^2 \ No newline at end of file diff --git a/src/org/unitConverter/UnitsDatabase.java b/src/org/unitConverter/UnitsDatabase.java index 626f145..c3d3131 100755 --- a/src/org/unitConverter/UnitsDatabase.java +++ b/src/org/unitConverter/UnitsDatabase.java @@ -65,7 +65,7 @@ public final class UnitsDatabase { final double exponent = exponentUnit.getConversionFactor(); if (DecimalComparison.equals(exponent % 1, 0)) // then exponentiate - return base.toExponent((int) (exponent % 1 + 0.5)); + return base.toExponent((int) (exponent + 0.5)); else // not an integer throw new UnsupportedOperationException("Decimal exponents are currently not supported."); diff --git a/src/org/unitConverter/converterGUI/DelegateListModel.java b/src/org/unitConverter/converterGUI/DelegateListModel.java index e375126..b80f63d 100755 --- a/src/org/unitConverter/converterGUI/DelegateListModel.java +++ b/src/org/unitConverter/converterGUI/DelegateListModel.java @@ -16,6 +16,7 @@ */ package org.unitConverter.converterGUI; +import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -49,6 +50,15 @@ final class DelegateListModel extends AbstractListModel implements List */ private final List delegate; + /** + * Creates an empty {@code DelegateListModel}. + * + * @since 2019-04-13 + */ + public DelegateListModel() { + this(new ArrayList<>()); + } + /** * Creates the {@code DelegateListModel}. * diff --git a/src/org/unitConverter/converterGUI/MutablePredicate.java b/src/org/unitConverter/converterGUI/MutablePredicate.java new file mode 100644 index 0000000..157903c --- /dev/null +++ b/src/org/unitConverter/converterGUI/MutablePredicate.java @@ -0,0 +1,60 @@ +/** + * 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.converterGUI; + +import java.util.function.Predicate; + +/** + * A container for a predicate, which can be changed later. + * + * @author Adrien Hopkins + * @since 2019-04-13 + */ +final class MutablePredicate implements Predicate { + private Predicate predicate; + + /** + * Creates the {@code MutablePredicate}. + * + * @since 2019-04-13 + */ + public MutablePredicate(final Predicate predicate) { + this.predicate = predicate; + } + + /** + * @return predicate + * @since 2019-04-13 + */ + public final Predicate getPredicate() { + return this.predicate; + } + + /** + * @param predicate + * new value of predicate + * @since 2019-04-13 + */ + public final void setPredicate(final Predicate predicate) { + this.predicate = predicate; + } + + @Override + public boolean test(final T t) { + return this.predicate.test(t); + } +} diff --git a/src/org/unitConverter/converterGUI/SearchBoxList.java b/src/org/unitConverter/converterGUI/SearchBoxList.java new file mode 100644 index 0000000..7d3b748 --- /dev/null +++ b/src/org/unitConverter/converterGUI/SearchBoxList.java @@ -0,0 +1,205 @@ +/** + * 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.converterGUI; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.util.ArrayList; +import java.util.Collection; +import java.util.function.Predicate; + +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; + +/** + * @author Adrien Hopkins + * @since 2019-04-13 + */ +final class SearchBoxList extends JPanel { + + /** + * @since 2019-04-13 + */ + private static final long serialVersionUID = 6226930279415983433L; + + /** + * The text to place in an empty search box. + */ + private static final String EMPTY_TEXT = "Search..."; + /** + * The color to use for an empty foreground. + */ + private static final Color EMPTY_FOREGROUND = new Color(192, 192, 192); + + // the components + private final Collection itemsToFilter; + private final DelegateListModel listModel; + private final JTextField searchBox; + private final JList searchItems; + + private boolean searchBoxEmpty = true; + + // I need to do this because, for some reason, Swing is auto-focusing my search box without triggering a focus + // event. + private boolean searchBoxFocused = false; + + private Predicate searchFilter = o -> true; + + /** + * Creates the {@code SearchBoxList}. + * + * @since 2019-04-13 + */ + public SearchBoxList(final Collection itemsToFilter) { + super(new BorderLayout(), true); + this.itemsToFilter = itemsToFilter; + + // create the components + this.listModel = new DelegateListModel<>(new ArrayList<>(itemsToFilter)); + this.searchItems = new JList<>(this.listModel); + + this.searchBox = new JTextField(EMPTY_TEXT); + this.searchBox.setForeground(EMPTY_FOREGROUND); + + // add them to the panel + this.add(this.searchBox, BorderLayout.PAGE_START); + this.add(new JScrollPane(this.searchItems), BorderLayout.CENTER); + + // set up the search box + this.searchBox.addFocusListener(new FocusListener() { + @Override + public void focusGained(final FocusEvent e) { + SearchBoxList.this.searchBoxFocusGained(e); + } + + @Override + public void focusLost(final FocusEvent e) { + SearchBoxList.this.searchBoxFocusLost(e); + } + }); + + this.searchBox.addCaretListener(e -> this.searchBoxTextChanged()); + this.searchBoxEmpty = true; + } + + /** + * Adds an additional filter for searching. + * + * @param filter + * filter to add. + * @since 2019-04-13 + */ + public void addSearchFilter(final Predicate filter) { + this.searchFilter = this.searchFilter.and(filter); + } + + /** + * Resets the search filter. + * + * @since 2019-04-13 + */ + public void clearSearchFilters() { + this.searchFilter = o -> true; + } + + /** + * @return value selected in item list + * @since 2019-04-13 + */ + public String getSelectedValue() { + return this.searchItems.getSelectedValue(); + } + + /** + * Re-applies the filters. + * + * @since 2019-04-13 + */ + public void reapplyFilter() { + final String searchText = this.searchBoxEmpty ? "" : this.searchBox.getText(); + final FilterComparator comparator = new FilterComparator(searchText); + + this.listModel.clear(); + this.itemsToFilter.forEach(string -> { + if (string.toLowerCase().contains(searchText.toLowerCase())) { + this.listModel.add(string); + } + }); + + // applies the custom filters + this.listModel.removeIf(this.searchFilter.negate()); + + // sorts the remaining items + this.listModel.sort(comparator); + } + + /** + * Runs whenever the search box gains focus. + * + * @param e + * focus event + * @since 2019-04-13 + */ + private void searchBoxFocusGained(final FocusEvent e) { + this.searchBoxFocused = true; + if (this.searchBoxEmpty) { + this.searchBox.setText(""); + this.searchBox.setForeground(Color.BLACK); + } + } + + /** + * Runs whenever the search box loses focus. + * + * @param e + * focus event + * @since 2019-04-13 + */ + private void searchBoxFocusLost(final FocusEvent e) { + this.searchBoxFocused = false; + if (this.searchBoxEmpty) { + this.searchBox.setText(EMPTY_TEXT); + this.searchBox.setForeground(EMPTY_FOREGROUND); + } + } + + private void searchBoxTextChanged() { + if (this.searchBoxFocused) { + this.searchBoxEmpty = this.searchBox.getText().equals(""); + } + final String searchText = this.searchBoxEmpty ? "" : this.searchBox.getText(); + final FilterComparator comparator = new FilterComparator(searchText); + + // initialize list with items that match the filter then sort + this.listModel.clear(); + this.itemsToFilter.forEach(string -> { + if (string.toLowerCase().contains(searchText.toLowerCase())) { + this.listModel.add(string); + } + }); + + // applies the custom filters + this.listModel.removeIf(this.searchFilter.negate()); + + // sorts the remaining items + this.listModel.sort(comparator); + } +} diff --git a/src/org/unitConverter/converterGUI/UnitConverterGUI.java b/src/org/unitConverter/converterGUI/UnitConverterGUI.java index 4f5ebeb..9314510 100755 --- a/src/org/unitConverter/converterGUI/UnitConverterGUI.java +++ b/src/org/unitConverter/converterGUI/UnitConverterGUI.java @@ -25,6 +25,7 @@ import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Set; import java.util.function.Predicate; import javax.swing.BorderFactory; @@ -116,9 +117,7 @@ final class UnitConverterGUI { this.units.addDimension("LENGTH", StandardDimensions.LENGTH); this.units.addDimension("MASS", StandardDimensions.MASS); this.units.addDimension("TIME", StandardDimensions.TIME); - this.units.addDimension("ELECTRIC_CURRENT", StandardDimensions.ELECTRIC_CURRENT); this.units.addDimension("TEMPERATURE", StandardDimensions.TEMPERATURE); - this.units.addDimension("QUANTITY", StandardDimensions.QUANTITY); this.units.addDimension("LUMINOUS_INTENSITY", StandardDimensions.LUMINOUS_INTENSITY); this.units.loadUnitsFile(new File("unitsfile.txt")); @@ -238,6 +237,52 @@ final class UnitConverterGUI { this.view.setOutputText(String.format("%s = %s %s", fromUnitString, output, toUnitString)); } + /** + * Converts in the dimension-based converter + * + * @since 2019-04-13 + */ + public final void convertDimensionBased() { + final String fromSelection = this.view.getFromSelection(); + if (fromSelection == null) { + this.view.showErrorDialog("Error", "No unit selected in From field"); + return; + } + final String toSelection = this.view.getToSelection(); + if (toSelection == null) { + this.view.showErrorDialog("Error", "No unit selected in To field"); + return; + } + + final Unit from = this.units.getUnit(fromSelection); + final Unit to = this.units.getUnit(toSelection); + + final String input = this.view.getDimensionBasedInput(); + if (input.equals("")) { + this.view.showErrorDialog("Error", "No value to convert entered."); + return; + } + final double beforeValue = Double.parseDouble(input); + final double value = to.convertFromBase(from.convertToBase(beforeValue)); + + // 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.setDimensionBasedOutputText( + String.format("%s %s = %s %s", input, fromSelection, output, toSelection)); + } + /** * @return a list of all of the unit dimensions * @since 2019-04-13 @@ -365,6 +410,23 @@ final class UnitConverterGUI { this.unitNamesFiltered.sort(new FilterComparator(filter)); } + /** + * Returns true if and only if the unit represented by {@code unitName} has the dimension represented by + * {@code dimensionName}. + * + * @param unitName + * name of unit to test + * @param dimensionName + * name of dimension to test + * @return whether unit has dimenision + * @since 2019-04-13 + */ + public boolean unitMatchesDimension(final String unitName, final String dimensionName) { + final Unit unit = this.units.getUnit(unitName); + final UnitDimension dimension = this.units.getDimension(dimensionName); + return unit.getDimension().equals(dimension); + } + /** * Runs whenever a unit is selected in the viewer. *

@@ -385,6 +447,10 @@ final class UnitConverterGUI { this.view.setUnitTextBoxText(unit.toString()); } } + + public final Set unitNameSet() { + return this.units.prefixlessUnitNameSet(); + } } private static class View { @@ -405,6 +471,14 @@ final class UnitConverterGUI { private final JTextField prefixFilterEntry; /** The text box for prefix data in the prefix viewer */ private final JTextArea prefixTextBox; + /** The panel for "From" in the dimension-based converter */ + private final SearchBoxList fromSearch; + /** The panel for "To" in the dimension-based converter */ + private final SearchBoxList toSearch; + /** The panel for inputting values in the dimension-based converter */ + private final JTextField valueInput; + /** The output area in the dimension-based converter */ + private final JTextArea dimensionBasedOutput; /** The "From" entry in the conversion panel */ private final JTextField fromEntry; /** The "To" entry in the conversion panel */ @@ -430,6 +504,10 @@ final class UnitConverterGUI { this.unitTextBox = new JTextArea(); this.prefixFilterEntry = new JTextField(); this.prefixTextBox = new JTextArea(); + this.fromSearch = new SearchBoxList(this.presenter.unitNameSet()); + this.toSearch = new SearchBoxList(this.presenter.unitNameSet()); + this.valueInput = new JFormattedTextField(new DecimalFormat("###############0.################")); + this.dimensionBasedOutput = new JTextArea(2, 32); this.fromEntry = new JTextField(); this.toEntry = new JTextField(); this.output = new JTextArea(2, 32); @@ -440,6 +518,22 @@ final class UnitConverterGUI { this.frame.pack(); } + /** + * @return value in dimension-based converter + * @since 2019-04-13 + */ + public String getDimensionBasedInput() { + return this.valueInput.getText(); + } + + /** + * @return selection in "From" selector in dimension-based converter + * @since 2019-04-13 + */ + public String getFromSelection() { + return this.fromSearch.getSelectedValue(); + } + /** * @return text in "From" box in converter panel * @since 2019-01-15 @@ -467,6 +561,14 @@ final class UnitConverterGUI { return this.prefixNameList.getSelectedIndex(); } + /** + * @return selection in "To" selector in dimension-based converter + * @since 2019-04-13 + */ + public String getToSelection() { + return this.toSearch.getSelectedValue(); + } + /** * @return text in "To" box in converter panel * @since 2019-01-26 @@ -531,22 +633,17 @@ final class UnitConverterGUI { inputPanel.setLayout(new GridLayout(1, 3)); - { // panel for From things - final JPanel fromPanel = new JPanel(); - inputPanel.add(fromPanel); + final JComboBox dimensionSelector = new JComboBox<>( + this.presenter.dimensionNameList().toArray(new String[0])); + dimensionSelector.setSelectedItem("LENGTH"); - fromPanel.setLayout(new BorderLayout()); + // handle dimension filter + final MutablePredicate dimensionFilter = new MutablePredicate<>(s -> true); - { // search box for from - final JTextField fromSearch = new JTextField("Search..."); - fromPanel.add(fromSearch, BorderLayout.PAGE_START); - } + // panel for From things + inputPanel.add(this.fromSearch); - { // list for From units - final JList fromList = new JList<>(); - fromPanel.add(fromList, BorderLayout.CENTER); - } - } + this.fromSearch.addSearchFilter(dimensionFilter); { // for dimension selector and arrow that represents conversion final JPanel inBetweenPanel = new JPanel(); @@ -555,10 +652,6 @@ final class UnitConverterGUI { inBetweenPanel.setLayout(new BorderLayout()); { // dimension selector - final List dimensionNameList = this.presenter.dimensionNameList(); - dimensionNameList.add(0, "Select a dimension..."); - final JComboBox dimensionSelector = new JComboBox<>( - dimensionNameList.toArray(new String[0])); inBetweenPanel.add(dimensionSelector, BorderLayout.PAGE_START); } @@ -568,23 +661,25 @@ final class UnitConverterGUI { } } - { // panel for To things - final JPanel toPanel = new JPanel(); - inputPanel.add(toPanel); + // panel for To things - toPanel.setLayout(new BorderLayout()); + inputPanel.add(this.toSearch); - { // search box for to - final JTextField toSearch = new JTextField("Search..."); - toPanel.add(toSearch, BorderLayout.PAGE_START); - } + this.toSearch.addSearchFilter(dimensionFilter); - { // list for To units - final JList toList = new JList<>(); - toPanel.add(toList, BorderLayout.CENTER); - } - } + // code for dimension filter + dimensionSelector.addItemListener(e -> { + dimensionFilter.setPredicate(string -> View.this.presenter.unitMatchesDimension(string, + (String) dimensionSelector.getSelectedItem())); + this.fromSearch.reapplyFilter(); + this.toSearch.reapplyFilter(); + }); + // apply the item listener once because I have a default selection + dimensionFilter.setPredicate(string -> View.this.presenter.unitMatchesDimension(string, + (String) dimensionSelector.getSelectedItem())); + this.fromSearch.reapplyFilter(); + this.toSearch.reapplyFilter(); } { // panel for submit and output, and also value entry @@ -605,20 +700,20 @@ final class UnitConverterGUI { } { // value to convert - final JTextField valueInput = new JFormattedTextField( - new DecimalFormat("###############0.################")); - valueInputPanel.add(valueInput, BorderLayout.CENTER); + valueInputPanel.add(this.valueInput, BorderLayout.CENTER); } } { // button to convert final JButton convertButton = new JButton("Convert"); outputPanel.add(convertButton); + + convertButton.addActionListener(e -> this.presenter.convertDimensionBased()); } { // output of conversion - final JLabel outputLabel = new JLabel(); - outputPanel.add(outputLabel); + outputPanel.add(this.dimensionBasedOutput); + this.dimensionBasedOutput.setEditable(false); } } } @@ -762,6 +857,17 @@ final class UnitConverterGUI { } } + /** + * Sets the text in the output of the dimension-based converter. + * + * @param text + * text to set + * @since 2019-04-13 + */ + public void setDimensionBasedOutputText(final String text) { + this.dimensionBasedOutput.setText(text); + } + /** * Sets the text in the output of the conversion panel. * diff --git a/unitsfile.txt b/unitsfile.txt index 2455c8a..14fb6fb 100755 --- a/unitsfile.txt +++ b/unitsfile.txt @@ -171,7 +171,7 @@ arcsecond 1 / 60 arcminute arcsec arcsecond # constants -waterdensity 1 kilogram / litre +waterdensity kilogram / litre # Imperial length units foot 0.3048 m @@ -183,6 +183,10 @@ yd yard mile 1760 yard mi mile +# Compressed notation +kph km / hour +mph mile / hour + # Imperial weight units pound 0.45359237 kg lb pound @@ -231,4 +235,16 @@ metricpint 2 metriccup pint metricpint metricquart 2 metricpint quart metricquart -metricgallon 4 metricquart \ No newline at end of file +metricgallon 4 metricquart + +# Energy units +calorie 4.18 J +cal calorie +Calorie kilocalorie +Cal Calorie + +# Extra units to only include in the dimension-based converter +m/s m / s +km/h km / h +ft/s foot / s +mi/h mile / hour \ No newline at end of file -- cgit v1.2.3 From 4cef115e3fbd228a84ad48eed7af5403e8c8c46e Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Sat, 13 Apr 2019 20:01:26 -0400 Subject: Longer prefixes are now favoured over shorter prefixes. Added 'da-' to the unit file, which was previously missing because it was interpreted as 'deciatto'. 'D-' can still be used. --- src/org/unitConverter/UnitsDatabase.java | 98 +++++++++++++------------------- unitsfile.txt | 1 + 2 files changed, 39 insertions(+), 60 deletions(-) (limited to 'unitsfile.txt') diff --git a/src/org/unitConverter/UnitsDatabase.java b/src/org/unitConverter/UnitsDatabase.java index 9749e9c..901c6ef 100755 --- a/src/org/unitConverter/UnitsDatabase.java +++ b/src/org/unitConverter/UnitsDatabase.java @@ -67,8 +67,8 @@ public final class UnitsDatabase { *

  • Before attempting to search for prefixes in a unit name, this map will first search for a unit name. So, if * there are two units, "B" and "AB", and a prefix "A", this map will favour the unit "AB" over the unit "B" with * the prefix "A", even though they have the same string.
  • - *
  • Shorter prefixes are preferred to longer prefixes. So, if you have units "BC" and "C", and prefixes "AB" and - * "A", inputting "ABC" will return the unit "BC" with the prefix "A", not "C" with the prefix "AB".
  • + *
  • Longer prefixes are preferred to shorter prefixes. So, if you have units "BC" and "C", and prefixes "AB" and + * "A", inputting "ABC" will return the unit "C" with the prefix "AB", not "BC" with the prefix "A".
  • * *

    * @@ -194,6 +194,7 @@ public final class UnitsDatabase { } } + // create the unit name final StringBuilder unitNameBuilder = new StringBuilder(); for (final int i : this.prefixCoordinates) { unitNameBuilder.append(this.prefixNames.get(i)); @@ -256,32 +257,20 @@ public final class UnitsDatabase { @Override public Object[] toArray() { - if (this.map.units.isEmpty()) - // finite, it will work + if (this.map.units.isEmpty() || this.map.prefixes.isEmpty()) return super.toArray(); - else { - if (this.map.prefixes.isEmpty()) - // finite, it will work - return super.toArray(); - else - // infinite set - throw new UnsupportedOperationException("Cannot make an infinite set into an array."); - } + else + // infinite set + throw new UnsupportedOperationException("Cannot make an infinite set into an array."); } @Override public T[] toArray(final T[] a) { - if (this.map.units.isEmpty()) - // finite, it will work + if (this.map.units.isEmpty() || this.map.prefixes.isEmpty()) return super.toArray(a); - else { - if (this.map.prefixes.isEmpty()) - // finite, it will work - return super.toArray(a); - else - // infinite set - throw new UnsupportedOperationException("Cannot make an infinite set into an array."); - } + else + // infinite set + throw new UnsupportedOperationException("Cannot make an infinite set into an array."); } } @@ -437,32 +426,21 @@ public final class UnitsDatabase { @Override public Object[] toArray() { - if (this.map.units.isEmpty()) - // finite, it will work + if (this.map.units.isEmpty() || this.map.prefixes.isEmpty()) return super.toArray(); - else { - if (this.map.prefixes.isEmpty()) - // finite, it will work - return super.toArray(); - else - // infinite set - throw new UnsupportedOperationException("Cannot make an infinite set into an array."); - } + else + // infinite set + throw new UnsupportedOperationException("Cannot make an infinite set into an array."); + } @Override public T[] toArray(final T[] a) { - if (this.map.units.isEmpty()) - // finite, it will work + if (this.map.units.isEmpty() || this.map.prefixes.isEmpty()) return super.toArray(a); - else { - if (this.map.prefixes.isEmpty()) - // finite, it will work - return super.toArray(a); - else - // infinite set - throw new UnsupportedOperationException("Cannot make an infinite set into an array."); - } + else + // infinite set + throw new UnsupportedOperationException("Cannot make an infinite set into an array."); } } @@ -532,26 +510,26 @@ public final class UnitsDatabase { throw new IllegalArgumentException("Attempted to test for a unit using a non-string name."); final String unitName = (String) key; - // Then, look for the shortest prefix that is attached to a valid unit - String shortestPrefix = null; - int shortestLength = Integer.MAX_VALUE; + // Then, look for the longest prefix that is attached to a valid unit + String longestPrefix = null; + int longestLength = 0; for (final String prefixName : this.prefixes.keySet()) { // a prefix name is valid if: // - it is prefixed (i.e. the unit name starts with it) - // - it is shorter than the existing largest prefix (since I am looking for the smallest valid prefix) + // - it is longer than the existing largest prefix (since I am looking for the longest valid prefix) // - the part after the prefix is a valid unit name // - the unit described that name is a linear unit (since only linear units can have prefixes) - if (unitName.startsWith(prefixName) && prefixName.length() < shortestLength) { + if (unitName.startsWith(prefixName) && prefixName.length() > longestLength) { final String rest = unitName.substring(prefixName.length()); if (this.containsKey(rest) && this.get(rest) instanceof LinearUnit) { - shortestPrefix = prefixName; - shortestLength = prefixName.length(); + longestPrefix = prefixName; + longestLength = prefixName.length(); } } } - return shortestPrefix != null; + return longestPrefix != null; } @Override @@ -578,34 +556,34 @@ public final class UnitsDatabase { throw new IllegalArgumentException("Attempted to obtain a unit using a non-string name."); final String unitName = (String) key; - // Then, look for the shortest prefix that is attached to a valid unit - String shortestPrefix = null; - int shortestLength = Integer.MAX_VALUE; + // Then, look for the longest prefix that is attached to a valid unit + String longestPrefix = null; + int longestLength = 0; for (final String prefixName : this.prefixes.keySet()) { // a prefix name is valid if: // - it is prefixed (i.e. the unit name starts with it) - // - it is shorter than the existing largest prefix (since I am looking for the smallest valid prefix) + // - it is longer than the existing largest prefix (since I am looking for the longest valid prefix) // - the part after the prefix is a valid unit name // - the unit described that name is a linear unit (since only linear units can have prefixes) - if (unitName.startsWith(prefixName) && prefixName.length() < shortestLength) { + if (unitName.startsWith(prefixName) && prefixName.length() > longestLength) { final String rest = unitName.substring(prefixName.length()); if (this.containsKey(rest) && this.get(rest) instanceof LinearUnit) { - shortestPrefix = prefixName; - shortestLength = prefixName.length(); + longestPrefix = prefixName; + longestLength = prefixName.length(); } } } // if none found, returns null - if (shortestPrefix == null) + if (longestPrefix == null) return null; else { // get necessary data - final String rest = unitName.substring(shortestLength); + final String rest = unitName.substring(longestLength); // this cast will not fail because I verified that it would work before selecting this prefix final LinearUnit unit = (LinearUnit) this.get(rest); - final UnitPrefix prefix = this.prefixes.get(shortestPrefix); + final UnitPrefix prefix = this.prefixes.get(longestPrefix); return unit.withPrefix(prefix); } diff --git a/unitsfile.txt b/unitsfile.txt index 14fb6fb..78f8117 100755 --- a/unitsfile.txt +++ b/unitsfile.txt @@ -55,6 +55,7 @@ atto- 1e-18 zepto- 1e-21 yocto- 1e-24 +da- deca D- deca h- hecto H- hecto -- cgit v1.2.3 From 77051c4f70f450a4363be7ae587de36efc1fdd54 Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Sun, 14 Apr 2019 07:57:38 -0400 Subject: Added more units and dimensions to the converter. --- .DS_Store | Bin 0 -> 6148 bytes .classpath | 1 - dimensionfile.txt | 5 ++- pom.xml | 15 ++++++- .../converterGUI/FilterComparator.java | 18 +++++--- .../converterGUI/UnitConverterGUI.java | 50 ++++++++++++--------- unitsfile.txt | 13 ++++++ 7 files changed, 70 insertions(+), 32 deletions(-) create mode 100644 .DS_Store (limited to 'unitsfile.txt') diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..5f8c120 Binary files /dev/null and b/.DS_Store differ diff --git a/.classpath b/.classpath index 72d7394..ef141e6 100644 --- a/.classpath +++ b/.classpath @@ -7,7 +7,6 @@ - diff --git a/dimensionfile.txt b/dimensionfile.txt index 7a1da10..3485de5 100644 --- a/dimensionfile.txt +++ b/dimensionfile.txt @@ -4,14 +4,15 @@ # ! means "look for an existing dimension which I will load at the start" # This is necessary because every dimension must be defined by others, and I need somewhere to start. -# I have excluded electric current and quantity since their units are exclusively SI. +# I have excluded electric current, quantity and luminous intensity since their units are exclusively SI. LENGTH ! MASS ! TIME ! TEMPERATURE ! -LUMINOUS_INTENSITY ! # Derived Dimensions +AREA LENGTH^2 +VOLUME LENGTH^3 VELOCITY LENGTH / TIME ENERGY MASS * VELOCITY^2 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 8b3bc0d..5b3e468 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,18 @@ org.unitConverter.converterGUI.UnitConverterGUI - + + + org.apache.maven.plugins + maven-jar-plugin + + + + org.unitConverter.converterGUI.UnitConverterGUI + + + + @@ -41,4 +52,4 @@ 4.11 - \ No newline at end of file + diff --git a/src/org/unitConverter/converterGUI/FilterComparator.java b/src/org/unitConverter/converterGUI/FilterComparator.java index bebc2df..ad8d0b0 100755 --- a/src/org/unitConverter/converterGUI/FilterComparator.java +++ b/src/org/unitConverter/converterGUI/FilterComparator.java @@ -20,7 +20,7 @@ import java.util.Comparator; import java.util.Objects; /** - * A comparator that compares strings using a filter. + * A comparator that compares strings using a filter. It is case-insensitive * * @author Adrien Hopkins * @since 2019-01-15 @@ -72,22 +72,26 @@ public final class FilterComparator implements Comparator { @Override public int compare(final String arg0, final String arg1) { + // this is case insensitive, so make them lowercase + final String arg0lower = arg0.toLowerCase(); + final String arg1lower = arg1.toLowerCase(); + // elements that start with the filter always go first - if (arg0.startsWith(this.filter) && !arg1.startsWith(this.filter)) + if (arg0lower.startsWith(this.filter) && !arg1lower.startsWith(this.filter)) return -1; - else if (!arg0.startsWith(this.filter) && arg1.startsWith(this.filter)) + else if (!arg0lower.startsWith(this.filter) && arg1lower.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)) + if (arg0lower.contains(this.filter) && !arg1lower.contains(this.filter)) return -1; - else if (!arg0.contains(this.filter) && !arg1.contains(this.filter)) + else if (!arg0lower.contains(this.filter) && !arg1lower.contains(this.filter)) return 1; // other elements go last if (this.comparator == null) - return arg0.compareTo(arg1); + return arg0lower.compareTo(arg1lower); else - return this.comparator.compare(arg0, arg1); + return this.comparator.compare(arg0lower, arg1lower); } } diff --git a/src/org/unitConverter/converterGUI/UnitConverterGUI.java b/src/org/unitConverter/converterGUI/UnitConverterGUI.java index 49a40d6..34cbef9 100755 --- a/src/org/unitConverter/converterGUI/UnitConverterGUI.java +++ b/src/org/unitConverter/converterGUI/UnitConverterGUI.java @@ -62,6 +62,35 @@ import org.unitConverter.unit.UnitPrefix; */ final class UnitConverterGUI { private static class Presenter { + /** + * Adds default units and dimensions to a database. + * + * @param database + * database to add to + * @since 2019-04-14 + */ + private static void addDefaults(final UnitsDatabase database) { + database.addUnit("metre", SI.METRE); + database.addUnit("kilogram", SI.KILOGRAM); + database.addUnit("gram", SI.KILOGRAM.dividedBy(1000)); + database.addUnit("second", SI.SECOND); + database.addUnit("ampere", SI.AMPERE); + database.addUnit("kelvin", SI.KELVIN); + database.addUnit("mole", SI.MOLE); + database.addUnit("candela", SI.CANDELA); + database.addUnit("bit", SI.SI.getBaseUnit(StandardDimensions.INFORMATION)); + database.addUnit("unit", SI.SI.getBaseUnit(UnitDimension.EMPTY)); + // nonlinear units - must be loaded manually + database.addUnit("tempCelsius", NonlinearUnits.CELSIUS); + database.addUnit("tempFahrenheit", NonlinearUnits.FAHRENHEIT); + + // load initial dimensions + database.addDimension("LENGTH", StandardDimensions.LENGTH); + database.addDimension("MASS", StandardDimensions.MASS); + database.addDimension("TIME", StandardDimensions.TIME); + database.addDimension("TEMPERATURE", StandardDimensions.TEMPERATURE); + } + /** The presenter's associated view. */ private final View view; @@ -100,26 +129,7 @@ final class UnitConverterGUI { // 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); - - // load initial dimensions - this.units.addDimension("LENGTH", StandardDimensions.LENGTH); - this.units.addDimension("MASS", StandardDimensions.MASS); - this.units.addDimension("TIME", StandardDimensions.TIME); - this.units.addDimension("TEMPERATURE", StandardDimensions.TEMPERATURE); - this.units.addDimension("LUMINOUS_INTENSITY", StandardDimensions.LUMINOUS_INTENSITY); + Presenter.addDefaults(this.units); this.units.loadUnitsFile(new File("unitsfile.txt")); this.units.loadDimensionFile(new File("dimensionfile.txt")); diff --git a/unitsfile.txt b/unitsfile.txt index 78f8117..553fd5e 100755 --- a/unitsfile.txt +++ b/unitsfile.txt @@ -124,6 +124,9 @@ T tesla hertz s^-1 Hz hertz +gram millikg +g gram + # Angle units and constants # Tau is the circle constant, equal to a circle's diameter divided by its radius @@ -243,8 +246,18 @@ calorie 4.18 J cal calorie Calorie kilocalorie Cal Calorie +Wh W h # Extra units to only include in the dimension-based converter +km km +cm cm +mm mm +mg mg +mL mL +ml ml +kJ kJ +MJ MJ +kWh kWh m/s m / s km/h km / h ft/s foot / s -- cgit v1.2.3 From 54b9f00faba367e1ef325c9d0bc75a848fadb906 Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Sun, 14 Apr 2019 15:39:38 -0400 Subject: The unit and prefix viewers now use SearchBoxList. --- src/org/unitConverter/UnitsDatabase.java | 2 + .../converterGUI/FilterComparator.java | 50 ++- .../unitConverter/converterGUI/SearchBoxList.java | 77 +++- .../converterGUI/UnitConverterGUI.java | 401 +++++++-------------- unitsfile.txt | 2 +- 5 files changed, 247 insertions(+), 285 deletions(-) (limited to 'unitsfile.txt') diff --git a/src/org/unitConverter/UnitsDatabase.java b/src/org/unitConverter/UnitsDatabase.java index 959c151..abe6546 100755 --- a/src/org/unitConverter/UnitsDatabase.java +++ b/src/org/unitConverter/UnitsDatabase.java @@ -134,6 +134,7 @@ public final class UnitsDatabase { // the indices of the prefixes attached to the current unit private final List prefixCoordinates = new ArrayList<>(); + // values from the unit entry set private final Map map; private final List unitNames; private final List prefixNames; @@ -366,6 +367,7 @@ public final class UnitsDatabase { // the indices of the prefixes attached to the current unit private final List prefixCoordinates = new ArrayList<>(); + // values from the unit name set private final Map map; private final List unitNames; private final List prefixNames; diff --git a/src/org/unitConverter/converterGUI/FilterComparator.java b/src/org/unitConverter/converterGUI/FilterComparator.java index ad8d0b0..ef94602 100755 --- a/src/org/unitConverter/converterGUI/FilterComparator.java +++ b/src/org/unitConverter/converterGUI/FilterComparator.java @@ -41,6 +41,12 @@ public final class FilterComparator implements Comparator { * @since v0.1.0 */ private final Comparator comparator; + /** + * Whether or not the comparison is case-sensitive. + * + * @since 2019-04-14 + */ + private final boolean caseSensitive; /** * Creates the {@code FilterComparator}. @@ -60,38 +66,62 @@ public final class FilterComparator implements Comparator { * string to filter by * @param comparator * comparator to fall back to if all else fails, null is compareTo. + * @throws NullPointerException + * if filter is null * @since 2019-01-15 * @since v0.1.0 + */ + public FilterComparator(final String filter, final Comparator comparator) { + this(filter, comparator, false); + } + + /** + * Creates the {@code FilterComparator}. + * + * @param filter + * string to filter by + * @param comparator + * comparator to fall back to if all else fails, null is compareTo. + * @param caseSensitive + * whether or not the comparator is case-sensitive * @throws NullPointerException * if filter is null + * @since 2019-04-14 */ - public FilterComparator(final String filter, final Comparator comparator) { + public FilterComparator(final String filter, final Comparator comparator, final boolean caseSensitive) { this.filter = Objects.requireNonNull(filter, "filter must not be null."); this.comparator = comparator; + this.caseSensitive = caseSensitive; } @Override public int compare(final String arg0, final String arg1) { - // this is case insensitive, so make them lowercase - final String arg0lower = arg0.toLowerCase(); - final String arg1lower = arg1.toLowerCase(); + // if this is case insensitive, make them lowercase + final String str0, str1; + if (this.caseSensitive) { + str0 = arg0; + str1 = arg1; + } else { + str0 = arg0.toLowerCase(); + str1 = arg1.toLowerCase(); + } // elements that start with the filter always go first - if (arg0lower.startsWith(this.filter) && !arg1lower.startsWith(this.filter)) + if (str0.startsWith(this.filter) && !str1.startsWith(this.filter)) return -1; - else if (!arg0lower.startsWith(this.filter) && arg1lower.startsWith(this.filter)) + else if (!str0.startsWith(this.filter) && str1.startsWith(this.filter)) return 1; // elements that contain the filter but don't start with them go next - if (arg0lower.contains(this.filter) && !arg1lower.contains(this.filter)) + if (str0.contains(this.filter) && !str1.contains(this.filter)) return -1; - else if (!arg0lower.contains(this.filter) && !arg1lower.contains(this.filter)) + else if (!str0.contains(this.filter) && !str1.contains(this.filter)) return 1; // other elements go last if (this.comparator == null) - return arg0lower.compareTo(arg1lower); + return str0.compareTo(str1); else - return this.comparator.compare(arg0lower, arg1lower); + return this.comparator.compare(str0, str1); } } diff --git a/src/org/unitConverter/converterGUI/SearchBoxList.java b/src/org/unitConverter/converterGUI/SearchBoxList.java index 7d3b748..35cc347 100644 --- a/src/org/unitConverter/converterGUI/SearchBoxList.java +++ b/src/org/unitConverter/converterGUI/SearchBoxList.java @@ -22,6 +22,7 @@ import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; import java.util.function.Predicate; import javax.swing.JList; @@ -61,16 +62,25 @@ final class SearchBoxList extends JPanel { // event. private boolean searchBoxFocused = false; - private Predicate searchFilter = o -> true; + private Predicate customSearchFilter = o -> true; + private final Comparator defaultOrdering; + private final boolean caseSensitive; + + public SearchBoxList(final Collection itemsToFilter) { + this(itemsToFilter, null, false); + } /** * Creates the {@code SearchBoxList}. * * @since 2019-04-13 */ - public SearchBoxList(final Collection itemsToFilter) { + public SearchBoxList(final Collection itemsToFilter, final Comparator defaultOrdering, + final boolean caseSensitive) { super(new BorderLayout(), true); this.itemsToFilter = itemsToFilter; + this.defaultOrdering = defaultOrdering; + this.caseSensitive = caseSensitive; // create the components this.listModel = new DelegateListModel<>(new ArrayList<>(itemsToFilter)); @@ -108,7 +118,7 @@ final class SearchBoxList extends JPanel { * @since 2019-04-13 */ public void addSearchFilter(final Predicate filter) { - this.searchFilter = this.searchFilter.and(filter); + this.customSearchFilter = this.customSearchFilter.and(filter); } /** @@ -117,7 +127,44 @@ final class SearchBoxList extends JPanel { * @since 2019-04-13 */ public void clearSearchFilters() { - this.searchFilter = o -> true; + this.customSearchFilter = o -> true; + } + + /** + * @return this component's search box component + * @since 2019-04-14 + */ + public final JTextField getSearchBox() { + return this.searchBox; + } + + /** + * @param searchText + * text to search for + * @return a filter that filters out that text, based on this list's case sensitive setting + * @since 2019-04-14 + */ + private Predicate getSearchFilter(final String searchText) { + if (this.caseSensitive) + return string -> string.contains(searchText); + else + return string -> string.toLowerCase().contains(searchText.toLowerCase()); + } + + /** + * @return this component's list component + * @since 2019-04-14 + */ + public final JList getSearchList() { + return this.searchItems; + } + + /** + * @return index selected in item list + * @since 2019-04-14 + */ + public int getSelectedIndex() { + return this.searchItems.getSelectedIndex(); } /** @@ -135,17 +182,18 @@ final class SearchBoxList extends JPanel { */ public void reapplyFilter() { final String searchText = this.searchBoxEmpty ? "" : this.searchBox.getText(); - final FilterComparator comparator = new FilterComparator(searchText); + final FilterComparator comparator = new FilterComparator(searchText, this.defaultOrdering, this.caseSensitive); + final Predicate searchFilter = this.getSearchFilter(searchText); this.listModel.clear(); this.itemsToFilter.forEach(string -> { - if (string.toLowerCase().contains(searchText.toLowerCase())) { + if (searchFilter.test(string)) { this.listModel.add(string); } }); // applies the custom filters - this.listModel.removeIf(this.searchFilter.negate()); + this.listModel.removeIf(this.customSearchFilter.negate()); // sorts the remaining items this.listModel.sort(comparator); @@ -181,23 +229,32 @@ final class SearchBoxList extends JPanel { } } + /** + * Runs whenever the text in the search box is changed. + *

    + * Reapplies the search filter, and custom filters. + *

    + * + * @since 2019-04-14 + */ private void searchBoxTextChanged() { if (this.searchBoxFocused) { this.searchBoxEmpty = this.searchBox.getText().equals(""); } final String searchText = this.searchBoxEmpty ? "" : this.searchBox.getText(); - final FilterComparator comparator = new FilterComparator(searchText); + final FilterComparator comparator = new FilterComparator(searchText, this.defaultOrdering, this.caseSensitive); + final Predicate searchFilter = this.getSearchFilter(searchText); // initialize list with items that match the filter then sort this.listModel.clear(); this.itemsToFilter.forEach(string -> { - if (string.toLowerCase().contains(searchText.toLowerCase())) { + if (searchFilter.test(string)) { this.listModel.add(string); } }); // applies the custom filters - this.listModel.removeIf(this.searchFilter.negate()); + this.listModel.removeIf(this.customSearchFilter.negate()); // sorts the remaining items this.listModel.sort(comparator); diff --git a/src/org/unitConverter/converterGUI/UnitConverterGUI.java b/src/org/unitConverter/converterGUI/UnitConverterGUI.java index 34cbef9..cacc3b7 100755 --- a/src/org/unitConverter/converterGUI/UnitConverterGUI.java +++ b/src/org/unitConverter/converterGUI/UnitConverterGUI.java @@ -35,16 +35,12 @@ 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; @@ -95,20 +91,14 @@ final class UnitConverterGUI { private final View view; /** The units known by the program. */ - private final UnitsDatabase units; + private final UnitsDatabase database; /** 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; - /** The names of all of the dimensions */ private final List dimensionNames; @@ -128,23 +118,23 @@ final class UnitConverterGUI { this.view = view; // load initial units - this.units = new UnitsDatabase(); - Presenter.addDefaults(this.units); + this.database = new UnitsDatabase(); + Presenter.addDefaults(this.database); - this.units.loadUnitsFile(new File("unitsfile.txt")); - this.units.loadDimensionFile(new File("dimensionfile.txt")); + this.database.loadUnitsFile(new File("unitsfile.txt")); + this.database.loadDimensionFile(new File("dimensionfile.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)) + if (!Presenter.this.database.containsPrefixName(o1)) return -1; - else if (!Presenter.this.units.containsPrefixName(o2)) + else if (!Presenter.this.database.containsPrefixName(o2)) return 1; - final UnitPrefix p1 = Presenter.this.units.getPrefix(o1); - final UnitPrefix p2 = Presenter.this.units.getPrefix(o2); + final UnitPrefix p1 = Presenter.this.database.getPrefix(o1); + final UnitPrefix p2 = Presenter.this.database.getPrefix(o2); if (p1.getMultiplier() < p2.getMultiplier()) return -1; @@ -154,19 +144,13 @@ final class UnitConverterGUI { return o1.compareTo(o2); }; - this.unitNames = new ArrayList<>(this.units.unitMapPrefixless().keySet()); + this.unitNames = new ArrayList<>(this.database.unitMapPrefixless().keySet()); this.unitNames.sort(null); // sorts it using Comparable - this.unitNamesFiltered = new DelegateListModel<>(new ArrayList<>(this.units.unitMapPrefixless().keySet())); - this.unitNamesFiltered.sort(null); // sorts it using Comparable - - this.prefixNames = new ArrayList<>(this.units.prefixMap().keySet()); + this.prefixNames = new ArrayList<>(this.database.prefixMap().keySet()); this.prefixNames.sort(this.prefixNameComparator); // sorts it using my comparator - this.prefixNamesFiltered = new DelegateListModel<>(new ArrayList<>(this.units.prefixMap().keySet())); - this.prefixNamesFiltered.sort(this.prefixNameComparator); // sorts it using my comparator - - this.dimensionNames = new DelegateListModel<>(new ArrayList<>(this.units.dimensionMap().keySet())); + this.dimensionNames = new DelegateListModel<>(new ArrayList<>(this.database.dimensionMap().keySet())); this.dimensionNames.sort(null); // sorts it using Comparable // a Predicate that returns true iff the argument is a full base unit @@ -174,9 +158,43 @@ final class UnitConverterGUI { // print out unit counts System.out.printf("Successfully loaded %d units with %d unit names (%d base units).%n", - new HashSet<>(this.units.unitMapPrefixless().values()).size(), - this.units.unitMapPrefixless().size(), - new HashSet<>(this.units.unitMapPrefixless().values()).stream().filter(isFullBase).count()); + new HashSet<>(this.database.unitMapPrefixless().values()).size(), + this.database.unitMapPrefixless().size(), + new HashSet<>(this.database.unitMapPrefixless().values()).stream().filter(isFullBase).count()); + } + + /** + * Converts in the dimension-based converter + * + * @since 2019-04-13 + */ + public final void convertDimensionBased() { + final String fromSelection = this.view.getFromSelection(); + if (fromSelection == null) { + this.view.showErrorDialog("Error", "No unit selected in From field"); + return; + } + final String toSelection = this.view.getToSelection(); + if (toSelection == null) { + this.view.showErrorDialog("Error", "No unit selected in To field"); + return; + } + + final Unit from = this.database.getUnit(fromSelection); + final Unit to = this.database.getUnit(toSelection); + + final String input = this.view.getDimensionConverterInput(); + if (input.equals("")) { + this.view.showErrorDialog("Error", "No value to convert entered."); + return; + } + final double beforeValue = Double.parseDouble(input); + final double value = to.convertFromBase(from.convertToBase(beforeValue)); + + final String output = this.getRoundedString(value); + + this.view.setDimensionConverterOutputText( + String.format("%s %s = %s %s", input, fromSelection, output, toSelection)); } /** @@ -190,7 +208,7 @@ final class UnitConverterGUI { * @since 2019-01-26 * @since v0.1.0 */ - public final void convert() { + public final void convertExpressions() { final String fromUnitString = this.view.getFromText(); final String toUnitString = this.view.getToText(); @@ -202,7 +220,7 @@ final class UnitConverterGUI { // try to parse from final Unit from; try { - from = this.units.getUnitFromExpression(fromUnitString); + from = this.database.getUnitFromExpression(fromUnitString); } catch (final IllegalArgumentException e) { this.view.showErrorDialog("Parse Error", "Could not recognize text in From entry: " + e.getMessage()); return; @@ -212,11 +230,11 @@ final class UnitConverterGUI { // try to parse to final Unit to; try { - if (this.units.containsUnitName(toUnitString)) { + if (this.database.containsUnitName(toUnitString)) { // if it's a unit, convert to that - to = this.units.getUnit(toUnitString); + to = this.database.getUnit(toUnitString); } else { - to = this.units.getUnitFromExpression(toUnitString); + to = this.database.getUnitFromExpression(toUnitString); } } catch (final IllegalArgumentException e) { this.view.showErrorDialog("Parse Error", "Could not recognize text in To entry: " + e.getMessage()); @@ -233,50 +251,35 @@ final class UnitConverterGUI { value = to.convertFromBase(from.convertToBase(1)); // round value - final BigDecimal bigValue = new BigDecimal(value).round(new MathContext(this.significantFigures)); - String output = bigValue.toString(); + final String output = this.getRoundedString(value); - // 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)); + this.view.setExpressionConverterOutputText( + String.format("%s = %s %s", fromUnitString, output, toUnitString)); } /** - * Converts in the dimension-based converter - * + * @return a list of all of the unit dimensions * @since 2019-04-13 */ - public final void convertDimensionBased() { - final String fromSelection = this.view.getFromSelection(); - if (fromSelection == null) { - this.view.showErrorDialog("Error", "No unit selected in From field"); - return; - } - final String toSelection = this.view.getToSelection(); - if (toSelection == null) { - this.view.showErrorDialog("Error", "No unit selected in To field"); - return; - } - - final Unit from = this.units.getUnit(fromSelection); - final Unit to = this.units.getUnit(toSelection); + public final List dimensionNameList() { + return this.dimensionNames; + } - final String input = this.view.getDimensionBasedInput(); - if (input.equals("")) { - this.view.showErrorDialog("Error", "No value to convert entered."); - return; - } - final double beforeValue = Double.parseDouble(input); - final double value = to.convertFromBase(from.convertToBase(beforeValue)); + /** + * @return a comparator to compare prefix names + * @since 2019-04-14 + */ + public final Comparator getPrefixNameComparator() { + return this.prefixNameComparator; + } + /** + * @param value + * value to round + * @return string of that value rounded to {@code significantDigits} significant digits. + * @since 2019-04-14 + */ + private final String getRoundedString(final double value) { // round value final BigDecimal bigValue = new BigDecimal(value).round(new MathContext(this.significantFigures)); String output = bigValue.toString(); @@ -291,86 +294,15 @@ final class UnitConverterGUI { } } - this.view.setDimensionBasedOutputText( - String.format("%s %s = %s %s", input, fromSelection, output, toSelection)); - } - - /** - * @return a list of all of the unit dimensions - * @since 2019-04-13 - */ - public final List dimensionNameList() { - return this.dimensionNames; - } - - /** - * 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 output; } /** - * @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 + * @return a set of all prefix names in the database + * @since 2019-04-14 */ - public final ListModel prefixNameListModel() { - return this.prefixNamesFiltered; + public final Set prefixNameSet() { + return this.database.prefixMap().keySet(); } /** @@ -383,12 +315,11 @@ final class UnitConverterGUI { * @since v0.1.0 */ public final void prefixSelected() { - final int index = this.view.getPrefixListSelection(); - if (index == -1) + final String prefixName = this.view.getPrefixViewerSelection(); + if (prefixName == null) return; else { - final String prefixName = this.prefixNamesFiltered.get(index); - final UnitPrefix prefix = this.units.getPrefix(prefixName); + final UnitPrefix prefix = this.database.getPrefix(prefixName); this.view.setPrefixTextBoxText(String.format("%s%nMultiplier: %s", prefixName, prefix.getMultiplier())); } @@ -403,25 +334,6 @@ final class UnitConverterGUI { 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)); - } - /** * Returns true if and only if the unit represented by {@code unitName} has the dimension represented by * {@code dimensionName}. @@ -433,9 +345,9 @@ final class UnitConverterGUI { * @return whether unit has dimenision * @since 2019-04-13 */ - public boolean unitMatchesDimension(final String unitName, final String dimensionName) { - final Unit unit = this.units.getUnit(unitName); - final UnitDimension dimension = this.units.getDimension(dimensionName); + public final boolean unitMatchesDimension(final String unitName, final String dimensionName) { + final Unit unit = this.database.getUnit(unitName); + final UnitDimension dimension = this.database.getDimension(dimensionName); return unit.getDimension().equals(dimension); } @@ -448,20 +360,23 @@ final class UnitConverterGUI { * @since 2019-01-15 * @since v0.1.0 */ - public void unitNameSelected() { - final int index = this.view.getUnitListSelection(); - if (index == -1) + public final void unitNameSelected() { + final String unitName = this.view.getUnitViewerSelection(); + if (unitName == null) return; else { - final String unitName = this.unitNamesFiltered.get(index); - final Unit unit = this.units.getUnit(unitName); + final Unit unit = this.database.getUnit(unitName); this.view.setUnitTextBoxText(unit.toString()); } } + /** + * @return a set of all of the unit names + * @since 2019-04-14 + */ public final Set unitNameSet() { - return this.units.unitMapPrefixless().keySet(); + return this.database.unitMapPrefixless().keySet(); } } @@ -471,26 +386,17 @@ final class UnitConverterGUI { /** 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; + // DIMENSION-BASED CONVERTER + /** The panel for inputting values in the dimension-based converter */ + private final JTextField valueInput; /** The panel for "From" in the dimension-based converter */ private final SearchBoxList fromSearch; /** The panel for "To" in the dimension-based converter */ private final SearchBoxList toSearch; - /** The panel for inputting values in the dimension-based converter */ - private final JTextField valueInput; /** The output area in the dimension-based converter */ private final JTextArea dimensionBasedOutput; + + // EXPRESSION-BASED CONVERTER /** The "From" entry in the conversion panel */ private final JTextField fromEntry; /** The "To" entry in the conversion panel */ @@ -498,6 +404,16 @@ final class UnitConverterGUI { /** The output area in the conversion panel */ private final JTextArea output; + // UNIT AND PREFIX VIEWERS + /** The searchable list of unit names in the unit viewer */ + private final SearchBoxList unitNameList; + /** The searchable list of prefix names in the prefix viewer */ + private final SearchBoxList prefixNameList; + /** The text box for unit data in the unit viewer */ + private final JTextArea unitTextBox; + /** The text box for prefix data in the prefix viewer */ + private final JTextArea prefixTextBox; + /** * Creates the {@code View}. * @@ -510,11 +426,10 @@ final class UnitConverterGUI { 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.unitNameList = new SearchBoxList(this.presenter.unitNameSet()); + this.prefixNameList = new SearchBoxList(this.presenter.prefixNameSet(), + this.presenter.getPrefixNameComparator(), true); this.unitTextBox = new JTextArea(); - this.prefixFilterEntry = new JTextField(); this.prefixTextBox = new JTextArea(); this.fromSearch = new SearchBoxList(this.presenter.unitNameSet()); this.toSearch = new SearchBoxList(this.presenter.unitNameSet()); @@ -534,7 +449,7 @@ final class UnitConverterGUI { * @return value in dimension-based converter * @since 2019-04-13 */ - public String getDimensionBasedInput() { + public String getDimensionConverterInput() { return this.valueInput.getText(); } @@ -556,21 +471,12 @@ final class UnitConverterGUI { } /** - * @return text in prefix filter + * @return index of selected prefix in prefix viewer * @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(); + public String getPrefixViewerSelection() { + return this.prefixNameList.getSelectedValue(); } /** @@ -591,20 +497,12 @@ final class UnitConverterGUI { } /** - * @return text in unit filter - * @see javax.swing.text.JTextComponent#getText() - */ - public String getUnitFilterText() { - return this.unitFilterEntry.getText(); - } - - /** - * @return index of selected unit + * @return index of selected unit in unit viewer * @since 2019-01-15 * @since v0.1.0 */ - public int getUnitListSelection() { - return this.unitNameList.getSelectedIndex(); + public String getUnitViewerSelection() { + return this.unitNameList.getSelectedValue(); } /** @@ -764,7 +662,7 @@ final class UnitConverterGUI { final JButton convertButton = new JButton("Convert!"); convertExpressionPanel.add(convertButton); - convertButton.addActionListener(e -> this.presenter.convert()); + convertButton.addActionListener(e -> this.presenter.convertExpressions()); } { // output of conversion @@ -808,24 +706,11 @@ final class UnitConverterGUI { unitLookupPanel.setLayout(new GridLayout()); - { // panel for listing and searching - final JPanel listPanel = new JPanel(); - unitLookupPanel.add(listPanel); - - listPanel.setLayout(new BorderLayout()); + { // search panel + unitLookupPanel.add(this.unitNameList); - { // 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(); - }); - } + this.unitNameList.getSearchList() + .addListSelectionListener(e -> this.presenter.unitNameSelected()); } { // the text box for unit's toString @@ -842,23 +727,10 @@ final class UnitConverterGUI { 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()); - } + prefixLookupPanel.add(this.prefixNameList); - { // 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(); - }); - } + this.prefixNameList.getSearchList() + .addListSelectionListener(e -> this.presenter.prefixSelected()); } { // the text box for prefix's toString @@ -876,7 +748,7 @@ final class UnitConverterGUI { * text to set * @since 2019-04-13 */ - public void setDimensionBasedOutputText(final String text) { + public void setDimensionConverterOutputText(final String text) { this.dimensionBasedOutput.setText(text); } @@ -888,12 +760,12 @@ final class UnitConverterGUI { * @since 2019-01-15 * @since v0.1.0 */ - public void setOutputText(final String text) { + public void setExpressionConverterOutputText(final String text) { this.output.setText(text); } /** - * Sets the text of the prefix text box. + * Sets the text of the prefix text box in the prefix viewer. * * @param text * text to set @@ -905,14 +777,15 @@ final class UnitConverterGUI { } /** - * Sets the text of the unit text box. + * Sets the text of the unit text box in the unit viewer. * - * @param t + * @param text * text to set - * @see javax.swing.text.JTextComponent#setText(java.lang.String) + * @since 2019-01-15 + * @since v0.1.0 */ - public void setUnitTextBoxText(final String t) { - this.unitTextBox.setText(t); + public void setUnitTextBoxText(final String text) { + this.unitTextBox.setText(text); } /** diff --git a/unitsfile.txt b/unitsfile.txt index 553fd5e..bda9b81 100755 --- a/unitsfile.txt +++ b/unitsfile.txt @@ -138,7 +138,7 @@ radian m / m rad radian steradian m^2 / m^2 sr steradian -degree 360 / tau * radian +degree tau / 360 radian deg degree ° degree -- cgit v1.2.3