Search in sources :

Example 1 with LocalizedParseException

use of org.apache.sis.internal.util.LocalizedParseException in project sis by apache.

the class CompoundFormat method parseObject.

/**
 * Creates an object from the given string representation.
 * The default implementation delegates to {@link #parse(CharSequence, ParsePosition)}
 * and ensures that the given string has been fully used, ignoring trailing
 * {@linkplain Character#isSpaceChar(int) spaces} and
 * {@linkplain Character#isISOControl(int) ISO control characters}.
 *
 * <div class="note"><b>Note:</b>
 * The usual SIS policy, as documented in the {@link org.apache.sis.util.CharSequences} class, is to test for
 * whitespaces using the {@code Character.isWhitespace(…)} method. The combination of {@code isSpaceChar(…)}
 * and {@code isISOControl(…)} done in this {@code parseObject(…)} method is more permissive since it encompasses
 * all whitespace characters, plus non-breaking spaces and non-white ISO controls.</div>
 *
 * @param  text  the string representation of the object to parse.
 * @return the parsed object.
 * @throws ParseException if an error occurred while parsing the object.
 */
@Override
public T parseObject(final String text) throws ParseException {
    final ParsePosition pos = new ParsePosition(0);
    final T value = parse(text, pos);
    if (value != null) {
        final int length = text.length();
        int c, n = 0, i = pos.getIndex();
        do {
            if ((i += n) >= length) {
                return value;
            }
            c = text.codePointAt(i);
            n = Character.charCount(c);
        } while (Character.isSpaceChar(c) || Character.isISOControl(c));
        pos.setErrorIndex(i);
    }
    throw new LocalizedParseException(getLocale(Locale.Category.DISPLAY), getValueType(), text, pos);
}
Also used : LocalizedParseException(org.apache.sis.internal.util.LocalizedParseException) ParsePosition(java.text.ParsePosition)

Example 2 with LocalizedParseException

use of org.apache.sis.internal.util.LocalizedParseException in project sis by apache.

the class TreeTableFormat method parse.

/**
 * Creates a tree from the given character sequence,
 * or returns {@code null} if the given text does not look like a tree for this method.
 * This method can parse the trees created by the {@code format(…)} methods
 * defined in this class.
 *
 * <div class="section">Parsing rules</div>
 * <ul>
 *   <li>Each node shall be represented by a single line made of two parts, in that order:
 *     <ol>
 *       <li>white spaces and tree drawing characters ({@code '│'}, {@code '├'}, {@code '└'} or {@code '─'});</li>
 *       <li>string representations of node values, separated by the
 *           {@linkplain #getColumnSeparatorPattern() colunm separator}.</li>
 *     </ol>
 *   </li>
 *   <li>The number of spaces and drawing characters before the node values determines the node
 *       indentation. This indentation does not need to be a factor of the {@link #getIndentation()}
 *       value, but must be consistent across all the parsed tree.</li>
 *   <li>The indentation determines the parent of each node.</li>
 *   <li>Parsing stops at first empty line (ignoring whitespaces), or at the end of the given text.</li>
 * </ul>
 *
 * <div class="section">Error index</div>
 * If the given text does not seem to be a tree table, then this method returns {@code null}.
 * Otherwise if parsing started but failed, then:
 *
 * <ul>
 *   <li>{@link ParsePosition#getErrorIndex()} will give the index at the beginning
 *       of line or beginning of cell where the error occurred, and</li>
 *   <li>{@link ParseException#getErrorOffset()} will give either the same value,
 *       or a slightly more accurate value inside the cell.</li>
 * </ul>
 *
 * @param  text  the character sequence for the tree to parse.
 * @param  pos   the position where to start the parsing.
 * @return the parsed tree, or {@code null} if the given character sequence can not be parsed.
 * @throws ParseException if an error occurred while parsing a node value.
 */
