com.espertech.esper.epl.parse.ExceptionConvertor.java Source code

Java tutorial

Introduction

Here is the source code for com.espertech.esper.epl.parse.ExceptionConvertor.java

Source

/*
 * *************************************************************************************
 *  Copyright (C) 2006-2015 EsperTech, Inc. All rights reserved.                       *
 *  http://www.espertech.com/esper                                                     *
 *  http://www.espertech.com                                                           *
 *  ---------------------------------------------------------------------------------- *
 *  The software in this package is published under the terms of the GPL license       *
 *  a copy of which has been included with this distribution in the license.txt file.  *
 * *************************************************************************************
 */

package com.espertech.esper.epl.parse;

import com.espertech.esper.client.EPStatementSyntaxException;
import com.espertech.esper.client.PropertyAccessException;
import com.espertech.esper.collection.UniformPair;
import com.espertech.esper.epl.generated.EsperEPL2GrammarLexer;
import com.espertech.esper.epl.generated.EsperEPL2GrammarParser;
import org.antlr.v4.runtime.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.StringWriter;
import java.util.Set;
import java.util.Stack;

/**
 * Converts recognition exceptions.
 */
public class ExceptionConvertor {
    private static final Log log = LogFactory.getLog(ExceptionConvertor.class);

    protected final static String END_OF_INPUT_TEXT = "end-of-input";

    /**
     * Converts from a syntax error to a nice statement exception.
     * @param e is the syntax error
     * @param expression is the expression text
     * @param parser the parser that parsed the expression
     * @param addPleaseCheck indicates to add "please check" paraphrases
     * @return syntax exception
     */
    public static EPStatementSyntaxException convertStatement(RecognitionException e, String expression,
            boolean addPleaseCheck, EsperEPL2GrammarParser parser) {
        UniformPair<String> pair = convert(e, expression, addPleaseCheck, parser);
        return new EPStatementSyntaxException(pair.getFirst(), pair.getSecond());
    }

    /**
     * Converts from a syntax error to a nice property exception.
     * @param e is the syntax error
     * @param expression is the expression text
     * @param parser the parser that parsed the expression
     * @param addPleaseCheck indicates to add "please check" paraphrases
     * @return syntax exception
     */
    public static PropertyAccessException convertProperty(RecognitionException e, String expression,
            boolean addPleaseCheck, EsperEPL2GrammarParser parser) {
        UniformPair<String> pair = convert(e, expression, addPleaseCheck, parser);
        return new PropertyAccessException(pair.getFirst(), pair.getSecond());
    }

