KickJava   Java API By Example, From Geeks To Geeks.

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


1 /**
2  * Speedo: an implementation of JDO compliant personality on top of JORM generic
3  * I/O sub-system.
4  * Copyright (C) 2001-2005 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  * Authors: S.Chassande-Barrioz.
23  * Created on 3 fevr. 2005
24  *
25  */

26 package org.objectweb.speedo.query.parser;
27
28 import java.io.Serializable JavaDoc;
29 import java.math.BigDecimal JavaDoc;
30 import java.math.BigInteger JavaDoc;
31 import java.util.ArrayList JavaDoc;
32 import java.util.Date JavaDoc;
33 import java.util.HashMap JavaDoc;
34 import java.util.List JavaDoc;
35 import java.util.Map JavaDoc;
36 import java.util.Stack JavaDoc;
37 import java.util.StringTokenizer JavaDoc;
38
39 import org.objectweb.jorm.api.PMapper;
40 import org.objectweb.jorm.metainfo.api.ClassRef;
41 import org.objectweb.jorm.metainfo.api.GenClassRef;
42 import org.objectweb.jorm.metainfo.api.Reference;
43 import org.objectweb.jorm.type.api.PType;
44 import org.objectweb.medor.api.Field;
45 import org.objectweb.medor.api.MedorException;
46 import org.objectweb.medor.expression.api.Expression;
47 import org.objectweb.medor.expression.api.ExpressionException;
48 import org.objectweb.medor.expression.api.Operator;
49 import org.objectweb.medor.filter.api.FieldOperand;
50 import org.objectweb.medor.filter.lib.Avg;
51 import org.objectweb.medor.filter.lib.BasicFieldOperand;
52 import org.objectweb.medor.filter.lib.Count;
53 import org.objectweb.medor.filter.lib.Max;
54 import org.objectweb.medor.filter.lib.Min;
55 import org.objectweb.medor.filter.lib.Sum;
56 import org.objectweb.medor.query.api.PropagatedField;
57 import org.objectweb.medor.query.api.QueryNode;
58 import org.objectweb.medor.query.api.QueryTree;
59 import org.objectweb.medor.query.api.QueryTreeField;
60 import org.objectweb.medor.query.jorm.lib.ClassExtent;
61 import org.objectweb.medor.query.jorm.lib.JormQueryTreeHelper;
62 import org.objectweb.medor.query.jorm.lib.PNameField;
63 import org.objectweb.medor.query.lib.Nest;
64 import org.objectweb.medor.query.lib.SelectProject;
65 import org.objectweb.speedo.api.SpeedoException;
66 import org.objectweb.speedo.mim.api.SpeedoHome;
67 import org.objectweb.speedo.query.api.QueryDefinition;
68 import org.objectweb.speedo.query.lib.QueryDefinitionImpl;
69 import org.objectweb.speedo.query.lib.QueryEvalContext;
70
71 /**
72  * This visitor parses select and group by clauses in order to build the
73  * projected field of a MEDOR query.
74  *
75  * @author S.Chassande-Barrioz
76  */

