KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > speedo > query > parser > SpeedoQLQueryFilterVisitor


1 /**
2  * Speedo: an implementation of JDO compliant personality on top of JORM generic
3  * I/O sub-system.
4  * Copyright (C) 2001-2004 France Telecom R&D
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  *
21  *
22  * Contact: speedo@objectweb.org
23  *
24  * Authors: S. Chassande-Barrioz
25  *
26  */

27
28 package org.objectweb.speedo.query.parser;
29
30 import org.objectweb.jorm.naming.api.PName;
31 import org.objectweb.jorm.type.api.PType;
32 import org.objectweb.medor.api.Field;
33 import org.objectweb.medor.expression.api.Expression;
34 import org.objectweb.medor.expression.api.Operator;
35 import org.objectweb.medor.expression.api.ParameterOperand;
36 import org.objectweb.medor.expression.api.TypingException;
37 import org.objectweb.medor.expression.lib.And;
38 import org.objectweb.medor.expression.lib.BasicOperand;
39 import org.objectweb.medor.expression.lib.BasicParameterOperand;
40 import org.objectweb.medor.expression.lib.Bitwize;
41 import org.objectweb.medor.expression.lib.ConditionalAnd;
42 import org.objectweb.medor.expression.lib.ConditionalOr;
43 import org.objectweb.medor.expression.lib.DivideBy;
44 import org.objectweb.medor.expression.lib.Equal;
45 import org.objectweb.medor.expression.lib.Greater;
46 import org.objectweb.medor.expression.lib.GreaterEqual;
47 import org.objectweb.medor.expression.lib.Length;
48 import org.objectweb.medor.expression.lib.Like;
49 import org.objectweb.medor.expression.lib.Lower;
50 import org.objectweb.medor.expression.lib.LowerEqual;
51 import org.objectweb.medor.expression.lib.Minus;
52 import org.objectweb.medor.expression.lib.Mult;
53 import org.objectweb.medor.expression.lib.Not;
54 import org.objectweb.medor.expression.lib.NotEqual;
55 import org.objectweb.medor.expression.lib.Or;
56 import org.objectweb.medor.expression.lib.Plus;
57 import org.objectweb.medor.expression.lib.StringComparatorParameterOperand;
58 import org.objectweb.medor.expression.lib.StringLower;
59 import org.objectweb.medor.expression.lib.StringUpper;
60 import org.objectweb.medor.expression.lib.UMinus;
61 import org.objectweb.medor.expression.lib.Substring;
62 import org.objectweb.medor.filter.lib.BasicFieldOperand;
63 import org.objectweb.medor.filter.lib.ExpressionPrinter;
64 import org.objectweb.medor.filter.lib.InCollection;
65 import org.objectweb.medor.filter.lib.IsEmpty;
66 import org.objectweb.medor.filter.lib.IsNull;
67 import org.objectweb.medor.filter.lib.MemberOf;
68 import org.objectweb.medor.query.api.QueryTree;
69 import org.objectweb.medor.query.api.QueryTreeField;
70 import org.objectweb.medor.query.jorm.lib.QueryBuilder;
71 import org.objectweb.medor.query.lib.SelectProject;
72 import org.objectweb.speedo.api.SpeedoException;
73 import org.objectweb.speedo.mapper.api.JormFactory;
74 import org.objectweb.util.monolog.api.BasicLevel;
75 import org.objectweb.util.monolog.api.Logger;
76
77 import javax.jdo.JDOException;
78 import java.util.Collections JavaDoc;
79 import java.util.Map JavaDoc;
80 import java.util.Stack JavaDoc;
81
82 /**
83  * Implementation of a visitor that creates the filter
84  */

