/******************************************************************************* * Copyright (c) 1998, 2016 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.internal.jpa.parsing.jpql; import java.util.ArrayList; import java.util.List; //toplink imports import org.eclipse.persistence.exceptions.JPQLException; import org.eclipse.persistence.internal.jpa.parsing.JPQLParseTree; import org.eclipse.persistence.internal.jpa.parsing.NodeFactory; import org.eclipse.persistence.internal.jpa.parsing.NodeFactoryImpl; import org.eclipse.persistence.internal.jpa.parsing.jpql.antlr.JPQLParserBuilder; // Third party (ANLTR) stuff import org.eclipse.persistence.internal.libraries.antlr.runtime.MismatchedTokenException; import org.eclipse.persistence.internal.libraries.antlr.runtime.NoViableAltException; import org.eclipse.persistence.internal.libraries.antlr.runtime.RecognitionException; import org.eclipse.persistence.internal.libraries.antlr.runtime.RecognizerSharedState; import org.eclipse.persistence.internal.libraries.antlr.runtime.Token; import org.eclipse.persistence.internal.libraries.antlr.runtime.TokenStream; /** * EJBQLParser is the superclass of the ANTLR generated parser. */ public abstract class JPQLParser extends org.eclipse.persistence.internal.libraries.antlr.runtime.Parser { /** List of errors. */ private List errors = new ArrayList(); /** The name of the query being compiled. * The variable is null for dynamic queries. */ private String queryName = null; /** The text of the query being compiled. */ private String queryText = null; /** The factory to create parse tree nodes. */ protected NodeFactory factory; protected JPQLParser(TokenStream stream) { super(stream); } public JPQLParser(TokenStream input, RecognizerSharedState state) { super(input, state); } /** * INTERNAL * Returns the ANTLR version currently used. */ public static String ANTLRVersion() throws Exception { return "3.5.2"; } /** * INTERNAL * Builds a parser, parses the specified query string and returns the * parse tree. Any error in the query text results in an JPQLException. * This method is used for dynamic queries. */ public static JPQLParseTree buildParseTree(String queryText) throws JPQLException { return buildParseTree(null, queryText); } /** * INTERNAL * Builds a parser, parses the specified query string and returns the * parse tree. Any error in the query text results in an JPQLException. */ public static JPQLParseTree buildParseTree(String queryName, String queryText) throws JPQLException { JPQLParser parser = buildParserFor(queryName, queryText); return parser.parse(); } /** * INTERNAL * Creates a parser for the specified query string. The query string is * not parsed (see method parse). * This method is used for dynamic queries. */ public static JPQLParser buildParserFor(String queryText) throws JPQLException { return buildParserFor(null, queryText); } /** * INTERNAL * Creates a parser for the specified query string. The query string is * not parsed (see method parse). */ public static JPQLParser buildParserFor(String queryName, String queryText) throws JPQLException { try { JPQLParser parser = JPQLParserBuilder.buildParser(queryText); parser.setQueryName(queryName); parser.setQueryText(queryText); parser.setNodeFactory(new NodeFactoryImpl(parser.getQueryInfo())); return parser; } catch (Exception ex) { throw JPQLException.generalParsingException(queryText, ex); } } /** * INTERNAL * Parse the query string that was specified on parser creation. */ public JPQLParseTree parse() throws JPQLException { try { document(); } catch (Exception e) { addError(e); } // Handle any errors generated by the Parser if (hasErrors()) { throw generateException(); } // return the parser tree return getParseTree(); } /** * INTERNAL * Returns the parse tree created by a successful run of the parse * method. */ public JPQLParseTree getParseTree() { return (JPQLParseTree)getRootNode(); } /** * INTERNAL * Return the text of the current query being compiled. */ public String getQueryText() { return queryText; } /** * INTERNAL * Set the text of the current query being compiled. * Please note, setting the query text using this method is for error * handling and debugging purposes. */ public void setQueryText(String queryText) { this.queryText = queryText; } /** * INTERNAL * Return the name of the current query being compiled. This method returns * <code>null</code> if the current query is a dynamic query and not a named * query. */ public String getQueryName() { return queryText; } /** * INTERNAL * Set the name of the current query being compiled. * Please note, setting the query name using this method is for error * handling and debugging purposes. */ public void setQueryName(String queryName) { this.queryName = queryName; } /** * INTERNAL * Return the the query text prefixed by the query name in case of a * named query. The method returns just the query text in case of a dynamic * query. */ public String getQueryInfo() { return (queryName == null) ? queryText : queryName + ": " + queryText; } /** * INTERNAL * Set the factory used by the parser to create a parse tree and parse * tree nodes. */ public void setNodeFactory(NodeFactory factory) { this.factory = factory; } /** * INTERNAL * Returns the factory used by the parser to create a parse tree and parse * tree nodes. */ public NodeFactory getNodeFactory() { return factory; } /** * INTERNAL * Returns the list of errors found during the parsing process. */ public List getErrors() { return errors; } /** * INTERNAL * Returns true if there were errors during the parsing process. */ public boolean hasErrors() { return !getErrors().isEmpty(); } /** * INTERNAL * Add the exception to the list of errors. */ public void addError(Exception e) { if (e instanceof RecognitionException) { e = handleRecognitionException((RecognitionException)e); } else if (!(e instanceof JPQLException)) { e = JPQLException.generalParsingException(getQueryInfo(), e); } errors.add(e); } /** * INTERNAL * Generate an exception which encapsulates all the exceptions generated * by this parser. Special case where the first exception is an * JPQLException. */ protected JPQLException generateException() { //Handle exceptions we expect (such as expressionSotSupported) Exception firstException = (Exception)getErrors().get(0); if (firstException instanceof JPQLException) { return (JPQLException)firstException; } //Handle general exceptions, such as NPE JPQLException exception = JPQLException.generalParsingException(getQueryInfo()); exception.setInternalExceptions(getErrors()); return exception; } /** * INTERNAL * Map an exception thrown by the ANTLR generated code to an * JPQLException. */ //gf1166 Wrap ANTLRException inside JPQLException protected JPQLException handleRecognitionException(RecognitionException ex) { JPQLException result = null; // TODO: figure out the equivalent /* if (ex instanceof MismatchedCharException) { MismatchedCharException mismatched = (MismatchedCharException)ex; if (mismatched.foundChar == EOF_CHAR) { result = JPQLException.unexpectedEOF(getQueryInfo(), mismatched.getLine(), mismatched.getColumn(), ex); } else if (mismatched.mismatchType == MismatchedCharException.CHAR) { result = JPQLException.expectedCharFound(getQueryInfo(), mismatched.getLine(), mismatched.getColumn(), String.valueOf((char)mismatched.expecting), String.valueOf((char)mismatched.foundChar), ex); } } else*/ if (ex instanceof MismatchedTokenException) { MismatchedTokenException mismatched = (MismatchedTokenException)ex; Token token = mismatched.token; if (token != null) { if (token.getType() == Token.EOF) { result = JPQLException.unexpectedEOF(getQueryInfo(), mismatched.line, mismatched.charPositionInLine, ex); } else { result = JPQLException.syntaxErrorAt(getQueryInfo(), mismatched.line, mismatched.charPositionInLine, token.getText(), ex); } } } else if (ex instanceof NoViableAltException) { NoViableAltException noviable = (NoViableAltException)ex; Token token = noviable.token; if (token != null) { if (token.getType() == Token.EOF) { result = JPQLException.unexpectedEOF(getQueryInfo(), noviable.line, noviable.charPositionInLine, ex); } else { result = JPQLException.unexpectedToken(getQueryInfo(), noviable.line, noviable.charPositionInLine, token.getText(), ex); } } } else if (ex instanceof InvalidIdentifierException){ InvalidIdentifierException invalid = (InvalidIdentifierException)ex; Token token = invalid.getToken(); if (token != null) { if (token.getType() == Token.EOF) { result = JPQLException.unexpectedEOF(getQueryInfo(), token.getLine(), token.getCharPositionInLine(), ex); } else { result = JPQLException.unexpectedToken(getQueryInfo(), token.getLine(), token.getCharPositionInLine(), token.getText(), ex); } } } else if (ex instanceof InvalidIdentifierStartException) { InvalidIdentifierStartException invalid = (InvalidIdentifierStartException)ex; result = JPQLException.unexpectedChar(getQueryInfo(), invalid.line, invalid.charPositionInLine, String.valueOf((char)invalid.c), ex); } /* else if (ex instanceof TokenStreamRecognitionException) { result = handleANTLRException(((TokenStreamRecognitionException)ex).recog); }*/ if (result == null) { // no special handling from aboves matches the exception if this // line is reached => make it a syntax error result = JPQLException.syntaxError(getQueryInfo(), ex); } return result; } /** * Method called by the ANTLR generated code in case of an error. */ @Override public void reportError(RecognitionException ex) { addError(ex); } /** * This is the parser start method. It will be implemented by the ANTLR * generated subclass. */ public abstract void document() throws RecognitionException; /** * Returns the root node after representing the parse tree for the current * query string. It will be implemented by the ANTLR generated subclass. */ public abstract Object getRootNode(); }