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.handlers.*;
028: import com.db4o.internal.marshall.*;
029: import com.db4o.query.*;
030: import com.db4o.reflect.*;
031:
032: /**
033: * Represents an actual object in the database. Forms a tree structure, indexed
034: * by id. Can have dependents that are doNotInclude'd in the query result when
035: * this is doNotInclude'd.
036: *
037: * @exclude
038: */
039: public class QCandidate extends TreeInt implements Candidate, Orderable {
040:
041: // db4o ID is stored in _key;
042:
043: // db4o byte stream storing the object
044: Buffer _bytes;
045:
046: final QCandidates _candidates;
047:
048: // Dependant candidates
049: private List4 _dependants;
050:
051: // whether to include in the result set
052: // may use id for optimisation ???
053: boolean _include = true;
054:
055: private Object _member;
056:
057: // Comparable
058: Orderable _order;
059:
060: // Possible pending joins on children
061: Tree _pendingJoins;
062:
063: // The evaluation root to compare all ORs
064: private QCandidate _root;
065:
066: // the YapClass of this object
067: ClassMetadata _yapClass;
068:
069: // temporary yapField and member for one field during evaluation
070: FieldMetadata _yapField; // null denotes null object
071:
072: private int _handlerVersion;
073:
074: private QCandidate(QCandidates qcandidates) {
075: super (0);
076: _candidates = qcandidates;
077: }
078:
079: public QCandidate(QCandidates candidates, Object obj, int id,
080: boolean include) {
081: super (id);
082: if (DTrace.enabled) {
083: DTrace.CREATE_CANDIDATE.log(id);
084: }
085: _candidates = candidates;
086: _order = this ;
087: _member = obj;
088: _include = include;
089:
090: if (id == 0) {
091: _key = candidates.generateCandidateId();
092: }
093: }
094:
095: public Object shallowClone() {
096: QCandidate qcan = new QCandidate(_candidates);
097: qcan.setBytes(_bytes);
098: qcan._dependants = _dependants;
099: qcan._include = _include;
100: qcan._member = _member;
101: qcan._order = _order;
102: qcan._pendingJoins = _pendingJoins;
103: qcan._root = _root;
104: qcan._yapClass = _yapClass;
105: qcan._yapField = _yapField;
106:
107: return super .shallowCloneInternal(qcan);
108: }
109:
110: void addDependant(QCandidate a_candidate) {
111: _dependants = new List4(_dependants, a_candidate);
112: }
113:
114: private void checkInstanceOfCompare() {
115: if (_member instanceof Compare) {
116: _member = ((Compare) _member).compare();
117: LocalObjectContainer stream = container();
118: _yapClass = stream.classMetadataForReflectClass(stream
119: .reflector().forObject(_member));
120: _key = stream.getID(transaction(), _member);
121: if (_key == 0) {
122: setBytes(null);
123: } else {
124: setBytes(stream.readReaderByID(transaction(), _key));
125: }
126: }
127: }
128:
129: public int compare(Tree a_to) {
130: return _order.compareTo(((QCandidate) a_to)._order);
131: }
132:
133: public int compareTo(Object a_object) {
134: if (a_object instanceof Order) {
135: return -((Order) a_object).compareTo(this );
136: }
137: return _key - ((TreeInt) a_object)._key;
138: }
139:
140: boolean createChild(final QCandidates a_candidates) {
141: if (!_include) {
142: return false;
143: }
144:
145: if (_yapField != null) {
146: TypeHandler4 handler = _yapField.getHandler();
147: if (handler != null) {
148:
149: final Buffer[] arrayBytes = { _bytes };
150:
151: TypeHandler4 tempHandler = null;
152:
153: if (handler instanceof FirstClassHandler) {
154: tempHandler = ((FirstClassHandler) handler)
155: .readArrayHandler(transaction(),
156: marshallerFamily(), arrayBytes);
157:
158: }
159:
160: if (tempHandler != null) {
161:
162: final TypeHandler4 arrayHandler = tempHandler;
163:
164: final int offset = arrayBytes[0]._offset;
165: boolean outerRes = true;
166:
167: // The following construct is worse than not ideal.
168: // For each constraint it completely reads the
169: // underlying structure again. The structure could b
170: // kept fairly easy. TODO: Optimize!
171:
172: Iterator4 i = a_candidates.iterateConstraints();
173: while (i.moveNext()) {
174:
175: QCon qcon = (QCon) i.current();
176: QField qf = qcon.getField();
177: if (qf == null
178: || qf.i_name
179: .equals(_yapField.getName())) {
180:
181: QCon tempParent = qcon.i_parent;
182: qcon.setParent(null);
183:
184: final QCandidates candidates = new QCandidates(
185: a_candidates.i_trans, null, qf);
186: candidates.addConstraint(qcon);
187:
188: qcon.setCandidates(candidates);
189:
190: if (arrayHandler instanceof FirstClassHandler) {
191: ((FirstClassHandler) arrayHandler)
192: .readCandidates(
193: _handlerVersion,
194: arrayBytes[0],
195: candidates);
196: }
197:
198: arrayBytes[0]._offset = offset;
199:
200: final boolean isNot = qcon.isNot();
201: if (isNot) {
202: qcon.removeNot();
203: }
204:
205: candidates.evaluate();
206:
207: final Tree.ByRef pending = new Tree.ByRef();
208: final boolean[] innerRes = { isNot };
209: candidates.traverse(new Visitor4() {
210: public void visit(Object obj) {
211:
212: QCandidate cand = (QCandidate) obj;
213:
214: if (cand.include()) {
215: innerRes[0] = !isNot;
216: }
217:
218: // Collect all pending subresults.
219:
220: if (cand._pendingJoins != null) {
221: cand._pendingJoins
222: .traverse(new Visitor4() {
223: public void visit(
224: Object a_object) {
225: QPending newPending = ((QPending) a_object)
226: .internalClonePayload();
227:
228: // We need to change
229: // the
230: // constraint here, so
231: // our
232: // pending collector
233: // uses
234: // the right
235: // comparator.
236: newPending
237: .changeConstraint();
238: QPending oldPending = (QPending) Tree
239: .find(
240: pending.value,
241: newPending);
242: if (oldPending != null) {
243:
244: // We only keep one
245: // pending result
246: // for
247: // all array
248: // elements.
249: // and memorize,
250: // whether we had a
251: // true or a false
252: // result.
253: // or both.
254:
255: if (oldPending._result != newPending._result) {
256: oldPending._result = QPending.BOTH;
257: }
258:
259: } else {
260: pending.value = Tree
261: .add(
262: pending.value,
263: newPending);
264: }
265: }
266: });
267: }
268: }
269: });
270:
271: if (isNot) {
272: qcon.not();
273: }
274:
275: // In case we had pending subresults, we
276: // need to communicate
277: // them up to our root.
278: if (pending.value != null) {
279: pending.value.traverse(new Visitor4() {
280: public void visit(Object a_object) {
281: getRoot().evaluate(
282: (QPending) a_object);
283: }
284: });
285: }
286:
287: if (!innerRes[0]) {
288:
289: if (Debug.queries) {
290: System.out
291: .println(" Array evaluation false. Constraint:"
292: + qcon.i_id);
293: }
294:
295: // Again this could be double triggering.
296: //
297: // We want to clean up the "No route"
298: // at some stage.
299:
300: qcon.visit(getRoot(), qcon.evaluator()
301: .not(false));
302:
303: outerRes = false;
304: }
305:
306: qcon.setParent(tempParent);
307:
308: }
309: }
310:
311: return outerRes;
312: }
313:
314: // We may get simple types here too, if the YapField was null
315: // in the higher level simple evaluation. Evaluate these
316: // immediately.
317:
318: if (Handlers4.handlesSimple(handler)) {
319: a_candidates.i_currentConstraint.visit(this );
320: return true;
321: }
322: }
323: }
324:
325: if (_yapField == null || _yapField instanceof NullFieldMetadata) {
326: return false;
327: }
328:
329: _yapClass.findOffset(_bytes, _yapField);
330: QCandidate candidate = readSubCandidate(a_candidates);
331: if (candidate == null) {
332: return false;
333: }
334:
335: // fast early check for YapClass
336: if (a_candidates.i_yapClass != null
337: && a_candidates.i_yapClass.isStrongTyped()) {
338: if (_yapField != null) {
339: TypeHandler4 handler = _yapField.getHandler();
340: if (handler instanceof ClassMetadata) {
341: ClassMetadata classMetadata = (ClassMetadata) handler;
342: if (classMetadata instanceof UntypedFieldHandler) {
343: classMetadata = candidate.readYapClass();
344: }
345: if (classMetadata == null) {
346: return false;
347: }
348: if (!Handlers4.handlerCanHold(classMetadata,
349: a_candidates.i_yapClass.classReflector())) {
350: return false;
351: }
352: }
353: }
354: }
355:
356: addDependant(a_candidates.addByIdentity(candidate));
357: return true;
358: }
359:
360: void doNotInclude() {
361: _include = false;
362: if (_dependants != null) {
363: Iterator4 i = new Iterator4Impl(_dependants);
364: _dependants = null;
365: while (i.moveNext()) {
366: ((QCandidate) i.current()).doNotInclude();
367: }
368: }
369: }
370:
371: public boolean duplicates() {
372: return _order.hasDuplicates();
373: }
374:
375: boolean evaluate(final QConObject a_constraint, final QE a_evaluator) {
376: if (a_evaluator.identity()) {
377: return a_evaluator.evaluate(a_constraint, this , null);
378: }
379: if (_member == null) {
380: _member = value();
381: }
382: return a_evaluator.evaluate(a_constraint, this , a_constraint
383: .translate(_member));
384: }
385:
386: boolean evaluate(QPending a_pending) {
387:
388: if (Debug.queries) {
389: System.out.println("Pending arrived Join: "
390: + a_pending._join.i_id + " Constraint:"
391: + a_pending._constraint.i_id + " res:"
392: + a_pending._result);
393: }
394:
395: QPending oldPending = (QPending) Tree.find(_pendingJoins,
396: a_pending);
397:
398: if (oldPending == null) {
399: a_pending.changeConstraint();
400: _pendingJoins = Tree.add(_pendingJoins, a_pending
401: .internalClonePayload());
402: return true;
403: }
404: _pendingJoins = _pendingJoins.removeNode(oldPending);
405: oldPending._join.evaluatePending(this , oldPending,
406: a_pending._result);
407: return false;
408: }
409:
410: ReflectClass classReflector() {
411: readYapClass();
412: if (_yapClass == null) {
413: return null;
414: }
415: return _yapClass.classReflector();
416: }
417:
418: boolean fieldIsAvailable() {
419: return classReflector() != null;
420: }
421:
422: // / ***<Candidate interface code>***
423:
424: public ObjectContainer objectContainer() {
425: return container();
426: }
427:
428: public Object getObject() {
429: Object obj = value(true);
430: if (obj instanceof Buffer) {
431: Buffer reader = (Buffer) obj;
432: int offset = reader._offset;
433: obj = readString(reader);
434: reader._offset = offset;
435: }
436: return obj;
437: }
438:
439: public String readString(Buffer buffer) {
440: return StringHandler
441: .readString(transaction().context(), buffer);
442: }
443:
444: QCandidate getRoot() {
445: return _root == null ? this : _root;
446: }
447:
448: final LocalObjectContainer container() {
449: return transaction().file();
450: }
451:
452: final LocalTransaction transaction() {
453: return _candidates.i_trans;
454: }
455:
456: public boolean hasDuplicates() {
457:
458: // Subcandidates are evaluated along with their constraints
459: // in one big QCandidates object. The tree can have duplicates
460: // so evaluation can be cascaded up to different roots.
461:
462: return _root != null;
463: }
464:
465: public void hintOrder(int a_order, boolean a_major) {
466: if (_order == this ) {
467: _order = new Order();
468: }
469: _order.hintOrder(a_order, a_major);
470: }
471:
472: public boolean include() {
473: return _include;
474: }
475:
476: /**
477: * For external interface use only. Call doNotInclude() internally so
478: * dependancies can be checked.
479: */
480: public void include(boolean flag) {
481: // TODO:
482: // Internal and external flag may need to be handled seperately.
483: _include = flag;
484: }
485:
486: public void onAttemptToAddDuplicate(Tree a_tree) {
487: _size = 0;
488: _root = (QCandidate) a_tree;
489: }
490:
491: private ReflectClass memberClass() {
492: return transaction().reflector().forObject(_member);
493: }
494:
495: Comparable4 prepareComparison(ObjectContainerBase a_stream,
496: Object a_constraint) {
497: if (_yapField != null) {
498: return _yapField.prepareComparison(a_constraint);
499: }
500: if (_yapClass == null) {
501:
502: ClassMetadata yc = null;
503: if (_bytes != null) {
504: yc = a_stream.produceClassMetadata(a_stream.reflector()
505: .forObject(a_constraint));
506: } else {
507: if (_member != null) {
508: yc = a_stream.classMetadataForReflectClass(a_stream
509: .reflector().forObject(_member));
510: }
511: }
512: if (yc != null) {
513: if (_member != null && _member.getClass().isArray()) {
514: TypeHandler4 ydt = (TypeHandler4) yc
515: .prepareComparison(a_constraint);
516: if (a_stream.reflector().array().isNDimensional(
517: memberClass())) {
518: MultidimensionalArrayHandler yan = new MultidimensionalArrayHandler(
519: a_stream, ydt, false);
520: return yan;
521: }
522: ArrayHandler ya = new ArrayHandler(a_stream, ydt,
523: false);
524: return ya;
525:
526: }
527: return yc.prepareComparison(a_constraint);
528:
529: }
530: return null;
531: }
532: return _yapClass.prepareComparison(a_constraint);
533: }
534:
535: private void read() {
536: if (_include) {
537: if (_bytes == null) {
538: if (_key > 0) {
539: if (DTrace.enabled) {
540: DTrace.CANDIDATE_READ.log(_key);
541: }
542: setBytes(container().readReaderByID(transaction(),
543: _key));
544: if (_bytes == null) {
545: _include = false;
546: }
547: } else {
548: _include = false;
549: }
550: }
551: }
552: }
553:
554: private int currentOffSet() {
555: return _bytes._offset;
556: }
557:
558: private QCandidate readSubCandidate(QCandidates candidateCollection) {
559: read();
560: if (_bytes == null || _yapField == null) {
561: return null;
562: }
563: final int offset = currentOffSet();
564: QueryingReadContext context = newQueryingReadContext();
565: TypeHandler4 handler = context.correctHandlerVersion(_yapField
566: .getHandler());
567: QCandidate subCandidate = candidateCollection.readSubCandidate(
568: context, handler);
569: seek(offset);
570: if (subCandidate != null) {
571: subCandidate._root = getRoot();
572: return subCandidate;
573: }
574: return null;
575: }
576:
577: private void seek(int offset) {
578: _bytes._offset = offset;
579: }
580:
581: private QueryingReadContext newQueryingReadContext() {
582: return new QueryingReadContext(transaction(), _handlerVersion,
583: _bytes);
584: }
585:
586: private void readThis(boolean a_activate) {
587: read();
588:
589: Transaction trans = transaction();
590: if (trans != null) {
591:
592: _member = trans.container().getByID(trans, _key);
593:
594: if (_member != null
595: && (a_activate || _member instanceof Compare)) {
596: trans.container().activateDefaultDepth(trans, _member);
597: checkInstanceOfCompare();
598: }
599: }
600: }
601:
602: ClassMetadata readYapClass() {
603: if (_yapClass == null) {
604: read();
605: if (_bytes != null) {
606: seek(0);
607: ObjectContainerBase stream = container();
608: ObjectHeader objectHeader = new ObjectHeader(stream,
609: _bytes);
610: _yapClass = objectHeader.classMetadata();
611:
612: if (_yapClass != null) {
613: if (stream._handlers.ICLASS_COMPARE
614: .isAssignableFrom(_yapClass
615: .classReflector())) {
616: readThis(false);
617: }
618: }
619: }
620: }
621: return _yapClass;
622: }
623:
624: public String toString() {
625: if (!Debug4.prettyToStrings) {
626: return super .toString();
627: }
628: String str = "QCandidate ";
629: if (_yapClass != null) {
630: str += "\n YapClass " + _yapClass.getName();
631: }
632: if (_yapField != null) {
633: str += "\n YapField " + _yapField.getName();
634: }
635: if (_member != null) {
636: str += "\n Member " + _member.toString();
637: }
638: if (_root != null) {
639: str += "\n rooted by:\n";
640: str += _root.toString();
641: } else {
642: str += "\n ROOT";
643: }
644: return str;
645: }
646:
647: void useField(QField a_field) {
648: read();
649: if (_bytes == null) {
650: _yapField = null;
651: return;
652: }
653: readYapClass();
654: _member = null;
655: if (a_field == null) {
656: _yapField = null;
657: return;
658: }
659: if (_yapClass == null) {
660: _yapField = null;
661: return;
662: }
663: _yapField = a_field.getYapField(_yapClass);
664: if (_yapField == null) {
665: fieldNotFound();
666: return;
667: }
668:
669: HandlerVersion handlerVersion = _yapClass.findOffset(_bytes,
670: _yapField);
671:
672: if (handlerVersion == HandlerVersion.INVALID) {
673: fieldNotFound();
674: return;
675: }
676:
677: _handlerVersion = handlerVersion._number;
678: }
679:
680: private void fieldNotFound() {
681: if (_yapClass.holdsAnyClass()) {
682: // retry finding the field on reading the value
683: _yapField = null;
684: } else {
685: // we can't get a value for the field, comparisons should definitely run against null
686: _yapField = new NullFieldMetadata();
687: }
688: _handlerVersion = MarshallingContext.HANDLER_VERSION;
689: }
690:
691: Object value() {
692: return value(false);
693: }
694:
695: // TODO: This is only used for Evaluations. Handling may need
696: // to be different for collections also.
697: Object value(boolean a_activate) {
698: if (_member == null) {
699: if (_yapField == null) {
700: readThis(a_activate);
701: } else {
702: int offset = currentOffSet();
703: _member = _yapField.read(newQueryingReadContext());
704: seek(offset);
705: checkInstanceOfCompare();
706: }
707: }
708: return _member;
709: }
710:
711: void setBytes(Buffer bytes) {
712: _bytes = bytes;
713: }
714:
715: private MarshallerFamily marshallerFamily() {
716: return MarshallerFamily.version(_handlerVersion);
717: }
718:
719: }
|