KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jofti > parser > EJBQueryParser


1 package com.jofti.parser;
2
3 import java.io.Reader JavaDoc;
4 import java.io.StringReader JavaDoc;
5 import java.util.Comparator JavaDoc;
6 import java.util.HashMap JavaDoc;
7 import java.util.Iterator JavaDoc;
8 import java.util.LinkedHashMap JavaDoc;
9 import java.util.List JavaDoc;
10 import java.util.Map JavaDoc;
11 import java.util.Stack JavaDoc;
12
13 import org.apache.commons.logging.Log;
14 import org.apache.commons.logging.LogFactory;
15
16 import antlr.collections.AST;
17
18 import com.jofti.api.IndexQuery;
19 import com.jofti.core.IParsedQuery;
20 import com.jofti.core.IPredicate;
21 import com.jofti.exception.JoftiException;
22 import com.jofti.introspect.ClassIntrospector;
23 import com.jofti.parser.ejb.EJB3QueryParser;
24 import com.jofti.query.EJBQuery;
25 import com.jofti.util.ArrayComparator;
26 import com.jofti.util.CompositeComparator;
27 import com.jofti.util.ReflectionComparator;
28 import com.jofti.util.ReflectionUtil;
29
30 /**
31  * This class does the work in turning a test EJBQuery into an IParsedQuery, which is the
32  * internal query representation.</p>
33  *
34  *
35  * @author xenephon
36  * @version 1.0
37  */