85 public class SpeedoQLQueryFilterVisitor extends SpeedoQLAbstractVisitor {
86
87     private final static String JavaDoc CONTAINS_PATH_SET = "Next element is the contain path set";
88     private final static String JavaDoc REMOVER = "Remover";
89
90
91     /**
92      * qf is the transformation of the filter into a MEDOR expression
93      */

94     private Expression qf = null;
95
96     /**
97      * qt is built by the SpeedoQLVariableVisitor and is just used here
98      */

99     private QueryTree qt = null;
100     
101     private SelectProject root;
102
103     /**
104      * speedoql is the result of the filter parsing
105      */

106     private ASTSpeedoQL speedoql = null;
107     
108     Map JavaDoc fields;
109     
110     QueryBuilder qb;
111
112     private int nbNot = 0;
113
114     private String JavaDoc tab ="";
115     
116     private JormFactory jf = null;
117     
118     private Class JavaDoc clazz = null;
119
120     public SpeedoQLQueryFilterVisitor(Map JavaDoc _fields,
121             SelectProject _root,
122             ASTSpeedoQL speedoql,
123             Logger logger,
124             Map JavaDoc hparam,
125             Map JavaDoc vparam,
126             Class JavaDoc clazz,
127             QueryBuilder _qb,
128             JormFactory jf) throws SpeedoException {
129         //qt = qtree;
130
this.fields = _fields;
131         this.speedoql = speedoql;
132         this.qt = _root;
133         this.root = _root;
134         this.qb = _qb;
135         this.jf = jf;
136         setLogger(logger);
137         setParams(hparam);
138         setVars(vparam);
139         setCurrentClass(clazz.getName());
140         this.clazz = clazz;
141         startVisiting();
142     }
143
144     /**
145      * the visit starts here, please before sets a list of variables.
146      * @throws java.lang.Exception
147      */

148     public void startVisiting() throws SpeedoException {
149         debug = logger.isLoggable(BasicLevel.DEBUG);
150         nbNot = 0;
151         try {
152             visit(speedoql);
153         } catch (Exception JavaDoc e) {
154             if (e instanceof SpeedoQLAbstractVisitor.VisitorException) {
155                 e = ((SpeedoQLAbstractVisitor.VisitorException) e).getNestedException();
156             }
157             throw new SpeedoException(
158                     "Impossible to built the queryTree during the filter visit", e);
159         }
160         // this expression is the filter of the SelectProject
161
Expression exprFilter = getQueryFilter();
162         if (exprFilter != null) {
163             ME me = replaceNullNode(exprFilter);
164             if (me != null && me.modified) {
165                 qf = me.e;
166                 exprFilter = getQueryFilter();
167             }
168             if (debug) {
169                 logger.log(BasicLevel.DEBUG, "filter = " + exprFilter.getClass().getName());
170             }
171             root.setQueryFilter(exprFilter);
172         }
173         
174     }
175
176     private static class ME {
177         public Expression e;
178         public boolean modified;
179         public ME(Expression _e) {
180             this.e = _e;
181             this.modified = true;
182         }
183     }
184     
185     /**
186      * Replace null node.
187      * @param exprFilter
188      */

189     private ME replaceNullNode(Expression exprFilter) throws SpeedoException {
190         ME me = null;
191         if ((exprFilter instanceof Equal) || (exprFilter instanceof NotEqual)){
192             Operator op = (Operator) exprFilter;
193             for (int i = 0; i < op.getOperandNumber(); i++) {
194                 if (op.getExpression(i) instanceof DummyOperand) {
195                     //get the other child operand
196
int j = (i+1) % 2;
197                     Expression other = op.getExpression(j);
198                     PType ptype = other.getType();
199                     if (ptype.getTypeCode() == PType.TYPECODE_REFERENCE) {
200                         PName val = null;
201                         try {
202                             val = jf.getPClassMapping(ptype.getJormName(), clazz.getClassLoader()).getPBinder().getNull();
203                         } catch (Exception JavaDoc e) {
204                             throw new SpeedoException(
205                                     "Try to replace null node. Impossible to find the null PName for the type " + ptype.getJormName(), e);
206                         }
207                         //replace the null node with null PName
208
op.setExpression(i, new BasicOperand(val, ptype));
209                     } else {
210                         //Transform the null [not]equality into a IsNull operator
211
me = new ME(new IsNull(other, exprFilter instanceof NotEqual));
212                         //recursion is done on the other operand
213
exprFilter = me.e;
214                     }
215                 }
216             }
217         }
218         if (exprFilter instanceof Operator) {
219             Operator op = (Operator) exprFilter;
220             for (int i = 0; i < op.getOperandNumber(); i++) {
221                 //recursivity
222
ME ime = replaceNullNode(op.getExpression(i));
223                 if (ime != null && ime.modified) {
224                     //replace the child
225
op.setExpression(i, ime.e);
226                     //mark the result as modified
227
if (me == null) {//create if not already done
228
me = new ME(exprFilter);
229                     }
230                     me.modified = true;
231                 }
232             }
233         }
234         return me; //can be null
235
}
236     
237     /**
238      * get the query filter that was built from visiting the syntaxic tree
239      */

240     public Expression getQueryFilter() {
241         return qf;
242     }
243
244     public Object JavaDoc visit(ASTPrimary node, Object JavaDoc data) {
245         Stack JavaDoc stack = (Stack JavaDoc) data;
246         boolean hasMethod = node.value != null;
247         if (hasMethod) {
248             stack.push(node.value);
249         }
250         visit((SimpleNode) node, data);
251         if (hasMethod) {
252             Expression e2 = (Expression) stack.pop();
253             Expression e1 = (Expression) stack.pop();
254             String JavaDoc methodName = (String JavaDoc) stack.pop();
255             if (methodName.startsWith("null.")) {
256                 methodName = methodName.substring("null.".length());
257             }
258             stack.push(e1);
259             stack.push(methodName);
260             e1 = treatMethodOperator(methodName, stack, e2);
261             if (e1 != null) {
262                 stack.push(e1);
263             }
264         }
265         return null;
266     }
267
268     public Object JavaDoc visit(ASTSpeedoPrimary node, Object JavaDoc data) {
269         visit((SimpleNode) node, data);
270         Stack JavaDoc stack = (Stack JavaDoc) data;
271         while(stack.size() >0 && REMOVER.equals(stack.peek())) {
272             stack.pop();
273         }
274         if (stack.size() >0) {
275             qf = (Expression) stack.pop();
276         } // else there is no filter
277
return null;
278     }
279
280     /**
281      *
282      */

283     public Object JavaDoc visit(ASTRelationalExpression node, Object JavaDoc data) {
284         if (debug) {
285             logger.log(BasicLevel.DEBUG, tab + "Visit RelationalExpression: " + node);
286         }
287         tab += '\t';
288         visit((SimpleNode) node, data);
289         tab = tab.substring(1);
290         Stack JavaDoc stack = (Stack JavaDoc) data;
291         if (stack.size() > 0) {
292             if (CONTAINS_PATH_SET.equals(stack.peek())) {
293                 if (debug) {
294                     logger.log(BasicLevel.ERROR, "remove the element of the stack");
295                 }
296             } else {
297                 if (debug) {
298                     logger.log(BasicLevel.DEBUG, "manage relational expression: "
299                         + "(children: " + node.jjtGetNumChildren()
300                         + ", stack: " + stack.size()
301                         + ", peek:" + stack.peek() + ")");
302                 }
303                 for (int i = 0; (i < (node.jjtGetNumChildren() - 1) && stack.size() > 0); i++) {
304                     int op = ((Integer JavaDoc) node.ops.get(node.jjtGetNumChildren() - 2 - i)).intValue();
305                     if (usedInRelationalExpresssion(op)) {
306                         Object JavaDoc child2 = stack.pop();
307                         Object JavaDoc child1 = stack.pop();
308
309                         if (debug) {
310                             logger.log(BasicLevel.DEBUG, "pop child1: " + child1);
311                             logger.log(BasicLevel.DEBUG, "pop child2: " + child2);
312                         }
313                         if (REMOVER.equals(child1)) {
314                             if (REMOVER.equals(child2)) {
315                                 //nothin
316
} else {
317                                 stack.push(child2);
318                                 if (debug) {
319                                     logger.log(BasicLevel.DEBUG, "push(" + child2 + ")");
320                                 }
321                             }
322                         } else {
323                             if (REMOVER.equals(child2)) {
324                                 stack.push(child1);
325                                 if (debug) {
326                                     logger.log(BasicLevel.DEBUG, "push(" + child1 + ")");
327                                 }
328                             } else {
329                                 Expression ret = null;
330                                 switch (op) {
331                                 case SpeedoQLConstants.OR:
332                                     ret = new ConditionalOr((Expression) child1, (Expression) child2);
333                                     break;
334                                 case SpeedoQLConstants.AND:
335                                     ret = new ConditionalAnd((Expression) child1, (Expression) child2);
336                                     break;
337                                 case SpeedoQLConstants.BITWISEOR:
338                                     ret = new Or((Expression) child1, (Expression) child2);
339                                     break;
340                                 case SpeedoQLConstants.BITWISEXOR:
341                                     ret = null;
342                                     break;
343                                 case SpeedoQLConstants.BITWISEAND:
344                                     ret = new And((Expression) child1, (Expression) child2);
345                                     break;
346                                 case SpeedoQLConstants.EQ:
347                                     ret = new Equal((Expression) child1, (Expression) child2);
348                                     break;
349                                 case SpeedoQLConstants.NEQ:
350                                     ret = new NotEqual((Expression) child1, (Expression) child2);
351                                     break;
352                                 case SpeedoQLConstants.LT:
353                                     ret = new Lower((Expression) child1, (Expression) child2);
354                                     break;
355                                 case SpeedoQLConstants.GT:
356                                     ret = new Greater((Expression) child1, (Expression) child2);
357                                     break;
358                                 case SpeedoQLConstants.GTE:
359                                     ret = new GreaterEqual((Expression) child1, (Expression) child2);
360                                     break;
361                                 case SpeedoQLConstants.LTE:
362                                     ret = new LowerEqual((Expression) child1, (Expression) child2);
363                                     break;
364                                 }
365                                 if (debug) {
366                                     logger.log(BasicLevel.DEBUG, "push(" + ret + ")");
367                                 }
368                                 stack.push(ret);
369                             }
370                         }
371                     }
372                 }
373             }
374         }
375         if (debug) {
376             logger.log(BasicLevel.DEBUG, "children:" + node.jjtGetNumChildren()
377                 + " / stack: " + stack.size());
378             logger.log(BasicLevel.DEBUG, tab + "End of Visit RelationalExpression: " + node);
379         }
380         return null;
381     }
382
383     private boolean usedInRelationalExpresssion(int op) {
384         switch (op) {
385         case SpeedoQLConstants.OR:
386         case SpeedoQLConstants.AND:
387         case SpeedoQLConstants.BITWISEOR:
388         case SpeedoQLConstants.BITWISEXOR:
389         case SpeedoQLConstants.BITWISEAND:
390         case SpeedoQLConstants.EQ:
391         case SpeedoQLConstants.NEQ:
392         case SpeedoQLConstants.LT:
393         case SpeedoQLConstants.GT:
394         case SpeedoQLConstants.GTE:
395         case SpeedoQLConstants.LTE:
396             if (debug) {
397                 logger.log(BasicLevel.DEBUG, "node useful");
398             }
399             return true;
400         default:
401             if (debug) {
402                 logger.log(BasicLevel.DEBUG, "node useless");
403             }
404             return false;
405         }
406     }
407
408     public Object JavaDoc visit(ASTAdditiveExpression node, Object JavaDoc data) {
409         if (debug) {
410             logger.log(BasicLevel.DEBUG, tab + "Visit AdditiveExpression: " + node);
411         }
412         tab += '\t';
413         visit((SimpleNode) node, data);
414         tab = tab.substring(1);
415         Stack JavaDoc stack = (Stack JavaDoc) data;
416         if (stack.size() > 0
417                 && !CONTAINS_PATH_SET.equals(stack.peek())
418                 && !REMOVER.equals(stack.peek())) {
419             Expression ret = (Expression) stack.pop();
420             for (int i = 0; i < node.jjtGetNumChildren() - 1; i++) {
421                 if (debug) {
422                     logger.log(BasicLevel.DEBUG, "Visit ConditionalExpression... children...[" + i + "]");
423                 }
424
425                 switch (((Integer JavaDoc) node.ops.get(node.jjtGetNumChildren() - 2 - i)).intValue()) {
426
427                 case SpeedoQLConstants.PLUS:
428                     ret = new Plus((Expression) stack.pop(), ret);
429                     break;
430                 case SpeedoQLConstants.MINUS:
431                     ret = new Minus((Expression) stack.pop(), ret);
432                     break;
433                 case SpeedoQLConstants.MULT:
434                     ret = new Mult((Expression) stack.pop(), ret);
435                     break;
436                 case SpeedoQLConstants.DIV:
437                     ret = new DivideBy((Expression) stack.pop(), ret);
438                     break;
439                 default:
440
441                 }
442             }
443             ((Stack JavaDoc) data).push(ret);
444         }
445         if (debug) {
446             logger.log(BasicLevel.DEBUG, tab + "End of Visit AdditiveExpression: " + node);
447         }
448         return null;
449     }
450
451     public Object JavaDoc visit(ASTUnaryExpression node, Object JavaDoc data) {
452         if (debug) {
453             logger.log(BasicLevel.DEBUG, tab + "Visit UnaryExpression" + node);
454         }
455         tab += '\t';
456         boolean hasNot = node.ops.size() > 0
457             && ((Integer JavaDoc) node.ops.get(0)).intValue() == SpeedoQLConstants.NOT;
458         if (hasNot) {
459             nbNot ++;
460             if (debug) {
461                 logger.log(BasicLevel.DEBUG, "remember a Not: "+ nbNot);
462             }
463         }
464         visit((SimpleNode) node, data);
465         if (hasNot && nbNot> 0) {
466             nbNot--;
467             if (debug) {
468                 logger.log(BasicLevel.DEBUG, "forget a Not: "+ nbNot);
469             }
470         }
471         tab = tab.substring(1);
472         Stack JavaDoc stack = (Stack JavaDoc) data;
473         if (debug && stack.size() > 0) {
474             logger.log(BasicLevel.DEBUG, tab + "stack.peek:" + stack.peek());
475         }
476         if (stack.size() > 0
477                 && !CONTAINS_PATH_SET.equals(stack.peek())
478                 && !REMOVER.equals(stack.peek())) {
479             Object JavaDoc o = stack.pop();
480             if (node.ops.size() > 0) {
481                 switch (((Integer JavaDoc) node.ops.get(0)).intValue()) {
482                 case SpeedoQLConstants.MINUS:
483                     o = new UMinus((Expression) o);
484                     break;
485                 case SpeedoQLConstants.BITWISECOMPL:
486                     o = new Bitwize((Expression) o);
487                     break;
488                 case SpeedoQLConstants.NOT:
489                     logger.log(BasicLevel.DEBUG, "NOT(" + o + "): " + node);
490                     o = new Not((Expression) o);
491                     break;
492                 }
493             }
494             ((Stack JavaDoc) data).push(o);
495         }
496         if (debug) {
497             logger.log(BasicLevel.DEBUG, tab + "End of Visit UnaryExpression: " + node);
498         }
499         return null;
500     }
501
502     // to be done
503
public Object JavaDoc visit(ASTCastExpression node, Object JavaDoc data) {
504         ((Stack JavaDoc) data).push(node);
505         return null;
506     }
507
508     /**
509      * 4 cases to manage: (en cours par equipe MEDOR)
510      * - Collection.contains(Object o)
511      * - Collection.isEmpty()
512      * - String.startsWith(String s)
513      * - String.endsWith(String s)
514      */

515     public Object JavaDoc visit(ASTArgumentList node, Object JavaDoc data) {
516         visit((SimpleNode) node, data);
517         return null;
518     }
519
520     public Object JavaDoc visit(ASTLiteral node, Object JavaDoc data) {
521         Stack JavaDoc stack = (Stack JavaDoc) data;
522         Expression e = null;
523         if (node.value == null) {
524             //special case for null: will be replaced later
525
e = new DummyOperand();
526         } else if (node.value instanceof Integer JavaDoc) {
527             e = new BasicOperand(((Integer JavaDoc) node.value).intValue());
528         } else if (node.value instanceof Float JavaDoc) {
529             e = new BasicOperand(((Float JavaDoc) node.value).floatValue());
530         } else if (node.value instanceof Character JavaDoc) {
531             e = new BasicOperand(((Character JavaDoc) node.value).charValue());
532         } else if (node.value instanceof String JavaDoc) {
533             String JavaDoc s = (String JavaDoc) (node.value);
534             s = s.substring(1, s.length()-1);
535             e = new BasicOperand(s);
536         } else if (node.value instanceof Boolean JavaDoc) {
537             e = new BasicOperand(((Boolean JavaDoc) node.value).booleanValue());
538         }
539         if (stack.size() > 0) {
540             Object JavaDoc top = stack.peek();
541             if (CONTAINS_PATH_SET.equals(top)) {
542 //TODO: Support the path.contains(path) operator
543
logger.log(BasicLevel.ERROR, "The path.contains(path) operator is not yet implemented");
544                 return null;
545             } else if (top instanceof String JavaDoc) {
546                 e = treatMethodOperator((String JavaDoc) top, stack, e);
547             }
548         }
549         if (e != null) {
550             if (debug) {
551                 logger.log(BasicLevel.DEBUG, "push(" + ExpressionPrinter.e2str(e) + ")");
552             }
553             stack.push(e);
554         }
555         return null;
556     }
557
558     private Expression treatMethodOperator(String JavaDoc opName,
559                                      Stack JavaDoc stack,
560                                      Expression e) {
561         int op = isMethodOperator(opName);
562         if (op == -1) {
563             return e;
564         }
565         switch (op) {
566         case STARTS_WITH_OPERATOR:
567             {
568                 stack.pop();
569                 Object JavaDoc oe = stack.pop();
570                 if (debug) {
571                     logger.log(BasicLevel.DEBUG, "pop expression: " + oe);
572                 }
573                 Expression str = (Expression) oe;
574                 if (e instanceof BasicParameterOperand) {
575                     e = new StringComparatorParameterOperand((BasicParameterOperand) e, null, "%");
576                 } else if (e instanceof BasicOperand) {
577                     try {
578                         e = new BasicOperand(((BasicOperand) e).getString() + "%");
579                     } catch (TypingException e1) {
580                         throw new JDOException(
581                             "Bad parameter type for the 'startsWith' method", e1);
582                     }
583                 }
584                 e = new Like(str, e);
585                 break;
586             }
587         case ENDS_WITH_OPERATOR:
588             {
589                 stack.pop();
590                 Object JavaDoc oe = stack.pop();
591                 if (debug) {
592                     logger.log(BasicLevel.DEBUG, "pop expression: " + oe);
593                 }
594                 Expression str = (Expression) oe;
595                 if (e instanceof BasicParameterOperand) {
596                     e = new StringComparatorParameterOperand((BasicParameterOperand) e, "%", null);
597                 } else if (e instanceof BasicOperand) {
598                     try {
599                         e = new BasicOperand("%" + ((BasicOperand) e).getString());
600                     } catch (TypingException e1) {
601                         throw new JDOException(
602                             "Bad parameter type for the 'startsWith' method", e1);
603                     }
604                 }
605                 e = new Like(str, e);
606                 break;
607             }
608         case EQUALS_OPERATOR:
609 //TODO: Support the equals operator
610
break;
611         case MATCHES_OPERATOR:
612             {
613                 stack.pop();
614                 Object JavaDoc oe = stack.pop();
615                 if (debug) {
616                     logger.log(BasicLevel.DEBUG, "pop expression: " + oe);
617                 }
618                 Expression str = (