private Range tryParse()

in endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/RangeFormat.java [840:1060]


    private Range<?> tryParse(final String source, final ParsePosition pos)
            throws UnconvertibleObjectException
    {
        final int length = source.length();
        /*
         * Skip leading whitespace and find the first non-blank character. It is usually an opening bracket,
         * except if minimal and maximal values are the same in which case the brackets may be omitted.
         */
        int index, c;
        for (index = pos.getIndex(); ; index += Character.charCount(c)) {
            if (index >= length) {
                pos.setErrorIndex(length);
                return null;
            }
            c = source.codePointAt(index);
            if (!Character.isWhitespace(c)) break;
        }
        final Object minValue, maxValue;
        final boolean isMinIncluded, isMaxIncluded;
        if (!isOpen(c)) {
            /*
             * No bracket, or curly bracket. We have eigher an empty range (as in "{}") or a single value for the range.
             * The braces are optional for single value. In other words, this block parses all of the following cases:
             *
             *  - {}
             *  - {value}
             *  - value         (not standard, but accepted by this parser)
             */
            final boolean hasBraces = (c == openSet);
            if (hasBraces) {
                // Skip the opening brace and following whitespaces.
                while ((index += Character.charCount(c)) < length) {
                    c = source.codePointAt(index);
                    if (!Character.isWhitespace(c)) break;
                }
            }
            if (hasBraces && c == closeSet) {
                // Empty range represented by {}
                minValue = maxValue = valueOfNil();
                isMinIncluded = isMaxIncluded = false;
            } else {
                // Singleton value, with or without braces.
                pos.setIndex(index);
                final Object value = elementFormat.parseObject(source, pos);
                if (value == null) {
                    return null;
                }
                pos.setErrorIndex(index);                   // In case of failure during the conversion.
                minValue = maxValue = convert(value);
                index = pos.getIndex();
                isMinIncluded = isMaxIncluded = true;
            }
            if (hasBraces) {
                // Skip whitespaces, then skip the closing brace.
                // Absence of closing brace is considered an error.
                do {
                    if (index >= length) {
                        pos.setErrorIndex(length);
                        return null;
                    }
                    c = source.codePointAt(index);
                    index += Character.charCount(c);
                } while (Character.isWhitespace(c));
                if (c != closeSet) {
                    pos.setErrorIndex(index - Character.charCount(c));
                    return null;
                }
                pos.setIndex(index);
            }
        } else {
            /*
             * We found an opening bracket. Skip the whitespaces. If the next character is a closing bracket,
             * then we have an empty range. The latter case is an extension to the standard format since empty
             * ranges are usually represented by {} instead of [].
             */
            isMinIncluded = (c == openInclusive);
            do {                                            // Skip whitespaces.
                index += Character.charCount(c);
                if (index >= length) {
                    pos.setErrorIndex(length);
                    return null;
                }
                c = source.codePointAt(index);
            } while (Character.isWhitespace(c));
            if (isClose(c)) {
                pos.setErrorIndex(index);                   // In case of failure during the conversion.
                minValue = maxValue = valueOfNil();
                isMaxIncluded = false;
                index += Character.charCount(c);
            } else {
                /*
                 * At this point, we have determined that the range is non-empty and there is at least one value to parse.
                 * First, parse the minimal value. If we fail to parse, check if it was the infinity value. Note that "-∞"
                 * and "∞" should have been parsed successfully if the format is DecimalFormat, but not necessarily "−∞".
                 * The difference is in the character used for the minus sign (ASCII hyphen versus Unicode minus sign).
                 */
                pos.setIndex(index);
                int savedIndex = index;
                Object value = elementFormat.parseObject(source, pos);
                if (value == null) {
                    if (c == minusSign || c == '−') {
                        index += Character.charCount(c);
                    }
                    if (!source.startsWith(infinity, index)) {
                        return null;
                    }
                    pos.setIndex(index += infinity.length());
                }
                pos.setErrorIndex(savedIndex);              // In case of failure during the conversion.
                minValue = convert(value);
                /*
                 * Parsing of 'minValue' succeed and its type is valid. Now look for the separator. If it is not present,
                 * then assume that we have a single value for the range. The default RangeFormat implementation does not
                 * format brackets in such case (see the "No bracket" case above), but we make the parser tolerant to the
                 * case where the brackets are present.
                 */
                for (index = pos.getIndex(); ; index += Character.charCount(c)) {
                    if (index >= length) {
                        pos.setErrorIndex(length);
                        return null;
                    }
                    c = source.codePointAt(index);
                    if (!Character.isWhitespace(c)) break;
                }
                final String separator = this.separator;
                if (source.startsWith(separator, index)) {
                    index += separator.length();
                    for (;; index += Character.charCount(c)) {
                        if (index >= length) {
                            pos.setErrorIndex(length);
                            return null;
                        }
                        c = source.codePointAt(index);
                        if (!Character.isWhitespace(c)) break;
                    }
                    /*
                     * Now parse the maximum value. A special case is applied for infinity value
                     * in a similar way than we did for the minimal value.
                     */
                    pos.setIndex(index);
                    value = elementFormat.parseObject(source, pos);
                    if (value == null) {
                        if (!source.startsWith(infinity, index)) {
                            return null;
                        }
                        pos.setIndex(index += infinity.length());
                    }
                    pos.setErrorIndex(index);               // In case of failure during the conversion.
                    maxValue = convert(value);
                    /*
                     * Skip one last time the whitespaces. The check for the closing bracket (which is mandatory)
                     * is performed outside the "if" block since it is common to the two "if ... else" cases.
                     */
                    for (index = pos.getIndex(); ; index += Character.charCount(c)) {
                        if (index >= length) {
                            pos.setErrorIndex(length);
                            return null;
                        }
                        c = source.charAt(index);
                        if (!Character.isWhitespace(c)) break;
                    }
                } else {
                    maxValue = minValue;
                }
                if (!isClose(c)) {
                    pos.setErrorIndex(index);
                    return null;
                }
                index += Character.charCount(c);
                isMaxIncluded = (c == closeInclusive);
            }
            pos.setIndex(index);
        }
        /*
         * Parses the unit, if any. The units are always optional: if we cannot parse
         * them, then we will consider that the parsing stopped before the unit.
         */
        Unit<?> unit = null;
        if (unitFormat != null) {
            while (index < length) {
                c = source.codePointAt(index);
                if (Character.isWhitespace(c)) {
                    index += Character.charCount(c);
                    continue;
                }
                // At this point we found a character that could be
                // the beginning of a unit symbol. Try to parse that.
                pos.setIndex(index);
                unit = unitFormat.parse(source, pos);
                break;
            }
        }
        /*
         * At this point, all required information are available. Now build the range.
         * In the special case were the target type is the generic Number type instead
         * than a more specialized type, the finest suitable type will be determined.
         */
        if (Number.class.isAssignableFrom(elementType)) {
            Class<? extends Number> type = (Class) elementType;
            Number min = (Number) minValue;
            Number max = (Number) maxValue;
            if (type == Number.class) {
                type = Numbers.widestClass(Numbers.narrowestClass(min), Numbers.narrowestClass(max));
                min  = Numbers.cast(min, type);
                max  = Numbers.cast(max, type);
            }
            if (min != null && min.doubleValue() == Double.NEGATIVE_INFINITY) min = null;
            if (max != null && max.doubleValue() == Double.POSITIVE_INFINITY) max = null;
            if (unit != null) {
                final MeasurementRange<?> range = new MeasurementRange(type, min, isMinIncluded, max, isMaxIncluded, unit);
                return range;
            }
            return new NumberRange(type, min, isMinIncluded, max, isMaxIncluded);
        } else if (Date.class.isAssignableFrom(elementType)) {
            return new Range(Date.class, (Date) minValue, isMinIncluded, (Date) maxValue, isMaxIncluded);
        } else {
            return new Range(elementType,
                    (Comparable<?>) minValue, isMinIncluded,
                    (Comparable<?>) maxValue, isMaxIncluded);
        }
    }