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.query.*;
028: import com.db4o.reflect.*;
029:
030: /**
031: * Object constraint on queries
032: *
033: * @exclude
034: */
035: public class QConObject extends QCon {
036:
037: // the constraining object
038: public Object i_object;
039:
040: // cache for the db4o object ID
041: public int i_objectID;
042:
043: // the YapClass
044: transient ClassMetadata i_yapClass;
045:
046: // needed for marshalling the request
047: public int i_yapClassID;
048:
049: public QField i_field;
050:
051: transient Comparable4 i_comparator;
052:
053: public ObjectAttribute i_attributeProvider;
054:
055: private transient boolean i_selfComparison = false;
056:
057: public QConObject() {
058: // C/S only
059: }
060:
061: public QConObject(Transaction a_trans, QCon a_parent,
062: QField a_field, Object a_object) {
063: super (a_trans);
064: i_parent = a_parent;
065: if (a_object instanceof Compare) {
066: a_object = ((Compare) a_object).compare();
067: }
068: i_object = a_object;
069: i_field = a_field;
070: associateYapClass(a_trans, a_object);
071: }
072:
073: private void associateYapClass(Transaction a_trans, Object a_object) {
074: if (a_object == null) {
075: i_object = null;
076: i_comparator = Null.INSTANCE;
077: i_yapClass = null;
078:
079: // FIXME: Setting the YapClass to null will prevent index use
080: // If the field is typed we can guess the right one with the
081: // following line. However this does break some SODA test cases.
082: // Revisit!
083:
084: // if(i_field != null){
085: // i_yapClass = i_field.getYapClass();
086: // }
087:
088: } else {
089: i_yapClass = a_trans.container().produceClassMetadata(
090: a_trans.reflector().forObject(a_object));
091: if (i_yapClass != null) {
092: i_object = i_yapClass.getComparableObject(a_object);
093: if (a_object != i_object) {
094: i_attributeProvider = i_yapClass.config()
095: .queryAttributeProvider();
096: i_yapClass = a_trans.container()
097: .produceClassMetadata(
098: a_trans.reflector().forObject(
099: i_object));
100: }
101: if (i_yapClass != null) {
102: i_yapClass.collectConstraints(a_trans, this ,
103: i_object, new Visitor4() {
104:
105: public void visit(Object obj) {
106: addConstraint((QCon) obj);
107: }
108: });
109: } else {
110: associateYapClass(a_trans, null);
111: }
112: } else {
113: associateYapClass(a_trans, null);
114: }
115: }
116: }
117:
118: public boolean canBeIndexLeaf() {
119: return (i_yapClass != null && i_yapClass.isPrimitive())
120: || evaluator().identity();
121: }
122:
123: public boolean canLoadByIndex() {
124: if (i_field == null) {
125: return false;
126: }
127: if (i_field.i_yapField == null) {
128: return false;
129: }
130: if (!i_field.i_yapField.hasIndex()) {
131: return false;
132: }
133: if (!i_evaluator.supportsIndex()) {
134: return false;
135: }
136:
137: return i_field.i_yapField.canLoadByIndex();
138: }
139:
140: boolean evaluate(QCandidate a_candidate) {
141: try {
142: return a_candidate.evaluate(this , i_evaluator);
143: } catch (Exception e) {
144: return false;
145: }
146: }
147:
148: void evaluateEvaluationsExec(final QCandidates a_candidates,
149: boolean rereadObject) {
150: if (i_field.isSimple()) {
151: boolean hasEvaluation = false;
152: Iterator4 i = iterateChildren();
153: while (i.moveNext()) {
154: if (i.current() instanceof QConEvaluation) {
155: hasEvaluation = true;
156: break;
157: }
158: }
159: if (hasEvaluation) {
160: a_candidates.traverse(i_field);
161: Iterator4 j = iterateChildren();
162: while (j.moveNext()) {
163: ((QCon) j.current()).evaluateEvaluationsExec(
164: a_candidates, false);
165: }
166: }
167: }
168: }
169:
170: void evaluateSelf() {
171: if (DTrace.enabled) {
172: DTrace.EVALUATE_SELF.log(i_id);
173: }
174: if (i_yapClass != null) {
175: if (!(i_yapClass instanceof PrimitiveFieldHandler)) {
176: if (!i_evaluator.identity()) {
177: // TODO: consider another strategy to avoid reevaluating the class constraint when
178: // the candidate collection is loaded from the class index
179: // if (i_yapClass == i_candidates.i_yapClass) {
180: // if (i_evaluator.isDefault() && (! hasJoins())) {
181: // return;
182: // }
183: // }
184: i_selfComparison = true;
185: }
186: Object transactionalObject = i_yapClass
187: .wrapWithTransactionContext(transaction(),
188: i_object);
189: i_comparator = i_yapClass
190: .prepareComparison(transactionalObject);
191: }
192: }
193: super .evaluateSelf();
194: i_selfComparison = false;
195: }
196:
197: void collect(QCandidates a_candidates) {
198: if (i_field.isClass()) {
199: a_candidates.traverse(i_field);
200: a_candidates.filter(i_candidates);
201: }
202: }
203:
204: void evaluateSimpleExec(QCandidates a_candidates) {
205:
206: // TODO: The following can be skipped if we used the index on
207: // this field to load the objects, if hasOrdering() is false
208:
209: if (i_field.isSimple() || isNullConstraint()) {
210: a_candidates.traverse(i_field);
211: prepareComparison(i_field);
212: a_candidates.filter(this );
213: }
214: }
215:
216: Comparable4 getComparator(QCandidate a_candidate) {
217: if (i_comparator == null) {
218: return a_candidate.prepareComparison(i_trans.container(),
219: i_object);
220: }
221: return i_comparator;
222: }
223:
224: ClassMetadata getYapClass() {
225: return i_yapClass;
226: }
227:
228: public QField getField() {
229: return i_field;
230: }
231:
232: int getObjectID() {
233: if (i_objectID == 0) {
234: i_objectID = i_trans.container().getID(i_trans, i_object);
235: if (i_objectID == 0) {
236: i_objectID = -1;
237: }
238: }
239: return i_objectID;
240: }
241:
242: public boolean hasObjectInParentPath(Object obj) {
243: if (obj == i_object) {
244: return true;
245: }
246: return super .hasObjectInParentPath(obj);
247: }
248:
249: public int identityID() {
250: if (i_evaluator.identity()) {
251: int id = getObjectID();
252: if (id != 0) {
253: if (!(i_evaluator instanceof QENot)) {
254: return id;
255: }
256: }
257: }
258: return 0;
259: }
260:
261: boolean isNullConstraint() {
262: return i_object == null;
263: }
264:
265: void log(String indent) {
266: if (Debug.queries) {
267: super .log(indent);
268: }
269: }
270:
271: String logObject() {
272: if (Debug.queries) {
273: if (i_object != null) {
274: return i_object.toString();
275: }
276: return "[NULL]";
277: }
278: return "";
279: }
280:
281: void marshall() {
282: super .marshall();
283: getObjectID();
284: if (i_yapClass != null) {
285: i_yapClassID = i_yapClass.getID();
286: }
287: }
288:
289: public boolean onSameFieldAs(QCon other) {
290: if (!(other instanceof QConObject)) {
291: return false;
292: }
293: return i_field == ((QConObject) other).i_field;
294: }
295:
296: void prepareComparison(QField a_field) {
297: if (isNullConstraint() & !a_field.isArray()) {
298: i_comparator = Null.INSTANCE;
299: } else {
300: i_comparator = a_field.prepareComparison(i_object);
301: }
302: }
303:
304: void removeChildrenJoins() {
305: super .removeChildrenJoins();
306: _children = null;
307: }
308:
309: QCon shareParent(Object a_object, boolean[] removeExisting) {
310: if (i_parent == null) {
311: return null;
312: }
313: Object obj = i_field.coerce(a_object);
314: if (obj == No4.INSTANCE) {
315: return null;
316: }
317: return i_parent.addSharedConstraint(i_field, obj);
318: }
319:
320: QConClass shareParentForClass(ReflectClass a_class,
321: boolean[] removeExisting) {
322: if (i_parent == null) {
323: return null;
324: }
325: if (!i_field.canHold(a_class)) {
326: return null;
327: }
328: QConClass newConstraint = new QConClass(i_trans, i_parent,
329: i_field, a_class);
330: i_parent.addConstraint(newConstraint);
331: return newConstraint;
332: }
333:
334: final Object translate(Object candidate) {
335: if (i_attributeProvider != null) {
336: i_candidates.i_trans.container().activateDefaultDepth(
337: i_candidates.i_trans, candidate);
338: return i_attributeProvider.attribute(candidate);
339: }
340: return candidate;
341: }
342:
343: void unmarshall(Transaction trans) {
344: if (i_trans == null) {
345: super .unmarshall(trans);
346:
347: if (i_object == null) {
348: i_comparator = Null.INSTANCE;
349: }
350: if (i_yapClassID != 0) {
351: i_yapClass = trans.container().classMetadataForId(
352: i_yapClassID);
353: }
354: if (i_field != null) {
355: i_field.unmarshall(trans);
356: }
357:
358: if (i_objectID > 0) {
359: Object obj = trans.container().getByID(trans,
360: i_objectID);
361: if (obj != null) {
362: i_object = obj;
363: }
364: }
365: }
366: }
367:
368: public void visit(Object obj) {
369: QCandidate qc = (QCandidate) obj;
370: boolean res = true;
371: boolean processed = false;
372: if (i_selfComparison) {
373: ClassMetadata yc = qc.readYapClass();
374: if (yc != null) {
375: res = i_evaluator
376: .not(i_yapClass.getHigherHierarchy(yc) == i_yapClass);
377: processed = true;
378: }
379: }
380: if (!processed) {
381: res = evaluate(qc);
382: }
383: if (hasOrdering() && res && qc.fieldIsAvailable()) {
384: Object cmp = qc.value();
385: if (cmp != null && i_field != null) {
386: Comparable4 comparatorBackup = i_comparator;
387: i_comparator = i_field.prepareComparison(qc.value());
388: i_candidates.addOrder(new QOrder(this , qc));
389: i_comparator = comparatorBackup
390: .prepareComparison(i_object);
391: }
392: }
393: visit1(qc.getRoot(), this , res);
394: }
395:
396: public Constraint contains() {
397: synchronized (streamLock()) {
398: i_evaluator = i_evaluator.add(new QEContains(true));
399: return this ;
400: }
401: }
402:
403: public Constraint equal() {
404: synchronized (streamLock()) {
405: i_evaluator = i_evaluator.add(new QEEqual());
406: return this ;
407: }
408: }
409:
410: public Object getObject() {
411: synchronized (streamLock()) {
412: return i_object;
413: }
414: }
415:
416: public Constraint greater() {
417: synchronized (streamLock()) {
418: i_evaluator = i_evaluator.add(new QEGreater());
419: return this ;
420: }
421: }
422:
423: public Constraint identity() {
424: synchronized (streamLock()) {
425:
426: if (i_object == null) {
427: return this ;
428: }
429:
430: int id = getObjectID();
431: if (id <= 0) {
432: i_objectID = 0;
433: Exceptions4.throwRuntimeException(51);
434: }
435:
436: // TODO: this may not be correct for NOT
437: // It may be necessary to add an if(i_evaluator.identity())
438: removeChildrenJoins();
439: i_evaluator = i_evaluator.add(new QEIdentity());
440: return this ;
441: }
442: }
443:
444: public Constraint like() {
445: synchronized (streamLock()) {
446: i_evaluator = i_evaluator.add(new QEContains(false));
447: return this ;
448: }
449: }
450:
451: public Constraint smaller() {
452: synchronized (streamLock()) {
453: i_evaluator = i_evaluator.add(new QESmaller());
454: return this ;
455: }
456: }
457:
458: public Constraint startsWith(boolean caseSensitive) {
459: synchronized (streamLock()) {
460: i_evaluator = i_evaluator.add(new QEStartsWith(
461: caseSensitive));
462: return this ;
463: }
464: }
465:
466: public Constraint endsWith(boolean caseSensitive) {
467: synchronized (streamLock()) {
468: i_evaluator = i_evaluator
469: .add(new QEEndsWith(caseSensitive));
470: return this ;
471: }
472: }
473:
474: public String toString() {
475: if (!Debug4.prettyToStrings) {
476: return super .toString();
477: }
478: String str = "QConObject ";
479: if (i_object != null) {
480: str += i_object.toString();
481: }
482: return str;
483: }
484: }
|