units;
+
+ /**
+ * The rule that specifies when prefix repetition is allowed. It takes in one
+ * argument: a list of the prefixes being applied to the unit
+ *
+ * The prefixes are inputted in application order. This means that
+ * testing whether "kilomegagigametre" is a valid unit is equivalent to
+ * running the following code (assuming all variables are defined correctly):
+ *
+ * {@code prefixRepetitionRule.test(Arrays.asList(giga, mega, kilo))}
+ */
+ private Predicate> prefixRepetitionRule;
+
+ /**
+ * A parser that can parse unit expressions.
+ *
+ * @since 2019-03-22
+ * @since v0.2.0
+ */
+ private final ExpressionParser unitExpressionParser = new ExpressionParser.Builder<>(
+ this::getLinearUnit).addBinaryOperator("+", (o1, o2) -> o1.plus(o2), 0)
+ .addBinaryOperator("-", (o1, o2) -> o1.minus(o2), 0)
+ .addBinaryOperator("*", (o1, o2) -> o1.times(o2), 1)
+ .addSpaceFunction("*")
+ .addBinaryOperator("/", (o1, o2) -> o1.dividedBy(o2), 1)
+ .addBinaryOperator("^", UnitDatabase::exponentiateUnits, 2)
+ .build();
+
+ /**
+ * A parser that can parse unit value expressions.
+ *
+ * @since 2020-08-04
+ */
+ private final ExpressionParser unitValueExpressionParser = new ExpressionParser.Builder<>(
+ this::getLinearUnitValue)
+ .addBinaryOperator("+", (o1, o2) -> o1.plus(o2), 0)
+ .addBinaryOperator("-", (o1, o2) -> o1.minus(o2), 0)
+ .addBinaryOperator("*", (o1, o2) -> o1.times(o2), 1)
+ .addSpaceFunction("*")
+ .addBinaryOperator("/", (o1, o2) -> o1.dividedBy(o2), 1)
+ .addBinaryOperator("^", UnitDatabase::exponentiateUnitValues, 2)
+ .build();
+
+ /**
+ * A parser that can parse unit prefix expressions
+ *
+ * @since 2019-04-13
+ * @since v0.2.0
+ */
+ private final ExpressionParser prefixExpressionParser = new ExpressionParser.Builder<>(
+ this::getPrefix).addBinaryOperator("*", (o1, o2) -> o1.times(o2), 0)
+ .addSpaceFunction("*")
+ .addBinaryOperator("/", (o1, o2) -> o1.dividedBy(o2), 0)
+ .addBinaryOperator("^",
+ (o1, o2) -> o1.toExponent(o2.getMultiplier()), 1)
+ .build();
+
+ /**
+ * A parser that can parse unit dimension expressions.
+ *
+ * @since 2019-04-13
+ * @since v0.2.0
+ */
+ private final ExpressionParser> unitDimensionParser = new ExpressionParser.Builder<>(
+ this::getDimension).addBinaryOperator("*", (o1, o2) -> o1.times(o2), 0)
+ .addSpaceFunction("*")
+ .addBinaryOperator("/", (o1, o2) -> o1.dividedBy(o2), 0).build();
+
+ /**
+ * Creates the {@code UnitsDatabase}.
+ *
+ * @since 2019-01-10
+ * @since v0.1.0
+ */
+ public UnitDatabase() {
+ this(prefixes -> true);
+ }
+
+ /**
+ * Creates the {@code UnitsDatabase}
+ *
+ * @param prefixRepetitionRule the rule that determines when prefix
+ * repetition is allowed
+ * @since 2020-08-26
+ */
+ public UnitDatabase(Predicate> prefixRepetitionRule) {
+ this.prefixlessUnits = new HashMap<>();
+ this.prefixes = new HashMap<>();
+ this.dimensions = new HashMap<>();
+ this.prefixRepetitionRule = prefixRepetitionRule;
+ this.units = ConditionalExistenceCollections.conditionalExistenceMap(
+ new PrefixedUnitMap(this.prefixlessUnits, this.prefixes),
+ entry -> this.prefixRepetitionRule
+ .test(this.getPrefixesFromName(entry.getKey())));
+ }
+
+ /**
+ * Adds a unit dimension to the database.
+ *
+ * @param name dimension's name
+ * @param dimension dimension to add
+ * @throws NullPointerException if name or dimension is null
+ * @since 2019-03-14
+ * @since v0.2.0
+ */
+ public void addDimension(final String name,
+ final ObjectProduct dimension) {
+ this.dimensions.put(
+ Objects.requireNonNull(name, "name must not be null."),
+ Objects.requireNonNull(dimension, "dimension must not be null."));
+ }
+
+ /**
+ * Adds to the list from a line in a unit dimension file.
+ *
+ * @param line line to look at
+ * @param lineCounter number of line, for error messages
+ * @since 2019-04-10
+ * @since v0.2.0
+ */
+ private void addDimensionFromLine(final String line,
+ final long lineCounter) {
+ // ignore lines that start with a # sign - they're comments
+ if (line.isEmpty())
+ return;
+ if (line.contains("#")) {
+ this.addDimensionFromLine(line.substring(0, line.indexOf("#")),
+ lineCounter);
+ return;
+ }
+
+ // divide line into name and expression
+ final Matcher lineMatcher = NAME_EXPRESSION.matcher(line);
+ if (!lineMatcher.matches())
+ throw new IllegalArgumentException(String.format(
+ "Error at line %d: Lines of a dimension file must consist of a dimension name, then spaces or tabs, then a dimension expression.",
+ lineCounter));
+ final String name = lineMatcher.group(1);
+ final String expression = lineMatcher.group(2);
+
+ if (name.endsWith(" ")) {
+ System.err.printf("Warning - line %d's dimension name ends in a space",
+ lineCounter);
+ }
+
+ // if expression is "!", search for an existing dimension
+ // if no unit found, throw an error
+ if (expression.equals("!")) {
+ if (!this.containsDimensionName(name))
+ throw new IllegalArgumentException(String.format(
+ "! used but no dimension found (line %d).", lineCounter));
+ } else {
+ // it's a unit, get the unit
+ final ObjectProduct dimension;
+ try {
+ dimension = this.getDimensionFromExpression(expression);
+ } catch (final IllegalArgumentException e) {
+ System.err.printf("Parsing error on line %d:%n", lineCounter);
+ throw e;
+ }
+
+ this.addDimension(name, dimension);
+ }
+ }
+
+ /**
+ * Adds a unit prefix to the database.
+ *
+ * @param name prefix's name
+ * @param prefix prefix to add
+ * @throws NullPointerException if name or prefix is null
+ * @since 2019-01-14
+ * @since v0.1.0
+ */
+ public void addPrefix(final String name, final UnitPrefix prefix) {
+ this.prefixes.put(Objects.requireNonNull(name, "name must not be null."),
+ Objects.requireNonNull(prefix, "prefix must not be null."));
+ }
+
+ /**
+ * Adds a unit to the database.
+ *
+ * @param name unit's name
+ * @param unit unit to add
+ * @throws NullPointerException if unit is null
+ * @since 2019-01-10
+ * @since v0.1.0
+ */
+ public void addUnit(final String name, final Unit unit) {
+ this.prefixlessUnits.put(
+ Objects.requireNonNull(name, "name must not be null."),
+ Objects.requireNonNull(unit, "unit must not be null."));
+ }
+
+ /**
+ * Adds to the list from a line in a unit file.
+ *
+ * @param line line to look at
+ * @param lineCounter number of line, for error messages
+ * @since 2019-04-10
+ * @since v0.2.0
+ */
+ private void addUnitOrPrefixFromLine(final String line,
+ final long lineCounter) {
+ // ignore lines that start with a # sign - they're comments
+ if (line.isEmpty())
+ return;
+ if (line.contains("#")) {
+ this.addUnitOrPrefixFromLine(line.substring(0, line.indexOf("#")),
+ lineCounter);
+ return;
+ }
+
+ // divide line into name and expression
+ final Matcher lineMatcher = NAME_EXPRESSION.matcher(line);
+ if (!lineMatcher.matches())
+ throw new IllegalArgumentException(String.format(
+ "Error at line %d: Lines of a unit file must consist of a unit name, then spaces or tabs, then a unit expression.",
+ lineCounter));
+ final String name = lineMatcher.group(1);
+
+ final String expression = lineMatcher.group(2);
+
+ if (name.endsWith(" ")) {
+ System.err.printf("Warning - line %d's unit name ends in a space",
+ lineCounter);
+ }
+
+ // if expression is "!", search for an existing unit
+ // if no unit found, throw an error
+ if (expression.equals("!")) {
+ if (!this.containsUnitName(name))
+ throw new IllegalArgumentException(String
+ .format("! used but no unit found (line %d).", lineCounter));
+ } else {
+ if (name.endsWith("-")) {
+ final UnitPrefix prefix;
+ try {
+ prefix = this.getPrefixFromExpression(expression);
+ } catch (final IllegalArgumentException e) {
+ System.err.printf("Parsing error on line %d:%n", lineCounter);
+ throw e;
+ }
+ this.addPrefix(name.substring(0, name.length() - 1), prefix);
+ } else {
+ // it's a unit, get the unit
+ final Unit unit;
+ try {
+ unit = this.getUnitFromExpression(expression);
+ } catch (final IllegalArgumentException e) {
+ System.err.printf("Parsing error on line %d:%n", lineCounter);
+ throw e;
+ }
+
+ this.addUnit(name, unit);
+ }
+ }
+ }
+
+ /**
+ * Tests if the database has a unit dimension with this name.
+ *
+ * @param name name to test
+ * @return if database contains name
+ * @since 2019-03-14
+ * @since v0.2.0
+ */
+ public boolean containsDimensionName(final String name) {
+ return this.dimensions.containsKey(name);
+ }
+
+ /**
+ * Tests if the database has a unit prefix with this name.
+ *
+ * @param name name to test
+ * @return if database contains name
+ * @since 2019-01-13
+ * @since v0.1.0
+ */
+ public boolean containsPrefixName(final String name) {
+ return this.prefixes.containsKey(name);
+ }
+
+ /**
+ * Tests if the database has a unit with this name, taking prefixes into
+ * consideration
+ *
+ * @param name name to test
+ * @return if database contains name
+ * @since 2019-01-13
+ * @since v0.1.0
+ */
+ public boolean containsUnitName(final String name) {
+ return this.units.containsKey(name);
+ }
+
+ /**
+ * @return a map mapping dimension names to dimensions
+ * @since 2019-04-13
+ * @since v0.2.0
+ */
+ public Map> dimensionMap() {
+ return Collections.unmodifiableMap(this.dimensions);
+ }
+
+ /**
+ * Evaluates a unit expression, following the same rules as
+ * {@link #getUnitFromExpression}.
+ *
+ * @param expression expression to parse
+ * @return {@code LinearUnitValue} representing value of expression
+ * @since 2020-08-04
+ */
+ public LinearUnitValue evaluateUnitExpression(final String expression) {
+ Objects.requireNonNull(expression, "expression must not be null.");
+
+ // attempt to get a unit as an alias, or a number with precision first
+ if (this.containsUnitName(expression))
+ return this.getLinearUnitValue(expression);
+
+ // force operators to have spaces
+ String modifiedExpression = expression;
+ modifiedExpression = modifiedExpression.replaceAll("\\+", " \\+ ");
+ modifiedExpression = modifiedExpression.replaceAll("-", " - ");
+
+ // format expression
+ for (final Entry replacement : EXPRESSION_REPLACEMENTS
+ .entrySet()) {
+ modifiedExpression = replacement.getKey().matcher(modifiedExpression)
+ .replaceAll(replacement.getValue());
+ }
+
+ // the previous operation breaks negative numbers, fix them!
+ // (i.e. -2 becomes - 2)
+ // FIXME the previous operaton also breaks stuff like "1e-5"
+ for (int i = 0; i < modifiedExpression.length(); i++) {
+ if (modifiedExpression.charAt(i) == '-'
+ && (i < 2 || Arrays.asList('+', '-', '*', '/', '^')
+ .contains(modifiedExpression.charAt(i - 2)))) {
+ // found a broken negative number
+ modifiedExpression = modifiedExpression.substring(0, i + 1)
+ + modifiedExpression.substring(i + 2);
+ }
+ }
+
+ return this.unitValueExpressionParser.parseExpression(modifiedExpression);
+ }
+
+ /**
+ * Gets a unit dimension from the database using its name.
+ *
+ *
+ * This method accepts exponents, like "L^3"
+ *
+ *
+ * @param name dimension's name
+ * @return dimension
+ * @since 2019-03-14
+ * @since v0.2.0
+ */
+ public ObjectProduct getDimension(final String name) {
+ Objects.requireNonNull(name, "name must not be null.");
+ if (name.contains("^")) {
+ final String[] baseAndExponent = name.split("\\^");
+
+ final ObjectProduct base = this
+ .getDimension(baseAndExponent[0]);
+
+ final int exponent;
+ try {
+ exponent = Integer
+ .parseInt(baseAndExponent[baseAndExponent.length - 1]);
+ } catch (final NumberFormatException e2) {
+ throw new IllegalArgumentException("Exponent must be an integer.");
+ }
+
+ return base.toExponent(exponent);
+ }
+ return this.dimensions.get(name);
+ }
+
+ /**
+ * Uses the database's data to parse an expression into a unit dimension
+ *
+ * The expression is a series of any of the following:
+ *
+ * - The name of a unit dimension, which multiplies or divides the result
+ * based on preceding operators
+ * - The operators '*' and '/', which multiply and divide (note that just
+ * putting two unit dimensions next to each other is equivalent to
+ * multiplication)
+ * - The operator '^' which exponentiates. Exponents must be integers.
+ *
+ *
+ * @param expression expression to parse
+ * @throws IllegalArgumentException if the expression cannot be parsed
+ * @throws NullPointerException if expression is null
+ * @since 2019-04-13
+ * @since v0.2.0
+ */
+ public ObjectProduct getDimensionFromExpression(
+ final String expression) {
+ Objects.requireNonNull(expression, "expression must not be null.");
+
+ // attempt to get a dimension as an alias first
+ if (this.containsDimensionName(expression))
+ return this.getDimension(expression);
+
+ // force operators to have spaces
+ String modifiedExpression = expression;
+
+ // format expression
+ for (final Entry replacement : EXPRESSION_REPLACEMENTS
+ .entrySet()) {
+ modifiedExpression = replacement.getKey().matcher(modifiedExpression)
+ .replaceAll(replacement.getValue());
+ }
+ modifiedExpression = modifiedExpression.replaceAll(" *\\^ *", "\\^");
+
+ return this.unitDimensionParser.parseExpression(modifiedExpression);
+ }
+
+ /**
+ * Gets a unit. If it is linear, cast it to a LinearUnit and return it.
+ * Otherwise, throw an {@code IllegalArgumentException}.
+ *
+ * @param name unit's name
+ * @return unit
+ * @since 2019-03-22
+ * @since v0.2.0
+ */
+ private LinearUnit getLinearUnit(final String name) {
+ // see if I am using a function-unit like tempC(100)
+ Objects.requireNonNull(name, "name may not be null");
+ if (name.contains("(") && name.contains(")")) {
+ // break it into function name and value
+ final List parts = Arrays.asList(name.split("\\("));
+ if (parts.size() != 2)
+ throw new IllegalArgumentException(
+ "Format nonlinear units like: unit(value).");
+
+ // solve the function
+ final Unit unit = this.getUnit(parts.get(0));
+ final double value = Double.parseDouble(
+ parts.get(1).substring(0, parts.get(1).length() - 1));
+ return LinearUnit.fromUnitValue(unit, value);
+ } else {
+ // get a linear unit
+ final Unit unit = this.getUnit(name);
+
+ if (unit instanceof LinearUnit)
+ return (LinearUnit) unit;
+ else
+ throw new IllegalArgumentException(
+ String.format("%s is not a linear unit.", name));
+ }
+ }
+
+ /**
+ * Gets a {@code LinearUnitValue} from a unit name. Nonlinear units will be
+ * converted to their base units.
+ *
+ * @param name name of unit
+ * @return {@code LinearUnitValue} instance
+ * @since 2020-08-04
+ */
+ private LinearUnitValue getLinearUnitValue(final String name) {
+ try {
+ // try to parse it as a number - otherwise it is not a number!
+ final BigDecimal number = new BigDecimal(name);
+
+ final double uncertainty = Math.pow(10, -number.scale());
+ return LinearUnitValue.of(SI.ONE,
+ UncertainDouble.of(number.doubleValue(), uncertainty));
+ } catch (final NumberFormatException e) {
+ return LinearUnitValue.getExact(this.getLinearUnit(name), 1);
+ }
+ }
+
+ /**
+ * Gets a unit prefix from the database from its name
+ *
+ * @param name prefix's name
+ * @return prefix
+ * @since 2019-01-10
+ * @since v0.1.0
+ */
+ public UnitPrefix getPrefix(final String name) {
+ try {
+ return UnitPrefix.valueOf(Double.parseDouble(name));
+ } catch (final NumberFormatException e) {
+ return this.prefixes.get(name);
+ }
+ }
+
+ /**
+ * Gets all of the prefixes that are on a unit name, in application order.
+ *
+ * @param unitName name of unit
+ * @return prefixes
+ * @since 2020-08-26
+ */
+ List getPrefixesFromName(final String unitName) {
+ final List prefixes = new ArrayList<>();
+ String name = unitName;
+
+ while (!this.prefixlessUnits.containsKey(name)) {
+ // find the longest prefix
+ String longestPrefixName = null;
+ int longestLength = name.length();
+
+ while (longestPrefixName == null) {
+ longestLength--;
+ if (longestLength <= 0)
+ throw new AssertionError(
+ "No prefix found in " + name + ", but it is not a unit!");
+ if (this.prefixes.containsKey(name.substring(0, longestLength))) {
+ longestPrefixName = name.substring(0, longestLength);
+ }
+ }
+
+ // longest prefix found!
+ final UnitPrefix prefix = this.getPrefix(longestPrefixName);
+ prefixes.add(0, prefix);
+ name = name.substring(longestLength);
+ }
+ return prefixes;
+ }
+
+ /**
+ * Gets a unit prefix from a prefix expression
+ *
+ * Currently, prefix expressions are much simpler than unit expressions: They
+ * are either a number or the name of another prefix
+ *
+ *
+ * @param expression expression to input
+ * @return prefix
+ * @throws IllegalArgumentException if expression cannot be parsed
+ * @throws NullPointerException if any argument is null
+ * @since 2019-01-14
+ * @since v0.1.0
+ */
+ public UnitPrefix getPrefixFromExpression(final String expression) {
+ Objects.requireNonNull(expression, "expression must not be null.");
+
+ // attempt to get a unit as an alias first
+ if (this.containsUnitName(expression))
+ return this.getPrefix(expression);
+
+ // force operators to have spaces
+ String modifiedExpression = expression;
+
+ // format expression
+ for (final Entry replacement : EXPRESSION_REPLACEMENTS
+ .entrySet()) {
+ modifiedExpression = replacement.getKey().matcher(modifiedExpression)
+ .replaceAll(replacement.getValue());
+ }
+
+ return this.prefixExpressionParser.parseExpression(modifiedExpression);
+ }
+
+ /**
+ * @return the prefixRepetitionRule
+ * @since 2020-08-26
+ */
+ public final Predicate> getPrefixRepetitionRule() {
+ return this.prefixRepetitionRule;
+ }
+
+ /**
+ * Gets a unit from the database from its name, looking for prefixes.
+ *
+ * @param name unit's name
+ * @return unit
+ * @since 2019-01-10
+ * @since v0.1.0
+ */
+ public Unit getUnit(final String name) {
+ try {
+ final double value = Double.parseDouble(name);
+ return SI.ONE.times(value);
+ } catch (final NumberFormatException e) {
+ final Unit unit = this.units.get(name);
+ if (unit == null)
+ throw new NoSuchElementException("No unit " + name);
+ else if (unit.getPrimaryName().isEmpty())
+ return unit.withName(NameSymbol.ofName(name));
+ else if (!unit.getPrimaryName().get().equals(name)) {
+ final Set otherNames = new HashSet<>(unit.getOtherNames());
+ otherNames.add(unit.getPrimaryName().get());
+ return unit.withName(NameSymbol.ofNullable(name,
+ unit.getSymbol().orElse(null), otherNames));
+ } else if (!unit.getOtherNames().contains(name)) {
+ final Set otherNames = new HashSet<>(unit.getOtherNames());
+ otherNames.add(name);
+ return unit.withName(
+ NameSymbol.ofNullable(unit.getPrimaryName().orElse(null),
+ unit.getSymbol().orElse(null), otherNames));
+ } else
+ return unit;
+ }
+
+ }
+
+ /**
+ * Uses the database's unit data to parse an expression into a unit
+ *
+ * The expression is a series of any of the following:
+ *
+ * - The name of a unit, which multiplies or divides the result based on
+ * preceding operators
+ * - The operators '*' and '/', which multiply and divide (note that just
+ * putting two units or values next to each other is equivalent to
+ * multiplication)
+ * - The operator '^' which exponentiates. Exponents must be integers.
+ * - A number which is multiplied or divided
+ *
+ * This method only works with linear units.
+ *
+ * @param expression expression to parse
+ * @throws IllegalArgumentException if the expression cannot be parsed
+ * @throws NullPointerException if expression is null
+ * @since 2019-01-07
+ * @since v0.1.0
+ */
+ public Unit getUnitFromExpression(final String expression) {
+ Objects.requireNonNull(expression, "expression must not be null.");
+
+ // attempt to get a unit as an alias first
+ if (this.containsUnitName(expression))
+ return this.getUnit(expression);
+
+ // force operators to have spaces
+ String modifiedExpression = expression;
+ modifiedExpression = modifiedExpression.replaceAll("\\+", " \\+ ");
+ modifiedExpression = modifiedExpression.replaceAll("-", " - ");
+
+ // format expression
+ for (final Entry replacement : EXPRESSION_REPLACEMENTS
+ .entrySet()) {
+ modifiedExpression = replacement.getKey().matcher(modifiedExpression)
+ .replaceAll(replacement.getValue());
+ }
+
+ // the previous operation breaks negative numbers, fix them!
+ // (i.e. -2 becomes - 2)
+ for (int i = 0; i < modifiedExpression.length(); i++) {
+ if (modifiedExpression.charAt(i) == '-'
+ && (i < 2 || Arrays.asList('+', '-', '*', '/', '^')
+ .contains(modifiedExpression.charAt(i - 2)))) {
+ // found a broken negative number
+ modifiedExpression = modifiedExpression.substring(0, i + 1)
+ + modifiedExpression.substring(i + 2);
+ }
+ }
+
+ return this.unitExpressionParser.parseExpression(modifiedExpression);
+ }
+
+ /**
+ * Adds all dimensions from a file, using data from the database to parse
+ * them.
+ *
+ * Each line in the file should consist of a name and an expression (parsed
+ * by getDimensionFromExpression) separated by any number of tab characters.
+ *
+ *
+ * Allowed exceptions:
+ *
+ * - Anything after a '#' character is considered a comment and
+ * ignored.
+ * - Blank lines are also ignored
+ * - If an expression consists of a single exclamation point, instead of
+ * parsing it, this method will search the database for an existing unit. If
+ * no unit is found, an IllegalArgumentException is thrown. This is used to
+ * define initial units and ensure that the database contains them.
+ *
+ *
+ * @param file file to read
+ * @throws IllegalArgumentException if the file cannot be parsed, found or
+ * read
+ * @throws NullPointerException if file is null
+ * @since 2019-01-13
+ * @since v0.1.0
+ */
+ public void loadDimensionFile(final Path file) {
+ Objects.requireNonNull(file, "file must not be null.");
+ try {
+ long lineCounter = 0;
+ for (final String line : Files.readAllLines(file)) {
+ this.addDimensionFromLine(line, ++lineCounter);
+ }
+ } catch (final FileNotFoundException e) {
+ throw new IllegalArgumentException("Could not find file " + file, e);
+ } catch (final IOException e) {
+ throw new IllegalArgumentException("Could not read file " + file, e);
+ }
+ }
+
+ /**
+ * Adds all units from a file, using data from the database to parse them.
+ *
+ * Each line in the file should consist of a name and an expression (parsed
+ * by getUnitFromExpression) separated by any number of tab characters.
+ *
+ *
+ * Allowed exceptions:
+ *
+ * - Anything after a '#' character is considered a comment and
+ * ignored.
+ * - Blank lines are also ignored
+ * - If an expression consists of a single exclamation point, instead of
+ * parsing it, this method will search the database for an existing unit. If
+ * no unit is found, an IllegalArgumentException is thrown. This is used to
+ * define initial units and ensure that the database contains them.
+ *
+ *
+ * @param file file to read
+ * @throws IllegalArgumentException if the file cannot be parsed, found or
+ * read
+ * @throws NullPointerException if file is null
+ * @since 2019-01-13
+ * @since v0.1.0
+ */
+ public void loadUnitsFile(final Path file) {
+ Objects.requireNonNull(file, "file must not be null.");
+ try {
+ long lineCounter = 0;
+ for (final String line : Files.readAllLines(file)) {
+ this.addUnitOrPrefixFromLine(line, ++lineCounter);
+ }
+ } catch (final FileNotFoundException e) {
+ throw new IllegalArgumentException("Could not find file " + file, e);
+ } catch (final IOException e) {
+ throw new IllegalArgumentException("Could not read file " + file, e);
+ }
+ }
+
+ /**
+ * @return a map mapping prefix names to prefixes
+ * @since 2019-04-13
+ * @since v0.2.0
+ */
+ public Map prefixMap() {
+ return Collections.unmodifiableMap(this.prefixes);
+ }
+
+ /**
+ * @param prefixRepetitionRule the prefixRepetitionRule to set
+ * @since 2020-08-26
+ */
+ public final void setPrefixRepetitionRule(
+ Predicate> prefixRepetitionRule) {
+ this.prefixRepetitionRule = prefixRepetitionRule;
+ }
+
+ /**
+ * @return a string stating the number of units, prefixes and dimensions in
+ * the database
+ */
+ @Override
+ public String toString() {
+ return String.format(
+ "Unit Database with %d units, %d unit prefixes and %d dimensions",
+ this.prefixlessUnits.size(), this.prefixes.size(),
+ this.dimensions.size());
+ }
+
+ /**
+ * Returns a map mapping unit names to units, including units with prefixes.
+ *
+ * The returned map is infinite in size if there is at least one unit and at
+ * least one prefix. If it is infinite, some operations that only work with
+ * finite collections, like converting name/entry sets to arrays, will throw
+ * an {@code IllegalStateException}.
+ *
+ *
+ * Specifically, the operations that will throw an IllegalStateException if
+ * the map is infinite in size are:
+ *
+ * - {@code unitMap.entrySet().toArray()} (either overloading)
+ * - {@code unitMap.keySet().toArray()} (either overloading)
+ *
+ *
+ *
+ * Because of ambiguities between prefixes (i.e. kilokilo = mega), the map's
+ * {@link PrefixedUnitMap#containsValue containsValue} and
+ * {@link PrefixedUnitMap#values() values()} methods currently ignore
+ * prefixes.
+ *
+ *
+ * @return a map mapping unit names to units, including prefixed names
+ * @since 2019-04-13
+ * @since v0.2.0
+ */
+ public Map unitMap() {
+ return this.units; // PrefixedUnitMap is immutable so I don't need to make
+ // an unmodifiable map.
+ }
+
+ /**
+ * @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);
+ }
+}
--
cgit v1.2.3
From 3e6fee9561a00e5d9958c5685336c9d68c8629e1 Mon Sep 17 00:00:00 2001
From: Adrien Hopkins
Date: Sat, 27 Mar 2021 18:00:14 -0500
Subject: Used resources instead of Paths to make the generated jar work
---
bin/main/.gitignore | 3 +
build.gradle | 16 +-
dimensionfile.txt | 18 --
metric_exceptions.txt | 19 --
.../converterGUI/UnitConverterGUI.java | 97 ++++++--
.../java/org/unitConverter/unit/UnitDatabase.java | 34 +++
src/main/resources/dimensionfile.txt | 18 ++
src/main/resources/metric_exceptions.txt | 19 ++
src/main/resources/unitsfile.txt | 267 +++++++++++++++++++++
unitsfile.txt | 267 ---------------------
10 files changed, 426 insertions(+), 332 deletions(-)
delete mode 100644 dimensionfile.txt
delete mode 100644 metric_exceptions.txt
create mode 100644 src/main/resources/dimensionfile.txt
create mode 100644 src/main/resources/metric_exceptions.txt
create mode 100644 src/main/resources/unitsfile.txt
delete mode 100644 unitsfile.txt
(limited to 'src/main/java/org/unitConverter/unit/UnitDatabase.java')
diff --git a/bin/main/.gitignore b/bin/main/.gitignore
index b2d0b77..e5fcaae 100644
--- a/bin/main/.gitignore
+++ b/bin/main/.gitignore
@@ -1,2 +1,5 @@
/about.txt
/org/
+/dimensionfile.txt
+/metric_exceptions.txt
+/unitsfile.txt
diff --git a/build.gradle b/build.gradle
index 11bf07b..1a2ef44 100644
--- a/build.gradle
+++ b/build.gradle
@@ -20,12 +20,18 @@ dependencies {
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}
+jar {
+ manifest {
+ attributes 'Main-Class': "org.unitConverter.converterGUI.UnitConverterGUI"
+ }
+}
+
test {
- useJUnitPlatform()
- testLogging {
- events 'passed', 'skipped', 'failed'
- }
- finalizedBy jacocoTestReport
+ useJUnitPlatform()
+ testLogging {
+ events 'passed', 'skipped', 'failed'
+ }
+ finalizedBy jacocoTestReport
}
jacoco {
diff --git a/dimensionfile.txt b/dimensionfile.txt
deleted file mode 100644
index 3485de5..0000000
--- a/dimensionfile.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-# A file for the unit dimensions in my unit converter program
-
-# SI Base Dimensions
-# ! 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, quantity and luminous intensity since their units are exclusively SI.
-
-LENGTH !
-MASS !
-TIME !
-TEMPERATURE !
-
-# Derived Dimensions
-AREA LENGTH^2
-VOLUME LENGTH^3
-VELOCITY LENGTH / TIME
-ENERGY MASS * VELOCITY^2
\ No newline at end of file
diff --git a/metric_exceptions.txt b/metric_exceptions.txt
deleted file mode 100644
index 73748c0..0000000
--- a/metric_exceptions.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-# This is a list of exceptions for the one-way conversion mode
-# Units in this list will be included in both From: and To:
-# regardless of whether or not one-way conversion is enabled.
-
-tempC
-tempCelsius
-s
-second
-min
-minute
-h
-hour
-d
-day
-wk
-week
-gregorianmonth
-gregorianyear
-km/h
\ No newline at end of file
diff --git a/src/main/java/org/unitConverter/converterGUI/UnitConverterGUI.java b/src/main/java/org/unitConverter/converterGUI/UnitConverterGUI.java
index c046846..ee1bcc3 100644
--- a/src/main/java/org/unitConverter/converterGUI/UnitConverterGUI.java
+++ b/src/main/java/org/unitConverter/converterGUI/UnitConverterGUI.java
@@ -23,6 +23,7 @@ import java.awt.GridLayout;
import java.awt.event.KeyEvent;
import java.io.BufferedWriter;
import java.io.IOException;
+import java.io.InputStream;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
@@ -36,6 +37,7 @@ import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.NoSuchElementException;
+import java.util.Scanner;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -93,13 +95,11 @@ final class UnitConverterGUI {
/** 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");
+ private static final String DEFAULT_UNITS_FILEPATH = "/unitsfile.txt";
/** The default place where dimensions are stored. */
- private static final Path DEFAULT_DIMENSION_FILE = Path
- .of("dimensionfile.txt");
+ private static final String DEFAULT_DIMENSIONS_FILEPATH = "/dimensionfile.txt";
/** The default place where exceptions are stored. */
- private static final Path DEFAULT_EXCEPTIONS_FILE = Path
- .of("metric_exceptions.txt");
+ private static final String DEFAULT_EXCEPTIONS_FILEPATH = "/metric_exceptions.txt";
/**
* Adds default units and dimensions to a database.
@@ -130,6 +130,41 @@ final class UnitConverterGUI {
database.addDimension("TEMPERATURE", SI.Dimensions.TEMPERATURE);
}
+ /**
+ * Gets the text of a resource file as a set of strings (each one is one
+ * line of the text).
+ *
+ * @param filename filename to get resource from
+ * @return contents of file
+ * @since 2021-03-27
+ */
+ public static final List getLinesFromResource(String filename) {
+ final List lines = new ArrayList<>();
+
+ try (InputStream stream = inputStream(filename);
+ Scanner scanner = new Scanner(stream)) {
+ while (scanner.hasNextLine()) {
+ lines.add(scanner.nextLine());
+ }
+ } catch (final IOException e) {
+ throw new AssertionError(
+ "Error occurred while loading file " + filename, e);
+ }
+
+ return lines;
+ }
+
+ /**
+ * Gets an input stream for a resource file.
+ *
+ * @param filepath file to use as resource
+ * @return obtained Path
+ * @since 2021-03-27
+ */
+ private static final InputStream inputStream(String filepath) {
+ return UnitConverterGUI.class.getResourceAsStream(filepath);
+ }
+
/**
* @return {@code line} with any comments removed.
* @since 2021-03-13
@@ -161,9 +196,9 @@ final class UnitConverterGUI {
/** 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 fromExistenceCondition = new MutablePredicate<>(
@@ -196,21 +231,44 @@ final class UnitConverterGUI {
DefaultPrefixRepetitionRule.NO_RESTRICTION);
Presenter.addDefaults(this.database);
- this.database.loadUnitsFile(DEFAULT_UNITS_FILE);
- this.database.loadDimensionFile(DEFAULT_DIMENSION_FILE);
+ // load units and prefixes
+ try (final InputStream units = inputStream(DEFAULT_UNITS_FILEPATH)) {
+ this.database.loadUnitsFromStream(units);
+ } catch (final IOException e) {
+ throw new AssertionError("Loading of unitsfile.txt failed.", e);
+ }
+
+ // load dimensions
+ try (final InputStream dimensions = inputStream(
+ DEFAULT_DIMENSIONS_FILEPATH)) {
+ this.database.loadDimensionsFromStream(dimensions);
+ } catch (final IOException e) {
+ throw new AssertionError("Loading of dimensionfile.txt failed.", e);
+ }
// load metric exceptions
try {
- this.metricExceptions = Files.readAllLines(DEFAULT_EXCEPTIONS_FILE)
- .stream().map(Presenter::withoutComments)
- .filter(s -> !s.isBlank()).collect(Collectors.toSet());
+ this.metricExceptions = new HashSet<>();
+ try (InputStream exceptions = inputStream(
+ DEFAULT_EXCEPTIONS_FILEPATH);
+ Scanner scanner = new Scanner(exceptions)) {
+ while (scanner.hasNextLine()) {
+ final String line = Presenter
+ .withoutComments(scanner.nextLine());
+ if (!line.isBlank()) {
+ this.metricExceptions.add(line);
+ }
+ }
+ }
} catch (final IOException e) {
throw new AssertionError("Loading of metric_exceptions.txt failed.",
e);
}
// load settings - requires database to exist
- this.loadSettings();
+ if (Files.exists(this.getSettingsFile())) {
+ 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.
@@ -1139,17 +1197,10 @@ final class UnitConverterGUI {
infoPanel.add(infoTextArea);
// get info text
- final String infoText;
- try {
- final Path aboutFile = Path.of("src", "main", "resources",
- "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",
- e);
- }
+ final String infoText = Presenter
+ .getLinesFromResource("/about.txt").stream()
+ .map(Presenter::withoutComments)
+ .collect(Collectors.joining("\n"));
infoTextArea.setText(infoText);
}
diff --git a/src/main/java/org/unitConverter/unit/UnitDatabase.java b/src/main/java/org/unitConverter/unit/UnitDatabase.java
index 000acf5..6322fef 100644
--- a/src/main/java/org/unitConverter/unit/UnitDatabase.java
+++ b/src/main/java/org/unitConverter/unit/UnitDatabase.java
@@ -18,6 +18,7 @@ package org.unitConverter.unit;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -34,6 +35,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Objects;
+import java.util.Scanner;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -1879,6 +1881,22 @@ public final class UnitDatabase {
}
}
+ /**
+ * Adds all dimensions from a {@code InputStream}. Otherwise, works like
+ * {@link #loadDimensionFile}.
+ *
+ * @param stream stream to load from
+ * @since 2021-03-27
+ */
+ public void loadDimensionsFromStream(final InputStream stream) {
+ try (final Scanner scanner = new Scanner(stream)) {
+ long lineCounter = 0;
+ while (scanner.hasNextLine()) {
+ this.addDimensionFromLine(scanner.nextLine(), ++lineCounter);
+ }
+ }
+ }
+
/**
* Adds all units from a file, using data from the database to parse them.
*
@@ -1918,6 +1936,22 @@ public final class UnitDatabase {
}
}
+ /**
+ * Adds all units from a {@code InputStream}. Otherwise, works like
+ * {@link #loadUnitsFile}.
+ *
+ * @param stream stream to load from
+ * @since 2021-03-27
+ */
+ public void loadUnitsFromStream(InputStream stream) {
+ try (final Scanner scanner = new Scanner(stream)) {
+ long lineCounter = 0;
+ while (scanner.hasNextLine()) {
+ this.addUnitOrPrefixFromLine(scanner.nextLine(), ++lineCounter);
+ }
+ }
+ }
+
/**
* @return a map mapping prefix names to prefixes
* @since 2019-04-13
diff --git a/src/main/resources/dimensionfile.txt b/src/main/resources/dimensionfile.txt
new file mode 100644
index 0000000..3485de5
--- /dev/null
+++ b/src/main/resources/dimensionfile.txt
@@ -0,0 +1,18 @@
+# A file for the unit dimensions in my unit converter program
+
+# SI Base Dimensions
+# ! 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, quantity and luminous intensity since their units are exclusively SI.
+
+LENGTH !
+MASS !
+TIME !
+TEMPERATURE !
+
+# Derived Dimensions
+AREA LENGTH^2
+VOLUME LENGTH^3
+VELOCITY LENGTH / TIME
+ENERGY MASS * VELOCITY^2
\ No newline at end of file
diff --git a/src/main/resources/metric_exceptions.txt b/src/main/resources/metric_exceptions.txt
new file mode 100644
index 0000000..73748c0
--- /dev/null
+++ b/src/main/resources/metric_exceptions.txt
@@ -0,0 +1,19 @@
+# This is a list of exceptions for the one-way conversion mode
+# Units in this list will be included in both From: and To:
+# regardless of whether or not one-way conversion is enabled.
+
+tempC
+tempCelsius
+s
+second
+min
+minute
+h
+hour
+d
+day
+wk
+week
+gregorianmonth
+gregorianyear
+km/h
\ No newline at end of file
diff --git a/src/main/resources/unitsfile.txt b/src/main/resources/unitsfile.txt
new file mode 100644
index 0000000..eafe885
--- /dev/null
+++ b/src/main/resources/unitsfile.txt
@@ -0,0 +1,267 @@
+# A file for the units in my unit converter program
+
+# SI Base Units
+# ! means "look for an existing unit which I will load at the start"
+# This is necessary because every unit must be defined by others, and I need somewhere to start.
+
+metre !
+kilogram !
+second !
+ampere !
+kelvin !
+mole !
+candela !
+
+# Symbols and aliases for base units
+
+meter metre
+m metre
+kg kilogram
+s second
+A ampere
+K kelvin
+mol mole
+cd candela
+
+# the bit and byte, units of information
+
+bit !
+b bit
+byte 8 bit
+B byte
+
+# SI prefixes
+
+deca- 10
+deka- deca
+hecto- 100
+kilo- 1e3
+mega- 1e6
+giga- 1e9
+tera- 1e12
+peta- 1e15
+exa- 1e18
+zetta- 1e21
+yotta- 1e24
+
+deci- 1e-1
+centi- 1e-2
+milli- 1e-3
+micro- 1e-6
+nano- 1e-9
+pico- 1e-12
+femto- 1e-15
+atto- 1e-18
+zepto- 1e-21
+yocto- 1e-24
+
+da- deca
+D- deca
+h- hecto
+H- hecto
+k- kilo
+K- kilo
+M- mega
+G- giga
+T- tera
+P- peta
+E- exa
+Z- zetta
+Y- yotta
+
+d- deci
+c- centi
+m- milli
+u- micro
+n- nano
+p- pico
+f- femto
+a- atto
+z- zepto
+y- yocto
+
+# Binary prefixes (i.e. metric but 1024 replaces 1000)
+
+kibi- 1024^1
+mebi- 1024^2
+gibi- 1024^3
+tebi- 1024^4
+pebi- 1024^5
+exbi- 1024^6
+Ki- kibi
+Mi- mebi
+Gi- gibi
+Ti- tebi
+Pi- pebi
+Ei- exbi
+
+# Derived SI units
+# Note: it is best to have these before any non-SI units
+
+newton kg m / s^2
+N newton
+pascal N / m^2
+Pa pascal
+joule N m
+J joule
+watt J/s
+W watt
+coulomb A s
+C coulomb
+volt W/A
+V volt
+ohm V/A
+siemens A/V
+S siemens
+farad C/V
+F farad
+weber V s
+Wb weber
+henry V s / A
+H henry
+tesla Wb / m^2
+T tesla
+hertz 1 / s
+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
+tau 6.28318530717958
+# Another common circle constant
+pi tau / 2
+
+radian m / m
+rad radian
+steradian m^2 / m^2
+sr steradian
+degree tau / 360 radian
+deg degree
+° degree
+
+# Nonlinear units, which are not supported by the file reader and must be defined manually
+# Use tempC(100) for 100 degrees Celsius
+
+tempCelsius !
+tempFahrenheit !
+tempC tempCelsius
+tempF tempFahrenheit
+
+# Common time units
+minute 60 second
+min minute
+hour 3600 second
+h hour
+day 86400 second
+d day
+week 7 day
+wk week
+julianyear 365.25 day
+gregorianyear 365.2425 day
+gregorianmonth gregorianyear / 12
+
+# Other non-SI "metric" units
+litre 0.001 m^3
+liter litre
+l litre
+L litre
+tonne 1000 kg
+t tonne
+are 100 m^2
+hectare hectoare
+arcminute 1 / 60 degree
+arcmin arcminute
+arcsecond 1 / 60 arcminute
+arcsec arcsecond
+
+# constants
+waterdensity kilogram / litre
+
+# Imperial length units
+foot 0.3048 m
+ft foot
+inch foot / 12
+in inch
+yard 3 foot
+yd yard
+mile 1760 yard
+mi mile
+
+# Compressed notation
+kph km / hour
+mph mile / hour
+
+# Imperial weight units
+pound 0.45359237 kg
+lb pound
+ounce pound / 16
+oz ounce
+stone 14 lb
+UShundredweight 100 lb
+UKhundredweight 8 stone
+USimperialton 20 UShundredweight
+UKimperialton 10 UKhundredweight
+
+# Imperial volume units
+UKfluidounce ounce / waterdensity
+UKfloz UKfluidounce
+UKcup 10 UKfloz
+UKpint 2 UKcup
+UKquart 2 UKpint
+UKgallon 4 UKquart
+UKgal UKgallon
+
+USgallon 231 inch^3
+USgal USgallon
+USquart USgallon / 4
+USpint USquart / 2
+UScup USpint / 2
+USfluidounce UScup / 8
+USfloz USfluidounce
+UStablespoon USfluidounce / 2
+UStbsp UStablespoon
+USteaspoon UStablespoon / 3
+UStsp USteaspoon
+
+# Metric versions!
+# tsp = 5 mL, tbsp = 15 mL, floz = 30 mL, cup = 240 mL, pint = 480 mL, quart = 960 mL, gallon = 3840 mL
+# only metrictsp, metrictbsp and metriccup are common, the rest are derived from the US formulae with 240 mL cup
+metricteaspoon 5 mL
+teaspoon metricteaspoon
+tsp metricteaspoon
+metrictablespoon 3 metricteaspoon
+tablespoon metrictablespoon
+tbsp metrictablespoon
+metricfluidounce 2 metrictablespoon
+metriccup 8 metricfluidounce
+cup metriccup
+metricpint 2 metriccup
+pint metricpint
+metricquart 2 metricpint
+quart metricquart
+metricgallon 4 metricquart
+
+# Energy units
+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
+mi/h mile / hour
\ No newline at end of file
diff --git a/unitsfile.txt b/unitsfile.txt
deleted file mode 100644
index eafe885..0000000
--- a/unitsfile.txt
+++ /dev/null
@@ -1,267 +0,0 @@
-# A file for the units in my unit converter program
-
-# SI Base Units
-# ! means "look for an existing unit which I will load at the start"
-# This is necessary because every unit must be defined by others, and I need somewhere to start.
-
-metre !
-kilogram !
-second !
-ampere !
-kelvin !
-mole !
-candela !
-
-# Symbols and aliases for base units
-
-meter metre
-m metre
-kg kilogram
-s second
-A ampere
-K kelvin
-mol mole
-cd candela
-
-# the bit and byte, units of information
-
-bit !
-b bit
-byte 8 bit
-B byte
-
-# SI prefixes
-
-deca- 10
-deka- deca
-hecto- 100
-kilo- 1e3
-mega- 1e6
-giga- 1e9
-tera- 1e12
-peta- 1e15
-exa- 1e18
-zetta- 1e21
-yotta- 1e24
-
-deci- 1e-1
-centi- 1e-2
-milli- 1e-3
-micro- 1e-6
-nano- 1e-9
-pico- 1e-12
-femto- 1e-15
-atto- 1e-18
-zepto- 1e-21
-yocto- 1e-24
-
-da- deca
-D- deca
-h- hecto
-H- hecto
-k- kilo
-K- kilo
-M- mega
-G- giga
-T- tera
-P- peta
-E- exa
-Z- zetta
-Y- yotta
-
-d- deci
-c- centi
-m- milli
-u- micro
-n- nano
-p- pico
-f- femto
-a- atto
-z- zepto
-y- yocto
-
-# Binary prefixes (i.e. metric but 1024 replaces 1000)
-
-kibi- 1024^1
-mebi- 1024^2
-gibi- 1024^3
-tebi- 1024^4
-pebi- 1024^5
-exbi- 1024^6
-Ki- kibi
-Mi- mebi
-Gi- gibi
-Ti- tebi
-Pi- pebi
-Ei- exbi
-
-# Derived SI units
-# Note: it is best to have these before any non-SI units
-
-newton kg m / s^2
-N newton
-pascal N / m^2
-Pa pascal
-joule N m
-J joule
-watt J/s
-W watt
-coulomb A s
-C coulomb
-volt W/A
-V volt
-ohm V/A
-siemens A/V
-S siemens
-farad C/V
-F farad
-weber V s
-Wb weber
-henry V s / A
-H henry
-tesla Wb / m^2
-T tesla
-hertz 1 / s
-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
-tau 6.28318530717958
-# Another common circle constant
-pi tau / 2
-
-radian m / m
-rad radian
-steradian m^2 / m^2
-sr steradian
-degree tau / 360 radian
-deg degree
-° degree
-
-# Nonlinear units, which are not supported by the file reader and must be defined manually
-# Use tempC(100) for 100 degrees Celsius
-
-tempCelsius !
-tempFahrenheit !
-tempC tempCelsius
-tempF tempFahrenheit
-
-# Common time units
-minute 60 second
-min minute
-hour 3600 second
-h hour
-day 86400 second
-d day
-week 7 day
-wk week
-julianyear 365.25 day
-gregorianyear 365.2425 day
-gregorianmonth gregorianyear / 12
-
-# Other non-SI "metric" units
-litre 0.001 m^3
-liter litre
-l litre
-L litre
-tonne 1000 kg
-t tonne
-are 100 m^2
-hectare hectoare
-arcminute 1 / 60 degree
-arcmin arcminute
-arcsecond 1 / 60 arcminute
-arcsec arcsecond
-
-# constants
-waterdensity kilogram / litre
-
-# Imperial length units
-foot 0.3048 m
-ft foot
-inch foot / 12
-in inch
-yard 3 foot
-yd yard
-mile 1760 yard
-mi mile
-
-# Compressed notation
-kph km / hour
-mph mile / hour
-
-# Imperial weight units
-pound 0.45359237 kg
-lb pound
-ounce pound / 16
-oz ounce
-stone 14 lb
-UShundredweight 100 lb
-UKhundredweight 8 stone
-USimperialton 20 UShundredweight
-UKimperialton 10 UKhundredweight
-
-# Imperial volume units
-UKfluidounce ounce / waterdensity
-UKfloz UKfluidounce
-UKcup 10 UKfloz
-UKpint 2 UKcup
-UKquart 2 UKpint
-UKgallon 4 UKquart
-UKgal UKgallon
-
-USgallon 231 inch^3
-USgal USgallon
-USquart USgallon / 4
-USpint USquart / 2
-UScup USpint / 2
-USfluidounce UScup / 8
-USfloz USfluidounce
-UStablespoon USfluidounce / 2
-UStbsp UStablespoon
-USteaspoon UStablespoon / 3
-UStsp USteaspoon
-
-# Metric versions!
-# tsp = 5 mL, tbsp = 15 mL, floz = 30 mL, cup = 240 mL, pint = 480 mL, quart = 960 mL, gallon = 3840 mL
-# only metrictsp, metrictbsp and metriccup are common, the rest are derived from the US formulae with 240 mL cup
-metricteaspoon 5 mL
-teaspoon metricteaspoon
-tsp metricteaspoon
-metrictablespoon 3 metricteaspoon
-tablespoon metrictablespoon
-tbsp metrictablespoon
-metricfluidounce 2 metrictablespoon
-metriccup 8 metricfluidounce
-cup metriccup
-metricpint 2 metriccup
-pint metricpint
-metricquart 2 metricpint
-quart metricquart
-metricgallon 4 metricquart
-
-# Energy units
-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
-mi/h mile / hour
\ No newline at end of file
--
cgit v1.2.3