@Override
@SuppressWarnings("null")
public TreeTable parse(final CharSequence text, final ParsePosition pos) throws ParseException {
    final Matcher matcher = getColumnSeparatorMatcher(text);
    final int length = text.length();
    int indexOfLineStart = pos.getIndex();
    // Current index in the 'indentations' array.
    int indentationLevel = 0;
    // Number of spaces (ignoring drawing characters) for each level.
    int[] indentations = new int[16];
    // Last parsed node, having 'indentation[level]' characters before its content.
    TreeTable.Node lastNode = null;
    // First node found while parsing.
    TreeTable.Node root = null;
    final DefaultTreeTable table = new DefaultTreeTable(columnIndices != null ? columnIndices : TableColumn.NAME_MAP);
    final TableColumn<?>[] columns = DefaultTreeTable.getColumns(table.columnIndices);
    final Format[] formats = getFormats(columns, true);
    do {
        final int startNextLine = CharSequences.indexOfLineStart(text, 1, indexOfLineStart);
        int endOfLine = startNextLine;
        while (endOfLine > indexOfLineStart) {
            final int c = text.charAt(endOfLine - 1);
            if (c != '\r' && c != '\n')
                break;
            // Skip trailing '\r' and '\n'.
            endOfLine--;
        }
        /*
             * Skip leading spaces using Character.isSpaceChar(…) instead than isWhitespace(…)
             * because we need to skip non-breaking spaces as well as ordinary space. We don't
             * need to consider line feeds since they were handled by the lines just above.
             */
        boolean hasChar = false;
        // The indentation of current line.
        int i;
        for (i = indexOfLineStart; i < endOfLine; ) {
            final int c = Character.codePointAt(text, i);
            if (!Character.isSpaceChar(c)) {
                hasChar = true;
                if ("─│└├".indexOf(c) < 0) {
                    break;
                }
            }
            i += Character.charCount(c);
        }
        if (!hasChar) {
            // The line contains only whitespaces.
            break;
        }
        /*
             * Go back to the fist non-space character (should be '─'). We do that in case the
             * user puts some spaces in the text of the node label, since we don't want those
             * user-spaces to interfer with the calculation of indentation.
             */
        int indexOfValue = i;
        i = CharSequences.skipTrailingWhitespaces(text, indexOfLineStart, i) - indexOfLineStart;
        /*
             * Found the first character which is not part of the indentation. Create a new root
             * (without parent for now) and parse the values for each column. Columns with empty
             * text are not parsed (the value is left to null).
             */
        final TreeTable.Node node = new DefaultTreeTable.Node(table);
        matcher.region(indexOfValue, endOfLine);
        for (int ci = 0; ci < columns.length; ci++) {
            final boolean found = matcher.find();
            int endOfColumn = found ? matcher.start() : endOfLine;
            indexOfValue = CharSequences.skipLeadingWhitespaces(text, indexOfValue, endOfColumn);
            int endOfValue = CharSequences.skipTrailingWhitespaces(text, indexOfValue, endOfColumn);
            if (endOfValue > indexOfValue) {
                final String valueText = text.subSequence(indexOfValue, endOfValue).toString();
                try {
                    parseValue(node, columns[ci], formats[ci], valueText);
                } catch (ParseException | ClassCastException e) {
                    // See method javadoc.
                    pos.setErrorIndex(indexOfValue);
                    if (e instanceof ParseException) {
                        indexOfValue += ((ParseException) e).getErrorOffset();
                    }
                    throw new LocalizedParseException(getDisplayLocale(), Errors.Keys.UnparsableStringForClass_2, new Object[] { columns[ci].getElementType(), valueText }, indexOfValue).initCause(e);
                }
            }
            if (!found)
                break;
            /*
                 * The end of this column will be the beginning of the next column,
                 * after skipping the last character of the column separator.
                 */
            indexOfValue = matcher.end();
        }
        /*
             * If this is the first node created so far, it will be the root.
             */
        if (root == null) {
            indentations[0] = i;
            root = node;
        } else {
            int p;
            while (i < (p = indentations[indentationLevel])) {
                /*
                     * Lower indentation level: go up in the tree until we find the new parent.
                     * Note that lastNode.getParent() should never return null, since only the
                     * node at 'indentationLevel == 0' has a null parent and we check that case.
                     */
                if (--indentationLevel < 0) {
                    pos.setErrorIndex(indexOfLineStart);
                    throw new LocalizedParseException(getDisplayLocale(), Errors.Keys.NodeHasNoParent_1, new Object[] { node }, indexOfLineStart);
                }
                lastNode = lastNode.getParent();
            }
            if (i == p) {
                /*
                     * The node we just created is a sibling of the previous node. This is
                     * illegal if level==0, in which case we have no parent. Otherwise add
                     * the sibling to the common parent and let the indentation level unchanged.
                     */
                final TreeTable.Node parent = lastNode.getParent();
                if (parent == null) {
                    pos.setErrorIndex(indexOfLineStart);
                    throw new LocalizedParseException(getDisplayLocale(), Errors.Keys.NodeHasNoParent_1, new Object[] { node }, indexOfLineStart);
                }
                parent.getChildren().add(node);
            } else if (i > p) {
                /*
                     * The node we just created is a child of the previous node.
                     * Add a new indentation level.
                     */
                lastNode.getChildren().add(node);
                if (++indentationLevel == indentations.length) {
                    indentations = Arrays.copyOf(indentations, indentationLevel * 2);
                }
                indentations[indentationLevel] = i;
            }
        }
        lastNode = node;
        indexOfLineStart = startNextLine;
    } while (indexOfLineStart != length);
    if (root == null) {
        return null;
    }
    pos.setIndex(indexOfLineStart);
    table.setRoot(root);
    return table;
}
Also used : Matcher(java.util.regex.Matcher) InternationalString(org.opengis.util.InternationalString) Format(java.text.Format) CompoundFormat(org.apache.sis.io.CompoundFormat) TabularFormat(org.apache.sis.io.TabularFormat) LocalizedParseException(org.apache.sis.internal.util.LocalizedParseException) ParseException(java.text.ParseException) LocalizedParseException(org.apache.sis.internal.util.LocalizedParseException)