    /**
     * Converts from a syntax error to a nice exception.
     * @param e is the syntax error
     * @param expression is the expression text
     * @param parser the parser that parsed the expression
     * @param addPleaseCheck indicates to add "please check" paraphrases
     * @return syntax exception
     */
    public static UniformPair<String> convert(RecognitionException e, String expression, boolean addPleaseCheck,
            EsperEPL2GrammarParser parser) {
        if (expression.trim().length() == 0) {
            String message = "Unexpected " + END_OF_INPUT_TEXT;
            return new UniformPair<String>(message, expression);
        }

        Token t;
        Token tBeforeBefore = null;
        Token tBefore = null;
        Token tAfter = null;

        int tIndex = e.getOffendingToken() != null ? e.getOffendingToken().getTokenIndex() : Integer.MAX_VALUE;
        if (tIndex < parser.getTokenStream().size()) {
            t = parser.getTokenStream().get(tIndex);
            if ((tIndex + 1) < parser.getTokenStream().size()) {
                tAfter = parser.getTokenStream().get(tIndex + 1);
            }
            if (tIndex - 1 >= 0) {
                tBefore = parser.getTokenStream().get(tIndex - 1);
            }
            if (tIndex - 2 >= 0) {
                tBeforeBefore = parser.getTokenStream().get(tIndex - 2);
            }
        } else {
            if (parser.getTokenStream().size() >= 1) {
                tBeforeBefore = parser.getTokenStream().get(parser.getTokenStream().size() - 1);
            }
            if (parser.getTokenStream().size() >= 2) {
                tBefore = parser.getTokenStream().get(parser.getTokenStream().size() - 2);
            }
            t = parser.getTokenStream().get(parser.getTokenStream().size() - 1);
        }

        Token tEnd = null;
        if (parser.getTokenStream().size() > 0) {
            tEnd = parser.getTokenStream().get(parser.getTokenStream().size() - 1);
        }

        String positionInfo = getPositionInfo(t);
        String token = t.getType() == EsperEPL2GrammarParser.EOF ? "end-of-input" : "'" + t.getText() + "'";

        Stack stack = parser.getParaphrases();
        String check = "";
        boolean isSelect = stack.size() == 1 && stack.get(0).equals("select clause");
        if ((stack.size() > 0) && addPleaseCheck) {
            String delimiter = "";
            StringBuilder checkList = new StringBuilder();
            checkList.append(", please check the ");
            while (stack.size() != 0) {
                checkList.append(delimiter);
                checkList.append(stack.pop());
                delimiter = " within the ";
            }
            check = checkList.toString();
        }

        // check if token is a reserved keyword
        Set<String> keywords = parser.getKeywords();
        boolean reservedKeyword = false;
        if (keywords.contains(token.toLowerCase())) {
            token += " (a reserved keyword)";
            reservedKeyword = true;
        } else if (tAfter != null && keywords.contains("'" + tAfter.getText().toLowerCase() + "'")) {
            token += " ('" + tAfter.getText() + "' is a reserved keyword)";
            reservedKeyword = true;
        } else {
            if ((tBefore != null) && (tAfter != null)
                    && (keywords.contains("'" + tBefore.getText().toLowerCase() + "'"))
                    && (keywords.contains("'" + tAfter.getText().toLowerCase() + "'"))) {
                token += " ('" + tBefore.getText() + "' and '" + tAfter.getText() + "' are a reserved keyword)";
                reservedKeyword = true;
            } else if ((tBefore != null) && (keywords.contains("'" + tBefore.getText().toLowerCase() + "'"))) {
                token += " ('" + tBefore.getText() + "' is a reserved keyword)";
                reservedKeyword = true;
            } else if (tEnd != null && keywords.contains("'" + tEnd.getText().toLowerCase() + "'")) {
                token += " ('" + tEnd.getText() + "' is a reserved keyword)";
                reservedKeyword = true;
            }
        }

        // special handling for the select-clause "as" keyword, which is required
        if (isSelect && !reservedKeyword) {
            check += getSelectClauseAsText(tBeforeBefore, t);
        }

        String message = "Incorrect syntax near " + token + positionInfo + check;
        if (e instanceof NoViableAltException || e instanceof LexerNoViableAltException
                || checkForInputMismatchWithNoExpected(e)) {
            Token nvaeToken = e.getOffendingToken();
            int nvaeTokenType = nvaeToken != null ? nvaeToken.getType() : EsperEPL2GrammarLexer.EOF;

            if (nvaeTokenType == EsperEPL2GrammarLexer.EOF) {
                if (token.equals(END_OF_INPUT_TEXT)) {
                    message = "Unexpected " + END_OF_INPUT_TEXT + positionInfo + check;
                } else {
                    if (ParseHelper.hasControlCharacters(expression)) {
                        message = "Unrecognized control characters found in text" + positionInfo;
                    } else {
                        message = "Unexpected " + END_OF_INPUT_TEXT + " near " + token + positionInfo + check;
                    }
                }
            } else {
                if (parser.getParserTokenParaphrases().get(nvaeTokenType) != null) {
                    message = "Incorrect syntax near " + token + positionInfo + check;
                } else {
                    // find next keyword in the next 3 tokens
                    int currentIndex = tIndex + 1;
                    while ((currentIndex > 0) && (currentIndex < parser.getTokenStream().size() - 1)
                            && (currentIndex < tIndex + 3)) {
                        Token next = parser.getTokenStream().get(currentIndex);
                        currentIndex++;

                        String quotedToken = "'" + next.getText() + "'";
                        if (parser.getKeywords().contains(quotedToken)) {
                            check += " near reserved keyword '" + next.getText() + "'";
                            break;
                        }
                    }
                    message = "Incorrect syntax near " + token + positionInfo + check;
                }
            }
        } else if (e instanceof InputMismatchException) {
            InputMismatchException mismatched = (InputMismatchException) e;

            String expected;
            if (mismatched.getExpectedTokens().size() > 1) {
                StringWriter writer = new StringWriter();
                writer.append("any of the following tokens {");
                String delimiter = "";
                for (int i = 0; i < mismatched.getExpectedTokens().size(); i++) {
                    writer.append(delimiter);
                    if (i > 5) {
                        writer.append("...");
                        writer.append(Integer.toString(mismatched.getExpectedTokens().size() - 5));
                        writer.append(" more");
                        break;
                    }
                    delimiter = ", ";
                    writer.append(getTokenText(parser, mismatched.getExpectedTokens().get(i)));
                }
                writer.append("}");
                expected = writer.toString();
            } else {
                expected = getTokenText(parser, mismatched.getExpectedTokens().get(0));
            }

            int offendingTokenType = mismatched.getOffendingToken().getType();
            String unexpected = getTokenText(parser, offendingTokenType);

            String expecting = " expecting " + expected.trim() + " but found " + unexpected.trim();
            message = "Incorrect syntax near " + token + expecting + positionInfo + check;
        }

        return new UniformPair<String>(message, expression);
    }

    private static boolean checkForInputMismatchWithNoExpected(RecognitionException e) {
        if (!(e instanceof InputMismatchException)) {
            return false;
        }
        if (e.getExpectedTokens().size() > 1) {
            return false;
        }
        return e.getExpectedTokens().size() == 1 && e.getExpectedTokens().get(0) == -1;
    }

    private static String getTokenText(EsperEPL2GrammarParser parser, int tokenIndex) {
        String expected = END_OF_INPUT_TEXT;
        if ((tokenIndex >= 0) && (tokenIndex < parser.getTokenNames().length)) {
            expected = parser.getTokenNames()[tokenIndex];
        }
        if (parser.getLexerTokenParaphrases().get(tokenIndex) != null) {
            expected = parser.getLexerTokenParaphrases().get(tokenIndex);
        }
        if (parser.getParserTokenParaphrases().get(tokenIndex) != null) {
            expected = parser.getParserTokenParaphrases().get(tokenIndex);
        }
        return expected;
    }

    /**
     * Returns the position information string for a parser exception.
     * @param t the token to return the information for
     * @return is a string with line and column information
     */
    private static String getPositionInfo(Token t) {
        return t.getLine() > 0 && t.getCharPositionInLine() > 0
                ? " at line " + t.getLine() + " column " + t.getCharPositionInLine()
                : "";
    }

    private static String getSelectClauseAsText(Token tBeforeBefore, Token t) {
        if (tBeforeBefore != null && tBeforeBefore.getType() == EsperEPL2GrammarParser.IDENT && t != null
                && t.getType() == EsperEPL2GrammarParser.IDENT) {
            return " (did you forget 'as'?)";
        }
        return "";
    }
}