* 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: *
* 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 * @param name * name of prefix if a new prefix is created * @return prefix * @throws IllegalArgumentException * if expression cannot be parsed * @throws NullPointerException * if any argument is null * @since 2019-01-14 */ public UnitPrefix getPrefixFromExpression(final String expression, final String name) { Objects.requireNonNull(expression, "expression must not be null."); Objects.requireNonNull(name, "name must not be null."); try { return new DefaultUnitPrefix(Double.parseDouble(expression)); } catch (final NumberFormatException e) { if (expression.contains("^")) { final String[] baseAndExponent = expression.split("\\^"); final double base; try { base = Double.parseDouble(baseAndExponent[0]); } catch (final NumberFormatException e2) { throw new IllegalArgumentException("Base of exponientation must be a number."); } final int exponent; try { exponent = Integer.parseInt(baseAndExponent[baseAndExponent.length - 1]); } catch (final NumberFormatException e2) { throw new IllegalArgumentException("Exponent must be an integer."); } return new DefaultUnitPrefix(Math.pow(base, exponent)); } else { if (!this.containsPrefixName(expression)) throw new IllegalArgumentException("Unrecognized prefix name \"" + expression + "\"."); return this.getPrefix(expression); } } } /** * Gets a unit from the database from its name, ignoring prefixes. * * @param name * unit's name * @return unit * @since 2019-01-10 */ public Unit getPrefixlessUnit(final String name) { return this.units.get(name); } /** * Gets a unit from the database from its name, looking for prefixes. * * @param name * unit's name * @return unit * @since 2019-01-10 */ public Unit getUnit(final String name) { if (name.contains("^")) { final String[] baseAndExponent = name.split("\\^"); LinearUnit base; try { base = SI.SI.getBaseUnit(UnitDimension.EMPTY).times(Double.parseDouble(baseAndExponent[0])); } catch (final NumberFormatException e2) { final Unit unit = this.getUnit(baseAndExponent[0]); if (unit instanceof LinearUnit) { base = (LinearUnit) unit; } else if (unit instanceof BaseUnit) { base = ((BaseUnit) unit).asLinearUnit(); } else throw new IllegalArgumentException("Base of exponientation must be a linear or base unit."); } final int exponent; try { exponent = Integer.parseInt(baseAndExponent[baseAndExponent.length - 1]); } catch (final NumberFormatException e2) { throw new IllegalArgumentException("Exponent must be an integer."); } final LinearUnit exponentiated = base.toExponent(exponent); if (exponentiated.getConversionFactor() == 1) return exponentiated.getSystem().getBaseUnit(exponentiated.getDimension()); else return exponentiated; } else { for (final String prefixName : this.prefixNameSet()) { // check for a prefix if (name.startsWith(prefixName)) { // prefix found! Make sure what comes after it is actually a unit! final String prefixless = name.substring(prefixName.length()); if (this.containsUnitName(prefixless)) { // yep, it's a proper prefix! Get the unit! final Unit unit = this.getUnit(prefixless); final UnitPrefix prefix = this.getPrefix(prefixName); // Prefixes only work with linear and base units, so make sure it's one of those if (unit instanceof LinearUnit) { final LinearUnit linearUnit = (LinearUnit) unit; return linearUnit.times(prefix.getMultiplier()); } else if (unit instanceof BaseUnit) { final BaseUnit baseUnit = (BaseUnit) unit; return baseUnit.times(prefix.getMultiplier()); } } } } return this.units.get(name); } } /** * Uses the database's unit data to parse an expression into a unit ** The expression is a series of any of the following: *
* If the expression contains just the name of a unit, returns that unit without changing name or symbol. This * exists for the creation of aliases. *
* * @param line * line to parse * @throws IllegalArgumentException * if the expression cannot be parsed * @throws NullPointerException * if any argument is null * @since 2019-01-07 */ public Unit getUnitFromExpression(final String expression) { Objects.requireNonNull(expression, "expression must not be null."); // parse the expression // start with an "empty" unit then apply operations on it LinearUnit unit = SI.SI.getBaseUnit(UnitDimension.EMPTY).asLinearUnit(); boolean dividing = false; // if I'm just creating an alias, just create one instead of going through the parsing process if (!expression.contains(" ") && !expression.contains("*") && !expression.contains("/") && !expression.contains("(") && !expression.contains(")") && !expression.contains("^")) { try { return SI.SI.getBaseUnit(UnitDimension.EMPTY).times(Double.parseDouble(expression)); } catch (final NumberFormatException e) { if (!this.containsUnitName(expression)) throw new IllegalArgumentException("Unrecognized unit name \"" + expression + "\"."); return this.getUnit(expression); } } // \\* means "asterisk", * is reserved for (final String part : expression.replaceAll("\\*", " \\* ").replaceAll("/", " / ").replaceAll(" \\^", "\\^") .replaceAll("\\^ ", "\\^").split(" ")) { if ("".equals(part)) { continue; } // "unit1 unit2" is the same as "unit1 * unit2", so multiplication signs do nothing if ("*".equals(part)) { continue; } // When I reach a division sign, don't parse a unit, instead tell myself I'm going to divide the next // thing if ("/".equals(part) || "per".equals(part)) { dividing = true; continue; } try { final double partAsNumber = Double.parseDouble(part); // if this works, it's a number // this code should not throw any exceptions, so I'm going to throw AssertionErrors if it does try { if (dividing) { unit = unit.dividedBy(partAsNumber); dividing = false; } else { unit = unit.times(partAsNumber); } } catch (final Exception e) { throw new AssertionError(e); } } catch (final NumberFormatException e) { // it's a unit, try that if (part.contains("(") && part.endsWith(")")) { // the unitsfile is looking for a nonlinear unit final String[] unitAndValue = part.split("\\("); // this will work because I've checked that it contains a ( final String unitName = unitAndValue[0]; final String valueStringWithBracket = unitAndValue[unitAndValue.length - 1]; final String valueString = valueStringWithBracket.substring(0, valueStringWithBracket.length() - 1); final double value; // try to get the value - else throw an error try { value = Double.parseDouble(valueString); } catch (final NumberFormatException e2) { throw new IllegalArgumentException("Unparseable value " + valueString); } // get this unit in a linear form if (!this.containsPrefixlessUnitName(unitName)) throw new IllegalArgumentException("Unrecognized unit name \"" + part + "\"."); final Unit partUnit = this.getPrefixlessUnit(unitName); final LinearUnit multiplier = partUnit.getBase().times(partUnit.convertToBase(value)); // finally, add it to the expression if (dividing) { unit = unit.dividedBy(multiplier); dividing = false; } else { unit = unit.times(multiplier); } } else { // check for exponientation if (part.contains("^")) { final String[] valueAndExponent = part.split("\\^"); // this will always work because of the contains check final String valueString = valueAndExponent[0]; final String exponentString = valueAndExponent[valueAndExponent.length - 1]; LinearUnit value; // first, try to get the value try { final double valueAsNumber = Double.parseDouble(valueString); value = SI.SI.getBaseUnit(UnitDimension.EMPTY).times(valueAsNumber); } catch (final NumberFormatException e2) { // look for a unit if (!this.containsUnitName(valueString)) throw new IllegalArgumentException("Unrecognized unit name \"" + valueString + "\"."); final Unit valueUnit = this.getUnit(valueString); // try to turn the value into a linear unit if (valueUnit instanceof LinearUnit) { value = (LinearUnit) valueUnit; } else if (valueUnit instanceof BaseUnit) { value = ((BaseUnit) valueUnit).asLinearUnit(); } else throw new IllegalArgumentException("Only linear and base units can be exponientated."); } // now, try to get the exponent final int exponent; try { exponent = Integer.parseInt(exponentString); } catch (final NumberFormatException e2) { throw new IllegalArgumentException("Exponents must be integers."); } final LinearUnit exponientated = value.toExponent(exponent); if (dividing) { unit = unit.dividedBy(exponientated); dividing = false; } else { unit = unit.times(exponientated); } } else { // no exponent - look for a unit // the unitsfile is looking for a linear unit if (!this.containsUnitName(part)) throw new IllegalArgumentException("Unrecognized unit name \"" + part + "\"."); Unit other = this.getUnit(part); if (other instanceof BaseUnit) { other = ((BaseUnit) other).asLinearUnit(); } if (other instanceof LinearUnit) { if (dividing) { unit = unit.dividedBy((LinearUnit) other); dividing = false; } else { unit = unit.times((LinearUnit) other); } } else throw new IllegalArgumentException( "Only linear or base units can be multiplied and divided. If you want to use a non-linear unit, try the format UNITNAME(VALUE)."); } } } } // replace conversion-factor-1 linear units with base units // this improves the autogenerated names, allowing them to use derived SI names if (unit != null && unit.getConversionFactor() == 1) return unit.getSystem().getBaseUnit(unit.getDimension()); else return unit; } /** * @return an immutable set of all of the unit names in this database, ignoring prefixes * @since 2019-01-14 */ public Set