Example 3 with LocalizedParseException

use of org.apache.sis.internal.util.LocalizedParseException in project sis by apache.

the class RangeFormat method parse.

/**
 * Parses text from the given string to produce a range. This method use the full string.
 * If there is some unparsed characters after the parsed range, then this method thrown an
 * exception.
 *
 * @param  source  the text to parse.
 * @return the parsed range (never {@code null}).
 * @throws ParseException if the given string can not be fully parsed.
 */
public Range<?> parse(final String source) throws ParseException {
    final ParsePosition pos = new ParsePosition(0);
    UnconvertibleObjectException failure = null;
    try {
        final Range<?> range = tryParse(source, pos);
        if (range != null) {
            return range;
        }
    } catch (UnconvertibleObjectException e) {
        failure = e;
    }
    throw new LocalizedParseException(locale, elementType, source, pos).initCause(failure);
}
Also used : UnconvertibleObjectException(org.apache.sis.util.UnconvertibleObjectException) LocalizedParseException(org.apache.sis.internal.util.LocalizedParseException) ParsePosition(java.text.ParsePosition)

Example 4 with LocalizedParseException

use of org.apache.sis.internal.util.LocalizedParseException in project sis by apache.

the class CoordinateFormat method parse.

/**
 * Parses a coordinate from the given character sequence.
 * This method presumes that the coordinate reference system is the {@linkplain #getDefaultCRS() default CRS}.
 * The parsing begins at the {@linkplain ParsePosition#getIndex() index} given by the {@code pos} argument.
 * If parsing succeeds, then the {@code pos} index is updated to the index after the last ordinate value and
 * the parsed coordinate is returned. Otherwise (if parsing fails), the {@code pos} index is left unchanged,
 * the {@code pos} {@linkplain ParsePosition#getErrorIndex() error index} is set to the index of the first
 * unparsable character and an exception is thrown with a similar {@linkplain ParseException#getErrorOffset()
 * error index}.
 *
 * @param  text  the character sequence for the coordinate to parse.
 * @param  pos   the index where to start the parsing.
 * @return the parsed coordinate (never {@code null}).
 * @throws ParseException if an error occurred while parsing the coordinate.
 */
