KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > medor > query > jorm > lib > QueryBuilder


1 package org.objectweb.medor.query.jorm.lib;
2
3 import org.objectweb.jorm.metainfo.api.Class;
4 import org.objectweb.jorm.metainfo.api.ClassRef;
5 import org.objectweb.jorm.metainfo.api.GenClassRef;
6 import org.objectweb.jorm.metainfo.api.Reference;
7 import org.objectweb.jorm.metainfo.api.TypedElement;
8 import org.objectweb.medor.api.Field;
9 import org.objectweb.medor.api.MedorException;
10 import org.objectweb.medor.api.TupleStructure;
11 import org.objectweb.medor.expression.api.Expression;
12 import org.objectweb.medor.expression.lib.And;
13 import org.objectweb.medor.expression.lib.Equal;
14 import org.objectweb.medor.filter.lib.BasicFieldOperand;
15 import org.objectweb.medor.lib.Log;
16 import org.objectweb.medor.query.api.PropagatedField;
17 import org.objectweb.medor.query.api.QueryTree;
18 import org.objectweb.medor.query.api.QueryTreeField;
19 import org.objectweb.medor.query.lib.JoinProject;
20 import org.objectweb.medor.query.jorm.api.JormExtent;
21 import org.objectweb.util.monolog.api.Logger;
22
23 import java.util.Map JavaDoc;
24 import java.util.HashMap JavaDoc;
25 import java.util.Iterator JavaDoc;
26
27 /**
28  * QueryBuilder
29  *
30  * @author Rafael H. Schloming <rhs@mit.edu>
31  **/

