diff options
Diffstat (limited to 'src/main/java/sevenUnitsGUI')
-rw-r--r-- | src/main/java/sevenUnitsGUI/PrefixSearchRule.java | 172 | ||||
-rw-r--r-- | src/main/java/sevenUnitsGUI/Presenter.java | 77 | ||||
-rw-r--r-- | src/main/java/sevenUnitsGUI/TabbedView.java | 47 |
3 files changed, 278 insertions, 18 deletions
diff --git a/src/main/java/sevenUnitsGUI/PrefixSearchRule.java b/src/main/java/sevenUnitsGUI/PrefixSearchRule.java new file mode 100644 index 0000000..2928082 --- /dev/null +++ b/src/main/java/sevenUnitsGUI/PrefixSearchRule.java @@ -0,0 +1,172 @@ +/** + * Copyright (C) 2022 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +package sevenUnitsGUI; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; + +import sevenUnits.unit.LinearUnit; +import sevenUnits.unit.Metric; +import sevenUnits.unit.UnitPrefix; + +/** + * A search rule that applies a certain set of prefixes to a unit. It always + * includes the original unit in the output map. + * + * @since 2022-07-06 + */ +public final class PrefixSearchRule implements + Function<Map.Entry<String, LinearUnit>, Map<String, LinearUnit>> { + /** + * A rule that does not add any prefixed versions of units. + */ + public static final PrefixSearchRule NO_PREFIXES = getUniversalRule( + Set.of()); + + /** + * A rule that gives every unit a common set of prefixes. + */ + public static final PrefixSearchRule COMMON_PREFIXES = getCoherentOnlyRule( + Set.of(Metric.MILLI, Metric.KILO)); + + /** + * A rule that gives every unit all metric prefixes. + */ + public static final PrefixSearchRule ALL_METRIC_PREFIXES = getCoherentOnlyRule( + Metric.ALL_PREFIXES); + + /** + * Gets a rule that applies the provided prefixes to coherent units only (as + * defined by {@link LinearUnit#isCoherent}), except the kilogram + * (specifically, units named "kilogram"). + * + * @param prefixes prefixes to apply + * @return prefix rule + * @since 2022-07-06 + */ + public static final PrefixSearchRule getCoherentOnlyRule( + Set<UnitPrefix> prefixes) { + return new PrefixSearchRule(prefixes, + u -> u.isCoherent() && !u.getName().equals("kilogram")); + } + + /** + * Gets a rule that applies the provided prefixes to all units. + * + * @param prefixes prefixes to apply + * @return prefix rule + * @since 2022-07-06 + */ + public static final PrefixSearchRule getUniversalRule( + Set<UnitPrefix> prefixes) { + return new PrefixSearchRule(prefixes, u -> true); + } + + /** + * The set of prefixes that will be applied to the unit. + */ + private final Set<UnitPrefix> prefixes; + + /** + * Determines which units are given prefixes. + */ + private final Predicate<LinearUnit> allowUnit; + + /** + * @param prefixes + * @param metricOnly + * @since 2022-07-06 + */ + public PrefixSearchRule(Set<UnitPrefix> prefixes, + Predicate<LinearUnit> allowUnit) { + this.prefixes = Collections.unmodifiableSet(new HashSet<>(prefixes)); + this.allowUnit = allowUnit; + } + + @Override + public Map<String, LinearUnit> apply(Entry<String, LinearUnit> t) { + final Map<String, LinearUnit> outputUnits = new HashMap<>(); + final String originalName = t.getKey(); + final LinearUnit originalUnit = t.getValue(); + outputUnits.put(originalName, originalUnit); + if (this.allowUnit.test(originalUnit)) { + for (final UnitPrefix prefix : this.prefixes) { + outputUnits.put(prefix.getName() + originalName, + originalUnit.withPrefix(prefix)); + } + } + return outputUnits; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof PrefixSearchRule)) + return false; + final PrefixSearchRule other = (PrefixSearchRule) obj; + if (this.allowUnit == null) { + if (other.allowUnit != null) + return false; + } else if (!this.allowUnit.equals(other.allowUnit)) + return false; + if (this.prefixes == null) { + if (other.prefixes != null) + return false; + } else if (!this.prefixes.equals(other.prefixes)) + return false; + return true; + } + + /** + * @return the allowUnit + * @since 2022-07-06 + */ + public Predicate<LinearUnit> getAllowUnit() { + return this.allowUnit; + } + + /** + * @return the prefixes that are applied by this rule + * @since 2022-07-06 + */ + public Set<UnitPrefix> getPrefixes() { + return this.prefixes; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + (this.allowUnit == null ? 0 : this.allowUnit.hashCode()); + result = prime * result + + (this.prefixes == null ? 0 : this.prefixes.hashCode()); + return result; + } + + @Override + public String toString() { + return "Apply the following prefixes: " + this.prefixes; + } +} diff --git a/src/main/java/sevenUnitsGUI/Presenter.java b/src/main/java/sevenUnitsGUI/Presenter.java index 4feea44..a5c4d48 100644 --- a/src/main/java/sevenUnitsGUI/Presenter.java +++ b/src/main/java/sevenUnitsGUI/Presenter.java @@ -32,6 +32,7 @@ import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.Stream; import sevenUnits.ProgramInfo; import sevenUnits.unit.BaseDimension; @@ -195,6 +196,13 @@ public final class Presenter { private Predicate<List<UnitPrefix>> prefixRepetitionRule = DefaultPrefixRepetitionRule.NO_RESTRICTION; /** + * A rule that accepts a prefixless name-unit pair and returns a map mapping + * names to prefixed versions of that unit (including the unit itself) that + * should be searchable. + */ + private Function<Map.Entry<String, LinearUnit>, Map<String, LinearUnit>> searchRule = PrefixSearchRule.NO_PREFIXES; + + /** * The set of units that is considered neither metric nor nonmetric for the * purposes of the metric-imperial one-way conversion. These units are * included in both From and To, even if One Way Conversion is enabled. @@ -275,6 +283,26 @@ public final class Presenter { } /** + * Applies a search rule to an entry in a name-unit map. + * + * @param e entry + * @return stream of entries, ready for flat-mapping + * @since 2022-07-06 + */ + private final Stream<Map.Entry<String, Unit>> applySearchRule( + Map.Entry<String, Unit> e) { + final Unit u = e.getValue(); + if (u instanceof LinearUnit) { + final String name = e.getKey(); + final Map.Entry<String, LinearUnit> linearEntry = Map.entry(name, + (LinearUnit) u); + return this.searchRule.apply(linearEntry).entrySet().stream().map( + entry -> Map.entry(entry.getKey(), (Unit) entry.getValue())); + } else + return Stream.of(e); + } + + /** * Converts from the view's input expression to its output expression. * Displays an error message if any of the required fields are invalid. * @@ -490,6 +518,23 @@ public final class Presenter { } /** + * @return the rule that determines which units are prefixed + * @since 2022-07-08 + */ + public Function<Map.Entry<String, LinearUnit>, Map<String, LinearUnit>> getSearchRule() { + return this.searchRule; + } + + /** + * @return a search rule that shows all single prefixes + * @since 2022-07-08 + */ + public Function<Map.Entry<String, LinearUnit>, Map<String, LinearUnit>> getUniversalSearchRule() { + return PrefixSearchRule.getCoherentOnlyRule( + new HashSet<>(this.database.prefixMap(true).values())); + } + + /** * @return the view associated with this presenter * @since 2022-04-19 */ @@ -551,6 +596,16 @@ public final class Presenter { case "include_duplicates": this.showDuplicates = Boolean.valueOf(value); break; + case "search_prefix_rule": + if (PrefixSearchRule.NO_PREFIXES.toString().equals(value)) { + this.searchRule = PrefixSearchRule.NO_PREFIXES; + } else if (PrefixSearchRule.COMMON_PREFIXES.toString() + .equals(value)) { + this.searchRule = PrefixSearchRule.COMMON_PREFIXES; + } else { + this.searchRule = this.getUniversalSearchRule(); + } + break; default: System.err.printf("Warning: unrecognized setting \"%s\".%n", param); @@ -628,6 +683,8 @@ public final class Presenter { String.format("one_way=%s\n", this.oneWayConversionEnabled)); writer.write( String.format("include_duplicates=%s\n", this.showDuplicates)); + writer.write( + String.format("search_prefix_rule=%s\n", this.searchRule)); } catch (final IOException e) { e.printStackTrace(); this.view.showErrorMessage("I/O Error", @@ -680,6 +737,18 @@ public final class Presenter { } /** + * @param searchRule A rule that accepts a prefixless name-unit pair and + * returns a map mapping names to prefixed versions of that + * unit (including the unit itself) that should be + * searchable. + * @since 2022-07-08 + */ + public void setSearchRule( + Function<Map.Entry<String, LinearUnit>, Map<String, LinearUnit>> searchRule) { + this.searchRule = searchRule; + } + + /** * @param showDuplicateUnits whether or not duplicate units should be shown * @since 2022-03-30 */ @@ -761,10 +830,10 @@ public final class Presenter { } // set unit names - ucview.setFromUnitNames( - fromUnits.map(Map.Entry::getKey).collect(Collectors.toSet())); - ucview.setToUnitNames( - toUnits.map(Map.Entry::getKey).collect(Collectors.toSet())); + ucview.setFromUnitNames(fromUnits.flatMap(this::applySearchRule) + .map(Map.Entry::getKey).collect(Collectors.toSet())); + ucview.setToUnitNames(toUnits.flatMap(this::applySearchRule) + .map(Map.Entry::getKey).collect(Collectors.toSet())); } } diff --git a/src/main/java/sevenUnitsGUI/TabbedView.java b/src/main/java/sevenUnitsGUI/TabbedView.java index be80ccb..e3b5610 100644 --- a/src/main/java/sevenUnitsGUI/TabbedView.java +++ b/src/main/java/sevenUnitsGUI/TabbedView.java @@ -531,33 +531,52 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { // searching rules final ButtonGroup searchRuleButtons = new ButtonGroup(); + final var searchRule = this.presenter.getSearchRule(); + final JRadioButton noPrefixes = new JRadioButton( "Never Include Prefixed Units"); - noPrefixes.setEnabled(false); + noPrefixes.addActionListener(e -> { + this.presenter.setSearchRule(PrefixSearchRule.NO_PREFIXES); + this.presenter.updateView(); + this.presenter.saveSettings(); + }); searchRuleButtons.add(noPrefixes); searchingPanel.add(noPrefixes, new GridBagBuilder(0, 0) .setAnchor(GridBagConstraints.LINE_START).build()); - final JRadioButton fixedPrefixes = new JRadioButton( - "Include Some Prefixes"); - fixedPrefixes.setEnabled(false); - searchRuleButtons.add(fixedPrefixes); - searchingPanel.add(fixedPrefixes, new GridBagBuilder(0, 1) - .setAnchor(GridBagConstraints.LINE_START).build()); - - final JRadioButton explicitPrefixes = new JRadioButton( - "Include Explicit Prefixes"); - explicitPrefixes.setEnabled(false); - searchRuleButtons.add(explicitPrefixes); - searchingPanel.add(explicitPrefixes, new GridBagBuilder(0, 2) + final JRadioButton commonPrefixes = new JRadioButton( + "Include Common Prefixes"); + commonPrefixes.addActionListener(e -> { + this.presenter.setSearchRule(PrefixSearchRule.COMMON_PREFIXES); + this.presenter.updateView(); + this.presenter.saveSettings(); + }); + searchRuleButtons.add(commonPrefixes); + searchingPanel.add(commonPrefixes, new GridBagBuilder(0, 1) .setAnchor(GridBagConstraints.LINE_START).build()); final JRadioButton alwaysInclude = new JRadioButton( "Include All Single Prefixes"); - alwaysInclude.setEnabled(false); + alwaysInclude.addActionListener(e -> { + this.presenter + .setSearchRule(this.presenter.getUniversalSearchRule()); + this.presenter.updateView(); + this.presenter.saveSettings(); + }); searchRuleButtons.add(alwaysInclude); searchingPanel.add(alwaysInclude, new GridBagBuilder(0, 3) .setAnchor(GridBagConstraints.LINE_START).build()); + + if (PrefixSearchRule.NO_PREFIXES.equals(searchRule)) { + noPrefixes.setSelected(true); + } else if (PrefixSearchRule.COMMON_PREFIXES.equals(searchRule)) { + commonPrefixes.setSelected(true); + } else { + alwaysInclude.setSelected(true); + this.presenter + .setSearchRule(this.presenter.getUniversalSearchRule()); + this.presenter.saveSettings(); + } } // ============ OTHER SETTINGS ============ |