    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *     http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.jxpath.ri;
    019
    020import java.io.StringReader;
    021
    022import org.apache.commons.jxpath.JXPathInvalidSyntaxException;
    023import org.apache.commons.jxpath.ri.parser.ParseException;
    024import org.apache.commons.jxpath.ri.parser.TokenMgrError;
    025import org.apache.commons.jxpath.ri.parser.XPathParser;
    026
    027/**
    028 * XPath parser.
    029 */
    030public class Parser {
    031
    032    private static final XPathParser PARSER = new XPathParser(new StringReader(""));
    033
    034    /**
    035     * Add escapes to the specified String.
    036     *
    037     * @param string incoming String
    038     * @return String
    039     */
    040    private static String addEscapes(final String string) {
    041        // Piggy-back on the code generated by JavaCC
    042        return TokenMgrError.addEscapes(string);
    043    }
    044
    045    /**
    046     * Describe a parse position.
    047     *
    048     * @param expression to parse
    049     * @param position   parse position
    050     * @return String
    051     */
    052    private static String describePosition(final String expression, final int position) {
    053        if (position <= 0) {
    054            return "at the beginning of the expression";
    055        }
    056        if (position >= expression.length()) {
    057            return "- expression incomplete";
    058        }
    059        return "after: '" + addEscapes(expression.substring(0, position)) + "'";
    060    }
    061
    062    /**
    063     * Parses the XPath expression. Throws a JXPathException in case of a syntax error.
    064     *
    065     * @param expression to parse
    066     * @param compiler   the compiler
    067     * @return parsed Object
    068     */
    069    public static Object parseExpression(final String expression, final Compiler compiler) {
    070        synchronized (PARSER) {
    071            PARSER.setCompiler(compiler);
    072            Object expr;
    073            try {
    074                PARSER.ReInit(new StringReader(expression));
    075                expr = PARSER.parseExpression();
    076            } catch (final TokenMgrError e) {
    077                throw new JXPathInvalidSyntaxException("Invalid XPath: '" + addEscapes(expression) + "'. Invalid symbol '"
    078                        + addEscapes(String.valueOf(e.getCharacter())) + "' " + describePosition(expression, e.getPosition()));
    079            } catch (final ParseException e) {
    080                throw new JXPathInvalidSyntaxException(
    081                        "Invalid XPath: '" + addEscapes(expression) + "'. Syntax error " + describePosition(expression, e.currentToken.beginColumn));
    082            }
    083            return expr;
    084        }
    085    }
    086
    087    /**
    088     * Constructs a new instance.
    089     *
    090     *@deprecated Will be private in the next major version.
    091     */
    092    @Deprecated
    093    public Parser() {
    094        // empty
    095    }
    096}