77 public class SelectGroupByVisitor {
78
79     /**
80      * key word for the count operator
81      */

82     private static final String JavaDoc COUNT = "count";
83
84     /**
85      * key word for the sum operator
86      */

87     private static final String JavaDoc SUM = "sum";
88
89     /**
90      * key word for the min operator
91      */

92     private static final String JavaDoc MIN = "min";
93
94     /**
95      * key word for the max operator
96      */

97     private static final String JavaDoc MAX = "max";
98
99     /**
100      * key word for the avg operator
101      */

102     private static final String JavaDoc AVG = "avg";
103
104     /**
105      * key word for the distrinct constraint
106      */

107     private static final String JavaDoc DISTINCT = "distinct";
108
109     /**
110      * key word for the unique constraint
111      */

112     private static final String JavaDoc UNIQUE = "unique";
113
114     /**
115      * list of keywords
116      */

117     private static final String JavaDoc[] keywords = new String JavaDoc[] {
118             COUNT.toLowerCase(), //0
119
COUNT.toUpperCase(), //1
120
SUM.toLowerCase(), //2
121
SUM.toUpperCase(), //3
122
MIN.toLowerCase(), //4
123
MIN.toUpperCase(), //5
124
MAX.toLowerCase(), //6
125
MAX.toUpperCase(), //7
126
AVG.toLowerCase(), //8
127
AVG.toUpperCase(), //9
128
DISTINCT.toLowerCase(), //10
129
DISTINCT.toUpperCase(), //11
130
"(", //12
131
")", //13
132
",", //14
133
"as", //15
134
"AS", //16
135
UNIQUE.toLowerCase(), //17
136
UNIQUE.toUpperCase() //18
137
};
138
139     /**
140      * Retrieves the String representation of the function identifier
141      */

142     private final static String JavaDoc getFunctionAsString(int functionIdx) {
143         switch (functionIdx / 2) {
144         case 0:
145             return "COUNT";
146         case 1:
147             return "SUM";
148         case 2:
149             return "MIN";
150         case 3:
151             return "MAX";
152         case 4:
153             return "AVG";
154         default:
155             return "UNKNOWN";
156         }
157
158     }
159
160     /**
161      * SelectProjet where the simple (not aggregat operation) selected fields
162      * must be projected.
163      */

164     private QueryNode top;
165
166     /**
167      * The query tree corresponding to the query
168      */

169     private QueryTree qt;
170
171     /**
172      * The JORM PMapper permitting to fetch PClassMapping for prefetching
173      */

174     private PMapper mapper;
175     
176     private ClassLoader JavaDoc classloader;
177
178     /**
179      * The visitor of variable used in Speedo for building the query. The
180      * visitor is used here for building paths of the select. Indeed the select
181      * clause can contains navigation path.
182      */

183     private SpeedoQLVariableVisitor sqvv;
184
185     /**
186      * The QueryEvalContext used in Speedo for evaluate the MEDOR query. During
187      * the visit of select clause, the algorithm determines if prefetching is
188      * possible and on which class. If prefetching is possible, the
189      * PClassMapping of the prefetched class and the pname index are assigned on
190      * the #qec.
191      */

192     private QueryEvalContext qec;
193
194     /**
195      * indicate if a distinct constraint is specified in the select clause This
196      * field is filled after the call of the parse(String) method.
197      */

198     private boolean unique = false;
199
200     /**
201      * indicate if a distinct constraint is specified in the select clause This
202      * field is filled after the call of the parse(String) method.
203      */

204     private boolean distinct = false;
205
206     /**
207      * indicate if the select clause contains aggregat operator(s) This field is
208      * filled after the call of the parse(String) method.
209      */

210     private boolean hasAggregate = false;
211
212     /**
213      * contains the MEDOR object permitting to build the projection part of the
214      * query. MEDOR object type can be one of the following: - Field : it means
215      * the selected part is a simple navigation (this, this.a.f1, this.a.myB,
216      * ...) - Expression: it means the selected part is a calculated field such
217      * as aggregate function (AVG(this.a.f1), this.f1 + this.f2, ...).
218      *
219      * This field is filled after the call of the parse(String) method.
220      */

221     private ArrayList JavaDoc selectfields = new ArrayList JavaDoc();
222
223     /**
224      * contains the alias name (String) of the projected fields. The content of
225      * this field is linked to the #selectfields field. A null value in the list
226      * means the corresponding selected fields (same index) has no alias.
227      *
228      * This field is filled after the call of the parse(String) method.
229      */

230     private ArrayList JavaDoc aliases = new ArrayList JavaDoc();
231
232     /**
233      * contains the list of grouped fields. The grouped fields are the one used
234      * in an aggregat operator.
235      *
236      * This field is filled after the call of the parse(String) method.
237      */

238     private ArrayList JavaDoc groupedFields = new ArrayList JavaDoc();
239
240     /**
241      * contains the list of fields specified in the group by clause.
242      *
243      * This field is filled after the call of the parse(String) method.
244      */

245     private ArrayList JavaDoc groupByFields = new ArrayList JavaDoc();
246
247     private ArrayList JavaDoc usedFields = new ArrayList JavaDoc();
248     
249     private ArrayList JavaDoc selectFieldTypes = new ArrayList JavaDoc();
250
251     /**
252      * @param qt
253      * @param sp
254      * @param mapper
255      * @param sqvv
256      * @param qd
257      * @param qec
258      */

259     public SelectGroupByVisitor(SelectProject sp, QueryTree qt, PMapper mapper,
260             SpeedoQLVariableVisitor sqvv, QueryDefinitionImpl qd,
261             QueryEvalContext qec, ClassLoader JavaDoc classloader) {
262         super();
263         this.qt = qt;
264         this.top = sp;
265         this.mapper = mapper;
266         this.sqvv = sqvv;
267         this.qec = qec;
268         this.classloader = classloader;
269     }
270
271     public Class JavaDoc[] getSelectFieldTypes() {
272         return (Class JavaDoc[]) selectFieldTypes.toArray(new Class JavaDoc[selectFieldTypes.size()]);
273     }
274     
275     /**
276      * Visit the select and Groupby clause
277      * @param qd is the definition of the query
278      */

279     public void visit(QueryDefinition qd)
280             throws SpeedoException {
281         try {
282             _visit(qd);
283         } catch (ExpressionException e) {
284             throw new SpeedoException(e);
285         } catch (MedorException e) {
286             throw new SpeedoException(e);
287         }
288     }
289     
290     /**
291      * Visit the select and Groupby clause
292      * @param qd is the definition of the query
293      */

294     private void _visit(QueryDefinition qd)
295         throws SpeedoException, MedorException, ExpressionException {
296         String JavaDoc select = qd.getResult();
297         String JavaDoc groupby = qd.getGrouping();
298         boolean withPrefetch = qd.withPrefetch();
299         if (select == null || select.length() == 0) {
300             select = "this";
301         }
302         parseSelect(select);
303         parseGroupBy(groupby);
304
305         //Prefetching or not
306
if (!hasAggregate //not aggregation
307
&& withPrefetch //Prefetching asked
308
&& selectfields.size() == 1 //only one part in the select
309
) {
310             addPrefetchField(qd);
311         }
312         if (hasAggregate) {
313             top = addAggregateNode();
314         }
315         top.setDistinct(distinct);
316         projectFields();
317         qec.query = top;
318     }
319     
320     /**
321      * Add the prefetch field to the current query tree
322      * @param qd is the definition of the query
323      */

324     private void addPrefetchField(QueryDefinition qd) throws MedorException {
325         Field f = ((PropagatedField) selectfields.get(0)).getOriginFields()[0];
326         if (f instanceof PNameField) {
327             PNameField pnf = (PNameField) f;
328             if (pnf.isClassPName() && !pnf.isInGenClass()) {
329                 //Add prefeched fields
330
ClassExtent ce = (ClassExtent) pnf.getQueryTree();
331                 String JavaDoc prefetchedClassName = ce.getJormName();
332                 SpeedoHome sh = (SpeedoHome) mapper.lookup(prefetchedClassName);
333                 if (sh.getPrefetchOnQuery()) {
334                     qec.pcm = sh;
335                     JormQueryTreeHelper.addPrefetchFields(ce, qt, top,
336                                 qd.getIncludeSubClasses());
337                     qec.pnIndex = top.getTupleStructure().getSize() + 1;
338                 }
339             }//else the selected field is a class identifier
340
} //else the selected field is not an identifier or a reference
341
}
342     
343     /**
344      * @return an aggregate node for aggregate operators. the returned node must
345      * become the root the query tree.
346      */

347     private Nest addAggregateNode() throws SpeedoException, MedorException {
348         Map JavaDoc old2newFields = new HashMap JavaDoc();
349         //project used fields on the QueryNode #top
350
for (int i = 0; i < usedFields.size(); i++) {
351             QueryTreeField qtf = (QueryTreeField) usedFields.get(i);
352             Field newf = top.addPropagatedField(qtf.getName(), qtf
353                         .getType(), new QueryTreeField[] { qtf });
354                 old2newFields.put(qtf, newf);
355         }
356         //replace the use of old fields in 'grouped' by the new projected
357
replaceFieldsInList(groupedFields, old2newFields);
358         //replace the use of old fields in 'groupby' by the new projected
359
replaceFieldsInList(groupByFields, old2newFields);
360         //replace the use of old fields in 'selected' by the new projected
361
replaceFieldsInList(selectfields, old2newFields);
362         return new Nest((QueryTreeField[]) groupedFields
363                     .toArray(new QueryTreeField[groupedFields.size()]),
364                     "grouped_fields", (QueryTreeField[]) groupByFields
365                             .toArray(new QueryTreeField[groupByFields
366                                     .size()]), "aggregate_node");
367         //The nest becomes the top node, selected field will add on it
368
}
369     
370     /**
371      * Projects the fields defined in the select clause on the top node.
372      */

373     private void projectFields()
374         throws SpeedoException, MedorException, ExpressionException {
375         //project field on the #top query node
376
for (int i = 0; i < selectfields.size(); i++) {
377             Object JavaDoc o = selectfields.get(i);
378             Field fieldOnTop = null;
379             String JavaDoc fieldName = (String JavaDoc) aliases.get(i);
380             if (o instanceof Expression) {
381                 if (fieldName == null || fieldName.length() == 0) {
382                     fieldName = "field_" + i;
383                 }
384                 Expression e = (Expression) o;
385                 e.compileExpression();
386                 fieldOnTop = top.addCalculatedField(fieldName, e.getType(), e);
387             } else if (o instanceof Field) {
388                 Field f = (Field) o;
389                 if (fieldName == null || fieldName.length() == 0) {
390                     fieldName = f.getName();
391                 }
392                 if (!groupByFields.contains(f) || (top instanceof Nest)) {
393                     fieldOnTop = top.addPropagatedField(fieldName, f.getType(),
394                             new QueryTreeField[] { (QueryTreeField) f });
395                 } else {
396                     String JavaDoc fn = top.getName();
397                     if (fn == null || fn.length() == 0) {
398                         fn = "";
399                     } else {
400                         fn += ".";
401                     }
402                     fn += f.getName();
403                     fieldOnTop = top.getTupleStructure().getField(fn);
404                 }
405             }
406             //Compute the field type
407
selectFieldTypes.add(getFieldClass(fieldOnTop));
408         }
409     }
410     
411     /**
412      * Computes the type of a field.
413      */

414     private Class JavaDoc getFieldClass(Field field) throws SpeedoException {
415         PType ptype = field.getType();
416         String JavaDoc className = null;
417         switch(ptype.getTypeCode()) {
418         case PType.TYPECODE_BIGDECIMAL:
419             return BigDecimal JavaDoc.class;
420         case PType.TYPECODE_BIGINTEGER:
421             return BigInteger JavaDoc.class;
422         case PType.TYPECODE_BOOLEAN:
423         case PType.TYPECODE_OBJBOOLEAN:
424             return Boolean JavaDoc.class;
425         case PType.TYPECODE_BYTE:
426         case PType.TYPECODE_OBJBYTE:
427             return Byte JavaDoc.class;
428         case PType.TYPECODE_BYTEARRAY:
429             return Byte JavaDoc[].class;
430         case PType.TYPECODE_CHAR:
431         case PType.TYPECODE_OBJCHAR:
432             return Character JavaDoc.class;
433         case PType.TYPECODE_CHARARRAY:
434             return Character JavaDoc[].class;
435         case PType.TYPECODE_DATE:
436             return Date JavaDoc.class;
437         case PType.TYPECODE_DOUBLE:
438         case PType.TYPECODE_OBJDOUBLE:
439             return Double JavaDoc.class;
440         case PType.TYPECODE_FLOAT:
441         case PType.TYPECODE_OBJFLOAT:
442             return Float JavaDoc.class;
443         case PType.TYPECODE_INT:
444         case PType.TYPECODE_OBJINT:
445             return Integer JavaDoc.class;
446         case PType.TYPECODE_LONG:
447         case PType.TYPECODE_OBJLONG:
448             return Integer JavaDoc.class;
449         case PType.TYPECODE_SERIALIZED:
450             return Serializable JavaDoc.class;
451         case PType.TYPECODE_SHORT:
452         case PType.TYPECODE_OBJSHORT:
453             return Integer JavaDoc.class;
454         case PType.TYPECODE_STRING:
455             return String JavaDoc.class;
456
457         case PType.TYPECODE_REFERENCE:
458             if (field instanceof PropagatedField) {
459                 Field f = ((PropagatedField) field).getOriginFields()[0];
460                 if (f instanceof PNameField) {
461                     PNameField pnf = (PNameField) f;
462                     if (pnf.isClassPName()) {
463                         if (pnf.isInGenClass()) {
464                             //identifier of the genclass
465
className = pnf.getGenClassRef().getGenClassId();
466                         } else {
467                             //identifier of a class
468
className = pnf.getMetaObjectClass().getFQName();
469                         }
470                     } else {
471                         Reference ref = pnf.getReference();
472                         if (ref instanceof ClassRef) {
473                             //reference to a class
474
className = ((ClassRef) ref).getMOClass().getFQName();
475                         } else if (ref instanceof GenClassRef) {
476                             //reference to a genclass
477
className = ((GenClassRef) ref).getGenClassId();
478                         }
479                     }
480                 }
481             }
482             break;
483         default:
484             className = ptype.getJavaName();
485         }
486         if (className == null) {
487             throw new SpeedoException(
488                     "Type '" + ptype.getJavaName()
489                     + "' not found for projected field:"
490                     + field.getName());
491         } else {
492             try {
493                 return classloader.loadClass(className);
494             } catch (ClassNotFoundException JavaDoc e) {
495                 throw new SpeedoException(
496                         "Type '" + className
497                         + "' not found for projected field:"
498                         + field.getName(), e);
499             }
500         }
501     }
502
503     /**
504      * Replaces the list elements by the associated value from the map.
505      * If an element of the list is an Expression (MEDOR), the use of Fields
506      * in the Expression is replaced.
507      * @param list
508      * is the list containg elements to replace
509      * @param old2newElement
510      * is the table associating the old element (to replace) to the
511      * new element.
512      */

513     private void replaceFieldsInList(ArrayList JavaDoc list, Map JavaDoc old2newElement)
514             throws SpeedoException {
515         for (int i = 0; i < list.size(); i++) {
516             Object JavaDoc old = list.get(i);
517             Object JavaDoc neo = old2newElement.get(old);
518             if (neo != null && old != neo) {
519                 list.set(i, neo);
520             } else if (old instanceof Expression) {
521                 replaceFieldsInExpression((Expression) old, old2newElement);
522             }
523         }
524     }
525
526     /**
527      * Replaces field usage in expression.
528      * @param e is the expression to treat
529      * @param old2newElement
530      * is the table associating the old element (to replace) to the
531      * new element.
532      * @throws SpeedoException
533      */

534     private void replaceFieldsInExpression(Expression e, Map JavaDoc old2newElement)
535             throws SpeedoException {
536         if (e instanceof FieldOperand) {
537             FieldOperand fo = (FieldOperand) e;
538             Field old = fo.getField();
539             Field neo = (Field) old2newElement.get(old);
540             if (neo != null && old != neo) {
541                 ((FieldOperand) e).setField(neo);
542             }
543         } else if (e instanceof Operator) {
544             Operator operator = (Operator) e;
545             for (int i = 0; i < operator.getOperandNumber(); i++) {
546                 replaceFieldsInExpression(operator.getExpression(i),
547                         old2newElement);
548             }
549         }
550     }
551
552     /**
553      * Parses a select clause (with or without 'SELECT' keyword).
554      *
555      * @see #selectfields, #aliases, #groupedFields, #distinct, #hasAggregate .
556      * It
557      * @param select
558      * @throws SpeedoException
559      */

560     private void parseSelect(String JavaDoc select) throws SpeedoException {
561         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(select, " (,)", true);
562         Stack JavaDoc stack = new Stack JavaDoc();
563         while (st.hasMoreTokens()) {
564             String JavaDoc token = st.nextToken().trim();
565             if (token.length() == 0) {
566                 continue;
567             }
568             if (token.equalsIgnoreCase("select")) {
569                 continue;
570             }
571             int keywordIdx = isKeyword(token);
572             if (keywordIdx < 0) {
573                 parseFieldExpression(token, stack);
574             } else if (keywordIdx < 10) {
575                 //Aggregate operator
576
stack.push(new Integer JavaDoc(keywordIdx));
577             } else if (keywordIdx == 10 || keywordIdx == 11) {
578                 //distinct
579
if (stack.size() > 0) {
580                     //the distinct is used in aggregate operator
581
stack.push(new Boolean JavaDoc(true));
582                 } else {
583                     distinct = true;
584                 }
585             } else if (keywordIdx == 17 || keywordIdx == 18) {
586                 //unique
587
unique = true;
588             } else if (keywordIdx == 14) {
589                 parseComma(stack);
590             } else if (keywordIdx == 12) {
591                 //(
592
stack.push(new Integer JavaDoc(keywordIdx));
593             } else if (keywordIdx == 13) {
594                 //)
595
parseRightParenthesis(stack);
596             }
597         }
598         if (stack.size() == 1) {
599             //There is only one element in the select clause
600
selectfields.add(stack.pop());
601             aliases.add(null);
602         }
603     }
604     private void parseFieldExpression(String JavaDoc token, Stack JavaDoc stack) throws SpeedoException {
605         //field expression
606
if (token.equals("*")) {
607             token = "this";
608         }
609         Field f = sqvv.getField(token);
610         if (!usedFields.contains(f)) {
611             usedFields.add(f);
612         }
613         stack.push(f);
614     }
615     
616     private void parseComma(Stack JavaDoc stack) throws SpeedoException {
617         if (stack.size() > 0) {
618             Object JavaDoc o1 = stack.pop();
619             if (stack.size() > 1) {
620                 //with an alias
621
Object JavaDoc o2 = stack.pop();
622                 if (stack.size() > 2) {
623                     //not the end of the selected field
624
stack.push(o2);
625                     stack.push(o1);
626                 } else if (!(o1 instanceof String JavaDoc)
627                         || !(o2 instanceof Expression)) {
628                     throw new SpeedoException(
629                             "Malformed selected field "
630                                     + (selectfields.size() + 1));
631                 } else {
632                     selectfields.add(o2);
633                     aliases.add(o1);
634                 }
635             } else {
636                 //without an alias
637
selectfields.add(o1);
638                 aliases.add(null);
639             }
640         } else {
641             throw new SpeedoException(
642                     "Empty definition of the selected field "
643                             + (selectfields.size() + 1));
644         }
645     }
646
647     private void parseRightParenthesis(Stack JavaDoc stack) throws SpeedoException {
648         ArrayList JavaDoc operands = new ArrayList JavaDoc();
649         boolean isOperand = true;
650         Integer JavaDoc functionIdx = null;
651         //pop all operand until the '(' and the function identifier
652
// (Integer)
653
boolean distinctOnOperand = false;
654         do {
655             Object JavaDoc o = stack.pop();
656             isOperand = !new Integer JavaDoc(12).equals(o);
657             if (isOperand) {
658                 //register the operand
659
operands.add(o);
660             } else {
661                 if (stack.peek() instanceof Boolean JavaDoc) {
662                     distinctOnOperand = ((Boolean JavaDoc) stack.pop())
663                         .booleanValue();
664                 }
665                 // fetch the function identifier
666
functionIdx = (Integer JavaDoc) stack.pop();
667             }
668         } while (isOperand);
669         Expression e;
670         //Verify the operand number
671
if (functionIdx.intValue() > 0 && functionIdx.intValue() < 10) {
672             if (operands.size() != 1) {
673                 throw new SpeedoException(
674                         "Bad number of operand for the function "
675                                 + getFunctionAsString(functionIdx
676                                         .intValue())
677                                 + ", expected 1 operand, found "
678                                 + operands.size());
679             }
680         }
681         //instanciate the medor Operator corresponding to the function
682
Object JavaDoc operand = operands.get(0);
683         if (operand instanceof Expression) {
684             e = (Expression) operand;
685         } else if (operand instanceof Field) {
686             e = new BasicFieldOperand((Field) operand);
687         } else {
688             throw new SpeedoException("Unexpect operand: " + operand);
689         }
690         getFieldsFromExpression(e, groupedFields);
691         hasAggregate = true;
692         switch (functionIdx.intValue() / 2) {
693         case 0:
694             e = new Count(e, distinctOnOperand);
695             break;
696         case 1:
697             e = new Sum(e, distinctOnOperand);
698             break;
699         case 2:
700             e = new Min(e, distinctOnOperand);
701             break;
702         case 3:
703             e = new Max(e, distinctOnOperand);
704             break;
705         case 4:
706             e = new Avg(e, distinctOnOperand);
707             break;
708         default:
709             throw new SpeedoException("Unknown function identifier: "
710                     + functionIdx.intValue());
711         }
712         stack.push(e);
713     }
714
715     /**
716      * Parses a groupby clause (with or without 'GROUP BY' keyword).
717      *
718      * @see #selectfields, #aliases, #groupedFields, #distinct, #hasAggregate .
719      * It
720      * @param select
721      * @throws SpeedoException
722      */

723     private void parseGroupBy(String JavaDoc groupby) throws SpeedoException {
724         if (groupby == null || groupby.length() == 0) {
725             return;
726         }
727         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(groupby, ",", false);
728         while (st.hasMoreTokens()) {
729             groupByFields.add(sqvv.getField(st.nextToken().trim()));
730         }
731         for (int i = 0; i < groupByFields.size(); i++) {
732             Field f = (Field) groupByFields.get(i);
733             if (!usedFields.contains(f)) {
734                 usedFields.add(f);
735             }
736         }
737     }
738
739     /**
740      * Parses an expression and register the list of Fiefd used in the
741      * expression.
742      *
743      * @param e
744      * is the expression to parse
745      * @param result
746      * is the list to fill with Field used in the expression
747      */

748     private void getFieldsFromExpression(Expression e, List JavaDoc result) {
749         if (e instanceof FieldOperand) {
750             Field f = ((FieldOperand) e).getField();
751             if (!result.contains(f)) {
752                 result.add(f);
753             }
754         } else if (e instanceof Operator) {
755             Operator operator = (Operator) e;
756             for (int i = 0; i < operator.getOperandNumber(); i++) {
757                 getFieldsFromExpression(operator.getExpression(i), result);
758             }
759         }
760     }
761
762     private int isKeyword(String JavaDoc str) {
763         if (str == null || str.length() == 0) {
764             return -1;
765         }
766         for (int i = 0; i < keywords.length; i++) {
767             if (keywords[i].equals(str)) {
768                 return i;
769             }
770         }
771         return -1;
772     }
773 }
Popular Tags