001: /*
002: * Copyright 2004-2007 Gary Bentley
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may
005: * not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software
010: * distributed under the License is distributed on an "AS IS" BASIS,
011: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: * See the License for the specific language governing permissions and
013: * limitations under the License.
014: */
015: package org.josql.expressions;
016:
017: import java.util.List;
018: import java.util.ArrayList;
019: import java.util.Collection;
020: import java.util.Iterator;
021:
022: import com.gentlyweb.utils.Getter;
023:
024: import org.josql.Query;
025: import org.josql.QueryResults;
026: import org.josql.QueryExecutionException;
027: import org.josql.QueryParseException;
028:
029: import org.josql.events.*;
030:
031: public class SubQueryExpression extends ValueExpression implements
032: BindVariableChangedListener, SaveValueChangedListener {
033:
034: private Query q = null;
035: private boolean inited = false;
036: private String acc = null;
037: private Getter get = null;
038: private boolean nullQuery = false;
039:
040: public SubQueryExpression(Query q) {
041:
042: this .q = q;
043:
044: this .q.addBindVariableChangedListener(this );
045: this .q.addSaveValueChangedListener(this );
046:
047: }
048:
049: public void bindVariableChanged(BindVariableChangedEvent ev) {
050:
051: if (this .q.getFrom() instanceof BindVariable) {
052:
053: BindVariable bv = (BindVariable) this .q.getFrom();
054:
055: if (bv.getName().equalsIgnoreCase(ev.getName())) {
056:
057: this .inited = false;
058:
059: }
060:
061: }
062:
063: }
064:
065: public void saveValueChanged(SaveValueChangedEvent ev) {
066:
067: if (this .q.getFrom() instanceof SaveValue) {
068:
069: SaveValue sv = (SaveValue) this .q.getFrom();
070:
071: if (sv.getName().equalsIgnoreCase(ev.getName())) {
072:
073: this .inited = false;
074:
075: }
076:
077: }
078:
079: }
080:
081: public Getter getGetter() {
082:
083: return this .get;
084:
085: }
086:
087: public void setAccessor(String acc) {
088:
089: this .acc = acc;
090:
091: }
092:
093: public String getAccessor() {
094:
095: return this .acc;
096:
097: }
098:
099: public Query getQuery() {
100:
101: return this .q;
102:
103: }
104:
105: public boolean hasFixedResult(Query q) {
106:
107: return false;
108:
109: }
110:
111: public Class getExpectedReturnType(Query q)
112: throws QueryParseException {
113:
114: if (this .get != null) {
115:
116: return this .get.getType();
117:
118: }
119:
120: return List.class;
121:
122: }
123:
124: public void init(Query q) throws QueryParseException {
125:
126: // Now see if we have an accessor for the function.
127: if (this .acc != null) {
128:
129: try {
130:
131: this .get = new Getter(this .acc, ArrayList.class);
132:
133: } catch (Exception e) {
134:
135: throw new QueryParseException(
136: "Sub-query: "
137: + this
138: + " has accessor: "
139: + this .acc
140: + " however no valid accessor has been found in return type: "
141: + ArrayList.class.getName(), e);
142:
143: }
144:
145: }
146:
147: }
148:
149: public boolean isTrue(Object o, Query q)
150: throws QueryExecutionException {
151:
152: List l = (List) this .getValue(o, q);
153:
154: return l.size() > 0;
155:
156: }
157:
158: private Object innerInitFromFunction(Expression from, Object o,
159: Query q) throws QueryExecutionException {
160:
161: try {
162:
163: from.init(this .q);
164:
165: } catch (Exception e) {
166:
167: throw new QueryExecutionException(
168: "Unable to init FROM clause: " + from
169: + " for sub-query: " + this .q, e);
170:
171: }
172:
173: // Need to pass the parent query here to ensure that if we are using any
174: // special bind variables they are gained from the correct place.
175: return this .q.getFrom().getValue(o, q);
176:
177: }
178:
179: private Object innerInitFromConstantExpression(Expression from,
180: Object o, Query q) throws QueryExecutionException {
181:
182: Object obj = null;
183:
184: // Assume this is a getter.
185: String g = (String) from.getValue(null, this .q);
186:
187: // See if it's the "special" null.
188: if (!g.equalsIgnoreCase("null")) {
189:
190: Accessor acc = new Accessor();
191:
192: acc.setAccessor(g);
193:
194: this .q.setFrom(acc);
195:
196: try {
197:
198: // Init the accessor (from) but with our parent.
199: acc.init(q);
200:
201: } catch (Exception e) {
202:
203: throw new QueryExecutionException(
204: "Unable to init accessor: " + g
205: + " from class: " + o.getClass()
206: + " for FROM clause, for sub-query: \""
207: + this .q + "\"", e);
208:
209: }
210:
211: // Get the value, this will constitute the class for our query.
212: obj = this .q.getFrom().getValue(o, this .q);
213:
214: } else {
215:
216: // This is a "null" query.
217: List l = new ArrayList();
218: l.add(new Object());
219:
220: obj = l;
221:
222: this .nullQuery = true;
223:
224: }
225:
226: return obj;
227:
228: }
229:
230: private Object innerInitFromBindVariable(Expression from, Object o,
231: Query q) throws QueryExecutionException {
232:
233: // Need to init the bind variable.
234: try {
235:
236: from.init(q.getTopLevelQuery());
237:
238: } catch (Exception e) {
239:
240: throw new QueryExecutionException(
241: "Unable to init FROM clause: " + from
242: + " for sub-query: " + this .q, e);
243:
244: }
245:
246: return from.getValue(o, q.getTopLevelQuery());
247:
248: }
249:
250: private Object innerInitFromAccessor(Expression from, Object o,
251: Query q) throws QueryExecutionException {
252:
253: try {
254:
255: from.init(q);
256:
257: } catch (Exception e) {
258:
259: throw new QueryExecutionException(
260: "Unable to init FROM clause: " + from
261: + " for sub-query: " + this .q, e);
262:
263: }
264:
265: // Get the value, this will constitute the class for our query.
266: return from.getValue(o, this .q);
267:
268: }
269:
270: private void innerInit(Object o, Query q)
271: throws QueryExecutionException {
272:
273: Object obj = null;
274:
275: Expression from = this .q.getFrom();
276:
277: if (from instanceof ConstantExpression) {
278:
279: obj = this .innerInitFromConstantExpression(from, o, q);
280:
281: }
282:
283: if (from instanceof Accessor) {
284:
285: obj = this .innerInitFromAccessor(from, o, q);
286:
287: }
288:
289: if (from instanceof Function) {
290:
291: obj = this .innerInitFromFunction(from, o, q);
292:
293: }
294:
295: if (from instanceof SaveValue) {
296:
297: obj = from.getValue(o, q.getTopLevelQuery());
298:
299: }
300:
301: if (from instanceof BindVariable) {
302:
303: obj = this .innerInitFromBindVariable(from, o, q);
304:
305: }
306:
307: if (obj == null) {
308:
309: return;
310:
311: }
312:
313: // Need to ensure that obj is an instance of Collection.
314: if (!(obj instanceof Collection)) {
315:
316: throw new QueryExecutionException("Expected FROM clause: "
317: + this .q.getFrom() + " for sub-query: " + this .q
318: + " to evaluate to an instance of: "
319: + Collection.class.getName()
320: + ", instead evaluates to: "
321: + obj.getClass().getName()
322: + " using from expression: " + from);
323:
324: }
325:
326: Collection col = (Collection) obj;
327:
328: // Now peek at the top of the collection.
329: Iterator iter = col.iterator();
330:
331: if (!iter.hasNext()) {
332:
333: return;
334:
335: }
336:
337: Object io = iter.next();
338:
339: // Get the class...
340: if (io == null) {
341:
342: // Crapola! Now need to iterate down the collection until we find
343: // an item that is not null.
344: while (iter.hasNext()) {
345:
346: io = iter.next();
347:
348: if (io != null) {
349:
350: break;
351:
352: }
353:
354: }
355:
356: }
357:
358: if (io == null) {
359:
360: // Just return, no elements.
361: return;
362:
363: }
364:
365: this .q.setFromObjectClass(io.getClass());
366:
367: try {
368:
369: this .q.init();
370:
371: } catch (Exception e) {
372:
373: throw new QueryExecutionException(
374: "Unable to init sub-query: " + this .q
375: + " with class: " + io.getClass().getName(),
376: e);
377:
378: }
379:
380: this .inited = true;
381:
382: }
383:
384: private List innerGetValue(Object o) throws QueryExecutionException {
385:
386: if (this .nullQuery) {
387:
388: return Query.nullQueryList;
389:
390: }
391:
392: Object obj = null;
393:
394: try {
395:
396: obj = this .q.getFrom().getValue(o,
397: this .q.getTopLevelQuery());
398:
399: } catch (Exception e) {
400:
401: throw new QueryExecutionException(
402: "Unable to evaluate FROM clause accessor: "
403: + this .q.getFrom() + " for sub-query: "
404: + this .q, e);
405:
406: }
407:
408: if (obj == null) {
409:
410: return new ArrayList();
411:
412: }
413:
414: if (!(obj instanceof Collection)) {
415:
416: throw new QueryExecutionException(
417: "Evaluation of FROM clause for sub-query: "
418: + this .q + " returns an instance of: "
419: + obj.getClass().getName()
420: + " however only sub-classes of: "
421: + Collection.class.getName()
422: + " are supported.");
423:
424: }
425:
426: List l = null;
427:
428: // Now, co-erce the collection to a List.
429: if (obj instanceof List) {
430:
431: l = (List) obj;
432:
433: } else {
434:
435: l = new ArrayList((Collection) obj);
436:
437: }
438:
439: return l;
440:
441: }
442:
443: public Object evaluate(Object o, Query q)
444: throws QueryExecutionException {
445:
446: this .q.setParent(q);
447:
448: if (!this .inited) {
449:
450: this .innerInit(o, q);
451:
452: }
453:
454: if (this .inited) {
455:
456: List l = this .innerGetValue(o);
457:
458: QueryResults qr = this .q.execute(l);
459:
460: if (this .get != null) {
461:
462: try {
463:
464: return this .get.getValue(qr.getResults());
465:
466: } catch (Exception e) {
467:
468: throw new QueryExecutionException(
469: "Unable to get value for accessor: "
470: + this .acc + " from return type: "
471: + ArrayList.class.getName()
472: + " after execution of sub-query: "
473: + this , e);
474:
475: }
476:
477: }
478:
479: return qr.getResults();
480:
481: }
482:
483: return new ArrayList();
484:
485: }
486:
487: public String toString() {
488:
489: return "(" + this .q.toString() + ")";
490:
491: }
492:
493: }
|