001: package com.jofti.parser;
002:
003: import java.io.IOException;
004: import java.io.Reader;
005: import java.io.StringReader;
006: import java.util.ArrayList;
007: import java.util.HashMap;
008: import java.util.Iterator;
009: import java.util.LinkedHashMap;
010: import java.util.List;
011: import java.util.Map;
012: import java.util.Stack;
013:
014: import org.apache.commons.logging.Log;
015: import org.apache.commons.logging.LogFactory;
016:
017: import antlr.collections.AST;
018:
019: import com.jofti.api.IndexQuery;
020: import com.jofti.core.INameSpaceAware;
021: import com.jofti.core.IParsedQuery;
022: import com.jofti.core.IPredicate;
023: import com.jofti.exception.JoftiException;
024: import com.jofti.introspect.ClassIntrospector;
025: import com.jofti.parser.sql.SQLQueryParser;
026: import com.jofti.query.EJBQuery;
027: import com.jofti.query.SQLQuery;
028: import com.jofti.util.ReflectionUtil;
029:
030: public class NativeQueryParser extends AbstractParser implements
031: IQueryParser {
032:
033: private static Log log = LogFactory.getLog(ParserManager.class);
034:
035: public NativeQueryParser(final int cachedQueries,
036: ClassIntrospector introspector) {
037: super (cachedQueries, introspector);
038: }
039:
040: public NativeQueryParser(ClassIntrospector introspector) {
041: this (100, introspector);
042: }
043:
044: public IParsedQuery parseQuery(IndexQuery originalQuery)
045: throws JoftiException {
046: try {
047: return parseQuery(((SQLQuery) originalQuery).getQuery(),
048: originalQuery);
049: } catch (ClassCastException ce) {
050: throw new JoftiException(
051: "Class Query is no longer supported - use SQLQuery");
052: }
053: }
054:
055: public IParsedQuery getQuery(String name) {
056:
057: ParsedQuery query = null;
058: synchronized (lockObject) {
059: query = (ParsedQuery) compiledQueries.get(name);
060: }
061: if (query != null) {
062: SQLQueryWrapper wrapper = new SQLQueryWrapper(query);
063:
064: return wrapper;
065: }
066: return query;
067:
068: }
069:
070: public IParsedQuery parseQuery(String name, IndexQuery originalQuery)
071: throws JoftiException {
072:
073: // get text string
074: SQLQuery tempQuery = (SQLQuery) originalQuery;
075:
076: ParsedQuery parsedQuery = null;
077:
078: //see if query is already cached - this ignores parameters - and only checks the String format
079: synchronized (lockObject) {
080: parsedQuery = (ParsedQuery) compiledQueries.get(name);
081: }
082:
083: // we have a cached query here
084: if (parsedQuery == null) {
085: // we have to parse the query
086: if (log.isDebugEnabled()) {
087: log.debug("parsing new query "
088: + ((SQLQuery) originalQuery).getQuery());
089: }
090: // parse the query
091:
092: parsedQuery = parseQuery(new ParsedQuery(), tempQuery);
093:
094: //add returned query into cache
095: synchronized (lockObject) {
096: compiledQueries.put(name, parsedQuery);
097: }
098: }
099:
100: SQLQueryWrapper wrapper = new SQLQueryWrapper(parsedQuery);
101:
102: //add in the name parameters if we have any
103:
104: return wrapper;
105:
106: }
107:
108: public ParsedQuery parseQuery(ParsedQuery parsedQuery,
109: IndexQuery originalQuery) throws JoftiException {
110:
111: String query = ((SQLQuery) originalQuery).getQuery();
112: // first get an input Steam
113: Reader input = null;
114: try {
115: input = new StringReader(query);
116:
117: CommonLexer lexer = new CommonLexer(input);
118:
119: SQLQueryParser parser = new SQLQueryParser(lexer);
120: parser.statement();
121:
122: // get the AST
123: AST orderStatement = null;
124: AST ast = parser.getAST();
125: AST selectStatement = null;
126:
127: if (ast == null || ast.getFirstChild() == null) {
128:
129: throw new JoftiException(
130: "SQLQuery statement '"
131: + formatQueryForError(query)
132: + "' is not of format 'select <classname(s)> [from <namespace>] where <condition(s)>'");
133: }
134: if (log.isDebugEnabled()) {
135: log.debug(ast.toStringTree());
136: }
137: AST expression = null;
138: // first try and process order
139: if (ast.getType() == CommonLexerTokenTypes.ORDER) {
140: orderStatement = ast;
141: //mark for later
142: ast = ast.getFirstChild();
143: }
144:
145: if (ast.getFirstChild().getType() == CommonLexerTokenTypes.FROM) {
146: String nameSpace = parseNameSpace(ast.getFirstChild()
147: .getFirstChild().getNextSibling());
148:
149: if (originalQuery instanceof INameSpaceAware) {
150: ((INameSpaceAware) originalQuery)
151: .setNameSpace(nameSpace);
152: }
153: parsedQuery.setNameSpace(nameSpace);
154: selectStatement = ast.getFirstChild().getFirstChild();
155: expression = ast.getFirstChild().getNextSibling();
156: } else {
157: selectStatement = ast.getFirstChild();
158: // now get the stuff out of the where clause
159:
160: expression = selectStatement.getNextSibling();
161: }
162:
163: // get the classes from here
164: AST classes = selectStatement.getFirstChild();
165: if (classes == null) {
166: throw new JoftiException(
167: "SQLQuery must contain a single className with no alias or every className must have an alias in query:"
168: + formatQueryForError(query));
169:
170: }
171:
172: parsedQuery = parseClasses(classes, parsedQuery);
173:
174: // we have classes and aliases
175:
176: // set a number of return classes
177: if (parsedQuery.getAliasMap() != null
178: && parsedQuery.getAliasMap().size() > 0) {
179: Iterator it = parsedQuery.getAliasMap().values()
180: .iterator();
181: int size = parsedQuery.getAliasMap().size();
182: for (int i = 0; i < size; i++) {
183: Object obj = it.next();
184: parsedQuery.getFieldReturnMap().put(obj, null);
185: }
186: }
187:
188: if (log.isDebugEnabled()) {
189:
190: log.debug(expression.getText());
191: }
192:
193: Stack stack = new Stack();
194:
195: stack = parseWherePredicates(expression, stack, parsedQuery);
196:
197: if (log.isDebugEnabled()) {
198: log.debug(stack);
199: }
200:
201: // we need to reverse the stack here
202: Stack temp = new Stack();
203:
204: boolean predicateElement = false;
205:
206: while (stack.size() != 0) {
207: Object obj = stack.pop();
208: if (predicateElement) {
209: if (obj instanceof IPredicate) {
210: throw new JoftiException(
211: "SQLQuery not formatted correctly a join operator must seperate two predicates");
212: }
213: predicateElement = false;
214: } else {
215: if (obj instanceof Operator) {
216: throw new JoftiException(
217: "SQLQuery not formatted correctly a join operator must seperate two predicates");
218: }
219: predicateElement = true;
220: }
221: temp.push(obj);
222: }
223:
224: parsedQuery.setPredicates(temp);
225:
226: if (orderStatement != null) {
227: parsedQuery = parseOrder(orderStatement, parsedQuery);
228: }
229: } catch (Throwable t) {
230: if (t instanceof JoftiException) {
231: throw (JoftiException) t;
232: } else {
233: throw new JoftiException(
234: "SQLQuery statement '"
235: + formatQueryForError(query)
236: + "' is not of format 'select <classname(s)> where <condition(s)>'",
237: t);
238: }
239:
240: } finally {
241: try {
242: if (input != null) {
243: input.close();
244: }
245: } catch (IOException e) {
246: // we do not care here
247: }
248: }
249:
250: return parsedQuery;
251:
252: }
253:
254: private String parseNameSpace(AST expression) throws JoftiException {
255:
256: String nameSpace = "";
257: while (expression != null) {
258:
259: nameSpace = nameSpace + expression.getText();
260: expression = expression.getNextSibling();
261:
262: }
263: if (log.isDebugEnabled()) {
264: log.debug("name space:" + nameSpace);
265: }
266: return nameSpace;
267: }
268:
269: private ParsedQuery parseClasses(AST classes, ParsedQuery query)
270: throws JoftiException {
271: //first check if classes has any children as this tells us if as has been used
272: AST node = classes;
273: if (node.getType() != CommonLexerTokenTypes.ALIAS_IDENTIFIER) {
274: Class clazz = null;
275: try {
276: clazz = ReflectionUtil.classForName(node.getText());
277: } catch (Exception e) {
278: throw new JoftiException("Class " + node.getText()
279: + " cannot be constructed in query", e);
280: }
281: query.setClassName(clazz);
282: } else {
283: do {
284: // we have some alias
285: if (log.isDebugEnabled()) {
286: log.debug("alias found for class "
287: + node.getFirstChild().toStringTree()
288: + " of "
289: + node.getFirstChild().getNextSibling()
290: .toStringTree());
291: }
292: Class clazz = null;
293: try {
294: clazz = ReflectionUtil.classForName(node
295: .getFirstChild().getText());
296: } catch (Exception e) {
297: throw new JoftiException("Class "
298: + node.getFirstChild().getText()
299: + " cannot be parsed in query", e);
300: }
301: query.getAliasMap()
302: .put(
303: node.getFirstChild().getNextSibling()
304: .getText(), clazz);
305: node = node.getNextSibling();
306: } while (node != null);
307: }
308: return query;
309:
310: }
311:
312: private String formatQueryForError(String query) {
313: if (query.lastIndexOf(";") != query.length()) {
314: return query.substring(0, query.length() - 1);
315: } else {
316: return query;
317: }
318: }
319:
320: }
|