in endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/UnitFormat.java [1428:1566]
private Unit<?> parseTerm(final CharSequence symbols, final int lower, final int upper, final Operation operation)
throws MeasurementParseException
{
final String uom = CharSequences.trimWhitespaces(symbols, lower, upper).toString();
/*
* Check for labels explicitly given by users. Those labels have precedence over the Apache SIS hard-coded
* symbols. If no explicit label was found, check for symbols and names known to this UnitFormat instance.
*/
Unit<?> unit = labelToUnit.get(uom);
operation.finished = (unit != null);
if (unit == null) {
unit = Prefixes.getUnit(uom);
if (unit == null) {
final int length = uom.length();
if (length == 0) {
return Units.UNITY;
} else {
/*
* If the first character is a digit, presume that the term is a multiplication factor.
* The "*" character is used for raising the number on the left to the power on the right.
* Example: "10*6" is equal to one million. SIS also handles the "^" character as "*".
*
* In principle, spaces are not allowed in unit symbols (in particular, UCUM specifies that
* spaces should not be interpreted as multication operators). However, in practice we have
* sometimes units written in a form like "100 feet".
*
* If the last character is a super-script, then we assume a notation like "10⁻⁴".
*/
final char c = uom.charAt(0); // No need for code point because next condition is true only for BMP.
if (isDigit(c) || isSign(c)) {
final double multiplier;
try {
int s = uom.indexOf(' ');
if (s >= 0) {
final int next = CharSequences.skipLeadingWhitespaces(uom, s, length);
if (next < length && AbstractUnit.isSymbolChar(uom.codePointAt(next))) {
operation.finished = true; // For preventing attempt to continue parsing after "100 feet".
multiplier = Double.parseDouble(uom.substring(0, s));
return parseTerm(uom, s, length, new Operation(uom)).multiply(multiplier);
}
}
multiplier = parseMultiplicationFactor(uom);
} catch (NumberFormatException e) {
throw (MeasurementParseException) new MeasurementParseException(Errors.format(
Errors.Keys.UnknownUnit_1, uom), symbols, lower).initCause(e);
}
if (operation.code == Operation.IMPLICIT) {
operation.code = Operation.EXPONENT;
}
return Units.UNITY.multiply(multiplier);
}
}
if (length >= 2) {
/*
* If the symbol ends with a digit (normal script or superscript), presume that this is the unit
* exponent. That exponent can be a Unicode character (only one character in current UnitFormat
* implementation) or a number parseable with Integer.parseInt(String).
*/
Fraction power = null;
int i = length;
int c = uom.codePointBefore(i);
i -= Character.charCount(c);
if (Characters.isSuperScript(c)) {
c = Characters.toNormalScript(c);
if (isDigit(c)) {
power = new Fraction(c - '0', 1);
}
} else if (isDigit(c)) {
while (i != 0) {
c = uom.codePointBefore(i);
final boolean isExponent = isDigit(c) || isDivisor(c);
if (isExponent || isSign(c)) {
i -= Character.charCount(c);
}
if (!isExponent) {
try {
power = new Fraction(uom.substring(i));
} catch (NumberFormatException e) {
// Should never happen unless the number is larger than `int` capacity.
throw (MeasurementParseException) new MeasurementParseException(Errors.format(
Errors.Keys.UnknownUnit_1, uom), symbols, lower+i).initCause(e);
}
break;
}
}
}
if (power != null) {
/*
* At this point we have parsed the exponent. Before to parse the raw unit symbol,
* skip the exponent symbol (^, * or **) if any.
*/
i = CharSequences.skipTrailingWhitespaces(uom, 0, i);
if (i != 0) {
// No need for code point because next conditions are true only in BMP.
switch (uom.charAt(i-1)) {
case Style.EXPONENT_OR_MULTIPLY: {
if (i != 1 && uom.charAt(i-2) == Style.EXPONENT_OR_MULTIPLY) i--;
// Fallthrough for skipping the next character and whitespaces.
}
case Style.EXPONENT: {
i = CharSequences.skipTrailingWhitespaces(uom, 0, i - 1);
break;
}
}
}
final String symbol = uom.substring(CharSequences.skipLeadingWhitespaces(uom, 0, i), i);
unit = labelToUnit.get(symbol);
operation.finished = (unit != null);
if (unit == null) {
unit = Prefixes.getUnit(symbol);
}
if (unit != null) {
int numerator = power.numerator;
int denominator = power.denominator;
if (numerator < 0 && operation.invert()) {
numerator = -numerator;
}
if (numerator != 1) unit = unit.pow (numerator);
if (denominator != 1) unit = unit.root(denominator);
return unit;
}
}
}
/*
* At this point, we have determined that the label is not a known unit symbol.
* It may be a unit name, in which case the label is not case-sensitive anymore.
*/
operation.finished = true;
unit = fromName(uom);
if (unit == null) {
if (CharSequences.regionMatches(symbols, lower, UNITY, true)) {
return Units.UNITY;
}
throw new MeasurementParseException(Errors.format(Errors.Keys.UnknownUnit_1, uom), symbols, lower);
}
}
}
return unit;
}