001: /**
002: * Speedo: an implementation of JDO compliant personality on top of JORM generic
003: * I/O sub-system.
004: * Copyright (C) 2001-2004 France Telecom R&D
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: *
021: *
022: * Contact: speedo@objectweb.org
023: *
024: * Authors: S. Chassande-Barrioz
025: *
026: */package org.objectweb.speedo.query.jdo.parser;
027:
028: import org.objectweb.jorm.naming.api.PName;
029: import org.objectweb.jorm.type.api.PType;
030: import org.objectweb.medor.api.Field;
031: import org.objectweb.medor.expression.api.Expression;
032: import org.objectweb.medor.expression.api.Operator;
033: import org.objectweb.medor.expression.api.ParameterOperand;
034: import org.objectweb.medor.expression.api.TypingException;
035: import org.objectweb.medor.expression.lib.And;
036: import org.objectweb.medor.expression.lib.BasicOperand;
037: import org.objectweb.medor.expression.lib.BasicParameterOperand;
038: import org.objectweb.medor.expression.lib.Bitwize;
039: import org.objectweb.medor.expression.lib.ConditionalAnd;
040: import org.objectweb.medor.expression.lib.ConditionalOr;
041: import org.objectweb.medor.expression.lib.DivideBy;
042: import org.objectweb.medor.expression.lib.Equal;
043: import org.objectweb.medor.expression.lib.Greater;
044: import org.objectweb.medor.expression.lib.GreaterEqual;
045: import org.objectweb.medor.expression.lib.Length;
046: import org.objectweb.medor.expression.lib.Like;
047: import org.objectweb.medor.expression.lib.Lower;
048: import org.objectweb.medor.expression.lib.LowerEqual;
049: import org.objectweb.medor.expression.lib.Minus;
050: import org.objectweb.medor.expression.lib.Mult;
051: import org.objectweb.medor.expression.lib.Not;
052: import org.objectweb.medor.expression.lib.NotEqual;
053: import org.objectweb.medor.expression.lib.Or;
054: import org.objectweb.medor.expression.lib.Plus;
055: import org.objectweb.medor.expression.lib.StringComparatorParameterOperand;
056: import org.objectweb.medor.expression.lib.StringLower;
057: import org.objectweb.medor.expression.lib.StringUpper;
058: import org.objectweb.medor.expression.lib.UMinus;
059: import org.objectweb.medor.expression.lib.Substring;
060: import org.objectweb.medor.filter.lib.BasicFieldOperand;
061: import org.objectweb.medor.filter.lib.ExpressionPrinter;
062: import org.objectweb.medor.filter.lib.InCollection;
063: import org.objectweb.medor.filter.lib.IsEmpty;
064: import org.objectweb.medor.filter.lib.IsNull;
065: import org.objectweb.medor.filter.lib.MemberOf;
066: import org.objectweb.medor.query.api.QueryTree;
067: import org.objectweb.medor.query.api.QueryTreeField;
068: import org.objectweb.medor.query.jorm.lib.QueryBuilder;
069: import org.objectweb.medor.query.lib.SelectProject;
070: import org.objectweb.speedo.api.SpeedoException;
071: import org.objectweb.speedo.mapper.api.JormFactory;
072: import org.objectweb.util.monolog.api.BasicLevel;
073: import org.objectweb.util.monolog.api.Logger;
074:
075: import javax.jdo.JDOException;
076: import java.util.Collections;
077: import java.util.Map;
078: import java.util.Stack;
079:
080: /**
081: * Implementation of a visitor that creates the filter
082: */
083: public class SpeedoQLQueryFilterVisitor extends SpeedoQLAbstractVisitor {
084:
085: private final static String CONTAINS_PATH_SET = "Next element is the contain path set";
086: private final static String REMOVER = "Remover";
087:
088: /**
089: * qf is the transformation of the filter into a MEDOR expression
090: */
091: private Expression qf = null;
092:
093: /**
094: * qt is built by the SpeedoQLVariableVisitor and is just used here
095: */
096: private QueryTree qt = null;
097:
098: private SelectProject root;
099:
100: /**
101: * speedoql is the result of the filter parsing
102: */
103: private ASTSpeedoQL speedoql = null;
104:
105: Map fields;
106:
107: QueryBuilder qb;
108:
109: private int nbNot = 0;
110:
111: private String tab = "";
112:
113: private JormFactory jf = null;
114:
115: private Class clazz = null;
116:
117: public SpeedoQLQueryFilterVisitor(Map _fields, SelectProject _root,
118: ASTSpeedoQL speedoql, Logger logger, Map hparam,
119: Map vparam, Class clazz, QueryBuilder _qb, JormFactory jf)
120: throws SpeedoException {
121: //qt = qtree;
122: this .fields = _fields;
123: this .speedoql = speedoql;
124: this .qt = _root;
125: this .root = _root;
126: this .qb = _qb;
127: this .jf = jf;
128: setLogger(logger);
129: setParams(hparam);
130: setVars(vparam);
131: setCurrentClass(clazz.getName());
132: this .clazz = clazz;
133: startVisiting();
134: }
135:
136: /**
137: * the visit starts here, please before sets a list of variables.
138: * @throws java.lang.Exception
139: */
140: public void startVisiting() throws SpeedoException {
141: debug = logger.isLoggable(BasicLevel.DEBUG);
142: nbNot = 0;
143: try {
144: visit(speedoql);
145: } catch (Exception e) {
146: if (e instanceof SpeedoQLAbstractVisitor.VisitorException) {
147: e = ((SpeedoQLAbstractVisitor.VisitorException) e)
148: .getNestedException();
149: }
150: throw new SpeedoException(
151: "Impossible to built the queryTree during the filter visit",
152: e);
153: }
154: // this expression is the filter of the SelectProject
155: Expression exprFilter = getQueryFilter();
156: if (exprFilter != null) {
157: ME me = replaceNullNode(exprFilter);
158: if (me != null && me.modified) {
159: qf = me.e;
160: exprFilter = getQueryFilter();
161: }
162: if (debug) {
163: logger.log(BasicLevel.DEBUG, "filter = "
164: + exprFilter.getClass().getName());
165: }
166: root.setQueryFilter(exprFilter);
167: }
168:
169: }
170:
171: private static class ME {
172: public Expression e;
173: public boolean modified;
174:
175: public ME(Expression _e) {
176: this .e = _e;
177: this .modified = true;
178: }
179: }
180:
181: /**
182: * Replace null node.
183: * @param exprFilter
184: */
185: private ME replaceNullNode(Expression exprFilter)
186: throws SpeedoException {
187: ME me = null;
188: if ((exprFilter instanceof Equal)
189: || (exprFilter instanceof NotEqual)) {
190: Operator op = (Operator) exprFilter;
191: for (int i = 0; i < op.getOperandNumber(); i++) {
192: if (op.getExpression(i) instanceof DummyOperand) {
193: //get the other child operand
194: int j = (i + 1) % 2;
195: Expression other = op.getExpression(j);
196: PType ptype = other.getType();
197: if (ptype.getTypeCode() == PType.TYPECODE_REFERENCE) {
198: PName val = null;
199: try {
200: val = jf.getPClassMapping(
201: ptype.getJormName(),
202: clazz.getClassLoader())
203: .getPBinder().getNull();
204: } catch (Exception e) {
205: throw new SpeedoException(
206: "Try to replace null node. Impossible to find the null PName for the type "
207: + ptype.getJormName(), e);
208: }
209: //replace the null node with null PName
210: op.setExpression(i,
211: new BasicOperand(val, ptype));
212: } else {
213: //Transform the null [not]equality into a IsNull operator
214: me = new ME(new IsNull(other,
215: exprFilter instanceof NotEqual));
216: //recursion is done on the other operand
217: exprFilter = me.e;
218: }
219: }
220: }
221: }
222: if (exprFilter instanceof Operator) {
223: Operator op = (Operator) exprFilter;
224: for (int i = 0; i < op.getOperandNumber(); i++) {
225: //recursivity
226: ME ime = replaceNullNode(op.getExpression(i));
227: if (ime != null && ime.modified) {
228: //replace the child
229: op.setExpression(i, ime.e);
230: //mark the result as modified
231: if (me == null) {//create if not already done
232: me = new ME(exprFilter);
233: }
234: me.modified = true;
235: }
236: }
237: }
238: return me; //can be null
239: }
240:
241: /**
242: * get the query filter that was built from visiting the syntaxic tree
243: */
244: public Expression getQueryFilter() {
245: return qf;
246: }
247:
248: public Object visit(ASTPrimary node, Object data) {
249: Stack stack = (Stack) data;
250: boolean hasMethod = node.value != null;
251: if (hasMethod) {
252: stack.push(node.value);
253: }
254: visit((SimpleNode) node, data);
255: if (hasMethod) {
256: Expression e2 = (Expression) stack.pop();
257: Expression e1 = (Expression) stack.pop();
258: String methodName = (String) stack.pop();
259: if (methodName.startsWith("null.")) {
260: methodName = methodName.substring("null.".length());
261: }
262: stack.push(e1);
263: stack.push(methodName);
264: e1 = treatMethodOperator(methodName, stack, e2);
265: if (e1 != null) {
266: stack.push(e1);
267: }
268: }
269: return null;
270: }
271:
272: public Object visit(ASTSpeedoPrimary node, Object data) {
273: visit((SimpleNode) node, data);
274: Stack stack = (Stack) data;
275: while (stack.size() > 0 && REMOVER.equals(stack.peek())) {
276: stack.pop();
277: }
278: if (stack.size() > 0) {
279: qf = (Expression) stack.pop();
280: } // else there is no filter
281: return null;
282: }
283:
284: /**
285: *
286: */
287: public Object visit(ASTRelationalExpression node, Object data) {
288: if (debug) {
289: logger.log(BasicLevel.DEBUG, tab
290: + "Visit RelationalExpression: " + node);
291: }
292: tab += '\t';
293: visit((SimpleNode) node, data);
294: tab = tab.substring(1);
295: Stack stack = (Stack) data;
296: if (stack.size() > 0) {
297: if (CONTAINS_PATH_SET.equals(stack.peek())) {
298: if (debug) {
299: logger.log(BasicLevel.ERROR,
300: "remove the element of the stack");
301: }
302: } else {
303: if (debug) {
304: logger.log(BasicLevel.DEBUG,
305: "manage relational expression: "
306: + "(children: "
307: + node.jjtGetNumChildren()
308: + ", stack: " + stack.size()
309: + ", peek:" + stack.peek() + ")");
310: }
311: for (int i = 0; (i < (node.jjtGetNumChildren() - 1) && stack
312: .size() > 0); i++) {
313: int op = ((Integer) node.ops.get(node
314: .jjtGetNumChildren()
315: - 2 - i)).intValue();
316: if (usedInRelationalExpresssion(op)) {
317: Object child2 = stack.pop();
318: Object child1 = stack.pop();
319:
320: if (debug) {
321: logger.log(BasicLevel.DEBUG, "pop child1: "
322: + child1);
323: logger.log(BasicLevel.DEBUG, "pop child2: "
324: + child2);
325: }
326: if (REMOVER.equals(child1)) {
327: if (REMOVER.equals(child2)) {
328: //nothin
329: } else {
330: stack.push(child2);
331: if (debug) {
332: logger.log(BasicLevel.DEBUG,
333: "push(" + child2 + ")");
334: }
335: }
336: } else {
337: if (REMOVER.equals(child2)) {
338: stack.push(child1);
339: if (debug) {
340: logger.log(BasicLevel.DEBUG,
341: "push(" + child1 + ")");
342: }
343: } else {
344: Expression ret = null;
345: switch (op) {
346: case SpeedoQLConstants.OR:
347: ret = new ConditionalOr(
348: (Expression) child1,
349: (Expression) child2);
350: break;
351: case SpeedoQLConstants.AND:
352: ret = new ConditionalAnd(
353: (Expression) child1,
354: (Expression) child2);
355: break;
356: case SpeedoQLConstants.BITWISEOR:
357: ret = new Or((Expression) child1,
358: (Expression) child2);
359: break;
360: case SpeedoQLConstants.BITWISEXOR:
361: ret = null;
362: break;
363: case SpeedoQLConstants.BITWISEAND:
364: ret = new And((Expression) child1,
365: (Expression) child2);
366: break;
367: case SpeedoQLConstants.EQ:
368: ret = new Equal(
369: (Expression) child1,
370: (Expression) child2);
371: break;
372: case SpeedoQLConstants.NEQ:
373: ret = new NotEqual(
374: (Expression) child1,
375: (Expression) child2);
376: break;
377: case SpeedoQLConstants.LT:
378: ret = new Lower(
379: (Expression) child1,
380: (Expression) child2);
381: break;
382: case SpeedoQLConstants.GT:
383: ret = new Greater(
384: (Expression) child1,
385: (Expression) child2);
386: break;
387: case SpeedoQLConstants.GTE:
388: ret = new GreaterEqual(
389: (Expression) child1,
390: (Expression) child2);
391: break;
392: case SpeedoQLConstants.LTE:
393: ret = new LowerEqual(
394: (Expression) child1,
395: (Expression) child2);
396: break;
397: }
398: if (debug) {
399: logger.log(BasicLevel.DEBUG,
400: "push(" + ret + ")");
401: }
402: stack.push(ret);
403: }
404: }
405: }
406: }
407: }
408: }
409: if (debug) {
410: logger.log(BasicLevel.DEBUG, "children:"
411: + node.jjtGetNumChildren() + " / stack: "
412: + stack.size());
413: logger.log(BasicLevel.DEBUG, tab
414: + "End of Visit RelationalExpression: " + node);
415: }
416: return null;
417: }
418:
419: private boolean usedInRelationalExpresssion(int op) {
420: switch (op) {
421: case SpeedoQLConstants.OR:
422: case SpeedoQLConstants.AND:
423: case SpeedoQLConstants.BITWISEOR:
424: case SpeedoQLConstants.BITWISEXOR:
425: case SpeedoQLConstants.BITWISEAND:
426: case SpeedoQLConstants.EQ:
427: case SpeedoQLConstants.NEQ:
428: case SpeedoQLConstants.LT:
429: case SpeedoQLConstants.GT:
430: case SpeedoQLConstants.GTE:
431: case SpeedoQLConstants.LTE:
432: if (debug) {
433: logger.log(BasicLevel.DEBUG, "node useful");
434: }
435: return true;
436: default:
437: if (debug) {
438: logger.log(BasicLevel.DEBUG, "node useless");
439: }
440: return false;
441: }
442: }
443:
444: public Object visit(ASTAdditiveExpression node, Object data) {
445: if (debug) {
446: logger.log(BasicLevel.DEBUG, tab
447: + "Visit AdditiveExpression: " + node);
448: }
449: tab += '\t';
450: visit((SimpleNode) node, data);
451: tab = tab.substring(1);
452: Stack stack = (Stack) data;
453: if (stack.size() > 0 && !CONTAINS_PATH_SET.equals(stack.peek())
454: && !REMOVER.equals(stack.peek())) {
455: Expression ret = (Expression) stack.pop();
456: for (int i = 0; i < node.jjtGetNumChildren() - 1; i++) {
457: if (debug) {
458: logger.log(BasicLevel.DEBUG,
459: "Visit ConditionalExpression... children...["
460: + i + "]");
461: }
462:
463: switch (((Integer) node.ops.get(node
464: .jjtGetNumChildren()
465: - 2 - i)).intValue()) {
466:
467: case SpeedoQLConstants.PLUS:
468: ret = new Plus((Expression) stack.pop(), ret);
469: break;
470: case SpeedoQLConstants.MINUS:
471: ret = new Minus((Expression) stack.pop(), ret);
472: break;
473: case SpeedoQLConstants.MULT:
474: ret = new Mult((Expression) stack.pop(), ret);
475: break;
476: case SpeedoQLConstants.DIV:
477: ret = new DivideBy((Expression) stack.pop(), ret);
478: break;
479: default:
480:
481: }
482: }
483: ((Stack) data).push(ret);
484: }
485: if (debug) {
486: logger.log(BasicLevel.DEBUG, tab
487: + "End of Visit AdditiveExpression: " + node);
488: }
489: return null;
490: }
491:
492: public Object visit(ASTUnaryExpression node, Object data) {
493: if (debug) {
494: logger.log(BasicLevel.DEBUG, tab + "Visit UnaryExpression"
495: + node);
496: }
497: tab += '\t';
498: boolean hasNot = node.ops.size() > 0
499: && ((Integer) node.ops.get(0)).intValue() == SpeedoQLConstants.NOT;
500: if (hasNot) {
501: nbNot++;
502: if (debug) {
503: logger
504: .log(BasicLevel.DEBUG, "remember a Not: "
505: + nbNot);
506: }
507: }
508: visit((SimpleNode) node, data);
509: if (hasNot && nbNot > 0) {
510: nbNot--;
511: if (debug) {
512: logger.log(BasicLevel.DEBUG, "forget a Not: " + nbNot);
513: }
514: }
515: tab = tab.substring(1);
516: Stack stack = (Stack) data;
517: if (debug && stack.size() > 0) {
518: logger.log(BasicLevel.DEBUG, tab + "stack.peek:"
519: + stack.peek());
520: }
521: if (stack.size() > 0 && !CONTAINS_PATH_SET.equals(stack.peek())
522: && !REMOVER.equals(stack.peek())) {
523: Object o = stack.pop();
524: if (node.ops.size() > 0) {
525: switch (((Integer) node.ops.get(0)).intValue()) {
526: case SpeedoQLConstants.MINUS:
527: o = new UMinus((Expression) o);
528: break;
529: case SpeedoQLConstants.BITWISECOMPL:
530: o = new Bitwize((Expression) o);
531: break;
532: case SpeedoQLConstants.NOT:
533: logger.log(BasicLevel.DEBUG, "NOT(" + o + "): "
534: + node);
535: o = new Not((Expression) o);
536: break;
537: }
538: }
539: ((Stack) data).push(o);
540: }
541: if (debug) {
542: logger.log(BasicLevel.DEBUG, tab
543: + "End of Visit UnaryExpression: " + node);
544: }
545: return null;
546: }
547:
548: // to be done
549: public Object visit(ASTCastExpression node, Object data) {
550: ((Stack) data).push(node);
551: return null;
552: }
553:
554: /**
555: * 4 cases to manage: (en cours par equipe MEDOR)
556: * - Collection.contains(Object o)
557: * - Collection.isEmpty()
558: * - String.startsWith(String s)
559: * - String.endsWith(String s)
560: */
561: public Object visit(ASTArgumentList node, Object data) {
562: visit((SimpleNode) node, data);
563: return null;
564: }
565:
566: public Object visit(ASTLiteral node, Object data) {
567: Stack stack = (Stack) data;
568: Expression e = null;
569: if (node.value == null) {
570: //special case for null: will be replaced later
571: e = new DummyOperand();
572: } else if (node.value instanceof Integer) {
573: e = new BasicOperand(((Integer) node.value).intValue());
574: } else if (node.value instanceof Float) {
575: e = new BasicOperand(((Float) node.value).floatValue());
576: } else if (node.value instanceof Character) {
577: e = new BasicOperand(((Character) node.value).charValue());
578: } else if (node.value instanceof String) {
579: String s = (String) (node.value);
580: s = s.substring(1, s.length() - 1);
581: e = new BasicOperand(s);
582: } else if (node.value instanceof Boolean) {
583: e = new BasicOperand(((Boolean) node.value).booleanValue());
584: }
585: if (stack.size() > 0) {
586: Object top = stack.peek();
587: if (CONTAINS_PATH_SET.equals(top)) {
588: //TODO: Support the path.contains(path) operator
589: logger
590: .log(BasicLevel.ERROR,
591: "The path.contains(path) operator is not yet implemented");
592: return null;
593: } else if (top instanceof String) {
594: e = treatMethodOperator((String) top, stack, e);
595: }
596: }
597: if (e != null) {
598: if (debug) {
599: logger.log(BasicLevel.DEBUG, "push("
600: + ExpressionPrinter.e2str(e) + ")");
601: }
602: stack.push(e);
603: }
604: return null;
605: }
606:
607: private Expression treatMethodOperator(String opName, Stack stack,
608: Expression e) {
609: int op = isMethodOperator(opName);
610: if (op == -1) {
611: return e;
612: }
613: switch (op) {
614: case STARTS_WITH_OPERATOR: {
615: stack.pop();
616: Object oe = stack.pop();
617: if (debug) {
618: logger.log(BasicLevel.DEBUG, "pop expression: " + oe);
619: }
620: Expression str = (Expression) oe;
621: if (e instanceof BasicParameterOperand) {
622: e = new StringComparatorParameterOperand(
623: (BasicParameterOperand) e, null, ".*");
624: } else if (e instanceof BasicOperand) {
625: try {
626: e = new BasicOperand(((BasicOperand) e).getString()
627: + ".*");
628: } catch (TypingException e1) {
629: throw new JDOException(
630: "Bad parameter type for the 'startsWith' method",
631: e1);
632: }
633: }
634: e = new Like(str, e);
635: break;
636: }
637: case ENDS_WITH_OPERATOR: {
638: stack.pop();
639: Object oe = stack.pop();
640: if (debug) {
641: logger.log(BasicLevel.DEBUG, "pop expression: " + oe);
642: }
643: Expression str = (Expression) oe;
644: if (e instanceof BasicParameterOperand) {
645: e = new StringComparatorParameterOperand(
646: (BasicParameterOperand) e, ".*", null);
647: } else if (e instanceof BasicOperand) {
648: try {
649: e = new BasicOperand("%"
650: + ((BasicOperand) e).getString());
651: } catch (TypingException e1) {
652: throw new JDOException(
653: "Bad parameter type for the 'startsWith' method",
654: e1);
655: }
656: }
657: e = new Like(str, e);
658: break;
659: }
660: case EQUALS_OPERATOR:
661: //TODO: Support the equals operator
662: break;
663: case MATCHES_OPERATOR: {
664: stack.pop();
665: Object oe = stack.pop();
666: if (debug) {
667: logger.log(BasicLevel.DEBUG, "pop expression: " + oe);
668: }
669: Expression str = (Expression) oe;
670: if (e instanceof BasicParameterOperand) {
671: //e = new StringComparatorParameterOperand((BasicParameterOperand) e, "%", "%");
672: } else if (e instanceof BasicOperand) {
673: try {
674: e = new BasicOperand(((BasicOperand) e).getString());
675: } catch (TypingException e1) {
676: throw new JDOException(
677: "Bad parameter type for the 'startsWith' method",
678: e1);
679: }
680: }
681: e = new Like(str, e);
682: break;
683: }
684: case SUBSTRING_OPERATOR: {
685: stack.pop();
686: Object o = stack.pop();
687: if (o == STR_OPERAND_SUBSTRING) {
688: if (debug) {
689: logger.log(BasicLevel.DEBUG, "push("
690: + ExpressionPrinter.e2str(e) + ")");
691: }
692: stack.push(e);
693: if (debug) {
694: logger.log(BasicLevel.DEBUG,
695: "push(BEGIN_OPERAND_SUBSTRING)");
696: }
697: stack.push(BEGIN_OPERAND_SUBSTRING);
698: if (debug) {
699: logger
700: .log(BasicLevel.DEBUG, "push(" + opName
701: + ")");
702: }
703: stack.push(opName);
704: return null;
705: } else if (o == BEGIN_OPERAND_SUBSTRING) {
706: Expression begin = (Expression) stack.pop();
707: Expression str = (Expression) stack.pop();
708: e = new Substring(str, begin, e);
709: } else {
710: throw new JDOException("Protocol error: " + o);
711: }
712: break;
713: }
714: }
715: return e;
716: }
717:
718: public Object visit(ASTType node, Object data) {
719: if (debug) {
720: logger.log(BasicLevel.DEBUG, "Visit Type: " + node);
721: }
722:
723: ((Stack) data).push(node);
724:
725: if (debug) {
726: logger.log(BasicLevel.DEBUG, "End of Visit Type");
727: }
728: return null;
729: }
730:
731: /**
732: * qualifiedname could be:
733: * - a class field (salary from Employee)
734: * - a parameter
735: * - a variable
736: *
737: */
738: public Object visit(ASTQualifiedName node, Object data) {
739: String name = (String) node.value;
740: String[] splitted = splitPath(name);
741: Stack stack = (Stack) data;
742: if (debug) {
743: logger.log(BasicLevel.DEBUG, tab
744: + "Visit QualifiedName value=[" + node.value
745: + "] stack size=" + stack.size());
746: }
747: try {
748: if (params != null && params.get(splitted[0]) != null) {
749: visitParameter(stack, splitted, name);
750: } else {
751: if (!"this".equals(splitted[0])
752: && (vars == null || vars.get(splitted[0]) == null)) {
753: //It is not a variable, but a navigation from the current
754: // class without 'this' at the begin. then add the
755: // class name in first
756: name = "this." + name;
757: String[] newsplitted = new String[splitted.length + 1];
758: newsplitted[0] = "this";
759: System.arraycopy(splitted, 0, newsplitted, 1,
760: splitted.length);
761: splitted = newsplitted;
762: }
763: visitFieldAccess(stack, splitted, name);
764: }
765: } catch (Exception e) {
766: throw new SpeedoQLAbstractVisitor.VisitorException(e);
767: }
768: return null;
769: }
770:
771: private void visitParameter(Stack stack, String[] splitted,
772: String name) throws Exception {
773: if (stack.size() > 0 && CONTAINS_PATH_SET.equals(stack.peek())) {
774: //x.y.bs.contains(myParam)
775: stack.pop(); // forget the CONTAINS_PATH_SET
776: Object o = stack.pop(); // pop the pathset
777: String pathset = (String) o;
778: if (debug) {
779: logger.log(BasicLevel.DEBUG, tab + "parameter(" + name
780: + ") MemberOf " + pathset);
781: }
782: String[] spli = splitPath(pathset);
783: String rest = mergePath(spli, 1, spli.length - 1);
784: QueryBuilder subquery = new QueryBuilder(qb);
785: subquery.define("", qb.navigate(spli[0]));
786: QueryTreeField setField = subquery.project(subquery
787: .navigate(rest));
788: Expression e = new MemberOf(Collections
789: .singletonList(((Object[]) params.get(name))[1]),
790: Collections.singletonList(new BasicFieldOperand(
791: setField)));
792: if (debug) {
793: logger.log(BasicLevel.DEBUG, tab + "push("
794: + ExpressionPrinter.e2str(e) + ")");
795: }
796: stack.push(e);
797: } else if (splitted.length == 1) {
798: if (debug) {
799: logger.log(BasicLevel.DEBUG, tab
800: + "Push the parameterOperand " + name);
801: }
802: Expression e = (Expression) ((Object[]) params.get(name))[1];
803: if (stack.size() > 0 && stack.peek() instanceof String) {
804: e = treatMethodOperator((String) stack.peek(), stack, e);
805: }
806: stack.push(e);
807: } else if (splitted.length == 2
808: && "contains".equals(splitted[1])) {
809: if (debug) {
810: logger.log(BasicLevel.DEBUG, tab
811: + "Push the parameterOperand " + splitted[0]);
812: }
813: ParameterOperand po = (ParameterOperand) ((Object[]) params
814: .get(splitted[0]))[1];
815:
816: stack.push(po);
817: stack.push(CONTAINS_PATH_SET);
818: } else {
819: throw new Exception(
820: "Does not support the naviation over a parameter: "
821: + name);
822: }
823: }
824:
825: private void visitFieldAccess(Stack stack, String[] splitted,
826: String name) throws Exception {
827: int operatorId = -1;
828: if (stack.size() > 0 && CONTAINS_PATH_SET.equals(stack.peek())) {
829: stack.pop(); // forget the CONTAINS_PATH_SET
830: Object o = stack.pop(); // pop the pathset
831: if (o instanceof ParameterOperand) {
832: //myParam.contains(...)
833: if (debug) {
834: logger.log(BasicLevel.DEBUG, tab + "(" + name
835: + ") InCollection (" + o + ")");
836: }
837: Field qtf = (Field) fields.get(name);
838: Expression e = new InCollection(new BasicFieldOperand(
839: qtf), (ParameterOperand) o, qtf.getType());
840: stack.push(e);
841:
842: } else if (o instanceof String) {
843: String pathset = (String) o;
844: String[] spli = splitPath(pathset);
845: if (vars.containsKey(splitted[0])) {
846: //x.y.bs.contains(b)
847: //The variable definition is managed by the Variable visitor
848: //thus forget the expression
849: if ((nbNot % 2) == 0) {
850: stack.push(REMOVER);
851: } else {
852: QueryTreeField f = (QueryTreeField) fields
853: .get(name);
854: stack.push(new Not(new IsEmpty(
855: new BasicFieldOperand(f))));
856: }
857: } else {
858: //x.y.bs.contains(u.v.b)
859: String rest = mergePath(spli, 1, spli.length - 2);
860: QueryBuilder subquery = new QueryBuilder(qb);
861: subquery.define("", qb.navigate(spli[0]));
862: QueryTreeField setField = subquery.project(subquery
863: .navigate(rest));
864: QueryTreeField f = (QueryTreeField) fields
865: .get(name);
866:
867: stack
868: .push(new MemberOf(
869: Collections
870: .singletonList(new BasicFieldOperand(
871: f)),
872: Collections
873: .singletonList(new BasicFieldOperand(
874: setField))));
875: }
876: } else {
877: if (debug) {
878: logger
879: .log(
880: BasicLevel.DEBUG,
881: tab
882: + "Do not use the pathset of the contain operator");
883: }
884: stack.push(REMOVER);
885: }
886: return;
887: }
888:
889: //maybe there is an operator
890: String last = splitted[splitted.length - 1];
891: operatorId = isMethodOperator(last);
892: if (operatorId == -1) {
893: //No operator found ==> default case
894: if (debug) {
895: logger.log(BasicLevel.DEBUG, tab
896: + "create a fieldOperand with:" + name);
897: }
898: Field f = (Field) fields.get(name);
899: if (f == null) {
900: throw new SpeedoException("Internal error: No field '"
901: + name + "' found during filter parsing");
902: }
903: stack.push(new BasicFieldOperand(f));
904: return;
905: }
906: //There is an operator
907: String begin = buildStringwithout(splitted,
908: splitted.length - 1, ".");
909: if (operatorId == CONTAINS_OPERATOR) {
910: //The contains contraint is managed during the QueryTree creation
911: //see the variable vistor
912: //However push some stuff in the stack in order to known that
913: // the next qualifiedName is used in a contain constraint.
914: if (debug) {
915: logger.log(BasicLevel.DEBUG, tab
916: + "contains operator: set=" + begin);
917: }
918: stack.push(begin);
919: stack.push(CONTAINS_PATH_SET);
920: return;
921: } else if (operatorId == IS_EMPTY_OPERATOR) {
922: if (debug) {
923: logger.log(BasicLevel.DEBUG, tab + "Visit IsEmpty: "
924: + begin);
925: }
926: String rest = mergePath(splitted, 1, splitted.length - 2);
927: QueryBuilder subquery = new QueryBuilder(qb);
928: subquery.define("", qb.navigate(splitted[0]));
929: Field f = subquery.project(subquery.navigate(rest));
930: stack.push(new IsEmpty(new BasicFieldOperand(f)));
931: return;
932: }
933:
934: if (debug) {
935: logger.log(BasicLevel.DEBUG, tab
936: + "create a fieldOperand with:" + begin);
937: }
938: Field f = (Field) fields.get(begin);
939: Expression e = new BasicFieldOperand(f);
940: switch (operatorId) {
941: case TO_LOWER_OPERATOR:
942: e = new StringLower(e);
943: stack.push(e);
944: break;
945: case TO_UPPER_OPERATOR:
946: e = new StringUpper(e);
947: stack.push(e);
948: break;
949: case LENGTH_OPERATOR:
950: e = new Length(e);
951: stack.push(e);
952: break;
953: case SUBSTRING_OPERATOR:
954: stack.push(e);
955: stack.push(STR_OPERAND_SUBSTRING);
956: stack.push(last);
957: break;
958: default:
959: stack.push(e);
960: stack.push(last);
961: break;
962: }
963: }
964:
965: static class DummyOperand extends BasicOperand {
966:
967: public DummyOperand() {
968: super();
969: }
970: }
971: }
|