32
33 public class QueryBuilder {
34
35     public static Logger log = Log.getLoggerFactory()
36         .getLogger("org.objectweb.medor.query.jorm.lib.QueryBuilder");
37
38     private static final String JavaDoc GENCLASS_ELEMENT_NAME = "genClassElement";
39
40     // our parent query if any
41
private QueryBuilder m_parent = null;
42
43     // the query we're building
44
private JoinProject m_join = new JoinProject();
45
46     // unique id for PName fields
47
private int m_id = 0;
48
49     // map from paths we've navigated to the fields resulting from
50
// that navigation
51
private Map JavaDoc m_fields = new HashMap JavaDoc();
52
53     // track the fields we've projected
54
private Map JavaDoc m_projected = new HashMap JavaDoc();
55
56     // track the joins made to navigate a reference
57
private Map JavaDoc m_joins = new HashMap JavaDoc();
58
59     /**
60      * Construct a new top level query builder instance.
61      */

62
63     public QueryBuilder() {
64         this(null);
65     }
66
67     /**
68      * Constructs a new query builder for creating subqueries of
69      * <code>parent</code>.
70      *
71      * @param parent the containing query
72      */

73
74     public QueryBuilder(QueryBuilder parent) {
75         m_parent = parent;
76     }
77
78     /**
79      * Returns the query tree built by calls to this QueryBuilder
80      * instance.
81      */

82
83     public QueryTree getQueryTree() {
84         return m_join;
85     }
86
87     /**
88      * Returns true if the query tree built by this query navigates to
89      * the given path.
90      *
91      * @param path a dot separated path
92      */

93
94     public boolean contains(String JavaDoc path) {
95         return m_fields.containsKey(path);
96     }
97
98     /**
99      * Assigns a name to a field value in the namespace of the query
100      * being built.
101      *
102      * @param name a path within the namespace of the query
103      * @param field the value for <code>name</code>
104      */

105
106     public void define(String JavaDoc name, QueryTreeField field)
107         throws MedorException {
108         if (m_fields.containsKey(name)) {
109             throw new IllegalArgumentException JavaDoc
110                 ("duplicate definition: " + name +
111                  ", old value: " + m_fields.get(name) +
112                  ", new value: " + field);
113         }
114         m_fields.put(name, field);
115     }
116
117     /**
118      * Adds the specified field to the set of projected fields in the
119      * query being built.
120      *
121      * @param qtf the field to project
122      * @return the propogated field used to project <code>qtf</code>
123      */

124
125     public QueryTreeField project(QueryTreeField qtf) throws MedorException {
126         QueryTreeField result = (QueryTreeField) m_projected.get(qtf);
127         if (result == null) {
128             result = m_join.addPropagatedField
129                 (qtf.getName(), qtf.getType(), new QueryTreeField[] { qtf });
130             m_projected.put(qtf, result);
131         }
132         return result;
133     }
134
135     /**
136      * converts from an array representation of a path to a dot
137      * separated string representation
138      */

139
140     private String JavaDoc name(String JavaDoc[] path, int length) {
141         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
142         String JavaDoc sep = "";
143         for (int i = 0; i < length; i++) {
144             buf.append(sep);
145             buf.append(path[i]);
146             sep = ".";
147         }
148         return buf.toString();
149     }
150
151     private int id() {
152         if (m_parent == null) {
153             return m_id++;
154         } else {
155             return m_parent.id();
156         }
157     }
158
159     /**
160      * Finds the underlying PNameField of a propogated field if it
161      * exists.
162      */

163
164     private PNameField getPNameField(Field field) {
165         if (field instanceof PNameField) {
166             return (PNameField) field;
167         } else if (field instanceof PropagatedField) {
168             return getPNameField
169                 (((PropagatedField) field).getPreviousFields()[0]);
170         } else {
171             return null;
172         }
173     }
174
175     /**
176      * This method returns the PNameField that should be used for
177      * further navigation starting from the set of instances
178      * identified by <code>parent</code>. The method may return
179      * <code>parent</code> itself if the field is already suitable for
180      * navigation, or it may join another extent into the query if no
181      * extent has already been joined for navigating from the
182      * <code>parent</code> field.
183      *
184      * @param parent the starting point for navigation
185      * @param name the node name to use when joining
186      * @return a pname field ready for navigation
187      */

188
189     private PNameField join(QueryTreeField parent)
190         throws MedorException {
191         PNameField ppnf = getPNameField(parent);
192         if (ppnf.isClassPName()) {
193             return ppnf;
194         }
195
196         PNameField pnf = (PNameField) m_joins.get(parent);
197         if (pnf != null) { return pnf; }
198
199         JormExtent ext;
200         Reference ref = ppnf.getReference();
201         String JavaDoc name = parent.getName();
202         String JavaDoc pnfname = "object" + id();
203         if (ref instanceof ClassRef) {
204             ClassRef cref = (ClassRef) ref;
205             ext = new ClassExtent(cref.getMOClass(), name, pnfname, true);
206         } else if (ref instanceof GenClassRef) {
207             GenClassRef gcref = (GenClassRef) ref;
208             ext = new GenClassExtent
209                 (gcref, name, pnfname, GENCLASS_ELEMENT_NAME);
210         } else {
211             throw new IllegalStateException JavaDoc
212                 ("unknown ref type: " + ref.getClass());
213         }
214         pnf = (PNameField) ext.getTupleStructure().getField
215             (ext.getPNameFieldName());
216
217         Expression cond = new Equal
218             (new BasicFieldOperand(parent), new BasicFieldOperand(pnf));
219         Expression filter = m_join.getQueryFilter();
220         if (filter == null) {
221             filter = cond;
222         } else {
223             filter = new And(filter, cond);
224         }
225         m_join.setQueryFilter(filter);
226
227         m_joins.put(parent, pnf);
228
229         return pnf;
230     }
231
232     /**
233      * Performs a single step in the navigation of a path. This may
234      * involve simple accessing an existing query tree field or adding
235      * a new field to the extent specified by the {@link
236      * #join(QueryTreeField, String)} method.
237      *
238      * @param parent the starting point for navigation
239      * @param field the field to navigate
240      * @return a query tree field referring to the result of the
241      * navigation operation
242      */

243
244     private QueryTreeField navigate(QueryTreeField parent, String JavaDoc field)
245         throws MedorException {
246         PNameField pnf = join(parent);
247
248         QueryTreeField qtf;
249         QueryTree qt = pnf.getQueryTree();
250         TupleStructure ts = qt.getTupleStructure();
251         String JavaDoc fq = qt.getName() + "." + field;
252         if (ts.contains(fq)) {
253             qtf = (QueryTreeField) ts.getField(fq);
254         } else if (qt instanceof ClassExtent) {
255             ClassExtent extent = (ClassExtent) qt;
256             Class JavaDoc klass = (Class JavaDoc) extent.getMetaObject();
257             TypedElement te = klass.getTypedElement(field);
258             if (te == null) {
259                 StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
260                 buf.append("No such field '").append(field);
261                 buf.append("' in '").append(klass.getFQName());
262                 buf.append("' from ").append(parent);
263                 for (Iterator JavaDoc it = klass.getAllFields().iterator();
264                      it.hasNext(); ) {
265                     TypedElement el = (TypedElement) it.next();
266                     buf.append(", ").append(el.getName());
267                 }
268                 throw new IllegalArgumentException JavaDoc(buf.toString());
269             } else {
270                 qtf = extent.addField(te);
271             }
272         } else {
273             throw new IllegalStateException JavaDoc
274                 ("wrong extent type: " + qt.getClass() +
275                  "\nfor field: " + field);
276         }
277
278         return qtf;
279     }
280
281     private boolean isGenClassReference(QueryTreeField qtf) {
282         PNameField pnf = getPNameField(qtf);
283         if (pnf == null) { return false; }
284         if (!pnf.isClassPName()) {
285             return pnf.getReference() instanceof GenClassRef;
286         }
287         return false;
288     }
289
290     /**
291      * Extends the query being built by this QueryBuilder instance
292      * along the path specified by <code>path</code> for
293      * <code>length</code> segments of that path.
294      *
295      * @param path the path to navigate
296      * @param length the number of segments of the path to navigate
297      * @return the query tree field referring to the result of the
298      * navigation operation
299      */

300
301     private QueryTreeField navigate(String JavaDoc[] path, int length)
302         throws MedorException {
303         String JavaDoc name = name(path, length);
304         QueryTreeField qtf = (QueryTreeField) m_fields.get(name);
305         // only join once for any field
306
if (qtf != null) { return qtf; }
307
308         if (length == 0) {
309             throw new IllegalArgumentException JavaDoc
310                 ("no such path: " + name(path, path.length));
311         }
312
313         QueryTreeField parent = navigate(path, length - 1);
314         qtf = navigate(parent, path[length - 1]);
315         if (isGenClassReference(qtf)) {
316             qtf = navigate(qtf, GENCLASS_ELEMENT_NAME);
317         }
318         define(name, qtf);
319
320         return qtf;
321     }
322
323     /**
324      * Extends the query being built by this QueryBuilder instance
325      * along the path specified by <code>path</code>.
326      *
327      * @param path the path to navigate
328      * @return the query tree field referring to the result of the
329      * navigation operation
330      */

331
332     public QueryTreeField navigate(String JavaDoc[] path) throws MedorException {
333         return navigate(path, path.length);
334     }
335
336     private static final String JavaDoc DOT = "\\.";
337
338     /**
339      * Extends the query being built by this QueryBuilder instance
340      * along the dot separated path specified by <code>path</code>.
341      *
342      * @param path the path to navigate
343      * @return the query tree field referring to the result of the
344      * navigation operation
345      */

346
347     public QueryTreeField navigate(String JavaDoc path) throws MedorException {
348         String JavaDoc[] parts = path.split(DOT);
349         return navigate(parts);
350     }
351
352 }
353
Popular Tags