blob: 7a2228f678c44bf13660560c1601f6bd82f7f9a7 [file] [log] [blame]
/*******************************************************************************
* 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();
}