From df3651baf72c799339268293fccb75e127bb336c Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Sat, 22 May 2021 14:51:05 -0500 Subject: Added an option to exclude duplicate units --- .../unitConverter/converterGUI/SearchBoxList.java | 15 ++++++- .../converterGUI/UnitConverterGUI.java | 46 +++++++++++++++++----- .../java/org/unitConverter/unit/UnitDatabase.java | 37 ++++++++++++++++- src/main/resources/unitsfile.txt | 9 ++++- .../org/unitConverter/unit/UnitDatabaseTest.java | 5 ++- 5 files changed, 96 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/unitConverter/converterGUI/SearchBoxList.java b/src/main/java/org/unitConverter/converterGUI/SearchBoxList.java index 10ef589..f52d57d 100644 --- a/src/main/java/org/unitConverter/converterGUI/SearchBoxList.java +++ b/src/main/java/org/unitConverter/converterGUI/SearchBoxList.java @@ -101,7 +101,7 @@ final class SearchBoxList extends JPanel { final Comparator defaultOrdering, final boolean caseSensitive) { super(new BorderLayout(), true); - this.itemsToFilter = itemsToFilter; + this.itemsToFilter = new ArrayList<>(itemsToFilter); this.defaultOrdering = defaultOrdering; this.caseSensitive = caseSensitive; @@ -296,6 +296,19 @@ final class SearchBoxList extends JPanel { this.listModel.sort(comparator); } + /** + * Resets the search box list's contents to the provided items, removing any + * old items + * + * @param newItems new items to put in list + * @since 2021-05-22 + */ + public void setItems(Collection newItems) { + this.itemsToFilter.clear(); + this.itemsToFilter.addAll(newItems); + this.reapplyFilter(); + } + /** * Manually updates the search box's item list. * diff --git a/src/main/java/org/unitConverter/converterGUI/UnitConverterGUI.java b/src/main/java/org/unitConverter/converterGUI/UnitConverterGUI.java index ee1bcc3..17ec5f9 100644 --- a/src/main/java/org/unitConverter/converterGUI/UnitConverterGUI.java +++ b/src/main/java/org/unitConverter/converterGUI/UnitConverterGUI.java @@ -216,6 +216,9 @@ final class UnitConverterGUI { private RoundingType roundingType = RoundingType.SIGNIFICANT_DIGITS; + // The "include duplicate units" setting + private boolean includeDuplicateUnits = true; + /** * Creates the presenter. * @@ -291,7 +294,7 @@ final class UnitConverterGUI { }; this.unitNames = new ArrayList<>( - this.database.unitMapPrefixless().keySet()); + this.database.unitMapPrefixless(true).keySet()); this.unitNames.sort(null); // sorts it using Comparable this.prefixNames = new ArrayList<>(this.database.prefixMap().keySet()); @@ -309,10 +312,10 @@ 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.database.unitMapPrefixless().values()).size(), - this.database.unitMapPrefixless().size(), - new HashSet<>(this.database.unitMapPrefixless().values()) - .stream().filter(isFullBase).count()); + this.database.unitMapPrefixless(false).size(), + this.database.unitMapPrefixless(true).size(), + this.database.unitMapPrefixless(false).values().stream() + .filter(isFullBase).count()); } /** @@ -586,6 +589,12 @@ final class UnitConverterGUI { this.toExistenceCondition.setPredicate(unitName -> true); } break; + case "include_duplicates": + this.includeDuplicateUnits = Boolean.valueOf(value); + if (this.view.presenter != null) { + this.view.update(); + } + break; default: System.err.printf("Warning: unrecognized setting \"%s\".", param); @@ -638,6 +647,8 @@ final class UnitConverterGUI { String.format("rounding_type=%s\n", this.roundingType)); writer.write(String.format("prefix_rule=%s\n", this.prefixRule)); writer.write(String.format("one_way=%s\n", this.oneWay)); + writer.write(String.format("include_duplicates=%s\n", + this.includeDuplicateUnits)); } catch (final IOException e) { e.printStackTrace(); this.view.showErrorDialog("I/O Error", @@ -646,6 +657,14 @@ final class UnitConverterGUI { } } + public final void setIncludeDuplicateUnits( + boolean includeDuplicateUnits) { + this.includeDuplicateUnits = includeDuplicateUnits; + + this.view.update(); + this.saveSettings(); + } + /** * Enables or disables one-way conversion. * @@ -760,8 +779,9 @@ final class UnitConverterGUI { * @since 2019-04-14 * @since v0.2.0 */ - private final Set unitNameSet() { - return this.database.unitMapPrefixless().keySet(); + public final Set unitNameSet() { + return this.database.unitMapPrefixless(this.includeDuplicateUnits) + .keySet(); } } @@ -1385,9 +1405,11 @@ final class UnitConverterGUI { .setAnchor(GridBagConstraints.LINE_START).build()); final JCheckBox showAllVariations = new JCheckBox( - "Show Symbols in \"Convert Units\""); - showAllVariations.setSelected(true); - showAllVariations.setEnabled(false); + "Show Duplicates in \"Convert Units\""); + showAllVariations + .setSelected(this.presenter.includeDuplicateUnits); + showAllVariations.addItemListener(e -> this.presenter + .setIncludeDuplicateUnits(e.getStateChange() == 1)); miscPanel.add(showAllVariations, new GridBagBuilder(0, 1) .setAnchor(GridBagConstraints.LINE_START).build()); @@ -1459,6 +1481,10 @@ final class UnitConverterGUI { } public void update() { + this.unitNameList.setItems(this.presenter.unitNameSet()); + this.fromSearch.setItems(this.presenter.fromEntries()); + this.toSearch.setItems(this.presenter.toEntries()); + switch (this.getActivePane()) { case UNIT_CONVERTER: this.fromSearch.updateList(); diff --git a/src/main/java/org/unitConverter/unit/UnitDatabase.java b/src/main/java/org/unitConverter/unit/UnitDatabase.java index 6322fef..673f119 100644 --- a/src/main/java/org/unitConverter/unit/UnitDatabase.java +++ b/src/main/java/org/unitConverter/unit/UnitDatabase.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -1096,6 +1097,13 @@ public final class UnitDatabase { private static final Pattern NAME_EXPRESSION = Pattern .compile("(\\S+)\\s+(\\S.*)"); + /** + * Like normal string comparisons, but shorter strings are always less than + * longer strings. + */ + private static final Comparator lengthFirstComparator = Comparator + .comparingInt(String::length).thenComparing(Comparator.naturalOrder()); + /** * The exponent operator * @@ -1149,6 +1157,22 @@ public final class UnitDatabase { throw new IllegalArgumentException("Exponents must be numbers."); } + /** + * @return true if entry represents a removable duplicate entry of unitMap. + * @since 2021-05-22 + */ + private static boolean isRemovableDuplicate(Map unitMap, + Entry entry) { + for (final Entry e : unitMap.entrySet()) { + final String name = e.getKey(); + final Unit value = e.getValue(); + if (lengthFirstComparator.compare(entry.getKey(), name) < 0 + && Objects.equals(unitMap.get(entry.getKey()), value)) + return true; + } + return false; + } + /** * The units in this system, excluding prefixes. * @@ -2015,11 +2039,20 @@ public final class UnitDatabase { } /** + * @param includeDuplicates if true, duplicate units will all exist in the + * map; if false, only one of each unit will exist, + * even if the names are different * @return a map mapping unit names to units, ignoring prefixes * @since 2019-04-13 * @since v0.2.0 */ - public Map unitMapPrefixless() { - return Collections.unmodifiableMap(this.prefixlessUnits); + public Map unitMapPrefixless(boolean includeDuplicates) { + if (includeDuplicates) + return Collections.unmodifiableMap(this.prefixlessUnits); + else + return Collections.unmodifiableMap(ConditionalExistenceCollections + .conditionalExistenceMap(this.prefixlessUnits, + entry -> !isRemovableDuplicate(this.prefixlessUnits, + entry))); } } diff --git a/src/main/resources/unitsfile.txt b/src/main/resources/unitsfile.txt index eafe885..340e8ea 100644 --- a/src/main/resources/unitsfile.txt +++ b/src/main/resources/unitsfile.txt @@ -251,15 +251,22 @@ Calorie kilocalorie Cal Calorie Wh W h -# Extra units to only include in the dimension-based converter +# Extra units to show in the dimension-based converter km km +kilometre km cm cm +centimetre cm mm mm +millimetre mm mg mg +milligram mg mL mL ml ml +millilitre mL kJ kJ +kilojoule kJ MJ MJ +megajoule MJ kWh kWh m/s m / s km/h km / h diff --git a/src/test/java/org/unitConverter/unit/UnitDatabaseTest.java b/src/test/java/org/unitConverter/unit/UnitDatabaseTest.java index f0ba8e5..7f957f3 100644 --- a/src/test/java/org/unitConverter/unit/UnitDatabaseTest.java +++ b/src/test/java/org/unitConverter/unit/UnitDatabaseTest.java @@ -134,7 +134,8 @@ class UnitDatabaseTest { @Test public void testPrefixlessUnitMap() { final UnitDatabase database = new UnitDatabase(); - final Map prefixlessUnits = database.unitMapPrefixless(); + final Map prefixlessUnits = database + .unitMapPrefixless(true); database.addUnit("U", U); database.addUnit("V", V); @@ -223,7 +224,7 @@ class UnitDatabaseTest { database.addPrefix("B", B); database.addPrefix("C", C); - final int NUM_UNITS = database.unitMapPrefixless().size(); + final int NUM_UNITS = database.unitMapPrefixless(true).size(); final int NUM_PREFIXES = database.prefixMap().size(); final Iterator nameIterator = database.unitMap().keySet() -- cgit v1.2.3