| /******************************************************************************* |
| * 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(); |
| |
| } |