@Override
public DirectPosition parse(final CharSequence text, final ParsePosition pos) throws ParseException {
    ArgumentChecks.ensureNonNull("text", text);
    ArgumentChecks.ensureNonNull("pos", pos);
    final int start = pos.getIndex();
    final int length = text.length();
    /*
         * The NumberFormat, DateFormat and AngleFormat work only on String values, not on CharSequence.
         * If the given text is not a String, we will convert an arbitrarily small section of the given
         * text. Note that this will require to adjust the ParsePosition indices.
         */
    final int offset;
    final String asString;
    final ParsePosition subPos;
    if (text instanceof String) {
        offset = 0;
        subPos = pos;
        asString = (String) text;
    } else {
        offset = start;
        subPos = new ParsePosition(0);
        asString = text.subSequence(start, Math.min(start + READ_AHEAD_LIMIT, length)).toString();
    }
    /*
         * The Format instances to be used for each ordinate values is determined by the default CRS.
         * If no such CRS has been specified, then we will parse everything as plain numbers.
         */
    if (lastCRS != defaultCRS) {
        initialize(defaultCRS);
    }
    final double[] ordinates;
    Format format;
    final Format[] formats = this.formats;
    if (formats != null) {
        format = null;
        ordinates = new double[formats.length];
    } else {
        format = getFormat(Number.class);
        ordinates = new double[DEFAULT_DIMENSION];
    }
    /*
         * For each ordinate value except the first one, we need to skip the separator.
         * If we do not find the separator, we may consider that we reached the coordinate
         * end ahead of time. We currently allow that only for coordinate without CRS.
         */
    for (int i = 0; i < ordinates.length; i++) {
        if (i != 0) {
            final int end = subPos.getIndex();
            int index = offset + end;
            while (!CharSequences.regionMatches(text, index, parseSeparator)) {
                if (index < length) {
                    final int c = Character.codePointAt(text, index);
                    if (Character.isSpaceChar(c)) {
                        index += Character.charCount(c);
                        continue;
                    }
                }
                if (formats == null) {
                    pos.setIndex(index);
                    return new GeneralDirectPosition(Arrays.copyOf(ordinates, i));
                }
                pos.setIndex(start);
                pos.setErrorIndex(index);
                throw new LocalizedParseException(getLocale(), Errors.Keys.UnexpectedCharactersAfter_2, new CharSequence[] { text.subSequence(start, end), CharSequences.token(text, index) }, index);
            }
            subPos.setIndex(index + parseSeparator.length() - offset);
        }
        /*
             * At this point 'subPos' is set to the beginning of the next ordinate to parse in 'asString'.
             * Parse the value as a number, angle or date, as determined from the coordinate system axis.
             */
        if (formats != null) {
            format = formats[i];
        }
        @SuppressWarnings("null") final Object object = format.parseObject(asString, subPos);
        if (object == null) {
            /*
                 * If we failed to parse, build an error message with the type that was expected for that ordinate.
                 * If the given CharSequence was not a String, we may need to update the error index since we tried
                 * to parse only a substring.
                 */
            Class<?> type = Number.class;
            if (types != null) {
                switch(types[i]) {
                    case LONGITUDE:
                        type = Longitude.class;
                        break;
                    case LATITUDE:
                        type = Latitude.class;
                        break;
                    case ANGLE:
                        type = Angle.class;
                        break;
                    case DATE:
                        type = Date.class;
                        break;
                }
            }
            pos.setIndex(start);
            if (subPos != pos) {
                pos.setErrorIndex(offset + subPos.getErrorIndex());
            }
            throw new LocalizedParseException(getLocale(), type, text, pos);
        }
        double value;
        if (object instanceof Angle) {
            value = ((Angle) object).degrees();
        } else if (object instanceof Date) {
            value = ((Date) object).getTime() - epochs[i];
        } else {
            value = ((Number) object).doubleValue();
        }
        /*
             * The conversions and sign reversal applied below shall be in exact reverse order than
             * in the 'format(…)' method. However we have one additional step compared to format(…):
             * the unit written after the ordinate value may not be the same than the unit declared
             * in the CRS axis, so we have to parse the unit and convert the value before to apply
             * the reverse of 'format(…)' steps.
             */
        if (units != null) {
            final Unit<?> target = units[i];
            if (target != null) {
                final int base = subPos.getIndex();
                int index = base;
                /*
                     * Skip whitespaces using Character.isSpaceChar(…), not Character.isWhitespace(…),
                     * because we need to skip also the non-breaking space (Characters.NO_BREAK_SPACE).
                     * If we can not parse the unit after those spaces, we will revert to the original
                     * position (absence of unit will not be considered an error).
                     */
                while (index < asString.length()) {
                    final int c = asString.codePointAt(index);
                    if (Character.isSpaceChar(c)) {
                        index += Character.charCount(c);
                        continue;
                    }
                    subPos.setIndex(index);
                    final Object unit = getFormat(Unit.class).parseObject(asString, subPos);
                    if (unit == null) {
                        subPos.setIndex(base);
                        subPos.setErrorIndex(-1);
                    } else
                        try {
                            value = ((Unit<?>) unit).getConverterToAny(target).convert(value);
                        } catch (IncommensurableException e) {
                            index += offset;
                            pos.setIndex(start);
                            pos.setErrorIndex(index);
                            throw (ParseException) new ParseException(e.getMessage(), index).initCause(e);
                        }
                    break;
                }
            }
        }
        if (toFormatUnit != null) {
            final UnitConverter c = toFormatUnit[i];
            if (c != null) {
                value = c.inverse().convert(value);
            }
        }
        if (isNegative(i)) {
            value = -value;
        }
        ordinates[i] = value;
    }
    final GeneralDirectPosition position = new GeneralDirectPosition(ordinates);
    position.setCoordinateReferenceSystem(defaultCRS);
    return position;
}
Also used : IncommensurableException(javax.measure.IncommensurableException) Unit(javax.measure.Unit) Date(java.util.Date) Format(java.text.Format) SimpleDateFormat(java.text.SimpleDateFormat) NumberFormat(java.text.NumberFormat) DateFormat(java.text.DateFormat) AngleFormat(org.apache.sis.measure.AngleFormat) CompoundFormat(org.apache.sis.io.CompoundFormat) DecimalFormat(java.text.DecimalFormat) LocalizedParseException(org.apache.sis.internal.util.LocalizedParseException) Angle(org.apache.sis.measure.Angle) UnitConverter(javax.measure.UnitConverter) ParseException(java.text.ParseException) LocalizedParseException(org.apache.sis.internal.util.LocalizedParseException) ParsePosition(java.text.ParsePosition)