38 public class EJBQueryParser extends AbstractParser implements IQueryParser {
39
40     // constructs a parser with a set number of cache queries
41
public EJBQueryParser(final int cachedQueries, ClassIntrospector introspector) {
42         super(cachedQueries, introspector);
43     }
44
45     // default constructor does 100 queries
46
public EJBQueryParser( ClassIntrospector introspector) {
47         this(100, introspector);
48     }
49
50     private static Log log = LogFactory.getLog(ParserManager.class);
51
52     /* (non-Javadoc)
53      * @see com.jofti.parser.IQueryParser#parseQuery(com.jofti.api.IndexQuery)
54      */

55     public IParsedQuery parseQuery(IndexQuery originalQuery)
56             throws JoftiException {
57
58
59         return parseQuery(((EJBQuery)originalQuery).getQuery(),originalQuery);
60
61     }
62
63     public IParsedQuery getQuery(String JavaDoc name){
64
65         ParsedQuery query = null;
66         synchronized (lockObject) {
67             query = (ParsedQuery) compiledQueries.get(name);
68         }
69         if (query != null){
70             ParsedQueryWrapper wrapper = new ParsedQueryWrapper(query);
71             
72 // add in the name parameters if we have any
73
wrapper.setNamedValueMap(new HashMap JavaDoc());
74
75             return wrapper;
76         }
77         return null;
78
79     }
80     
81     public IParsedQuery parseQuery(String JavaDoc name, IndexQuery originalQuery)
82     throws JoftiException {
83
84 // get text string
85
EJBQuery tempQuery = (EJBQuery) originalQuery;
86
87
88
89 ParsedQuery parsedQuery = null;
90
91 //see if query is already cached - this ignores parameters - and only checks the String format
92
synchronized (lockObject) {
93     parsedQuery = (ParsedQuery) compiledQueries.get(name);
94 }
95
96 // we have a cached query here
97
if (parsedQuery == null) {
98     // we have to parse the query
99
if (log.isDebugEnabled()) {
100         log.debug("parsing new query " + tempQuery.getQuery());
101     }
102     // parse the query
103

104     parsedQuery = parseQuery(new ParsedQuery(), tempQuery.getQuery());
105     
106     parsedQuery.setFirstResult(tempQuery.getFirstResult());
107     
108     //add returned query into cache
109
synchronized (lockObject) {
110         compiledQueries.put(name, parsedQuery);
111     }
112 }
113
114 // we use a wrapper here as queries are actually cached for reuse
115
// and wrapper is where we set the values - otherwise we would have to
116
// produce a copy of the query
117

118 ParsedQueryWrapper wrapper = new ParsedQueryWrapper(parsedQuery);
119
120 // add in the name parameters if we have any
121
wrapper.setNamedValueMap(tempQuery.getParameterMap());
122     
123
124     wrapper.setFirstResult(tempQuery.getFirstResult());
125     wrapper.setMaxResults(tempQuery.getMaxResults());
126
127 return wrapper;
128 }
129     
130     private ParsedQuery parseQuery(ParsedQuery parsedQuery, String JavaDoc selectClause)
131             throws JoftiException {
132         Reader JavaDoc input = null;
133         try {
134             input = new StringReader JavaDoc(selectClause);
135
136             CommonLexer lexer = new CommonLexer(input);
137
138             EJB3QueryParser parser = new EJB3QueryParser(lexer);
139
140             parser.queryStatement();
141
142             AST ast = parser.getAST();
143             AST selectStatement = null;
144
145             if (ast == null || ast.getFirstChild() == null) {
146
147                 throw new JoftiException(
148                         "Query '"
149                                 + selectClause
150                                 + "' is not of format 'SELECT variable[[,] variable]* FROM Class [AS] identifier[,Class [AS] identifier] WHERE predicate [ORDER BY identifier [ASC|DESC]]*'");
151             }
152             if (log.isDebugEnabled()) {
153                 log.debug(ast.toStringTree());
154             }
155
156             parsedQuery = parseClause(ast, parsedQuery);
157
158         } catch (Exception JavaDoc e) {
159             throw new JoftiException(e);
160         } finally {
161             try {
162                 input.close();
163             } catch (Exception JavaDoc e) {
164                 log.warn("problem closing reader", e);
165             }
166         }
167         return parsedQuery;
168     }
169
170     private ParsedQuery parseClause(AST ast, ParsedQuery parsedQuery)
171             throws JoftiException {
172         AST orderNode =null;
173         do {
174             //we have no where clause
175
switch (ast.getType()) {
176             case CommonLexerTokenTypes.NAMESPACE:
177                 // we need to do from first
178
parsedQuery = parseNameSpace(ast.getFirstChild()
179                         .getNextSibling(), parsedQuery);
180
181                 // now reset the node to the where or from clause node
182
ast = ast.getFirstChild();
183                 break;
184             case CommonLexerTokenTypes.WHERE:
185                 // we need to do from first
186
parsedQuery = parseFrom(ast.getFirstChild().getFirstChild()
187                         .getNextSibling(), parsedQuery);
188
189                 // now get the stuff out of the where clause
190

191                 parsedQuery = parseWhere(ast.getFirstChild().getNextSibling(),
192                         parsedQuery);
193
194                 // now reset the node to the select node
195
ast = ast.getFirstChild().getFirstChild();
196                 break;
197             // now reset the node to the select node
198
case CommonLexerTokenTypes.FROM:
199                 parsedQuery = parseFrom(ast.getFirstChild().getNextSibling(),
200                         parsedQuery);
201
202                 // we can now parse the select part
203
ast = ast.getFirstChild();
204                 break;
205             // where is first
206

207             case CommonLexerTokenTypes.SELECT:
208                 parsedQuery = parseSelect(ast, parsedQuery);
209
210                 // we can now parse the select part
211
ast = ast.getFirstChild();
212                 break;
213             case CommonLexerTokenTypes.ORDER:
214                 orderNode =ast;
215                 //mark for later
216
// parsedQuery = parseOrder(ast, parsedQuery);
217
ast = ast.getFirstChild();
218                 break;
219             // skip through the remianing arguments in the select
220
default:
221                 ast = ast.getFirstChild();
222                 break;
223             }
224         } while (ast != null);
225         
226         if (orderNode != null){
227             parsedQuery = parseOrder(orderNode, parsedQuery);
228         }
229         return parsedQuery;
230     }
231
232     private ParsedQuery parseWhere(AST ast, ParsedQuery parsedQuery)
233             throws JoftiException {
234         Stack JavaDoc stack = new Stack JavaDoc();
235
236         stack = parseWherePredicates(ast, stack, parsedQuery);
237
238         if (log.isDebugEnabled()) {
239             log.debug(stack);
240         }
241
242         // we need to reverse the stack here
243
Stack JavaDoc temp = new Stack JavaDoc();
244
245         boolean predicateElement = false;
246
247         while (stack.size() != 0) {
248             Object JavaDoc obj = stack.pop();
249             if (predicateElement) {
250                 if (obj instanceof IPredicate) {
251                     throw new JoftiException(
252                             "Query not formatted correctly a join operator must seperate two predicates");
253                 }
254                 predicateElement = false;
255             } else {
256                 if (obj instanceof Operator) {
257                     throw new JoftiException(
258                             "Query not formatted correctly a join operator must seperate two predicates");
259                 }
260                 predicateElement = true;
261             }
262             temp.push(obj);
263         }
264
265         parsedQuery.setPredicates(temp);
266         return parsedQuery;
267     }
268
269     private ParsedQuery parseNameSpace(AST expression, ParsedQuery query)
270             throws JoftiException {
271
272         AST node = expression;
273         String JavaDoc nameSpace = "";
274         do {
275             if (log.isDebugEnabled()) {
276                 log.debug("parsing select for " + node.toStringTree());
277             }
278
279             nameSpace = nameSpace + node.getText();
280             node = node.getNextSibling();
281
282         } while (node != null);
283
284         query.setNameSpace(nameSpace);
285         return query;
286
287     }
288
289     private ParsedQuery parseSelect(AST expression, ParsedQuery query)
290             throws JoftiException {
291
292         // will be alias for field
293
AST node = expression.getFirstChild();
294
295         do {
296             // we have an as
297

298             if (log.isDebugEnabled()) {
299                 log.debug("parsing select for " + node.toStringTree());
300             }
301             // put the aliases into the alias and return map
302
addToFieldMap(query.getAliasMap(), query.getResultFieldsMap(), node
303                     .getText());
304             node = node.getNextSibling();
305
306         } while (node != null);
307
308         return query;
309
310     }
311     
312     
313
314     private ParsedQuery parseFrom(AST expression, ParsedQuery query)
315             throws JoftiException {
316
317         //first one must be an alias identifier
318
AST node = expression;
319
320         do {
321             // we have an as
322
if (node.getType() == CommonLexerTokenTypes.ALIAS_IDENTIFIER) {
323                 if (log.isDebugEnabled()) {
324                     log.debug("alias found for class "
325                             + node.getFirstChild().toStringTree()
326                             + " of "
327                             + node.getFirstChild().getNextSibling()
328                                     .toStringTree());
329                 }
330
331                 // try and construct a className from the class identifier
332
Class JavaDoc clazz = null;
333                 try {
334                     clazz = ReflectionUtil.classForName(node.getFirstChild()
335                             .getText());
336                 } catch (Exception JavaDoc e) {
337                     throw new JoftiException("Class "
338                             + node.getFirstChild().getText()
339                             + " cannot be parsed in From clause", e);
340                 }
341                 if (clazz.isPrimitive() || clazz == Boolean JavaDoc.class){
342                     clazz = introspector.boxPrimitive(clazz);
343                 }
344                 query.getAliasMap().put(
345                         node.getFirstChild().getNextSibling().getText(), clazz);
346                 node = node.getNextSibling();
347             } else {
348                 // if we want to deal with in() we should do it here
349
// even though the AST copes with it I do not think the
350
// feature lends itself to the index query
351

352                 // we have no as - we just use it as normal
353

354                 // not dealing with in stuff here
355
Class JavaDoc clazz = null;
356                 try {
357                     clazz = ReflectionUtil.classForName(node.getText());
358                 } catch (Exception JavaDoc e) {
359                     throw new JoftiException("Class " + node.getText()
360                             + " cannot be parsed in From clause", e);
361                 }
362                 if (clazz.isPrimitive() || clazz == Boolean JavaDoc.class){
363                     clazz = introspector.boxPrimitive(clazz);
364                 }
365                 query.getAliasMap().put(node.getNextSibling().getText(), clazz);
366                 //skip the sibling
367
node = node.getNextSibling().getNextSibling();
368             }
369
370         } while (node != null);
371
372         return query;
373
374     }
375
376     
377     
378     
379     
380     
381     
382     private void addToFieldMap(Map JavaDoc aliasMap, Map JavaDoc returnMap, String JavaDoc alias)
383             throws JoftiException {
384         int aliasSeperator = alias.indexOf('.');
385         
386         if (aliasSeperator > 0) {
387             Class JavaDoc clazz = getClassForAlias(aliasMap, alias);
388             ClassFieldMethods fieldSet = (ClassFieldMethods) returnMap.get(clazz);
389             if (fieldSet == null) {
390                 fieldSet = new ClassFieldMethods(clazz, null);
391                 if (fieldSet.getFieldMap() == null) {
392                     fieldSet.setFieldMap(new LinkedHashMap JavaDoc());
393                 }
394                 returnMap.put(clazz,fieldSet);
395             }
396                 
397 // we need to get the methods for the field -
398
// we do it here as it is only done once for the query
399

400                 String JavaDoc attribute = alias.substring(aliasSeperator + 1, alias.length());
401                 
402                 if (fieldSet.getFieldMap().containsKey(attribute)){
403                     throw new JoftiException("Field "+ attribute + " is already part of the return set for "+clazz);
404                 }
405                 Object JavaDoc[] methods = introspector.getMethodsForAttribute(clazz,attribute);
406                 if (methods == null || methods.length ==0){
407                     // log.warn
408
throw new JoftiException("Attribute "+ attribute + " is not valid for "+ clazz);
409                 }else{
410                     fieldSet.getFieldMap().put(attribute,methods);
411                 }
412
413         } else {
414             if (!aliasMap.containsKey(alias)) {
415                 throw new JoftiException("select for identifier " + alias
416                         + " is not mentioned in from clause");
417             } else if (returnMap.containsKey(aliasMap.get(alias))) {
418                 if (returnMap.get(aliasMap.get(alias)) == null ||
419                         ((ClassFieldMethods)returnMap.get(aliasMap.get(alias))).getFieldMap() == null ||
420                         ((ClassFieldMethods)returnMap.get(aliasMap.get(alias))).getFieldMap().size()==0 ){
421                     throw new JoftiException(""+ aliasMap.get(alias) +
422                             " under alias " + alias + " is listed more than once in select clause");
423                 }else
424                 throw new JoftiException(" "+ aliasMap.get(alias) +
425                          " as whole object under alias " + alias +" cannot be selected if fields from class are already in select clause");
426             }
427
428             returnMap.put(aliasMap.get(alias), new ClassFieldMethods(
429                     (Class JavaDoc) aliasMap.get(alias), null));
430
431         }
432     }
433 }
Popular Tags