KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > medor > query > rdb > lib > BasicRdbExpQueryLeaf


1 /**
2  * MEDOR: Middleware Enabling Distributed Object Requests
3  *
4  * Copyright (C) 2001-2004 France Telecom R&D
5  * Contact: alexandre.lefebvre@rd.francetelecom.com
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  *
21  * Initial developers: M. Alia, A. Lefebvre, S. Chassande-Barrioz
22  */

23
24 package org.objectweb.medor.query.rdb.lib;
25
26 import org.objectweb.jorm.mapper.rdb.adapter.api.JoinedTable;
27 import org.objectweb.jorm.mapper.rdb.adapter.api.RdbAdapter;
28 import org.objectweb.jorm.mapper.rdb.adapter.BasicRdbAdapter;
29 import org.objectweb.jorm.type.api.PType;
30 import org.objectweb.medor.api.MedorException;
31 import org.objectweb.medor.api.QueryLeafException;
32 import org.objectweb.medor.datasource.api.DataStore;
33 import org.objectweb.medor.expression.api.Expression;
34 import org.objectweb.medor.expression.api.ExpressionException;
35 import org.objectweb.medor.expression.api.ParameterOperand;
36 import org.objectweb.medor.filter.api.AggregateOperator;
37 import org.objectweb.medor.filter.api.FieldOperand;
38 import org.objectweb.medor.filter.lib.Count;
39 import org.objectweb.medor.filter.lib.ExpressionPrinter;
40 import org.objectweb.medor.query.api.CalculatedField;
41 import org.objectweb.medor.query.api.OrderField;
42 import org.objectweb.medor.query.api.QueryTreeField;
43 import org.objectweb.medor.query.rdb.api.QualifiedTable;
44 import org.objectweb.medor.query.rdb.api.RdbExpField;
45 import org.objectweb.medor.query.rdb.api.RdbExpQueryLeaf;
46 import org.objectweb.medor.type.lib.QType;
47 import org.objectweb.util.monolog.api.BasicLevel;
48
49 import java.util.ArrayList JavaDoc;
50 import java.util.List JavaDoc;
51 import java.util.Map JavaDoc;
52
53 /**
54  * This class represents a QueryLeaf that maps onto a relational database store.
55  * It implements the case where the relational query is provided as a set of
56  * QualifiedTables and an Expression representing the filter.
57  */

