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.foundation.*;
025: import com.db4o.internal.*;
026: import com.db4o.query.*;
027: import com.db4o.reflect.*;
028: import com.db4o.types.*;
029:
030: /**
031: * Base class for all constraints on queries.
032: *
033: * @exclude
034: */
035: public abstract class QCon implements Constraint, Visitor4, Unversioned {
036:
037: //Used for query debug only.
038: static final IDGenerator idGenerator = new IDGenerator();
039:
040: // our candidate object tree
041: transient QCandidates i_candidates;
042:
043: // collection of QCandidates to collect children elements and to
044: // execute children. For convenience we hold them in the constraint,
045: // so we can do collection and execution in two steps
046: public Collection4 i_childrenCandidates;
047:
048: // all subconstraints
049: public List4 _children;
050:
051: // for evaluation
052: public QE i_evaluator = QE.DEFAULT;
053:
054: // ID handling for fast find of QConstraint objects in
055: // pending OR evaluations
056: public int i_id;
057:
058: // ANDs and ORs on this constraint
059: public Collection4 i_joins;
060:
061: // positive indicates ascending, negative indicates descending
062: // value indicates ID supplied by ID generator.
063: // lower IDs are applied first
064: public int i_orderID = 0;
065:
066: // the parent of this constraint or null, if this is a root
067: public QCon i_parent;
068:
069: // prevents circular calls on removal
070: public boolean i_removed = false;
071:
072: // our transaction to get a stream object anywhere
073: transient Transaction i_trans;
074:
075: public QCon() {
076: // C/S only
077: }
078:
079: QCon(Transaction a_trans) {
080: i_id = idGenerator.next();
081: i_trans = a_trans;
082: }
083:
084: QCon addConstraint(QCon a_child) {
085: _children = new List4(_children, a_child);
086: return a_child;
087: }
088:
089: public ObjectContainerBase container() {
090: return transaction().container();
091: }
092:
093: public Transaction transaction() {
094: return i_trans;
095: }
096:
097: void addJoin(QConJoin a_join) {
098: if (i_joins == null) {
099: i_joins = new Collection4();
100: }
101: i_joins.add(a_join);
102: }
103:
104: QCon addSharedConstraint(QField a_field, Object a_object) {
105: QConObject newConstraint = new QConObject(i_trans, this ,
106: a_field, a_object);
107: addConstraint(newConstraint);
108: return newConstraint;
109: }
110:
111: public Constraint and(Constraint andWith) {
112: synchronized (streamLock()) {
113: return join(andWith, true);
114: }
115: }
116:
117: void applyOrdering() {
118: if (hasOrdering()) {
119: QCon root = getRoot();
120: root.i_candidates.applyOrdering(i_candidates.i_ordered,
121: i_orderID);
122: }
123: }
124:
125: boolean attach(final QQuery query, final String a_field) {
126:
127: final QCon qcon = this ;
128:
129: ClassMetadata yc = getYapClass();
130: final boolean[] foundField = { false };
131: forEachChildField(a_field, new Visitor4() {
132: public void visit(Object obj) {
133: foundField[0] = true;
134: query.addConstraint((QCon) obj);
135: }
136: });
137:
138: if (foundField[0]) {
139: return true;
140: }
141:
142: QField qf = null;
143:
144: if (yc == null || yc.holdsAnyClass()) {
145:
146: final int[] count = { 0 };
147: final FieldMetadata[] yfs = { null };
148:
149: i_trans.container().classCollection().attachQueryNode(
150: a_field, new Visitor4() {
151: public void visit(Object obj) {
152: yfs[0] = (FieldMetadata) ((Object[]) obj)[1];
153: count[0]++;
154: }
155: });
156:
157: if (count[0] == 0) {
158: return false;
159: }
160:
161: if (count[0] == 1) {
162: qf = yfs[0].qField(i_trans);
163: } else {
164: qf = new QField(i_trans, a_field, null, 0, 0);
165: }
166:
167: } else {
168: if (yc.customizedNewInstance()) {
169: i_trans.container()._handlers._diagnosticProcessor
170: .descendIntoTranslator(yc, a_field);
171: }
172: FieldMetadata yf = yc.fieldMetadataForName(a_field);
173: if (yf != null) {
174: qf = yf.qField(i_trans);
175: }
176: if (qf == null) {
177: qf = new QField(i_trans, a_field, null, 0, 0);
178: }
179: }
180:
181: QConPath qcp = new QConPath(i_trans, qcon, qf);
182: query.addConstraint(qcp);
183: qcon.addConstraint(qcp);
184: return true;
185: }
186:
187: public boolean canBeIndexLeaf() {
188: return false;
189: }
190:
191: public boolean canLoadByIndex() {
192: // virtual
193: return false;
194: }
195:
196: void checkLastJoinRemoved() {
197: if (i_joins.size() == 0) {
198: i_joins = null;
199: }
200: }
201:
202: /** @param candidates */
203: void collect(QCandidates candidates) {
204: // virtual
205: }
206:
207: public Constraint contains() {
208: throw notSupported();
209: }
210:
211: void createCandidates(Collection4 a_candidateCollection) {
212: Iterator4 j = a_candidateCollection.iterator();
213: while (j.moveNext()) {
214: QCandidates candidates = (QCandidates) j.current();
215: if (candidates.tryAddConstraint(this )) {
216: i_candidates = candidates;
217: return;
218: }
219: }
220: i_candidates = new QCandidates((LocalTransaction) i_trans,
221: getYapClass(), getField());
222: i_candidates.addConstraint(this );
223: a_candidateCollection.add(i_candidates);
224: }
225:
226: void doNotInclude(QCandidate a_root) {
227: if (DTrace.enabled) {
228: DTrace.DONOTINCLUDE.log(i_id);
229: }
230: if (Debug.queries) {
231: System.out.println("QCon.doNotInclude " + i_id /*+ " " + getYapClass()*/
232: );
233: }
234: if (i_parent != null) {
235: i_parent.visit1(a_root, this , false);
236: } else {
237: a_root.doNotInclude();
238: }
239: }
240:
241: public Constraint equal() {
242: throw notSupported();
243: }
244:
245: /** @param candidate */
246: boolean evaluate(QCandidate candidate) {
247: throw Exceptions4.virtualException();
248: }
249:
250: void evaluateChildren() {
251: Iterator4 i = i_childrenCandidates.iterator();
252: while (i.moveNext()) {
253: ((QCandidates) i.current()).evaluate();
254: }
255: }
256:
257: void evaluateCollectChildren() {
258: if (DTrace.enabled) {
259: DTrace.COLLECT_CHILDREN.log(i_id);
260: }
261: Iterator4 i = i_childrenCandidates.iterator();
262: while (i.moveNext()) {
263: ((QCandidates) i.current()).collect(i_candidates);
264: }
265: }
266:
267: void evaluateCreateChildrenCandidates() {
268: i_childrenCandidates = new Collection4();
269: Iterator4 i = iterateChildren();
270: while (i.moveNext()) {
271: ((QCon) i.current()).createCandidates(i_childrenCandidates);
272: }
273: }
274:
275: void evaluateEvaluations() {
276: Iterator4 i = iterateChildren();
277: while (i.moveNext()) {
278: ((QCon) i.current()).evaluateEvaluationsExec(i_candidates,
279: true);
280: }
281: }
282:
283: /**
284: * @param candidates
285: * @param rereadObject
286: */
287: void evaluateEvaluationsExec(QCandidates candidates,
288: boolean rereadObject) {
289: // virtual
290: }
291:
292: void evaluateSelf() {
293: i_candidates.filter(this );
294: }
295:
296: void evaluateSimpleChildren() {
297:
298: // TODO: sort the constraints for YapFields first,
299: // so we stay with the same YapField
300:
301: if (_children == null) {
302: return;
303: }
304:
305: Iterator4 i = iterateChildren();
306: while (i.moveNext()) {
307: QCon qcon = (QCon) i.current();
308: i_candidates.setCurrentConstraint(qcon);
309: qcon.setCandidates(i_candidates);
310: qcon.evaluateSimpleExec(i_candidates);
311: qcon.applyOrdering();
312: i_candidates.clearOrdering();
313: }
314: i_candidates.setCurrentConstraint(null);
315: }
316:
317: /** @param candidates */
318: void evaluateSimpleExec(QCandidates candidates) {
319: // virtual
320: }
321:
322: void exchangeConstraint(QCon a_exchange, QCon a_with) {
323: List4 previous = null;
324: List4 current = _children;
325: while (current != null) {
326: if (current._element == a_exchange) {
327: if (previous == null) {
328: _children = current._next;
329: } else {
330: previous._next = current._next;
331: }
332: }
333: previous = current;
334: current = current._next;
335: }
336:
337: _children = new List4(_children, a_with);
338: }
339:
340: void forEachChildField(final String name, final Visitor4 visitor) {
341: Iterator4 i = iterateChildren();
342: while (i.moveNext()) {
343: Object obj = i.current();
344: if (obj instanceof QConObject) {
345: if (((QConObject) obj).i_field.i_name.equals(name)) {
346: visitor.visit(obj);
347: }
348: }
349: }
350: }
351:
352: public QField getField() {
353: return null;
354: }
355:
356: public Object getObject() {
357: throw notSupported();
358: }
359:
360: QCon getRoot() {
361: if (i_parent != null) {
362: return i_parent.getRoot();
363: }
364: return this ;
365: }
366:
367: QCon produceTopLevelJoin() {
368: if (!hasJoins()) {
369: return this ;
370: }
371: Iterator4 i = iterateJoins();
372: if (i_joins.size() == 1) {
373: i.moveNext();
374: return ((QCon) i.current()).produceTopLevelJoin();
375: }
376: Collection4 col = new Collection4();
377: while (i.moveNext()) {
378: col.ensure(((QCon) i.current()).produceTopLevelJoin());
379: }
380: i = col.iterator();
381: i.moveNext();
382: QCon qcon = (QCon) i.current();
383: if (col.size() == 1) {
384: return qcon;
385: }
386: while (i.moveNext()) {
387: qcon = (QCon) qcon.and((Constraint) i.current());
388: }
389: return qcon;
390: }
391:
392: ClassMetadata getYapClass() {
393: return null;
394: }
395:
396: public Constraint greater() {
397: throw notSupported();
398: }
399:
400: public boolean hasChildren() {
401: return _children != null;
402: }
403:
404: public boolean hasParent() {
405: return i_parent != null;
406: }
407:
408: public QCon parent() {
409: return i_parent;
410: }
411:
412: public boolean hasOrJoins() {
413: Collection4 lookedAt = new Collection4();
414: return hasOrJoins(lookedAt);
415: }
416:
417: boolean hasOrJoins(Collection4 lookedAt) {
418: if (lookedAt.containsByIdentity(this )) {
419: return false;
420: }
421: lookedAt.add(this );
422: if (i_joins == null) {
423: return false;
424: }
425: Iterator4 i = iterateJoins();
426: while (i.moveNext()) {
427: QConJoin join = (QConJoin) i.current();
428: if (join.isOr()) {
429: return true;
430: }
431: if (join.hasOrJoins(lookedAt)) {
432: return true;
433: }
434: }
435: return false;
436: }
437:
438: public boolean hasOrJoinWith(QConObject y) {
439: Iterator4 i = iterateJoins();
440: while (i.moveNext()) {
441: QConJoin join = (QConJoin) i.current();
442: if (join.isOr()) {
443: if (y == join.getOtherConstraint(this )) {
444: return true;
445: }
446: }
447: }
448: return false;
449: }
450:
451: public boolean hasJoins() {
452: if (i_joins == null) {
453: return false;
454: }
455: return i_joins.size() > 0;
456: }
457:
458: public boolean hasObjectInParentPath(Object obj) {
459: if (i_parent != null) {
460: return i_parent.hasObjectInParentPath(obj);
461: }
462: return false;
463: }
464:
465: public Constraint identity() {
466: throw notSupported();
467: }
468:
469: public int identityID() {
470: return 0;
471: }
472:
473: boolean isNot() {
474: return i_evaluator instanceof QENot;
475: }
476:
477: boolean isNullConstraint() {
478: return false;
479: }
480:
481: Iterator4 iterateJoins() {
482: if (i_joins == null) {
483: return Iterators.EMPTY_ITERATOR;
484: }
485: return i_joins.iterator();
486: }
487:
488: public Iterator4 iterateChildren() {
489: if (_children == null) {
490: return Iterators.EMPTY_ITERATOR;
491: }
492: return new Iterator4Impl(_children);
493: }
494:
495: Constraint join(Constraint a_with, boolean a_and) {
496: if (!(a_with instanceof QCon) /*|| a_with == this*/
497: ) {
498:
499: // TODO: one of our STOr test cases somehow carries
500: // the same constraint twice. This may be a result
501: // of a funny AND. Check!
502:
503: return null;
504: }
505: if (a_with == this ) {
506: return this ;
507: }
508: return join1((QCon) a_with, a_and);
509: }
510:
511: Constraint join1(QCon a_with, boolean a_and) {
512:
513: if (a_with instanceof QConstraints) {
514: int j = 0;
515: Collection4 joinHooks = new Collection4();
516: Constraint[] constraints = ((QConstraints) a_with)
517: .toArray();
518: for (j = 0; j < constraints.length; j++) {
519: joinHooks.ensure(((QCon) constraints[j]).joinHook());
520: }
521: Constraint[] joins = new Constraint[joinHooks.size()];
522: j = 0;
523: Iterator4 i = joinHooks.iterator();
524: while (i.moveNext()) {
525: joins[j++] = join((Constraint) i.current(), a_and);
526: }
527: return new QConstraints(i_trans, joins);
528: }
529:
530: QCon myHook = joinHook();
531: QCon otherHook = a_with.joinHook();
532: if (myHook == otherHook) {
533: // You might like to check out, what happens, if you
534: // remove this line. It seems to open a bug in an
535: // StOr testcase.
536: return myHook;
537: }
538:
539: QConJoin cj = new QConJoin(i_trans, myHook, otherHook, a_and);
540: myHook.addJoin(cj);
541: otherHook.addJoin(cj);
542: return cj;
543: }
544:
545: QCon joinHook() {
546: return produceTopLevelJoin();
547: }
548:
549: public Constraint like() {
550: throw notSupported();
551: }
552:
553: public Constraint startsWith(boolean caseSensitive) {
554: throw notSupported();
555: }
556:
557: public Constraint endsWith(boolean caseSensitive) {
558: throw notSupported();
559: }
560:
561: void log(String indent) {
562: if (Debug.queries) {
563:
564: final String childIndent = " " + indent;
565: String name = getClass().getName();
566: int pos = name.lastIndexOf(".") + 1;
567: name = name.substring(pos);
568: System.out.println(indent + name + " " + logObject()
569: + " " + i_id);
570: // System.out.println(indent + "JOINS");
571: if (hasJoins()) {
572: Iterator4 i = iterateJoins();
573: while (i.moveNext()) {
574: QCon join = (QCon) i.current();
575: // joins += join.i_id + " ";
576: join.log(childIndent);
577: }
578: }
579: // System.out.println(joins);
580: // System.out.println(indent + getClass().getName() + " " + i_id + " " + i_debugField + " " + joins );
581: // System.out.println(indent + "CONSTRAINTS");
582:
583: if (_children != null) {
584: Iterator4 i = new Iterator4Impl(_children);
585: while (i.moveNext()) {
586: ((QCon) i.current()).log(childIndent);
587: }
588: }
589: }
590: }
591:
592: String logObject() {
593: return "";
594: }
595:
596: void marshall() {
597: Iterator4 i = iterateChildren();
598: while (i.moveNext()) {
599: ((QCon) i.current()).marshall();
600: }
601: }
602:
603: public Constraint not() {
604: synchronized (streamLock()) {
605: if (!(i_evaluator instanceof QENot)) {
606: i_evaluator = new QENot(i_evaluator);
607: }
608: return this ;
609: }
610: }
611:
612: private RuntimeException notSupported() {
613: return new RuntimeException("Not supported.");
614: }
615:
616: /** @param other */
617: public boolean onSameFieldAs(QCon other) {
618: return false;
619: }
620:
621: public Constraint or(Constraint orWith) {
622: synchronized (streamLock()) {
623: return join(orWith, false);
624: }
625: }
626:
627: boolean remove() {
628: if (!i_removed) {
629: i_removed = true;
630: removeChildrenJoins();
631: return true;
632: }
633: return false;
634: }
635:
636: void removeChildrenJoins() {
637: if (!hasJoins()) {
638: return;
639: }
640: Iterator4 i = iterateJoins();
641: while (i.moveNext()) {
642: QConJoin qcj = (QConJoin) i.current();
643: if (qcj.removeForParent(this )) {
644: i_joins.remove(qcj);
645: }
646: }
647: checkLastJoinRemoved();
648: }
649:
650: void removeJoin(QConJoin a_join) {
651: i_joins.remove(a_join);
652: checkLastJoinRemoved();
653: }
654:
655: void removeNot() {
656: if (isNot()) {
657: i_evaluator = ((QENot) i_evaluator).i_evaluator;
658: }
659: }
660:
661: public void setCandidates(QCandidates a_candidates) {
662: i_candidates = a_candidates;
663: }
664:
665: void setOrdering(int a_ordering) {
666: i_orderID = a_ordering;
667: }
668:
669: public int ordering() {
670: return i_orderID;
671: }
672:
673: void setParent(QCon a_newParent) {
674: i_parent = a_newParent;
675: }
676:
677: /**
678: * @param obj
679: * @param removeExisting
680: */
681: QCon shareParent(Object obj, boolean[] removeExisting) {
682: // virtual
683: return null;
684: }
685:
686: /**
687: * @param claxx
688: * @param removeExisting
689: */
690: QConClass shareParentForClass(ReflectClass claxx,
691: boolean[] removeExisting) {
692: // virtual
693: return null;
694: }
695:
696: public Constraint smaller() {
697: throw notSupported();
698: }
699:
700: protected Object streamLock() {
701: return i_trans.container()._lock;
702: }
703:
704: boolean supportsOrdering() {
705: return false;
706: }
707:
708: void unmarshall(final Transaction a_trans) {
709: if (i_trans != null) {
710: return;
711: }
712: i_trans = a_trans;
713: unmarshallParent(a_trans);
714: unmarshallJoins(a_trans);
715: unmarshallChildren(a_trans);
716: }
717:
718: private void unmarshallParent(final Transaction a_trans) {
719: if (i_parent != null) {
720: i_parent.unmarshall(a_trans);
721: }
722: }
723:
724: private void unmarshallChildren(final Transaction a_trans) {
725: Iterator4 i = iterateChildren();
726: while (i.moveNext()) {
727: ((QCon) i.current()).unmarshall(a_trans);
728: }
729: }
730:
731: private void unmarshallJoins(final Transaction a_trans) {
732: if (hasJoins()) {
733: Iterator4 i = iterateJoins();
734: while (i.moveNext()) {
735: ((QCon) i.current()).unmarshall(a_trans);
736: }
737: }
738: }
739:
740: public void visit(Object obj) {
741: QCandidate qc = (QCandidate) obj;
742: visit1(qc.getRoot(), this , evaluate(qc));
743: }
744:
745: void visit(QCandidate a_root, boolean res) {
746: visit1(a_root, this , i_evaluator.not(res));
747: }
748:
749: /** @param reason */
750: void visit1(QCandidate root, QCon reason, boolean res) {
751:
752: // The a_reason parameter makes it eays to distinguish
753: // between calls from above (a_reason == this) and below.
754:
755: if (hasJoins()) {
756: // this should probably be on the Join
757: Iterator4 i = iterateJoins();
758: while (i.moveNext()) {
759: root.evaluate(new QPending((QConJoin) i.current(),
760: this , res));
761: }
762: } else {
763: if (!res) {
764: doNotInclude(root);
765: }
766: }
767: }
768:
769: final void visitOnNull(final QCandidate a_root) {
770:
771: // TODO: It may be more efficient to rule out
772: // all possible keepOnNull issues when starting
773: // evaluation.
774:
775: if (Debug.queries) {
776: System.out.println("QCon.visitOnNull " + i_id);
777: }
778:
779: Iterator4 i = iterateChildren();
780: while (i.moveNext()) {
781: ((QCon) i.current()).visitOnNull(a_root);
782: }
783:
784: if (visitSelfOnNull()) {
785: visit(a_root, isNullConstraint());
786: }
787:
788: }
789:
790: boolean visitSelfOnNull() {
791: return true;
792: }
793:
794: public QE evaluator() {
795: return i_evaluator;
796: }
797:
798: public boolean requiresSort() {
799: if (hasOrdering()) {
800: return true;
801: }
802: Iterator4 i = iterateChildren();
803: while (i.moveNext()) {
804: if (((QCon) i.current()).requiresSort()) {
805: return true;
806: }
807: }
808: return false;
809: }
810:
811: protected boolean hasOrdering() {
812: return i_orderID != 0;
813: }
814: }
|