Example 5 with LocalizedParseException

use of org.apache.sis.internal.util.LocalizedParseException in project sis by apache.

the class AngleFormat method parse.

/**
 * Parses the given string as an angle. This full string is expected to represents an
 * angle value. This assumption allows {@code parse(String)} to be more tolerant than
 * {@link #parse(String, ParsePosition)} regarding white spaces between degrees, minutes
 * and seconds fields.
 *
 * @param  source  the string to parse.
 * @return the parsed string as an {@link Angle}, {@link Latitude} or {@link Longitude} object.
 * @throws ParseException if the string can not be fully parsed.
 *
 * @see #isFallbackAllowed()
 */
public Angle parse(final String source) throws ParseException {
    final ParsePosition pos = new ParsePosition(0);
    final Angle angle = parse(source, pos, true);
    final int offset = pos.getIndex();
    final int length = source.length();
    if (skipSpaces(source, offset, length) < length) {
        throw new LocalizedParseException(locale, Angle.class, source, pos);
    }
    return angle;
}
Also used : LocalizedParseException(org.apache.sis.internal.util.LocalizedParseException) ParsePosition(java.text.ParsePosition)

Aggregations

LocalizedParseException (org.apache.sis.internal.util.LocalizedParseException)5 ParsePosition (java.text.ParsePosition)4 Format (java.text.Format)2 ParseException (java.text.ParseException)2 CompoundFormat (org.apache.sis.io.CompoundFormat)2 DateFormat (java.text.DateFormat)1 DecimalFormat (java.text.DecimalFormat)1 NumberFormat (java.text.NumberFormat)1 SimpleDateFormat (java.text.SimpleDateFormat)1 Date (java.util.Date)1 Matcher (java.util.regex.Matcher)1 IncommensurableException (javax.measure.IncommensurableException)1 Unit (javax.measure.Unit)1 UnitConverter (javax.measure.UnitConverter)1 TabularFormat (org.apache.sis.io.TabularFormat)1 Angle (org.apache.sis.measure.Angle)1 AngleFormat (org.apache.sis.measure.AngleFormat)1 UnconvertibleObjectException (org.apache.sis.util.UnconvertibleObjectException)1 InternationalString (org.opengis.util.InternationalString)1