private Unit parseTerm()

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;
    }