001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
005: *
006: *
007: * The contents of this file are subject to the terms of either the GNU
008: * General Public License Version 2 only ("GPL") or the Common Development
009: * and Distribution License("CDDL") (collectively, the "License"). You
010: * may not use this file except in compliance with the License. You can obtain
011: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
012: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
013: * language governing permissions and limitations under the License.
014: *
015: * When distributing the software, include this License Header Notice in each
016: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
017: * Sun designates this particular file as subject to the "Classpath" exception
018: * as provided by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the License
020: * Header, with the fields enclosed by brackets [] replaced by your own
021: * identifying information: "Portions Copyrighted [year]
022: * [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * If you wish your version of this file to be governed by only the CDDL or
027: * only the GPL Version 2, indicate your decision by adding "[Contributor]
028: * elects to include this software in this distribution under the [CDDL or GPL
029: * Version 2] license." If you don't indicate a single choice of license, a
030: * recipient has the option to distribute your version of this file under
031: * either the CDDL, the GPL Version 2 or to extend the choice of license to
032: * its licensees as provided above. However, if you add GPL Version 2 code
033: * and therefore, elected the GPL Version 2 license, then the option applies
034: * only if the new code is made subject to such option by the copyright
035: * holder.
036: */
037: // Copyright (c) 1998, 2007, Oracle. All rights reserved.
038: package oracle.toplink.essentials.internal.parsing.ejbql;
039:
040: import java.util.List;
041: import java.util.ArrayList;
042:
043: // Third party (ANLTR) stuff
044: import persistence.antlr.ANTLRException;
045: import persistence.antlr.LLkParser;
046: import persistence.antlr.MismatchedCharException;
047: import persistence.antlr.MismatchedTokenException;
048: import persistence.antlr.NoViableAltException;
049: import persistence.antlr.NoViableAltForCharException;
050: import persistence.antlr.ParserSharedInputState;
051: import persistence.antlr.RecognitionException;
052: import persistence.antlr.Token;
053: import persistence.antlr.TokenBuffer;
054: import persistence.antlr.TokenStream;
055: import persistence.antlr.TokenStreamException;
056: import persistence.antlr.TokenStreamRecognitionException;
057:
058: //toplink imports
059: import oracle.toplink.essentials.exceptions.EJBQLException;
060: import oracle.toplink.essentials.exceptions.QueryException;
061: import oracle.toplink.essentials.internal.parsing.EJBQLParseTree;
062: import oracle.toplink.essentials.internal.parsing.NodeFactory;
063: import oracle.toplink.essentials.internal.parsing.NodeFactoryImpl;
064: import oracle.toplink.essentials.internal.parsing.ejbql.antlr273.EJBQLParserBuilder;
065:
066: /**
067: * EJBQLParser is the superclass of the ANTLR generated parser.
068: */
069: public abstract class EJBQLParser extends LLkParser {
070:
071: /** The ANTLR end of file character. */
072: private static final int EOF_CHAR = 65535; // = (char) -1 = EOF
073:
074: /** List of errors. */
075: private List errors = new ArrayList();
076:
077: /** The name of the query being compiled.
078: * The variable is null for dynamic queries.
079: */
080: private String queryName = null;
081:
082: /** The text of the query being compiled. */
083: private String queryText = null;
084:
085: /** The factory to create parse tree nodes. */
086: protected NodeFactory factory;
087:
088: protected EJBQLParser(TokenBuffer tokenBuf, int k_) {
089: super (tokenBuf, k_);
090: }
091:
092: protected EJBQLParser(ParserSharedInputState state, int k_) {
093: super (state, k_);
094: }
095:
096: protected EJBQLParser(TokenStream lexer, int k) {
097: super (lexer, k);
098: }
099:
100: /**
101: * INTERNAL
102: * Returns the ANTLR version currently used.
103: */
104: public static String ANTLRVersion() throws Exception {
105: return "2.7.3";
106: }
107:
108: /**
109: * INTERNAL
110: * Builds a parser, parses the specified query string and returns the
111: * parse tree. Any error in the query text results in an EJBQLException.
112: * This method is used for dynamic queries.
113: */
114: public static EJBQLParseTree buildParseTree(String queryText)
115: throws EJBQLException {
116: return buildParseTree(null, queryText);
117: }
118:
119: /**
120: * INTERNAL
121: * Builds a parser, parses the specified query string and returns the
122: * parse tree. Any error in the query text results in an EJBQLException.
123: */
124: public static EJBQLParseTree buildParseTree(String queryName,
125: String queryText) throws EJBQLException {
126: EJBQLParser parser = buildParserFor(queryName, queryText);
127: return parser.parse();
128: }
129:
130: /**
131: * INTERNAL
132: * Creates a parser for the specified query string. The query string is
133: * not parsed (see method parse).
134: * This method is used for dynamic queries.
135: */
136: public static EJBQLParser buildParserFor(String queryText)
137: throws EJBQLException {
138: return buildParserFor(null, queryText);
139: }
140:
141: /**
142: * INTERNAL
143: * Creates a parser for the specified query string. The query string is
144: * not parsed (see method parse).
145: */
146: public static EJBQLParser buildParserFor(String queryName,
147: String queryText) throws EJBQLException {
148: try {
149: EJBQLParser parser = EJBQLParserBuilder
150: .buildParser(queryText);
151: parser.setQueryName(queryName);
152: parser.setQueryText(queryText);
153: parser.setNodeFactory(new NodeFactoryImpl(parser
154: .getQueryInfo()));
155: return parser;
156: } catch (Exception ex) {
157: throw EJBQLException.generalParsingException(queryText, ex);
158: }
159: }
160:
161: /**
162: * INTERNAL
163: * Parse the query string that was specified on parser creation.
164: */
165: public EJBQLParseTree parse() throws EJBQLException {
166: try {
167: document();
168: } catch (Exception e) {
169: addError(e);
170: }
171:
172: // Handle any errors generated by the Parser
173: if (hasErrors()) {
174: throw generateException();
175: }
176:
177: // return the parser tree
178: return getParseTree();
179: }
180:
181: /**
182: * INTERNAL
183: * Returns the parse tree created by a successful run of the parse
184: * method.
185: */
186: public EJBQLParseTree getParseTree() {
187: return (EJBQLParseTree) getRootNode();
188: }
189:
190: /**
191: * INTERNAL
192: * Return the text of the current query being compiled.
193: */
194: public String getQueryText() {
195: return queryText;
196: }
197:
198: /**
199: * INTERNAL
200: * Set the text of the current query being compiled.
201: * Please note, setting the query text using this method is for error
202: * handling and debugging purposes.
203: */
204: public void setQueryText(String queryText) {
205: this .queryText = queryText;
206: }
207:
208: /**
209: * INTERNAL
210: * Return the name of the current query being compiled. This method returns
211: * <code>null</code> if the current query is a dynamic query and not a named
212: * query.
213: */
214: public String getQueryName() {
215: return queryText;
216: }
217:
218: /**
219: * INTERNAL
220: * Set the name of the current query being compiled.
221: * Please note, setting the query name using this method is for error
222: * handling and debugging purposes.
223: */
224: public void setQueryName(String queryName) {
225: this .queryName = queryName;
226: }
227:
228: /**
229: * INTERNAL
230: * Return the the query text prefixed by the query name in case of a
231: * named query. The method returns just the query text in case of a dynamic
232: * query.
233: */
234: public String getQueryInfo() {
235: return (queryName == null) ? queryText : queryName + ": "
236: + queryText;
237: }
238:
239: /**
240: * INTERNAL
241: * Set the factory used by the parser to create a parse tree and parse
242: * tree nodes.
243: */
244: public void setNodeFactory(NodeFactory factory) {
245: this .factory = factory;
246: }
247:
248: /**
249: * INTERNAL
250: * Returns the factory used by the parser to create a parse tree and parse
251: * tree nodes.
252: */
253: public NodeFactory getNodeFactory() {
254: return factory;
255: }
256:
257: /**
258: * INTERNAL
259: * Returns the list of errors found during the parsing process.
260: */
261: public List getErrors() {
262: return errors;
263: }
264:
265: /**
266: * INTERNAL
267: * Returns true if there were errors during the parsing process.
268: */
269: public boolean hasErrors() {
270: return !getErrors().isEmpty();
271: }
272:
273: /**
274: * INTERNAL
275: * Add the exception to the list of errors.
276: */
277: public void addError(Exception e) {
278: if (e instanceof ANTLRException) {
279: e = handleANTLRException((ANTLRException) e);
280: } else if (!(e instanceof EJBQLException)) {
281: e = EJBQLException.generalParsingException(getQueryInfo(),
282: e);
283: }
284: errors.add(e);
285: }
286:
287: /**
288: * INTERNAL
289: * Generate an exception which encapsulates all the exceptions generated
290: * by this parser. Special case where the first exception is an
291: * EJBQLException.
292: */
293: protected EJBQLException generateException() {
294: //Handle exceptions we expect (such as expressionSotSupported)
295: Exception firstException = (Exception) getErrors().get(0);
296: if (firstException instanceof EJBQLException) {
297: return (EJBQLException) firstException;
298: }
299:
300: //Handle general exceptions, such as NPE
301: EJBQLException exception = EJBQLException
302: .generalParsingException(getQueryInfo());
303: exception.setInternalExceptions(getErrors());
304: return exception;
305: }
306:
307: /**
308: * INTERNAL
309: * Map an exception thrown by the ANTLR generated code to an
310: * EJBQLException.
311: */
312: //gf1166 Wrap ANTLRException inside EJBQLException
313: protected EJBQLException handleANTLRException(ANTLRException ex) {
314: EJBQLException result = null;
315: if (ex instanceof MismatchedCharException) {
316: MismatchedCharException mismatched = (MismatchedCharException) ex;
317: if (mismatched.foundChar == EOF_CHAR) {
318: result = EJBQLException.unexpectedEOF(getQueryInfo(),
319: mismatched.getLine(), mismatched.getColumn(),
320: ex);
321: } else if (mismatched.mismatchType == MismatchedCharException.CHAR) {
322: result = EJBQLException
323: .expectedCharFound(
324: getQueryInfo(),
325: mismatched.getLine(),
326: mismatched.getColumn(),
327: String
328: .valueOf((char) mismatched.expecting),
329: String
330: .valueOf((char) mismatched.foundChar),
331: ex);
332: }
333: } else if (ex instanceof MismatchedTokenException) {
334: MismatchedTokenException mismatched = (MismatchedTokenException) ex;
335: Token token = mismatched.token;
336: if (token != null) {
337: if (token.getType() == Token.EOF_TYPE) {
338: result = EJBQLException.unexpectedEOF(
339: getQueryInfo(), mismatched.getLine(),
340: mismatched.getColumn(), ex);
341: } else {
342: result = EJBQLException
343: .syntaxErrorAt(getQueryInfo(), mismatched
344: .getLine(), mismatched.getColumn(),
345: token.getText(), ex);
346: }
347: }
348: } else if (ex instanceof NoViableAltException) {
349: NoViableAltException noviable = (NoViableAltException) ex;
350: Token token = noviable.token;
351: if (token != null) {
352: if (token.getType() == Token.EOF_TYPE) {
353: result = EJBQLException.unexpectedEOF(
354: getQueryInfo(), noviable.getLine(),
355: noviable.getColumn(), ex);
356: } else {
357: result = EJBQLException.unexpectedToken(
358: getQueryInfo(), noviable.getLine(),
359: noviable.getColumn(), token.getText(), ex);
360: }
361: }
362: } else if (ex instanceof NoViableAltForCharException) {
363: NoViableAltForCharException noViableAlt = (NoViableAltForCharException) ex;
364: result = EJBQLException.unexpectedChar(getQueryInfo(),
365: noViableAlt.getLine(), noViableAlt.getColumn(),
366: String.valueOf((char) noViableAlt.foundChar), ex);
367: } else if (ex instanceof TokenStreamRecognitionException) {
368: result = handleANTLRException(((TokenStreamRecognitionException) ex).recog);
369: }
370:
371: if (result == null) {
372: // no special handling from aboves matches the exception if this
373: // line is reached => make it a syntax error
374: result = EJBQLException.syntaxError(getQueryInfo(), ex);
375: }
376: return result;
377: }
378:
379: /**
380: * Method called by the ANTLR generated code in case of an error.
381: */
382: public void reportError(RecognitionException ex) {
383: addError(ex);
384: }
385:
386: /**
387: * This is the parser start method. It will be implemented by the ANTLR
388: * generated subclass.
389: */
390: public abstract void document() throws RecognitionException,
391: TokenStreamException;
392:
393: /**
394: * Returns the root node after representing the parse tree for the current
395: * query string. It will be implemented by the ANTLR generated subclass.
396: */
397: public abstract Object getRootNode();
398:
399: }
|