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