diff options
| author | Adrien Hopkins <ahopk127@my.yorku.ca> | 2021-03-27 18:10:56 -0500 | 
|---|---|---|
| committer | Adrien Hopkins <ahopk127@my.yorku.ca> | 2021-03-27 18:10:56 -0500 | 
| commit | 782cf0c76e09abab1c2e11288d610330f383e732 (patch) | |
| tree | f8cd3fa0dc799349a2b4746f5d31da89190b66ff /src/org/unitConverter/converterGUI/UnitConverterGUI.java | |
| parent | fe4135a68cfed92ef336eec663e9c42c2c97dcbc (diff) | |
| parent | 0f9a56ac310a1276dbf3b42859a2374828223879 (diff) | |
Merge branch 'feature-gradle' into develop
Diffstat (limited to 'src/org/unitConverter/converterGUI/UnitConverterGUI.java')
| -rw-r--r-- | src/org/unitConverter/converterGUI/UnitConverterGUI.java | 1424 | 
1 files changed, 0 insertions, 1424 deletions
| diff --git a/src/org/unitConverter/converterGUI/UnitConverterGUI.java b/src/org/unitConverter/converterGUI/UnitConverterGUI.java deleted file mode 100644 index 6ddc4a0..0000000 --- a/src/org/unitConverter/converterGUI/UnitConverterGUI.java +++ /dev/null @@ -1,1424 +0,0 @@ -/** - * Copyright (C) 2018-2021 Adrien Hopkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program.  If not, see <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.converterGUI; - -import java.awt.BorderLayout; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.GridLayout; -import java.awt.event.KeyEvent; -import java.io.BufferedWriter; -import java.io.IOException; -import java.math.BigDecimal; -import java.math.MathContext; -import java.math.RoundingMode; -import java.nio.file.Files; -import java.nio.file.Path; -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -import javax.swing.BorderFactory; -import javax.swing.BoxLayout; -import javax.swing.ButtonGroup; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JFormattedTextField; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JRadioButton; -import javax.swing.JScrollPane; -import javax.swing.JSlider; -import javax.swing.JTabbedPane; -import javax.swing.JTextArea; -import javax.swing.JTextField; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; -import javax.swing.WindowConstants; -import javax.swing.border.TitledBorder; - -import org.unitConverter.math.ConditionalExistenceCollections; -import org.unitConverter.math.ObjectProduct; -import org.unitConverter.unit.BaseDimension; -import org.unitConverter.unit.BritishImperial; -import org.unitConverter.unit.LinearUnit; -import org.unitConverter.unit.LinearUnitValue; -import org.unitConverter.unit.NameSymbol; -import org.unitConverter.unit.SI; -import org.unitConverter.unit.Unit; -import org.unitConverter.unit.UnitDatabase; -import org.unitConverter.unit.UnitPrefix; -import org.unitConverter.unit.UnitValue; - -/** - * @author Adrien Hopkins - * @since 2018-12-27 - * @since v0.1.0 - */ -final class UnitConverterGUI { -	/** -	 * A tab in the View. -	 */ -	private enum Pane { -		UNIT_CONVERTER, EXPRESSION_CONVERTER, UNIT_VIEWER, PREFIX_VIEWER, ABOUT, -		SETTINGS; -	} -	 -	private static class Presenter { -		/** The default place where settings are stored. */ -		private static final String DEFAULT_SETTINGS_FILEPATH = "settings.txt"; -		/** The default place where units are stored. */ -		private static final Path DEFAULT_UNITS_FILE = Path.of("unitsfile.txt"); -		/** The default place where dimensions are stored. */ -		private static final Path DEFAULT_DIMENSION_FILE = Path -				.of("dimensionfile.txt"); -		/** The default place where exceptions are stored. */ -		private static final Path DEFAULT_EXCEPTIONS_FILE = Path -				.of("metric_exceptions.txt"); -		 -		/** -		 * Adds default units and dimensions to a database. -		 *  -		 * @param database database to add to -		 * @since 2019-04-14 -		 * @since v0.2.0 -		 */ -		private static void addDefaults(final UnitDatabase database) { -			database.addUnit("metre", SI.METRE); -			database.addUnit("kilogram", SI.KILOGRAM); -			database.addUnit("gram", SI.KILOGRAM.dividedBy(1000)); -			database.addUnit("second", SI.SECOND); -			database.addUnit("ampere", SI.AMPERE); -			database.addUnit("kelvin", SI.KELVIN); -			database.addUnit("mole", SI.MOLE); -			database.addUnit("candela", SI.CANDELA); -			database.addUnit("bit", SI.BIT); -			database.addUnit("unit", SI.ONE); -			// nonlinear units - must be loaded manually -			database.addUnit("tempCelsius", SI.CELSIUS); -			database.addUnit("tempFahrenheit", BritishImperial.FAHRENHEIT); -			 -			// load initial dimensions -			database.addDimension("LENGTH", SI.Dimensions.LENGTH); -			database.addDimension("MASS", SI.Dimensions.MASS); -			database.addDimension("TIME", SI.Dimensions.TIME); -			database.addDimension("TEMPERATURE", SI.Dimensions.TEMPERATURE); -		} -		 -		/** -		 * @return {@code line} with any comments removed. -		 * @since 2021-03-13 -		 */ -		private static final String withoutComments(String line) { -			final int index = line.indexOf('#'); -			return index == -1 ? line : line.substring(index); -		} -		 -		/** The presenter's associated view. */ -		private final View view; -		 -		/** The units known by the program. */ -		private final UnitDatabase database; -		 -		/** The names of all of the units */ -		private final List<String> unitNames; -		 -		/** The names of all of the prefixes */ -		private final List<String> prefixNames; -		 -		/** The names of all of the dimensions */ -		private final List<String> dimensionNames; -		 -		/** Unit names that are ignored by the metric-only/imperial-only filter */ -		private final Set<String> metricExceptions; -		 -		private final Comparator<String> prefixNameComparator; -		 -		/** A boolean remembering whether or not one-way conversion is on */ -		private boolean oneWay = true; -		 -		/** The prefix rule */ -		private DefaultPrefixRepetitionRule prefixRule = null; -		// conditions for existence of From and To entries -		// used for one-way conversion -		private final MutablePredicate<String> fromExistenceCondition = new MutablePredicate<>( -				s -> true); -		 -		private final MutablePredicate<String> toExistenceCondition = new MutablePredicate<>( -				s -> true); -		 -		/* -		 * Rounding-related settings. I am using my own system, and not -		 * MathContext, because MathContext does not support decimal place based -		 * or scientific rounding, only significant digit based rounding. -		 */ -		private int precision = 6; -		 -		private RoundingType roundingType = RoundingType.SIGNIFICANT_DIGITS; -		 -		/** -		 * Creates the presenter. -		 *  -		 * @param view presenter's associated view -		 * @since 2018-12-27 -		 * @since v0.1.0 -		 */ -		Presenter(final View view) { -			this.view = view; -			 -			// load initial units -			this.database = new UnitDatabase( -					DefaultPrefixRepetitionRule.NO_RESTRICTION); -			Presenter.addDefaults(this.database); -			 -			this.database.loadUnitsFile(DEFAULT_UNITS_FILE); -			this.database.loadDimensionFile(DEFAULT_DIMENSION_FILE); -			 -			// load metric exceptions -			try { -				this.metricExceptions = Files.readAllLines(DEFAULT_EXCEPTIONS_FILE) -						.stream().map(Presenter::withoutComments) -						.filter(s -> !s.isBlank()).collect(Collectors.toSet()); -			} catch (final IOException e) { -				throw new AssertionError("Loading of metric_exceptions.txt failed.", -						e); -			} -			 -			// load settings - requires database to exist -			this.loadSettings(); -			 -			// a comparator that can be used to compare prefix names -			// any name that does not exist is less than a name that does. -			// otherwise, they are compared by value -			this.prefixNameComparator = (o1, o2) -> { -				if (!Presenter.this.database.containsPrefixName(o1)) -					return -1; -				else if (!Presenter.this.database.containsPrefixName(o2)) -					return 1; -				 -				final UnitPrefix p1 = Presenter.this.database.getPrefix(o1); -				final UnitPrefix p2 = Presenter.this.database.getPrefix(o2); -				 -				if (p1.getMultiplier() < p2.getMultiplier()) -					return -1; -				else if (p1.getMultiplier() > p2.getMultiplier()) -					return 1; -				 -				return o1.compareTo(o2); -			}; -			 -			this.unitNames = new ArrayList<>( -					this.database.unitMapPrefixless().keySet()); -			this.unitNames.sort(null); // sorts it using Comparable -			 -			this.prefixNames = new ArrayList<>(this.database.prefixMap().keySet()); -			this.prefixNames.sort(this.prefixNameComparator); // sorts it using my -																				// comparator -			 -			this.dimensionNames = new DelegateListModel<>( -					new ArrayList<>(this.database.dimensionMap().keySet())); -			this.dimensionNames.sort(null); // sorts it using Comparable -			 -			// a Predicate that returns true iff the argument is a full base unit -			final Predicate<Unit> isFullBase = unit -> unit instanceof LinearUnit -					&& ((LinearUnit) unit).isBase(); -			 -			// print out unit counts -			System.out.printf( -					"Successfully loaded %d units with %d unit names (%d base units).%n", -					new HashSet<>(this.database.unitMapPrefixless().values()).size(), -					this.database.unitMapPrefixless().size(), -					new HashSet<>(this.database.unitMapPrefixless().values()) -							.stream().filter(isFullBase).count()); -		} -		 -		/** -		 * Converts in the dimension-based converter -		 *  -		 * @since 2019-04-13 -		 * @since v0.2.0 -		 */ -		public final void convertDimensionBased() { -			final String fromSelection = this.view.getFromSelection(); -			if (fromSelection == null) { -				this.view.showErrorDialog("Error", -						"No unit selected in From field"); -				return; -			} -			final String toSelection = this.view.getToSelection(); -			if (toSelection == null) { -				this.view.showErrorDialog("Error", "No unit selected in To field"); -				return; -			} -			 -			final Unit from = this.database.getUnit(fromSelection); -			final Unit to = this.database.getUnit(toSelection) -					.withName(NameSymbol.ofName(toSelection)); -			 -			final UnitValue beforeValue; -			try { -				beforeValue = UnitValue.of(from, -						this.view.getDimensionConverterInput()); -			} catch (final ParseException e) { -				this.view.showErrorDialog("Error", -						"Error in parsing: " + e.getMessage()); -				return; -			} -			final UnitValue value = beforeValue.convertTo(to); -			 -			final String output = this.getRoundedString(value); -			 -			this.view.setDimensionConverterOutputText( -					String.format("%s = %s", beforeValue, output)); -		} -		 -		/** -		 * Runs whenever the convert button is pressed. -		 *  -		 * <p> -		 * Reads and parses a unit expression from the from and to boxes, then -		 * converts {@code from} to {@code to}. Any errors are shown in -		 * JOptionPanes. -		 * </p> -		 *  -		 * @since 2019-01-26 -		 * @since v0.1.0 -		 */ -		public final void convertExpressions() { -			final String fromUnitString = this.view.getFromText(); -			final String toUnitString = this.view.getToText(); -			 -			if (fromUnitString.isEmpty()) { -				this.view.showErrorDialog("Parse Error", -						"Please enter a unit expression in the From: box."); -				return; -			} -			if (toUnitString.isEmpty()) { -				this.view.showErrorDialog("Parse Error", -						"Please enter a unit expression in the To: box."); -				return; -			} -			 -			final LinearUnitValue from; -			final Unit to; -			try { -				from = this.database.evaluateUnitExpression(fromUnitString); -			} catch (final IllegalArgumentException | NoSuchElementException e) { -				this.view.showErrorDialog("Parse Error", -						"Could not recognize text in From entry: " + e.getMessage()); -				return; -			} -			try { -				to = this.database.getUnitFromExpression(toUnitString); -			} catch (final IllegalArgumentException | NoSuchElementException e) { -				this.view.showErrorDialog("Parse Error", -						"Could not recognize text in To entry: " + e.getMessage()); -				return; -			} -			 -			if (to instanceof LinearUnit) { -				// convert to LinearUnitValue -				final LinearUnitValue from2; -				final LinearUnit to2 = ((LinearUnit) to) -						.withName(NameSymbol.ofName(toUnitString)); -				final boolean useSlash; -				 -				if (from.canConvertTo(to2)) { -					from2 = from; -					useSlash = false; -				} else if (LinearUnitValue.ONE.dividedBy(from).canConvertTo(to2)) { -					from2 = LinearUnitValue.ONE.dividedBy(from); -					useSlash = true; -				} else { -					// if I can't convert, leave -					this.view.showErrorDialog("Conversion Error", -							String.format("Cannot convert between %s and %s", -									fromUnitString, toUnitString)); -					return; -				} -				 -				final LinearUnitValue converted = from2.convertTo(to2); -				this.view.setExpressionConverterOutputText((useSlash ? "1 / " : "") -						+ String.format("%s = %s", fromUnitString, -								this.getRoundedString(converted, false))); -				return; -			} else { -				// convert to UnitValue -				final UnitValue from2 = from.asUnitValue(); -				if (from2.canConvertTo(to)) { -					final UnitValue converted = from2.convertTo(to); -					 -					this.view -							.setExpressionConverterOutputText(String.format("%s = %s", -									fromUnitString, this.getRoundedString(converted))); -				} else { -					// if I can't convert, leave -					this.view.showErrorDialog("Conversion Error", -							String.format("Cannot convert between %s and %s", -									fromUnitString, toUnitString)); -				} -			} -		} -		 -		/** -		 * @return a list of all of the unit dimensions -		 * @since 2019-04-13 -		 * @since v0.2.0 -		 */ -		public final List<String> dimensionNameList() { -			return this.dimensionNames; -		} -		 -		/** -		 * @return a list of all the entries in the dimension-based converter's -		 *         From box -		 * @since 2020-08-27 -		 */ -		public final Set<String> fromEntries() { -			return ConditionalExistenceCollections.conditionalExistenceSet( -					this.unitNameSet(), this.fromExistenceCondition); -		} -		 -		/** -		 * @return a comparator to compare prefix names -		 * @since 2019-04-14 -		 * @since v0.2.0 -		 */ -		public final Comparator<String> getPrefixNameComparator() { -			return this.prefixNameComparator; -		} -		 -		/** -		 * Like {@link LinearUnitValue#toString(boolean)}, but obeys this unit -		 * converter's rounding settings. -		 *  -		 * @since 2020-08-04 -		 */ -		private final String getRoundedString(final LinearUnitValue value, -				boolean showUncertainty) { -			switch (this.roundingType) { -			case DECIMAL_PLACES: -			case SIGNIFICANT_DIGITS: -				return this.getRoundedString(value.asUnitValue()); -			case SCIENTIFIC: -				return value.toString(showUncertainty); -			default: -				throw new AssertionError("Invalid switch condition."); -			} -		} -		 -		/** -		 * Like {@link UnitValue#toString()}, but obeys this unit converter's -		 * rounding settings. -		 *  -		 * @since 2020-08-04 -		 */ -		private final String getRoundedString(final UnitValue value) { -			final BigDecimal unrounded = new BigDecimal(value.getValue()); -			final BigDecimal rounded; -			int precision = this.precision; -			 -			switch (this.roundingType) { -			case DECIMAL_PLACES: -				rounded = unrounded.setScale(precision, RoundingMode.HALF_EVEN); -				break; -			case SCIENTIFIC: -				precision = 12; -				//$FALL-THROUGH$ -			case SIGNIFICANT_DIGITS: -				rounded = unrounded -						.round(new MathContext(precision, RoundingMode.HALF_EVEN)); -				break; -			default: -				throw new AssertionError("Invalid switch condition."); -			} -			 -			String output = rounded.toString(); -			 -			// remove trailing zeroes -			if (output.contains(".")) { -				while (output.endsWith("0")) { -					output = output.substring(0, output.length() - 1); -				} -				if (output.endsWith(".")) { -					output = output.substring(0, output.length() - 1); -				} -			} -			 -			return output + " " + value.getUnit().getPrimaryName().get(); -		} -		 -		/** -		 * @return The file where settings are stored; -		 * @since 2020-12-11 -		 */ -		private final Path getSettingsFile() { -			return Path.of(DEFAULT_SETTINGS_FILEPATH); -		} -		 -		/** -		 * Loads settings from the settings file. -		 *  -		 * @since 2021-02-17 -		 */ -		public final void loadSettings() { -			try { -				// read file line by line -				final int lineNum = 0; -				for (final String line : Files -						.readAllLines(this.getSettingsFile())) { -					final int equalsIndex = line.indexOf('='); -					if (equalsIndex == -1) -						throw new IllegalStateException( -								"Settings file is malformed at line " + lineNum); -					 -					final String param = line.substring(0, equalsIndex); -					final String value = line.substring(equalsIndex + 1); -					 -					switch (param) { -					// set manually to avoid the unnecessary saving of the non-manual -					// methods -					case "precision": -						this.precision = Integer.valueOf(value); -						break; -					case "rounding_type": -						this.roundingType = RoundingType.valueOf(value); -						break; -					case "prefix_rule": -						this.prefixRule = DefaultPrefixRepetitionRule.valueOf(value); -						this.database.setPrefixRepetitionRule(this.prefixRule); -						break; -					case "one_way": -						this.oneWay = Boolean.valueOf(value); -						if (this.oneWay) { -							this.fromExistenceCondition.setPredicate( -									unitName -> this.metricExceptions.contains(unitName) -											|| !this.database.getUnit(unitName) -													.isMetric()); -							this.toExistenceCondition.setPredicate( -									unitName -> this.metricExceptions.contains(unitName) -											|| this.database.getUnit(unitName).isMetric()); -						} else { -							this.fromExistenceCondition.setPredicate(unitName -> true); -							this.toExistenceCondition.setPredicate(unitName -> true); -						} -						break; -					default: -						System.err.printf("Warning: unrecognized setting \"%s\".", -								param); -						break; -					} -				} -			} catch (final IOException e) {} -		} -		 -		/** -		 * @return a set of all prefix names in the database -		 * @since 2019-04-14 -		 * @since v0.2.0 -		 */ -		public final Set<String> prefixNameSet() { -			return this.database.prefixMap().keySet(); -		} -		 -		/** -		 * Runs whenever a prefix is selected in the viewer. -		 * <p> -		 * Shows its information in the text box to the right. -		 * </p> -		 *  -		 * @since 2019-01-15 -		 * @since v0.1.0 -		 */ -		public final void prefixSelected() { -			final String prefixName = this.view.getPrefixViewerSelection(); -			if (prefixName == null) -				return; -			else { -				final UnitPrefix prefix = this.database.getPrefix(prefixName); -				 -				this.view.setPrefixTextBoxText(String.format("%s%nMultiplier: %s", -						prefixName, prefix.getMultiplier())); -			} -		} -		 -		/** -		 * Saves the settings to the settings file. -		 * -		 * @since 2021-02-17 -		 */ -		public final void saveSettings() { -			try (BufferedWriter writer = Files -					.newBufferedWriter(this.getSettingsFile())) { -				writer.write(String.format("precision=%d\n", this.precision)); -				writer.write( -						String.format("rounding_type=%s\n", this.roundingType)); -				writer.write(String.format("prefix_rule=%s\n", this.prefixRule)); -				writer.write(String.format("one_way=%s\n", this.oneWay)); -			} catch (final IOException e) { -				e.printStackTrace(); -				this.view.showErrorDialog("I/O Error", -						"Error occurred while saving settings: " -								+ e.getLocalizedMessage()); -			} -		} -		 -		/** -		 * Enables or disables one-way conversion. -		 *  -		 * @param oneWay whether one-way conversion should be on (true) or off -		 *               (false) -		 * @since 2020-08-27 -		 */ -		public final void setOneWay(boolean oneWay) { -			this.oneWay = oneWay; -			if (oneWay) { -				this.fromExistenceCondition.setPredicate( -						unitName -> this.metricExceptions.contains(unitName) -								|| !this.database.getUnit(unitName).isMetric()); -				this.toExistenceCondition.setPredicate( -						unitName -> this.metricExceptions.contains(unitName) -								|| this.database.getUnit(unitName).isMetric()); -			} else { -				this.fromExistenceCondition.setPredicate(unitName -> true); -				this.toExistenceCondition.setPredicate(unitName -> true); -			} -			 -			this.saveSettings(); -		} -		 -		/** -		 * @param precision new value of precision -		 * @since 2019-01-15 -		 * @since v0.1.0 -		 */ -		public final void setPrecision(final int precision) { -			this.precision = precision; -			 -			this.saveSettings(); -		} -		 -		/** -		 * @param prefixRepetitionRule the prefixRepetitionRule to set -		 * @since 2020-08-26 -		 */ -		public void setPrefixRepetitionRule( -				Predicate<List<UnitPrefix>> prefixRepetitionRule) { -			if (prefixRepetitionRule instanceof DefaultPrefixRepetitionRule) { -				this.prefixRule = (DefaultPrefixRepetitionRule) prefixRepetitionRule; -			} else { -				this.prefixRule = null; -			} -			this.database.setPrefixRepetitionRule(prefixRepetitionRule); -			 -			this.saveSettings(); -		} -		 -		/** -		 * @param roundingType the roundingType to set -		 * @since 2020-07-16 -		 */ -		public final void setRoundingType(RoundingType roundingType) { -			this.roundingType = roundingType; -			 -			this.saveSettings(); -		} -		 -		/** -		 * @return a list of all the entries in the dimension-based converter's To -		 *         box -		 * @since 2020-08-27 -		 */ -		public final Set<String> toEntries() { -			return ConditionalExistenceCollections.conditionalExistenceSet( -					this.unitNameSet(), this.toExistenceCondition); -		} -		 -		/** -		 * Returns true if and only if the unit represented by {@code unitName} -		 * has the dimension represented by {@code dimensionName}. -		 *  -		 * @param unitName      name of unit to test -		 * @param dimensionName name of dimension to test -		 * @return whether unit has dimenision -		 * @since 2019-04-13 -		 * @since v0.2.0 -		 */ -		public final boolean unitMatchesDimension(final String unitName, -				final String dimensionName) { -			final Unit unit = this.database.getUnit(unitName); -			final ObjectProduct<BaseDimension> dimension = this.database -					.getDimension(dimensionName); -			return unit.getDimension().equals(dimension); -		} -		 -		/** -		 * Runs whenever a unit is selected in the viewer. -		 * <p> -		 * Shows its information in the text box to the right. -		 * </p> -		 *  -		 * @since 2019-01-15 -		 * @since v0.1.0 -		 */ -		public final void unitNameSelected() { -			final String unitName = this.view.getUnitViewerSelection(); -			if (unitName == null) -				return; -			else { -				final Unit unit = this.database.getUnit(unitName); -				 -				this.view.setUnitTextBoxText(unit.toString()); -			} -		} -		 -		/** -		 * @return a set of all of the unit names -		 * @since 2019-04-14 -		 * @since v0.2.0 -		 */ -		private final Set<String> unitNameSet() { -			return this.database.unitMapPrefixless().keySet(); -		} -	} -	 -	/** -	 * Different types of rounding. -	 *  -	 * Significant digits: Rounds to a number of digits. i.e. with precision 5, -	 * 12345.6789 rounds to 12346. Decimal places: Rounds to a number of digits -	 * after the decimal point, i.e. with precision 5, 12345.6789 rounds to -	 * 12345.67890. Scientific: Rounds based on the number of digits and -	 * operations, following standard scientific rounding. -	 */ -	private static enum RoundingType { -		SIGNIFICANT_DIGITS, DECIMAL_PLACES, SCIENTIFIC; -	} -	 -	private static class View { -		private static final NumberFormat NUMBER_FORMATTER = new DecimalFormat(); -		 -		/** The view's frame. */ -		private final JFrame frame; -		/** The view's associated presenter. */ -		private final Presenter presenter; -		/** The master pane containing all of the tabs. */ -		private final JTabbedPane masterPane; -		 -		// DIMENSION-BASED CONVERTER -		/** The panel for inputting values in the dimension-based converter */ -		private final JTextField valueInput; -		/** The panel for "From" in the dimension-based converter */ -		private final SearchBoxList fromSearch; -		/** The panel for "To" in the dimension-based converter */ -		private final SearchBoxList toSearch; -		/** The output area in the dimension-based converter */ -		private final JTextArea dimensionBasedOutput; -		 -		// EXPRESSION-BASED CONVERTER -		/** The "From" entry in the conversion panel */ -		private final JTextField fromEntry; -		/** The "To" entry in the conversion panel */ -		private final JTextField toEntry; -		/** The output area in the conversion panel */ -		private final JTextArea output; -		 -		// UNIT AND PREFIX VIEWERS -		/** The searchable list of unit names in the unit viewer */ -		private final SearchBoxList unitNameList; -		/** The searchable list of prefix names in the prefix viewer */ -		private final SearchBoxList prefixNameList; -		/** The text box for unit data in the unit viewer */ -		private final JTextArea unitTextBox; -		/** The text box for prefix data in the prefix viewer */ -		private final JTextArea prefixTextBox; -		 -		/** -		 * Creates the {@code View}. -		 *  -		 * @since 2019-01-14 -		 * @since v0.1.0 -		 */ -		public View() { -			this.presenter = new Presenter(this); -			this.frame = new JFrame("Unit Converter"); -			this.frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); -			 -			// enable system look and feel -			try { -				UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); -			} catch (ClassNotFoundException | InstantiationException -					| IllegalAccessException | UnsupportedLookAndFeelException e) { -				// oh well, just use default theme -				System.err.println("Failed to enable system look-and-feel."); -				e.printStackTrace(); -			} -			 -			// create the components -			this.masterPane = new JTabbedPane(); -			this.unitNameList = new SearchBoxList(this.presenter.unitNameSet()); -			this.prefixNameList = new SearchBoxList(this.presenter.prefixNameSet(), -					this.presenter.getPrefixNameComparator(), true); -			this.unitTextBox = new JTextArea(); -			this.prefixTextBox = new JTextArea(); -			this.fromSearch = new SearchBoxList(this.presenter.fromEntries()); -			this.toSearch = new SearchBoxList(this.presenter.toEntries()); -			this.valueInput = new JFormattedTextField(NUMBER_FORMATTER); -			this.dimensionBasedOutput = new JTextArea(2, 32); -			this.fromEntry = new JTextField(); -			this.toEntry = new JTextField(); -			this.output = new JTextArea(2, 32); -			 -			// create more components -			this.initComponents(); -			 -			this.frame.pack(); -		} -		 -		/** -		 * @return the currently selected pane. -		 * @throws AssertionError if no pane (or an invalid pane) is selected -		 */ -		public Pane getActivePane() { -			switch (this.masterPane.getSelectedIndex()) { -			case 0: -				return Pane.UNIT_CONVERTER; -			case 1: -				return Pane.EXPRESSION_CONVERTER; -			case 2: -				return Pane.UNIT_VIEWER; -			case 3: -				return Pane.PREFIX_VIEWER; -			case 4: -				return Pane.ABOUT; -			case 5: -				return Pane.SETTINGS; -			default: -				throw new AssertionError("No selected pane, or invalid pane."); -			} -		} -		 -		/** -		 * @return value in dimension-based converter -		 * @throws ParseException -		 * @since 2020-07-07 -		 */ -		public double getDimensionConverterInput() throws ParseException { -			final Number value = NUMBER_FORMATTER.parse(this.valueInput.getText()); -			if (value instanceof Double) -				return (double) value; -			else if (value instanceof Long) -				return ((Long) value).longValue(); -			else -				throw new AssertionError(); -		} -		 -		/** -		 * @return selection in "From" selector in dimension-based converter -		 * @since 2019-04-13 -		 * @since v0.2.0 -		 */ -		public String getFromSelection() { -			return this.fromSearch.getSelectedValue(); -		} -		 -		/** -		 * @return text in "From" box in converter panel -		 * @since 2019-01-15 -		 * @since v0.1.0 -		 */ -		public String getFromText() { -			return this.fromEntry.getText(); -		} -		 -		/** -		 * @return index of selected prefix in prefix viewer -		 * @since 2019-01-15 -		 * @since v0.1.0 -		 */ -		public String getPrefixViewerSelection() { -			return this.prefixNameList.getSelectedValue(); -		} -		 -		/** -		 * @return selection in "To" selector in dimension-based converter -		 * @since 2019-04-13 -		 * @since v0.2.0 -		 */ -		public String getToSelection() { -			return this.toSearch.getSelectedValue(); -		} -		 -		/** -		 * @return text in "To" box in converter panel -		 * @since 2019-01-26 -		 * @since v0.1.0 -		 */ -		public String getToText() { -			return this.toEntry.getText(); -		} -		 -		/** -		 * @return index of selected unit in unit viewer -		 * @since 2019-01-15 -		 * @since v0.1.0 -		 */ -		public String getUnitViewerSelection() { -			return this.unitNameList.getSelectedValue(); -		} -		 -		/** -		 * Starts up the application. -		 *  -		 * @since 2018-12-27 -		 * @since v0.1.0 -		 */ -		public final void init() { -			this.frame.setVisible(true); -		} -		 -		/** -		 * Initializes the view's components. -		 *  -		 * @since 2018-12-27 -		 * @since v0.1.0 -		 */ -		private final void initComponents() { -			final JPanel masterPanel = new JPanel(); -			this.frame.add(masterPanel); -			 -			masterPanel.setLayout(new BorderLayout()); -			 -			{ // pane with all of the tabs -				masterPanel.add(this.masterPane, BorderLayout.CENTER); -				 -				// update stuff -				this.masterPane.addChangeListener(e -> this.update()); -				 -				{ // a panel for unit conversion using a selector -					final JPanel convertUnitPanel = new JPanel(); -					this.masterPane.addTab("Convert Units", convertUnitPanel); -					this.masterPane.setMnemonicAt(0, KeyEvent.VK_U); -					 -					convertUnitPanel.setLayout(new BorderLayout()); -					 -					{ // panel for input part -						final JPanel inputPanel = new JPanel(); -						convertUnitPanel.add(inputPanel, BorderLayout.CENTER); -						 -						inputPanel.setLayout(new GridLayout(1, 3)); -						 -						final JComboBox<String> dimensionSelector = new JComboBox<>( -								this.presenter.dimensionNameList() -										.toArray(new String[0])); -						dimensionSelector.setSelectedItem("LENGTH"); -						 -						// handle dimension filter -						final MutablePredicate<String> dimensionFilter = new MutablePredicate<>( -								s -> true); -						 -						// panel for From things -						inputPanel.add(this.fromSearch); -						 -						this.fromSearch.addSearchFilter(dimensionFilter); -						 -						{ // for dimension selector and arrow that represents -							// conversion -							final JPanel inBetweenPanel = new JPanel(); -							inputPanel.add(inBetweenPanel); -							 -							inBetweenPanel.setLayout(new BorderLayout()); -							 -							{ // dimension selector -								inBetweenPanel.add(dimensionSelector, -										BorderLayout.PAGE_START); -							} -							 -							{ // the arrow in the middle -								final JLabel arrowLabel = new JLabel("->"); -								inBetweenPanel.add(arrowLabel, BorderLayout.CENTER); -							} -						} -						 -						// panel for To things -						 -						inputPanel.add(this.toSearch); -						 -						this.toSearch.addSearchFilter(dimensionFilter); -						 -						// code for dimension filter -						dimensionSelector.addItemListener(e -> { -							dimensionFilter.setPredicate(string -> View.this.presenter -									.unitMatchesDimension(string, -											(String) dimensionSelector.getSelectedItem())); -							this.fromSearch.reapplyFilter(); -							this.toSearch.reapplyFilter(); -						}); -						 -						// apply the item listener once because I have a default -						// selection -						dimensionFilter.setPredicate(string -> View.this.presenter -								.unitMatchesDimension(string, -										(String) dimensionSelector.getSelectedItem())); -						this.fromSearch.reapplyFilter(); -						this.toSearch.reapplyFilter(); -					} -					 -					{ // panel for submit and output, and also value entry -						final JPanel outputPanel = new JPanel(); -						convertUnitPanel.add(outputPanel, BorderLayout.PAGE_END); -						 -						outputPanel.setLayout(new GridLayout(3, 1)); -						 -						{ // unit input -							final JPanel valueInputPanel = new JPanel(); -							outputPanel.add(valueInputPanel); -							 -							valueInputPanel.setLayout(new BorderLayout()); -							 -							{ // prompt -								final JLabel valuePrompt = new JLabel( -										"Value to convert: "); -								valueInputPanel.add(valuePrompt, -										BorderLayout.LINE_START); -							} -							 -							{ // value to convert -								valueInputPanel.add(this.valueInput, -										BorderLayout.CENTER); -							} -						} -						 -						{ // button to convert -							final JButton convertButton = new JButton("Convert"); -							outputPanel.add(convertButton); -							 -							convertButton.addActionListener( -									e -> this.presenter.convertDimensionBased()); -							convertButton.setMnemonic(KeyEvent.VK_ENTER); -						} -						 -						{ // output of conversion -							outputPanel.add(this.dimensionBasedOutput); -							this.dimensionBasedOutput.setEditable(false); -						} -					} -				} -				 -				{ // panel for unit conversion using expressions -					final JPanel convertExpressionPanel = new JPanel(); -					this.masterPane.addTab("Convert Unit Expressions", -							convertExpressionPanel); -					this.masterPane.setMnemonicAt(1, KeyEvent.VK_E); -					 -					convertExpressionPanel.setLayout(new GridLayout(4, 1)); -					 -					{ // panel for units to convert from -						final JPanel fromPanel = new JPanel(); -						convertExpressionPanel.add(fromPanel); -						 -						fromPanel.setBorder(BorderFactory.createTitledBorder("From")); -						fromPanel.setLayout(new GridLayout(1, 1)); -						 -						{ // entry for units -							fromPanel.add(this.fromEntry); -						} -					} -					 -					{ // panel for units to convert to -						final JPanel toPanel = new JPanel(); -						convertExpressionPanel.add(toPanel); -						 -						toPanel.setBorder(BorderFactory.createTitledBorder("To")); -						toPanel.setLayout(new GridLayout(1, 1)); -						 -						{ // entry for units -							toPanel.add(this.toEntry); -						} -					} -					 -					{ // button to convert -						final JButton convertButton = new JButton("Convert"); -						convertExpressionPanel.add(convertButton); -						 -						convertButton.addActionListener( -								e -> this.presenter.convertExpressions()); -						convertButton.setMnemonic(KeyEvent.VK_ENTER); -					} -					 -					{ // output of conversion -						final JPanel outputPanel = new JPanel(); -						convertExpressionPanel.add(outputPanel); -						 -						outputPanel -								.setBorder(BorderFactory.createTitledBorder("Output")); -						outputPanel.setLayout(new GridLayout(1, 1)); -						 -						{ // output -							outputPanel.add(this.output); -							this.output.setEditable(false); -						} -					} -				} -				 -				{ // panel to look up units -					final JPanel unitLookupPanel = new JPanel(); -					this.masterPane.addTab("Unit Viewer", unitLookupPanel); -					this.masterPane.setMnemonicAt(2, KeyEvent.VK_V); -					 -					unitLookupPanel.setLayout(new GridLayout()); -					 -					{ // search panel -						unitLookupPanel.add(this.unitNameList); -						 -						this.unitNameList.getSearchList().addListSelectionListener( -								e -> this.presenter.unitNameSelected()); -					} -					 -					{ // the text box for unit's toString -						unitLookupPanel.add(this.unitTextBox); -						this.unitTextBox.setEditable(false); -						this.unitTextBox.setLineWrap(true); -					} -				} -				 -				{ // panel to look up prefixes -					final JPanel prefixLookupPanel = new JPanel(); -					this.masterPane.addTab("Prefix Viewer", prefixLookupPanel); -					this.masterPane.setMnemonicAt(3, KeyEvent.VK_P); -					 -					prefixLookupPanel.setLayout(new GridLayout(1, 2)); -					 -					{ // panel for listing and seaching -						prefixLookupPanel.add(this.prefixNameList); -						 -						this.prefixNameList.getSearchList().addListSelectionListener( -								e -> this.presenter.prefixSelected()); -					} -					 -					{ // the text box for prefix's toString -						prefixLookupPanel.add(this.prefixTextBox); -						this.prefixTextBox.setEditable(false); -						this.prefixTextBox.setLineWrap(true); -					} -				} -				 -				{ // Info panel -					final JPanel infoPanel = new JPanel(); -					this.masterPane.addTab("\uD83D\uDEC8", // info (i) character -							new JScrollPane(infoPanel)); -					 -					final JTextArea infoTextArea = new JTextArea(); -					infoTextArea.setEditable(false); -					infoTextArea.setOpaque(false); -					infoPanel.add(infoTextArea); -					 -					// get info text -					final String infoText; -					try { -						final Path aboutFile = Path.of("src", "about.txt"); -						infoText = Files.readAllLines(aboutFile).stream() -								.map(Presenter::withoutComments) -								.collect(Collectors.joining("\n")); -					} catch (final IOException e) { -						throw new AssertionError("I/O exception loading about.txt"); -					} -					infoTextArea.setText(infoText); -				} -				 -				{ // Settings panel -					final JPanel settingsPanel = new JPanel(); -					this.masterPane.addTab("\u2699", new JScrollPane(settingsPanel)); -					this.masterPane.setMnemonicAt(5, KeyEvent.VK_S); -					 -					settingsPanel.setLayout( -							new BoxLayout(settingsPanel, BoxLayout.PAGE_AXIS)); -					 -					{ // rounding settings -						final JPanel roundingPanel = new JPanel(); -						settingsPanel.add(roundingPanel); -						roundingPanel -								.setBorder(new TitledBorder("Rounding Settings")); -						roundingPanel.setLayout(new GridBagLayout()); -						 -						// rounding rule selection -						final ButtonGroup roundingRuleButtons = new ButtonGroup(); -						 -						final JLabel roundingRuleLabel = new JLabel("Rounding Rule:"); -						roundingPanel.add(roundingRuleLabel, new GridBagBuilder(0, 0) -								.setAnchor(GridBagConstraints.LINE_START).build()); -						 -						final JRadioButton fixedPrecision = new JRadioButton( -								"Fixed Precision"); -						if (this.presenter.roundingType == RoundingType.SIGNIFICANT_DIGITS) { -							fixedPrecision.setSelected(true); -						} -						fixedPrecision.addActionListener(e -> this.presenter -								.setRoundingType(RoundingType.SIGNIFICANT_DIGITS)); -						roundingRuleButtons.add(fixedPrecision); -						roundingPanel.add(fixedPrecision, new GridBagBuilder(0, 1) -								.setAnchor(GridBagConstraints.LINE_START).build()); -						 -						final JRadioButton fixedDecimals = new JRadioButton( -								"Fixed Decimal Places"); -						if (this.presenter.roundingType == RoundingType.DECIMAL_PLACES) { -							fixedDecimals.setSelected(true); -						} -						fixedDecimals.addActionListener(e -> this.presenter -								.setRoundingType(RoundingType.DECIMAL_PLACES)); -						roundingRuleButtons.add(fixedDecimals); -						roundingPanel.add(fixedDecimals, new GridBagBuilder(0, 2) -								.setAnchor(GridBagConstraints.LINE_START).build()); -						 -						final JRadioButton relativePrecision = new JRadioButton( -								"Scientific Precision"); -						if (this.presenter.roundingType == RoundingType.SCIENTIFIC) { -							relativePrecision.setSelected(true); -						} -						relativePrecision.addActionListener(e -> this.presenter -								.setRoundingType(RoundingType.SCIENTIFIC)); -						roundingRuleButtons.add(relativePrecision); -						roundingPanel.add(relativePrecision, new GridBagBuilder(0, 3) -								.setAnchor(GridBagConstraints.LINE_START).build()); -						 -						final JLabel sliderLabel = new JLabel("Precision:"); -						roundingPanel.add(sliderLabel, new GridBagBuilder(0, 4) -								.setAnchor(GridBagConstraints.LINE_START).build()); -						 -						final JSlider sigDigSlider = new JSlider(0, 12); -						roundingPanel.add(sigDigSlider, new GridBagBuilder(0, 5) -								.setAnchor(GridBagConstraints.LINE_START).build()); -						 -						sigDigSlider.setMajorTickSpacing(4); -						sigDigSlider.setMinorTickSpacing(1); -						sigDigSlider.setSnapToTicks(true); -						sigDigSlider.setPaintTicks(true); -						sigDigSlider.setPaintLabels(true); -						sigDigSlider.setValue(this.presenter.precision); -						 -						sigDigSlider.addChangeListener(e -> this.presenter -								.setPrecision(sigDigSlider.getValue())); -					} -					 -					{ // prefix repetition settings -						final JPanel prefixRepetitionPanel = new JPanel(); -						settingsPanel.add(prefixRepetitionPanel); -						prefixRepetitionPanel.setBorder( -								new TitledBorder("Prefix Repetition Settings")); -						prefixRepetitionPanel.setLayout(new GridBagLayout()); -						 -						// prefix rules -						final ButtonGroup prefixRuleButtons = new ButtonGroup(); -						 -						final JRadioButton noRepetition = new JRadioButton( -								"No Repetition"); -						if (this.presenter.prefixRule == DefaultPrefixRepetitionRule.NO_REPETITION) { -							noRepetition.setSelected(true); -						} -						noRepetition.addActionListener( -								e -> this.presenter.setPrefixRepetitionRule( -										DefaultPrefixRepetitionRule.NO_REPETITION)); -						prefixRuleButtons.add(noRepetition); -						prefixRepetitionPanel.add(noRepetition, -								new GridBagBuilder(0, 0) -										.setAnchor(GridBagConstraints.LINE_START) -										.build()); -						 -						final JRadioButton noRestriction = new JRadioButton( -								"No Restriction"); -						if (this.presenter.prefixRule == DefaultPrefixRepetitionRule.NO_RESTRICTION) { -							noRestriction.setSelected(true); -						} -						noRestriction.addActionListener( -								e -> this.presenter.setPrefixRepetitionRule( -										DefaultPrefixRepetitionRule.NO_RESTRICTION)); -						prefixRuleButtons.add(noRestriction); -						prefixRepetitionPanel.add(noRestriction, -								new GridBagBuilder(0, 1) -										.setAnchor(GridBagConstraints.LINE_START) -										.build()); -						 -						final JRadioButton customRepetition = new JRadioButton( -								"Complex Repetition"); -						if (this.presenter.prefixRule == DefaultPrefixRepetitionRule.COMPLEX_REPETITION) { -							customRepetition.setSelected(true); -						} -						customRepetition.addActionListener( -								e -> this.presenter.setPrefixRepetitionRule( -										DefaultPrefixRepetitionRule.COMPLEX_REPETITION)); -						prefixRuleButtons.add(customRepetition); -						prefixRepetitionPanel.add(customRepetition, -								new GridBagBuilder(0, 2) -										.setAnchor(GridBagConstraints.LINE_START) -										.build()); -					} -					 -					{ // search settings -						final JPanel searchingPanel = new JPanel(); -						settingsPanel.add(searchingPanel); -						searchingPanel.setBorder(new TitledBorder("Search Settings")); -						searchingPanel.setLayout(new GridBagLayout()); -						 -						// searching rules -						final ButtonGroup searchRuleButtons = new ButtonGroup(); -						 -						final JRadioButton noPrefixes = new JRadioButton( -								"Never Include Prefixed Units"); -						noPrefixes.setEnabled(false); -						searchRuleButtons.add(noPrefixes); -						searchingPanel.add(noPrefixes, new GridBagBuilder(0, 0) -								.setAnchor(GridBagConstraints.LINE_START).build()); -						 -						final JRadioButton fixedPrefixes = new JRadioButton( -								"Include Some Prefixes"); -						fixedPrefixes.setEnabled(false); -						searchRuleButtons.add(fixedPrefixes); -						searchingPanel.add(fixedPrefixes, new GridBagBuilder(0, 1) -								.setAnchor(GridBagConstraints.LINE_START).build()); -						 -						final JRadioButton explicitPrefixes = new JRadioButton( -								"Include Explicit Prefixes"); -						explicitPrefixes.setEnabled(false); -						searchRuleButtons.add(explicitPrefixes); -						searchingPanel.add(explicitPrefixes, new GridBagBuilder(0, 2) -								.setAnchor(GridBagConstraints.LINE_START).build()); -						 -						final JRadioButton alwaysInclude = new JRadioButton( -								"Include All Single Prefixes"); -						alwaysInclude.setEnabled(false); -						searchRuleButtons.add(alwaysInclude); -						searchingPanel.add(alwaysInclude, new GridBagBuilder(0, 3) -								.setAnchor(GridBagConstraints.LINE_START).build()); -					} -					 -					{ // miscellaneous settings -						final JPanel miscPanel = new JPanel(); -						settingsPanel.add(miscPanel); -						miscPanel -								.setBorder(new TitledBorder("Miscellaneous Settings")); -						miscPanel.setLayout(new GridBagLayout()); -						 -						final JCheckBox oneWay = new JCheckBox( -								"Convert One Way Only"); -						oneWay.setSelected(this.presenter.oneWay); -						oneWay.addItemListener( -								e -> this.presenter.setOneWay(e.getStateChange() == 1)); -						miscPanel.add(oneWay, new GridBagBuilder(0, 0) -								.setAnchor(GridBagConstraints.LINE_START).build()); -						 -						final JCheckBox showAllVariations = new JCheckBox( -								"Show Symbols in \"Convert Units\""); -						showAllVariations.setSelected(true); -						showAllVariations.setEnabled(false); -						miscPanel.add(showAllVariations, new GridBagBuilder(0, 1) -								.setAnchor(GridBagConstraints.LINE_START).build()); -						 -						final JButton unitFileButton = new JButton( -								"Manage Unit Data Files"); -						unitFileButton.setEnabled(false); -						miscPanel.add(unitFileButton, new GridBagBuilder(0, 2) -								.setAnchor(GridBagConstraints.LINE_START).build()); -					} -				} -			} -		} -		 -		/** -		 * Sets the text in the output of the dimension-based converter. -		 *  -		 * @param text text to set -		 * @since 2019-04-13 -		 * @since v0.2.0 -		 */ -		public void setDimensionConverterOutputText(final String text) { -			this.dimensionBasedOutput.setText(text); -		} -		 -		/** -		 * Sets the text in the output of the conversion panel. -		 *  -		 * @param text text to set -		 * @since 2019-01-15 -		 * @since v0.1.0 -		 */ -		public void setExpressionConverterOutputText(final String text) { -			this.output.setText(text); -		} -		 -		/** -		 * Sets the text of the prefix text box in the prefix viewer. -		 *  -		 * @param text text to set -		 * @since 2019-01-15 -		 * @since v0.1.0 -		 */ -		public void setPrefixTextBoxText(final String text) { -			this.prefixTextBox.setText(text); -		} -		 -		/** -		 * Sets the text of the unit text box in the unit viewer. -		 *  -		 * @param text text to set -		 * @since 2019-01-15 -		 * @since v0.1.0 -		 */ -		public void setUnitTextBoxText(final String text) { -			this.unitTextBox.setText(text); -		} -		 -		/** -		 * Shows an error dialog. -		 *  -		 * @param title   title of dialog -		 * @param message message in dialog -		 * @since 2019-01-14 -		 * @since v0.1.0 -		 */ -		public void showErrorDialog(final String title, final String message) { -			JOptionPane.showMessageDialog(this.frame, message, title, -					JOptionPane.ERROR_MESSAGE); -		} -		 -		public void update() { -			switch (this.getActivePane()) { -			case UNIT_CONVERTER: -				this.fromSearch.updateList(); -				this.toSearch.updateList(); -				break; -			default: -				// do nothing, for now -				break; -			} -		} -	} -	 -	public static void main(final String[] args) { -		new View().init(); -	} -} | 