58 public class BasicRdbExpQueryLeaf extends BasicRdbQueryLeaf implements
59         RdbExpQueryLeaf {
60
61     /**
62      * The associated MEDOR Expression2WhereClause converter
63      */

64     private MedorExpression2WhereClause exp2where;
65
66     private boolean noGroupBy = false;
67
68     QualifiedTable[] from;
69
70     /**
71      * ArrayList of RdbExpFields corresponding to the group by clause
72      */

73     private ArrayList JavaDoc groupByFields = new ArrayList JavaDoc();
74
75     String JavaDoc rdbAdapterName = null;
76
77     RdbAdapter rdbAdapter = null;
78
79     List JavaDoc rootJoinedTables;
80
81     /**
82      * Indicates if the SQL request can be reused next time
83      */

84     boolean canReuseSQL = false;
85
86     public BasicRdbExpQueryLeaf() {
87     }
88
89     public BasicRdbExpQueryLeaf(DataStore ds, String JavaDoc nodeName) {
90         super(nodeName, ds);
91         exp2where = new MedorExpression2WhereClause();
92     }
93
94     /**
95      * Constructs a BasicRdbExpQueryLeaf from an Expression and a set of
96      * QualifiedTables.
97      * <p>
98      * The SQL request (string) will be calculated.
99      *
100      * @param ds
101      * is the DataStore associated to the QueryLeaf to be created
102      * @param from
103      * is the array of QualifiedTables associated to the QueryLeaf to
104      * be created
105      * @throws QueryLeafException
106      * if the QualifiedTables have all the same names, and in this
107      * case also the same aliases.
108      */

109     public BasicRdbExpQueryLeaf(DataStore ds, QualifiedTable[] from,
110             String JavaDoc nodeName) throws QueryLeafException {
111         this(ds, nodeName);
112         //all QualifiedTables should have different names, and if not,
113
//different aliases
114
//TODO throw an exception if all QualifiedTables have the same name
115
//and the same alias name
116
this.from = from;
117     }
118
119     public Object JavaDoc clone(Object JavaDoc clone, Map JavaDoc obj2clone)
120             throws CloneNotSupportedException JavaDoc {
121         clone = super.clone(clone, obj2clone);
122         BasicRdbExpQueryLeaf ql = (BasicRdbExpQueryLeaf) clone;
123         if (from != null) {
124             ql.from = new QualifiedTable[from.length];
125             for (int i = 0; i < from.length; i++) {
126                 ql.from[i] = (QualifiedTable) getClone(from[i], obj2clone);
127             }
128         }
129         if (groupByFields != null) {
130             ql.groupByFields = new ArrayList JavaDoc();
131             for (int i = 0; i < groupByFields.size(); i++) {
132                 RdbExpField gb = (RdbExpField) groupByFields.get(i);
133                 if (gb == null) {
134                     ql.groupByFields.add(null);
135                 } else {
136                     ql.groupByFields.add(gb.clone(null, obj2clone));
137                 }
138             }
139         }
140         ql.rdbAdapterName = rdbAdapterName;
141         ql.rdbAdapter = rdbAdapter;
142         ql.exp2where = exp2where;
143         if (rootJoinedTables != null) {
144             ql.rootJoinedTables = new ArrayList JavaDoc();
145             for (int i = 0; i < rootJoinedTables.size(); i++) {
146                 JoinedTable jt = (JoinedTable) rootJoinedTables.get(i);
147                 if (jt == null) {
148                     ql.rootJoinedTables.add(null);
149                 } else {
150                     ql.rootJoinedTables.add(jt.clone(null, obj2clone));
151                 }
152             }
153         }
154         return clone;
155     }
156
157     public void setRdbAdapterName(String JavaDoc an) {
158         logger.log(BasicLevel.DEBUG, "Setting rdbAdapterName to " + an);
159         this.rdbAdapterName = an;
160     }
161
162     public String JavaDoc getRdbAdapterName() {
163         return rdbAdapterName;
164     }
165
166     public void setRdbAdapter(RdbAdapter adapter) {
167         logger.log(BasicLevel.DEBUG, "Setting rdbAdapter to " + adapter);
168         rdbAdapter = adapter;
169     }
170
171     public RdbAdapter getRdbAdapter() {
172         return rdbAdapter;
173     }
174
175     public void setRootJoinedTables(List JavaDoc rootjts) {
176         rootJoinedTables = rootjts;
177     }
178
179     /**
180      * Adds a Field to the RdbExpQueryLeaf
181      *
182      * @param name
183      * is the name of the Field to be added.
184      * @param type
185      * is the PTyme of the Field to be added.
186      * @param colName
187      * is the name of the column in the associated QualifiedTable.
188      * @param table
189      * is the QualifiedTable from which the Field is to be created.
190      * @return the created RdbExpField.
191      */

192     public RdbExpField addRdbField(String JavaDoc name, PType type, String JavaDoc colName,
193             QualifiedTable table) throws QueryLeafException {
194         //check that the parameter QualifiedTable is a valid QualifiedTable
195
//in this RdbExpQueryLeaf
196
QualifiedTable[] qts = this.getQualifiedTables();
197         int i = 0;
198         while (i < qts.length) {
199             if (table == qts[i])
200                 break;
201             else
202                 i++;
203         }
204         if (i == qts.length)
205             throw new QueryLeafException(
206                     "The parameter QualifiedTable is not a QualifiedTable of the current RdbExpQueryLeaf");
207         else {
208             RdbExpField f = new BasicRdbExpField(name, type, colName, table,
209                     this);
210             name2field.put(f.getName(), f);
211             fields.add(f);
212             return f;
213         }
214     }
215
216     public void addQualifiedTable(QualifiedTable qt) {
217         if (from == null)
218             from = new QualifiedTable[] { qt };
219         else {
220             QualifiedTable[] qts = new QualifiedTable[from.length + 1];
221             System.arraycopy(from, 0, qts, 0, from.length);
222             qts[from.length] = qt;
223             from = qts;
224         }
225     }
226
227     public void removeQualifiedTable(QualifiedTable qt) throws MedorException {
228         if (from == null)
229             throw new MedorException("QualifiedTable " + qt
230                     + " is not in leaf " + this);
231         for (int i = 0; i < from.length; i++) {
232             if (from[i] == qt) {
233                 if (logger != null && logger.isLoggable(BasicLevel.DEBUG)) {
234                     logger.log(BasicLevel.DEBUG, "Found table "
235                             + qt.getTableName());
236                 }
237                 QualifiedTable[] qts = new QualifiedTable[from.length - 1];
238                 System.arraycopy(from, 0, qts, 0, i);
239                 System.arraycopy(from, i + 1, qts, i, from.length - i - 1);
240                 from = qts;
241                 return;
242             }
243         }
244         throw new MedorException("QualifiedTable " + qt + " is not in leaf "
245                 + this);
246     }
247
248     public boolean containsQualifiedTable(QualifiedTable qt) {
249         if (from == null)
250             return false;
251         for (int i = 0; i < from.length; i++) {
252             if (from[i] == qt)
253                 return true;
254         }
255         return false;
256     }
257
258     /**
259      * Adds an RdbExpField to the RdbExpQueryLeaf
260      *
261      * @param rdbField
262      * is the RdbField to be added.
263      */

264     public RdbExpField addRdbField(RdbExpField rdbField)
265             throws QueryLeafException {
266         rdbField.setQueryLeaf(this);
267         name2field.put(rdbField.getName(), rdbField);
268         fields.add(rdbField);
269         return rdbField;
270     }
271
272     public void removeRdbField(RdbExpField rdbField) throws QueryLeafException {
273         name2field.remove(rdbField.getName());
274         fields.remove(rdbField);
275     }
276
277     public void resetSqlRequest() {
278         noGroupBy = false;
279         query = null;
280     }
281
282     public String JavaDoc getSqlRequest(ParameterOperand[] pos, boolean rangeStartAt,
283             boolean rangeSize) throws MedorException, ExpressionException {
284         return getSqlRequest(pos, null, rangeStartAt, rangeSize);
285     }
286
287     private static final RdbAdapter BASIC = new BasicRdbAdapter();
288
289     private RdbAdapter sqlAdapter() {
290         if (rdbAdapter == null) {
291             return BASIC;
292         } else {
293             return rdbAdapter;
294         }
295     }
296
297     public String JavaDoc getSqlRequest(ParameterOperand[] pos, ArrayList JavaDoc selectFields,
298             boolean rangeStart, boolean rangeSize) throws MedorException,
299             ExpressionException {
300         boolean debug = logger != null && logger.isLoggable(BasicLevel.DEBUG);
301         if (debug) {
302             logger.log(BasicLevel.DEBUG, "Entering getSqlRequest for " + this);
303         }
304         if (canReuseSQL && query != null) {
305             if (debug) {
306                 logger.log(BasicLevel.DEBUG, "Query already computed " + query);
307             }
308             return query;
309         }
310         if (selectFields == null) {
311             if (debug) {
312                 logger.log(BasicLevel.DEBUG,
313                         "Setting selectFields (was null) to local fields");
314             }
315             selectFields = fields;
316         }
317         String JavaDoc where = (sqlFilter == null ? ""
318                 : parseExpression(sqlFilter, pos));
319         if (debug) {
320             logger.log(BasicLevel.DEBUG, "WHERE clause for " + this + " : "
321                     + where);
322         }
323         //adding order by fields to the selection
324
ArrayList JavaDoc allSelected = new ArrayList JavaDoc();
325         allSelected.addAll(selectFields);
326         OrderField[] odfs = getOrderBy();
327         if (getDistinct() && odfs != null) {
328             for (int i = 0; i < odfs.length; i++) {
329                 Object JavaDoc orderField = odfs[i].getField();
330                 if (!allSelected.contains(orderField)) {
331                     allSelected.add(orderField);
332                 }
333             }
334         }
335         String JavaDoc from;
336         if (rootJoinedTables != null && rootJoinedTables.size() > 0) {
337             if (debug) {
338                 logger.log(BasicLevel.DEBUG,
339                         "Query with rootJoinedTables (adapter is "
340                                 + rdbAdapterName + " - " + rdbAdapter + ")");
341             }
342             from = sqlAdapter().getFromClause(rootJoinedTables);
343         } else {
344             if (debug) {
345                 logger.log(BasicLevel.DEBUG,
346                         "Query without rootJoinedTables (adapter is "
347                                 + rdbAdapterName + " - " + rdbAdapter + ")");
348             }
349             from = getFromList();
350         }
351
352         String JavaDoc orderBy = getOrderByClause();
353         String JavaDoc groupBy = groupByFields.isEmpty() ? null : getGroupByString();
354
355         query = sqlAdapter().getQuery(getSelectList("", allSelected, true),
356                 from, where, orderBy, groupBy, rangeStart, rangeSize);
357
358         //In the case of OrderBy, and for Oracle, embed the query inside
359
//a select *
360
if (orderBy != null) {
361             query = sqlAdapter().handleOrderBy(query);
362         }
363         if (!canReuseSQL) {
364             //If among parameters there is a Collection, then it means there is
365
// a InCollection operator in filter. Then the query must be
366
// recompute each time
367
canReuseSQL = true;
368             if (pos != null) {
369                 for (int i = 0; i < pos.length && canReuseSQL; i++) {
370                     if (((ParameterOperand) pos[i]).getType().getTypeCode() == QType.TYPECODE_COLLECTION) {
371                         canReuseSQL = false;
372                     }
373                 }
374             }
375         }
376         return query;
377     }
378
379     private String JavaDoc getOrderByClause() {
380         OrderField[] orderByFields = getOrderBy();
381         if (orderByFields == null || orderByFields.length == 0) {
382             return null;
383         }
384         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
385         String JavaDoc sep = "";
386         for (int cpt = 0; (cpt < orderByFields.length); cpt++) {
387             sb.append(sep);
388             sep = ", ";
389             sb.append(getQualifiedFieldName((RdbExpField) orderByFields[cpt]
390                     .getField()));
391             sb.append(orderByFields[cpt].getDesc() ? " DESC" : " ASC");
392         }
393         return sb.toString();
394     }
395
396     /**
397      * Returns a String corresponding to the sequence of group by
398      * @return a String corresponding to the sequence of group by
399      */

400     private String JavaDoc getGroupByString() {
401         String JavaDoc groupBy = "";
402         for (int i = 0; i < groupByFields.size(); i++) {
403             groupBy += getQualifiedFieldName((RdbExpField) (groupByFields.get(i)));
404             if (i < groupByFields.size() - 1) {
405                 groupBy += ", ";
406             }
407         }
408         return groupBy;
409     }
410
411     /**
412      * Sets the filter associated to the RbdQueryLeaf.
413      * <p>
414      * This filter is represented by an Expression. Unlike the filter of a
415      * QueryNode, is applies to its own Fields, instead of the Fields of its
416      * children.
417      *
418      * @param exp
419      * is the Medor Expression representing the filter to be applied
420      * to the relational source tables.
421      */

422     public void setQueryFilter(Expression exp) {
423         this.sqlFilter = exp;
424     }
425
426     /**
427      * Retrieves the filter associated to the RbdQueryLeaf.
428      *
429      * @return the Medor Expression representing the filter to be applied to the
430      * relational source tables.
431      */

432     public Expression getQueryFilter() {
433         return this.sqlFilter;
434     }
435
436     /**
437      * Returns the associated QualifiedTables.
438      *
439      * @return the array of associated QualifiedTables.
440      */

441     public QualifiedTable[] getQualifiedTables() {
442         return this.from;
443     }
444
445     public void addGroupBy(RdbExpField groupBy) throws QueryLeafException {
446         if (fields.contains(groupBy)) {
447             groupByFields.add(groupBy);
448         } else
449             throw new QueryLeafException("GroupBy error: RdbExpField "
450                     + groupBy.getName()
451                     + " does not exist in the TupleStructure of RdbQueryLeaf "
452                     + this);
453     }
454
455     public RdbExpField[] getGroupByFields() {
456         return (RdbExpField[]) groupByFields
457                 .toArray(new RdbExpField[groupByFields.size()]);
458     }
459
460     public void setNoGroupBy(boolean noGB) {
461         noGroupBy = noGB;
462     }
463
464     public String JavaDoc getGroupBy() {
465         if (groupByFields.size() != 0) {
466             StringBuffer JavaDoc querySB = new StringBuffer JavaDoc();
467             querySB.append(" GROUP BY ");
468             for (int i = 0; i < groupByFields.size(); i++) {
469                 querySB
470                         .append(getQualifiedFieldName((RdbExpField) groupByFields
471                                 .get(i)));
472                 if (i < groupByFields.size() - 1) {
473                     querySB.append(", ");
474                 }
475             }
476             return querySB.toString();
477         } else
478             return "";
479     }
480
481     /**
482      * Builds the SELECT clause of the query (without "SELECT ").
483      *
484      * @param selectList
485      * is the start of the SELECT clause to which the list of
486      * qualified columns is appended.
487      * @param selectFields
488      * is the fields for the SELECT clause. Such fields can either be
489      * plain fields, or can be aggregate fields
490      * @param qualified
491      * indicates whether the field names should be qualified with the
492      * table name or not.
493      * @return the list of qualified columns for the SELECT clause
494      */

495     public String JavaDoc getSelectList(String JavaDoc selectList, ArrayList JavaDoc selectFields,
496             boolean qualified) {
497         logger.log(BasicLevel.DEBUG, "Computing select list");
498         selectList += (getDistinct() ? "DISTINCT " : "");
499         for (int i = 0; i < selectFields.size(); i++) {
500             logger.log(BasicLevel.DEBUG, "Select field " + selectFields.get(i)
501                     + " of node "
502                     + ((QueryTreeField) selectFields.get(i)).getQueryTree());
503             if (selectFields.get(i) instanceof RdbExpField) {
504                 logger.log(BasicLevel.DEBUG, "Found RdbExpField "
505                         + selectFields.get(i));
506                 if (qualified) {
507                     selectList += getQualifiedFieldName(((RdbExpField) selectFields
508                             .get(i)));
509                 } else {
510                     selectList += ((RdbExpField) selectFields.get(i))
511                             .getColumnName();
512                 }
513             } else {
514                 logger.log(BasicLevel.DEBUG, "Found CalculatedField "
515                         + selectFields.get(i));
516                 AggregateOperator op = (AggregateOperator) ((CalculatedField) selectFields
517                         .get(i)).getExpression();
518                 selectList += op.getOperatorString();
519                 selectList += "(";
520                 if (op.getDistinct()) {
521                     selectList += "DISTINCT ";
522                 }
523                 if ((op instanceof Count) && (((Count) op).countAll())) {
524                     selectList += "*";
525                 } else {
526                     RdbExpField theField = (RdbExpField) ((FieldOperand) (op
527                             .getExpression(0))).getField();
528                     if (qualified) {
529                         selectList += getQualifiedFieldName(theField);
530                     } else {
531                         selectList += theField.getColumnName();
532                     }
533                 }
534                 selectList += ")";
535             }
536             if (i < selectFields.size() - 1)
537                 selectList += ", ";
538         }
539         return selectList;
540     }
541
542     /**
543      * Builds the qualified name of a RdbExpField.
544      *
545      * @return the qualified name of an RdbExpField.
546      * @param field
547      * is the RdbExpField for which the qualified name is built.
548      */

549     protected static String JavaDoc getQualifiedFieldName(RdbExpField field) {
550         String JavaDoc res = null;
551         if (field.getTable().getAliasName() == null)
552             res = field.getTable().getTableName();
553         else
554             res = field.getTable().getAliasName();
555         return res + "." + field.getColumnName();
556     }
557
558     /**
559      * Builds the FROM clause of the query (without "FROM").
560      *
561      * @return the list of qualified table names for the FROM clause tables is
562      * appended.
563      */

564     protected String JavaDoc getFromList() {
565         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
566         for (int i = 0; i < from.length; i++) {
567             sb.append(from[i].getTableName());
568             if (from[i].getAliasName() != null) {
569                 if (rdbAdapter != null) {
570                     rdbAdapter.writeTableAlias(from[i].getAliasName(), sb);
571                 } else {
572                     sb.append(" as ");
573                     sb.append(from[i].getAliasName());
574                 }
575             }
576             if (i < from.length - 1)
577                 sb.append(", ");
578         }
579         logger.log(BasicLevel.DEBUG, "FROM clause: " + sb.toString());
580         return sb.toString();
581     }
582
583     /**
584      * Computes a portion of SQL statement for the WHERE clause from a Medor
585      * Expression.
586      *
587      * @param exp
588      * is the Medor Expression from which to compute the portion of
589      * the SQL WHERE clause.
590      * @return the portion of the SQL WHERE clause corresponding to the
591      * Expression.
592      */

593     private String JavaDoc parseExpression(Expression exp, ParameterOperand[] pos)
594             throws ExpressionException {
595         logger.log(BasicLevel.DEBUG, "Parsing " + ExpressionPrinter.e2str(exp));
596         return exp2where.convertExp2WhereClause(exp, rdbAdapter, this, pos);
597     }
598
599 }
Popular Tags