001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package com.db4o.internal.query.processor;
022:
023: import com.db4o.*;
024: import com.db4o.config.*;
025: import com.db4o.foundation.*;
026: import com.db4o.internal.*;
027: import com.db4o.internal.callbacks.*;
028: import com.db4o.internal.marshall.*;
029: import com.db4o.internal.query.*;
030: import com.db4o.internal.query.result.*;
031: import com.db4o.query.*;
032: import com.db4o.reflect.*;
033: import com.db4o.types.*;
034:
035: /**
036: * QQuery is the users hook on our graph.
037: *
038: * A QQuery is defined by it's constraints.
039: *
040: * NOTE: This is just a 'partial' base class to allow for variant implementations
041: * in db4oj and db4ojdk1.2. It assumes that itself is an instance of QQuery
042: * and should never be used explicitly.
043: *
044: * @exclude
045: */
046: public abstract class QQueryBase implements Unversioned {
047:
048: private static final transient IDGenerator i_orderingGenerator = new IDGenerator();
049:
050: transient Transaction _trans;
051:
052: public Collection4 i_constraints = new Collection4();
053:
054: public QQuery i_parent;
055:
056: public String i_field;
057:
058: private transient QueryEvaluationMode _evaluationMode;
059:
060: public int _evaluationModeAsInt;
061:
062: public QueryComparator _comparator;
063:
064: private transient final QQuery _this ;
065:
066: protected QQueryBase() {
067: // C/S only
068: _this = cast(this );
069: }
070:
071: protected QQueryBase(Transaction a_trans, QQuery a_parent,
072: String a_field) {
073: _this = cast(this );
074: _trans = a_trans;
075: i_parent = a_parent;
076: i_field = a_field;
077: }
078:
079: void addConstraint(QCon a_constraint) {
080: i_constraints.add(a_constraint);
081: }
082:
083: private void addConstraint(Collection4 col, Object obj) {
084: if (attachToExistingConstraints(col, obj, true)) {
085: return;
086: }
087: if (attachToExistingConstraints(col, obj, false)) {
088: return;
089: }
090: QConObject newConstraint = new QConObject(_trans, null, null,
091: obj);
092: addConstraint(newConstraint);
093: col.add(newConstraint);
094: }
095:
096: private boolean attachToExistingConstraints(Collection4 col,
097: Object obj, boolean onlyForPaths) {
098: boolean found = false;
099: Iterator4 j = iterateConstraints();
100: while (j.moveNext()) {
101: QCon existingConstraint = (QCon) j.current();
102: boolean[] removeExisting = { false };
103: if (!onlyForPaths
104: || (existingConstraint instanceof QConPath)) {
105: QCon newConstraint = existingConstraint.shareParent(
106: obj, removeExisting);
107: if (newConstraint != null) {
108: addConstraint(newConstraint);
109: col.add(newConstraint);
110: if (removeExisting[0]) {
111: removeConstraint(existingConstraint);
112: }
113: found = true;
114: if (!onlyForPaths) {
115: return true;
116: }
117: }
118: }
119: }
120: return found;
121: }
122:
123: /**
124: * Search for slot that corresponds to class. <br>If not found add it.
125: * <br>Constrain it. <br>
126: */
127: public Constraint constrain(Object example) {
128: synchronized (streamLock()) {
129: example = Platform4.getClassForType(example);
130:
131: ReflectClass claxx = reflectClassForClass(example);
132: if (claxx != null) {
133: return addClassConstraint(claxx);
134: }
135:
136: QConEvaluation eval = Platform4.evaluationCreate(_trans,
137: example);
138: if (eval != null) {
139: return addEvaluationToAllConstraints(eval);
140: }
141:
142: Collection4 constraints = new Collection4();
143: addConstraint(constraints, example);
144: return toConstraint(constraints);
145: }
146: }
147:
148: private Constraint addEvaluationToAllConstraints(QConEvaluation eval) {
149:
150: if (i_constraints.size() == 0) {
151: _trans.container().classCollection()
152: .iterateTopLevelClasses(new Visitor4() {
153: public void visit(Object obj) {
154: ClassMetadata classMetadata = (ClassMetadata) obj;
155: QConClass qcc = new QConClass(_trans,
156: classMetadata.classReflector());
157: addConstraint(qcc);
158: }
159: });
160: }
161:
162: Iterator4 i = iterateConstraints();
163: while (i.moveNext()) {
164: ((QCon) i.current()).addConstraint(eval);
165: }
166:
167: // FIXME: should return valid Constraint object
168: return null;
169: }
170:
171: private Constraint addClassConstraint(ReflectClass claxx) {
172: if (claxx.equals(stream()._handlers.ICLASS_OBJECT)) {
173: return null;
174: }
175:
176: Collection4 col = new Collection4();
177: if (claxx.isInterface()) {
178: return addInterfaceConstraint(claxx);
179: }
180:
181: Iterator4 constraintsIterator = iterateConstraints();
182: while (constraintsIterator.moveNext()) {
183: QCon existingConstraint = (QConObject) constraintsIterator
184: .current();
185: boolean[] removeExisting = { false };
186: QCon newConstraint = existingConstraint
187: .shareParentForClass(claxx, removeExisting);
188: if (newConstraint != null) {
189: addConstraint(newConstraint);
190: col.add(newConstraint);
191: if (removeExisting[0]) {
192: removeConstraint(existingConstraint);
193: }
194: }
195: }
196: if (col.size() == 0) {
197: QConClass qcc = new QConClass(_trans, claxx);
198: addConstraint(qcc);
199: return qcc;
200: }
201:
202: return toConstraint(col);
203: }
204:
205: private Constraint addInterfaceConstraint(ReflectClass claxx) {
206: Collection4 classes = stream().classCollection().forInterface(
207: claxx);
208: if (classes.size() == 0) {
209: QConClass qcc = new QConClass(_trans, null, null, claxx);
210: addConstraint(qcc);
211: return qcc;
212: }
213: Iterator4 i = classes.iterator();
214: Constraint constr = null;
215: while (i.moveNext()) {
216: ClassMetadata yapClass = (ClassMetadata) i.current();
217: ReflectClass yapClassClaxx = yapClass.classReflector();
218: if (yapClassClaxx != null) {
219: if (!yapClassClaxx.isInterface()) {
220: if (constr == null) {
221: constr = constrain(yapClassClaxx);
222: } else {
223: constr = constr.or(constrain(yapClass
224: .classReflector()));
225: }
226: }
227: }
228:
229: }
230: return constr;
231: }
232:
233: private ReflectClass reflectClassForClass(Object example) {
234: if (example instanceof ReflectClass) {
235: return (ReflectClass) example;
236: }
237: if (example instanceof Class) {
238: return _trans.reflector().forClass((Class) example);
239: }
240: return null;
241: }
242:
243: public Constraints constraints() {
244: synchronized (streamLock()) {
245: Constraint[] constraints = new Constraint[i_constraints
246: .size()];
247: i_constraints.toArray(constraints);
248: return new QConstraints(_trans, constraints);
249: }
250: }
251:
252: public Query descend(final String a_field) {
253: synchronized (streamLock()) {
254: final QQuery query = new QQuery(_trans, _this , a_field);
255: int[] run = { 1 };
256: if (!descend1(query, a_field, run)) {
257:
258: // try to add unparented nodes on the second run,
259: // if not added in the first run and a descendant
260: // was not found
261:
262: if (run[0] == 1) {
263: run[0] = 2;
264: if (!descend1(query, a_field, run)) {
265: new QConUnconditional(_trans, false).attach(
266: query, a_field);
267: }
268: }
269: }
270: return query;
271: }
272: }
273:
274: private boolean descend1(final QQuery query, final String a_field,
275: int[] run) {
276: final boolean[] foundClass = { false };
277: if (run[0] == 2 || i_constraints.size() == 0) {
278:
279: // On the second run we are really creating a second independant
280: // query network that is not joined to other higher level
281: // constraints.
282: // Let's see how this works out. We may need to join networks.
283:
284: run[0] = 0; // prevent a double run of this code
285:
286: final boolean[] anyClassCollected = { false };
287:
288: stream().classCollection().attachQueryNode(a_field,
289: new Visitor4() {
290:
291: public void visit(Object obj) {
292:
293: Object[] pair = ((Object[]) obj);
294: ClassMetadata parentYc = (ClassMetadata) pair[0];
295: FieldMetadata yf = (FieldMetadata) pair[1];
296: ClassMetadata childYc = yf
297: .handlerClassMetadata(stream());
298:
299: boolean take = true;
300:
301: if (childYc instanceof UntypedFieldHandler) {
302: if (anyClassCollected[0]) {
303: take = false;
304: } else {
305: anyClassCollected[0] = true;
306: }
307: }
308:
309: if (take) {
310:
311: QConClass qcc = new QConClass(_trans,
312: null, yf.qField(_trans),
313: parentYc.classReflector());
314: addConstraint(qcc);
315: }
316:
317: }
318:
319: });
320:
321: }
322: Iterator4 i = iterateConstraints();
323: while (i.moveNext()) {
324: if (((QCon) i.current()).attach(query, a_field)) {
325: foundClass[0] = true;
326: }
327: }
328: return foundClass[0];
329: }
330:
331: public ObjectSet execute() {
332: synchronized (streamLock()) {
333: Callbacks callbacks = stream().callbacks();
334: callbacks.queryOnStarted(_trans, cast(this ));
335: QueryResult qresult = getQueryResult();
336: callbacks.queryOnFinished(_trans, cast(this ));
337: return new ObjectSetFacade(qresult);
338: }
339: }
340:
341: public QueryResult getQueryResult() {
342: synchronized (streamLock()) {
343: if (i_constraints.size() == 0) {
344: return stream().getAll(_trans);
345: }
346: QueryResult result = classOnlyQuery();
347: if (result != null) {
348: return result;
349: }
350: return stream().executeQuery(_this );
351: }
352: }
353:
354: protected ObjectContainerBase stream() {
355: return _trans.container();
356: }
357:
358: private QueryResult classOnlyQuery() {
359:
360: if (i_constraints.size() != 1 || _comparator != null) {
361: return null;
362: }
363: Constraint constr = singleConstraint();
364: if (constr.getClass() != QConClass.class) {
365: return null;
366: }
367: QConClass clazzconstr = (QConClass) constr;
368: ClassMetadata clazz = clazzconstr.i_yapClass;
369: if (clazz == null) {
370: return null;
371: }
372: if (clazzconstr.hasChildren() || clazz.isArray()) {
373: return null;
374: }
375:
376: QueryResult queryResult = stream()
377: .classOnlyQuery(_trans, clazz);
378: if (queryResult == null) {
379: return null;
380: }
381: sort(queryResult);
382:
383: return queryResult;
384:
385: }
386:
387: private Constraint singleConstraint() {
388: return (Constraint) i_constraints.singleElement();
389: }
390:
391: public static class CreateCandidateCollectionResult {
392: public final boolean checkDuplicates;
393: public final boolean topLevel;
394: public final List4 candidateCollection;
395:
396: public CreateCandidateCollectionResult(
397: List4 candidateCollection_, boolean checkDuplicates_,
398: boolean topLevel_) {
399: candidateCollection = candidateCollection_;
400: topLevel = topLevel_;
401: checkDuplicates = checkDuplicates_;
402: }
403: }
404:
405: public Iterator4 executeSnapshot() {
406:
407: final CreateCandidateCollectionResult r = createCandidateCollection();
408:
409: final Collection4 executionPath = executionPath(r);
410:
411: Iterator4 candidatesIterator = new Iterator4Impl(
412: r.candidateCollection);
413:
414: Collection4 snapshots = new Collection4();
415: while (candidatesIterator.moveNext()) {
416: QCandidates candidates = (QCandidates) candidatesIterator
417: .current();
418: snapshots.add(candidates.executeSnapshot(executionPath));
419: }
420:
421: Iterator4 snapshotsIterator = snapshots.iterator();
422: final CompositeIterator4 resultingIDs = new CompositeIterator4(
423: snapshotsIterator);
424:
425: if (!r.checkDuplicates) {
426: return resultingIDs;
427: }
428:
429: return checkDuplicates(resultingIDs);
430: }
431:
432: public Iterator4 executeLazy() {
433:
434: final CreateCandidateCollectionResult r = createCandidateCollection();
435:
436: final Collection4 executionPath = executionPath(r);
437:
438: Iterator4 candidateCollection = new Iterator4Impl(
439: r.candidateCollection);
440:
441: MappingIterator executeCandidates = new MappingIterator(
442: candidateCollection) {
443: protected Object map(Object current) {
444: return ((QCandidates) current)
445: .executeLazy(executionPath);
446: }
447: };
448:
449: CompositeIterator4 resultingIDs = new CompositeIterator4(
450: executeCandidates);
451:
452: if (!r.checkDuplicates) {
453: return resultingIDs;
454: }
455:
456: return checkDuplicates(resultingIDs);
457: }
458:
459: private MappingIterator checkDuplicates(
460: CompositeIterator4 executeAllCandidates) {
461: return new MappingIterator(executeAllCandidates) {
462: private TreeInt ids = new TreeInt(0);
463:
464: protected Object map(Object current) {
465: int id = ((Integer) current).intValue();
466: if (ids.find(id) != null) {
467: return MappingIterator.SKIP;
468: }
469: ids = (TreeInt) ids.add(new TreeInt(id));
470: return current;
471: }
472:
473: };
474: }
475:
476: private Collection4 executionPath(
477: final CreateCandidateCollectionResult r) {
478: return r.topLevel ? null : fieldPathFromTop();
479: }
480:
481: public void executeLocal(final IdListQueryResult result) {
482:
483: CreateCandidateCollectionResult r = createCandidateCollection();
484:
485: boolean checkDuplicates = r.checkDuplicates;
486: boolean topLevel = r.topLevel;
487: List4 candidateCollection = r.candidateCollection;
488:
489: if (Debug.queries) {
490: logConstraints();
491: }
492:
493: if (candidateCollection != null) {
494:
495: final Collection4 executionPath = topLevel ? null
496: : fieldPathFromTop();
497:
498: Iterator4 i = new Iterator4Impl(candidateCollection);
499: while (i.moveNext()) {
500: ((QCandidates) i.current()).execute();
501: }
502:
503: if (candidateCollection._next != null) {
504: checkDuplicates = true;
505: }
506:
507: if (checkDuplicates) {
508: result.checkDuplicates();
509: }
510:
511: final ObjectContainerBase stream = stream();
512: i = new Iterator4Impl(candidateCollection);
513: while (i.moveNext()) {
514: QCandidates candidates = (QCandidates) i.current();
515: if (topLevel) {
516: candidates.traverse(result);
517: } else {
518: candidates.traverse(new Visitor4() {
519: public void visit(Object a_object) {
520: QCandidate candidate = (QCandidate) a_object;
521: if (candidate.include()) {
522: TreeInt ids = new TreeInt(
523: candidate._key);
524: final TreeInt[] idsNew = new TreeInt[1];
525: Iterator4 itPath = executionPath
526: .iterator();
527: while (itPath.moveNext()) {
528: idsNew[0] = null;
529: final String fieldName = (String) (itPath
530: .current());
531: if (ids != null) {
532: ids.traverse(new Visitor4() {
533: public void visit(
534: Object treeInt) {
535: int id = ((TreeInt) treeInt)._key;
536: StatefulBuffer reader = stream
537: .readWriterByID(
538: _trans,
539: id);
540: if (reader != null) {
541: ObjectHeader oh = new ObjectHeader(
542: stream,
543: reader);
544: idsNew[0] = oh
545: .classMetadata()
546: .collectFieldIDs(
547: oh._marshallerFamily,
548: oh._headerAttributes,
549: idsNew[0],
550: reader,
551: fieldName);
552: }
553: }
554: });
555: }
556: ids = idsNew[0];
557: }
558: if (ids != null) {
559: ids.traverse(new Visitor4() {
560: public void visit(Object treeInt) {
561: result
562: .addKeyCheckDuplicates(((TreeInt) treeInt)._key);
563: }
564: });
565: }
566: }
567: }
568: });
569: }
570: }
571: }
572: sort(result);
573: }
574:
575: private Collection4 fieldPathFromTop() {
576: QQueryBase q = this ;
577: final Collection4 fieldPath = new Collection4();
578: while (q.i_parent != null) {
579: fieldPath.prepend(q.i_field);
580: q = q.i_parent;
581: }
582: return fieldPath;
583: }
584:
585: private void logConstraints() {
586: if (Debug.queries) {
587: Iterator4 i = iterateConstraints();
588: while (i.moveNext()) {
589: ((QCon) i.current()).log("");
590: }
591: }
592: }
593:
594: public CreateCandidateCollectionResult createCandidateCollection() {
595: boolean checkDuplicates = false;
596: boolean topLevel = true;
597: List4 candidateCollection = null;
598: Iterator4 i = iterateConstraints();
599: while (i.moveNext()) {
600: QCon qcon = (QCon) i.current();
601: QCon old = qcon;
602: qcon = qcon.getRoot();
603: if (qcon != old) {
604: checkDuplicates = true;
605: topLevel = false;
606: }
607: ClassMetadata yc = qcon.getYapClass();
608: if (yc == null) {
609: break;
610: }
611: candidateCollection = addConstraintToCandidateCollection(
612: candidateCollection, qcon);
613: }
614: return new CreateCandidateCollectionResult(candidateCollection,
615: checkDuplicates, topLevel);
616: }
617:
618: private List4 addConstraintToCandidateCollection(
619: List4 candidateCollection, QCon qcon) {
620:
621: if (candidateCollection != null) {
622: if (tryToAddToExistingCandidate(candidateCollection, qcon)) {
623: return candidateCollection;
624: }
625: }
626:
627: QCandidates candidates = new QCandidates(
628: (LocalTransaction) _trans, qcon.getYapClass(), null);
629: candidates.addConstraint(qcon);
630: return new List4(candidateCollection, candidates);
631: }
632:
633: private boolean tryToAddToExistingCandidate(
634: List4 candidateCollection, QCon qcon) {
635: Iterator4 j = new Iterator4Impl(candidateCollection);
636: while (j.moveNext()) {
637: QCandidates candidates = (QCandidates) j.current();
638: if (candidates.tryAddConstraint(qcon)) {
639: return true;
640: }
641: }
642: return false;
643: }
644:
645: public final Transaction getTransaction() {
646: return _trans;
647: }
648:
649: Iterator4 iterateConstraints() {
650: // clone the collection first to avoid
651: // InvalidIteratorException as i_constraints might be
652: // modified during the execution of callee
653: return new Collection4(i_constraints).iterator();
654: }
655:
656: public Query orderAscending() {
657: synchronized (streamLock()) {
658: setOrdering(i_orderingGenerator.next());
659: return _this ;
660: }
661: }
662:
663: public Query orderDescending() {
664: synchronized (streamLock()) {
665: setOrdering(-i_orderingGenerator.next());
666: return _this ;
667: }
668: }
669:
670: private void setOrdering(final int ordering) {
671: Iterator4 i = iterateConstraints();
672: while (i.moveNext()) {
673: ((QCon) i.current()).setOrdering(ordering);
674: }
675: }
676:
677: public void marshall() {
678: _evaluationModeAsInt = _evaluationMode.asInt();
679: Iterator4 i = iterateConstraints();
680: while (i.moveNext()) {
681: ((QCon) i.current()).getRoot().marshall();
682: }
683: }
684:
685: public void unmarshall(final Transaction a_trans) {
686: _evaluationMode = QueryEvaluationMode
687: .fromInt(_evaluationModeAsInt);
688: _trans = a_trans;
689: Iterator4 i = iterateConstraints();
690: while (i.moveNext()) {
691: ((QCon) i.current()).unmarshall(a_trans);
692: }
693: }
694:
695: void removeConstraint(QCon a_constraint) {
696: i_constraints.remove(a_constraint);
697: }
698:
699: Constraint toConstraint(final Collection4 constraints) {
700: if (constraints.size() == 1) {
701: return (Constraint) constraints.singleElement();
702: } else if (constraints.size() > 0) {
703: Constraint[] constraintArray = new Constraint[constraints
704: .size()];
705: constraints.toArray(constraintArray);
706: return new QConstraints(_trans, constraintArray);
707: }
708: return null;
709: }
710:
711: protected Object streamLock() {
712: return stream()._lock;
713: }
714:
715: public Query sortBy(QueryComparator comparator) {
716: _comparator = comparator;
717: return _this ;
718: }
719:
720: private void sort(QueryResult result) {
721: if (_comparator != null) {
722: result.sort(_comparator);
723: }
724: }
725:
726: // cheat emulating '(QQuery)this'
727: private static QQuery cast(QQueryBase obj) {
728: return (QQuery) obj;
729: }
730:
731: public boolean requiresSort() {
732: if (_comparator != null) {
733: return true;
734: }
735: Iterator4 i = iterateConstraints();
736: while (i.moveNext()) {
737: QCon qCon = (QCon) i.current();
738: if (qCon.requiresSort()) {
739: return true;
740: }
741: }
742: return false;
743: }
744:
745: public QueryComparator comparator() {
746: return _comparator;
747: }
748:
749: public QueryEvaluationMode evaluationMode() {
750: return _evaluationMode;
751: }
752:
753: public void evaluationMode(QueryEvaluationMode mode) {
754: _evaluationMode = mode;
755: }
756:
757: }
|