/* This file is part of the db4o object database http://www.db4o.com Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com db4o is free software; you can redistribute it and/or modify it under the terms of version 3 of the GNU General Public License as published by the Free Software Foundation. db4o is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. */ package com.db4o.internal.query.processor; import com.db4o.*; import com.db4o.foundation.*; import com.db4o.internal.*; import com.db4o.query.*; import com.db4o.reflect.*; import com.db4o.types.*; /** * Base class for all constraints on queries. * * @exclude */ public abstract class QCon implements Constraint, Visitor4, Unversioned { //Used for query debug only. static final IDGenerator idGenerator = new IDGenerator(); // our candidate object tree transient QCandidates i_candidates; // collection of QCandidates to collect children elements and to // execute children. For convenience we hold them in the constraint, // so we can do collection and execution in two steps @decaf.Public private Collection4 i_childrenCandidates; // all subconstraints @decaf.Public protected List4 _children; // for evaluation @decaf.Public protected QE i_evaluator = QE.DEFAULT; // ID handling for fast find of QConstraint objects in // pending OR evaluations @decaf.Public private int i_id; // ANDs and ORs on this constraint @decaf.Public Collection4 i_joins; // the parent of this constraint or null, if this is a root @decaf.Public protected QCon i_parent; // our transaction to get a stream object anywhere transient Transaction i_trans; // whether or not this constraint was used to get the initial set // in the FieldIndexProcessor private transient boolean _processedByIndex; public QCon() { // C/S only } QCon(Transaction a_trans) { i_id = idGenerator.next(); i_trans = a_trans; } QCon addConstraint(QCon a_child) { _children = new List4(_children, a_child); return a_child; } public ObjectContainerBase container(){ return transaction().container(); } public Transaction transaction() { return i_trans; } void addJoin(QConJoin a_join) { if (i_joins == null) { i_joins = new Collection4(); } i_joins.add(a_join); } QCon addSharedConstraint(QField a_field, Object a_object) { QConObject newConstraint = new QConObject(i_trans, this, a_field, a_object); addConstraint(newConstraint); return newConstraint; } public Constraint and(Constraint andWith) { synchronized (streamLock()) { return join(andWith, true); } } boolean attach(final QQuery query, final String a_field) { final QCon qcon = this; ClassMetadata yc = getYapClass(); final boolean[] foundField = { false }; forEachChildField(a_field, new Visitor4() { public void visit(Object obj) { foundField[0] = true; query.addConstraint((QCon) obj); } }); if (foundField[0]) { return true; } QField qf = null; if (yc == null || yc.holdsAnyClass()) { final int[] count = { 0 }; final FieldMetadata[] yfs = { null }; i_trans.container().classCollection().attachQueryNode(a_field, new Visitor4() { public void visit(Object obj) { yfs[0] = (FieldMetadata) ((Object[]) obj)[1]; count[0]++; } }); if (count[0] == 0) { return false; } if (count[0] == 1) { qf = yfs[0].qField(i_trans); } else { qf = new QField(i_trans, a_field, null, 0, 0); } } else { if(yc.isTranslated()) { i_trans.container()._handlers.diagnosticProcessor().descendIntoTranslator(yc, a_field); } FieldMetadata yf = yc.fieldMetadataForName(a_field); if (yf != null) { qf = yf.qField(i_trans); } if (qf == null) { qf = new QField(i_trans, a_field, null, 0, 0); } } QConPath qcp = new QConPath(i_trans, qcon, qf); query.addConstraint(qcp); qcon.addConstraint(qcp); return true; } public boolean canBeIndexLeaf(){ return false; } public boolean canLoadByIndex(){ // virtual return false; } void checkLastJoinRemoved() { if (i_joins.size() == 0) { i_joins = null; } } /** @param candidates */ void collect(QCandidates candidates) { // virtual } public Constraint contains() { throw notSupported(); } void createCandidates(Collection4 a_candidateCollection) { Iterator4 j = a_candidateCollection.iterator(); while (j.moveNext()) { QCandidates candidates = (QCandidates) j.current(); if (candidates.tryAddConstraint(this)) { i_candidates = candidates; return; } } i_candidates = new QCandidates((LocalTransaction) i_trans, getYapClass(), getField(), false); i_candidates.addConstraint(this); a_candidateCollection.add(i_candidates); } void doNotInclude(InternalCandidate root) { if(DTrace.enabled){ DTrace.DONOTINCLUDE.log(id()); } if (Debug4.queries) { System.out.println("QCon.doNotInclude " + id() + " " + root.id()); } if (i_parent != null) { i_parent.visit1(root, this, false); } else { root.doNotInclude(); } } public Constraint equal() { throw notSupported(); } /** @param candidate */ boolean evaluate(InternalCandidate candidate) { throw Exceptions4.virtualException(); } void evaluateChildren() { Iterator4 i = i_childrenCandidates.iterator(); while (i.moveNext()) { ((QCandidates) i.current()).evaluate(); } } void evaluateCollectChildren() { if(DTrace.enabled){ DTrace.COLLECT_CHILDREN.log(id()); } Iterator4 i = i_childrenCandidates.iterator(); while (i.moveNext()) { ((QCandidates) i.current()).collect(i_candidates); } } void evaluateCreateChildrenCandidates() { i_childrenCandidates = new Collection4(); Iterator4 i = iterateChildren(); while(i.moveNext()){ QCon constraint = (QCon) i.current(); if(! constraint.resolvedByIndex()){ constraint.createCandidates(i_childrenCandidates); } } } private boolean resolvedByIndex() { if(! canResolveByFieldIndex()){ return false; } boolean result = false; Iterator4 it = iterateChildren(); while(it.moveNext()){ QCon childConstraint = (QCon) it.current(); if(! childConstraint.processedByIndex()){ return false; } else { result = true; } } return result; } protected abstract boolean canResolveByFieldIndex(); void evaluateEvaluations() { Iterator4 i = iterateChildren(); while(i.moveNext()){ ((QCon)i.current()).evaluateEvaluationsExec(i_candidates, true); } } /** * @param candidates * @param rereadObject */ void evaluateEvaluationsExec(QCandidates candidates, boolean rereadObject) { // virtual } void evaluateSelf() { i_candidates.filter(this); } void evaluateSimpleChildren() { // TODO: sort the constraints for YapFields first, // so we stay with the same YapField if(_children == null) { return; } Iterator4 i = iterateChildren(); while(i.moveNext()){ QCon qcon = (QCon)i.current(); if(! qcon.processedByIndex()){ i_candidates.setCurrentConstraint(qcon); qcon.setCandidates(i_candidates); qcon.evaluateSimpleExec(i_candidates); } } i_candidates.setCurrentConstraint(null); } /** @param candidates */ void evaluateSimpleExec(QCandidates candidates) { // virtual } void exchangeConstraint(QCon a_exchange, QCon a_with) { List4 previous = null; List4 current = _children; while (current != null) { if (current._element == a_exchange) { if (previous == null) { _children = current._next; } else { previous._next = current._next; } } previous = current; current = current._next; } _children = new List4(_children, a_with); } void forEachChildField(final String name, final Visitor4 visitor) { Iterator4 i = iterateChildren(); while(i.moveNext()){ Object obj = i.current(); if (obj instanceof QConObject) { if (((QConObject) obj).getField().name().equals(name)) { visitor.visit(obj); } } } } public QField getField() { return null; } public Object getObject() { throw notSupported(); } QCon getRoot() { if (i_parent != null) { return i_parent.getRoot(); } return this; } QCon produceTopLevelJoin() { if(! hasJoins()){ return this; } Iterator4 i = iterateJoins(); if (i_joins.size() == 1) { i.moveNext(); return ((QCon) i.current()).produceTopLevelJoin(); } Collection4 col = new Collection4(); while (i.moveNext()) { col.ensure(((QCon) i.current()).produceTopLevelJoin()); } i = col.iterator(); i.moveNext(); QCon qcon = (QCon) i.current(); if (col.size() == 1) { return qcon; } while (i.moveNext()) { qcon = (QCon) qcon.and((Constraint) i.current()); } return qcon; } ClassMetadata getYapClass() { return null; } public Constraint greater() { throw notSupported(); } public boolean hasChildren(){ return _children != null; } public boolean hasParent() { return i_parent != null; } public QCon parent() { return i_parent; } public boolean hasJoins(){ if(i_joins == null){ return false; } return i_joins.size() > 0; } public boolean hasObjectInParentPath(Object obj) { if (i_parent != null) { return i_parent.hasObjectInParentPath(obj); } return false; } public Constraint identity() { throw notSupported(); } public Constraint byExample() { throw notSupported(); } public int identityID() { return 0; } boolean isNot() { return i_evaluator instanceof QENot; } boolean isNullConstraint() { return false; } public Iterator4 iterateJoins(){ if(i_joins == null){ return Iterators.EMPTY_ITERATOR; } return i_joins.iterator(); } public Iterator4 iterateChildren(){ if(_children == null){ return Iterators.EMPTY_ITERATOR; } return new Iterator4Impl(_children); } Constraint join(Constraint a_with, boolean a_and) { if (!(a_with instanceof QCon) /*|| a_with == this*/ ) { // TODO: one of our STOr test cases somehow carries // the same constraint twice. This may be a result // of a funny AND. Check! return null; } if (a_with == this) { return this; } return join1((QCon) a_with, a_and); } Constraint join1(QCon a_with, boolean a_and) { if (a_with instanceof QConstraints) { int j = 0; Collection4 joinHooks = new Collection4(); Constraint[] constraints = ((QConstraints) a_with).toArray(); for (j = 0; j < constraints.length; j++) { joinHooks.ensure(((QCon) constraints[j]).joinHook()); } Constraint[] joins = new Constraint[joinHooks.size()]; j = 0; Iterator4 i = joinHooks.iterator(); while (i.moveNext()) { joins[j++] = join((Constraint) i.current(), a_and); } return new QConstraints(i_trans, joins); } QCon myHook = joinHook(); QCon otherHook = a_with.joinHook(); if (myHook == otherHook) { // You might like to check out, what happens, if you // remove this line. It seems to open a bug in an // StOr testcase. return myHook; } QConJoin cj = new QConJoin(i_trans, myHook, otherHook, a_and); myHook.addJoin(cj); otherHook.addJoin(cj); return cj; } QCon joinHook() { return produceTopLevelJoin(); } public Constraint like() { throw notSupported(); } public Constraint startsWith(boolean caseSensitive) { throw notSupported(); } public Constraint endsWith(boolean caseSensitive) { throw notSupported(); } void log(String indent) { if (Debug4.queries) { final String childIndent = " " + indent; String name = getClass().getName(); int pos = name.lastIndexOf(".") + 1; name = name.substring(pos); System.out.println(indent + name + " " + logObject() + " " + id()); // System.out.println(indent + "JOINS"); if (hasJoins()) { Iterator4 i = iterateJoins(); while (i.moveNext()) { QCon join = (QCon) i.current(); // joins += join.i_id + " "; join.log(childIndent); } } // System.out.println(joins); // System.out.println(indent + getClass().getName() + " " + i_id + " " + i_debugField + " " + joins ); // System.out.println(indent + "CONSTRAINTS"); if(_children != null){ Iterator4 i = new Iterator4Impl(_children); while(i.moveNext()){ ((QCon)i.current()).log(childIndent); } } } } String logObject() { return ""; } void marshall() { Iterator4 i = iterateChildren(); while(i.moveNext()){ ((QCon)i.current()).marshall(); } } public Constraint not() { synchronized (streamLock()) { if (!(i_evaluator instanceof QENot)) { i_evaluator = new QENot(i_evaluator); } return this; } } private RuntimeException notSupported() { return new RuntimeException("Not supported."); } /** @param other */ public boolean onSameFieldAs(QCon other){ return false; } public Constraint or(Constraint orWith) { synchronized (streamLock()) { return join(orWith, false); } } void removeNot() { if (isNot()) { i_evaluator = ((QENot) i_evaluator).evaluator(); } } public void setCandidates(QCandidates a_candidates) { i_candidates = a_candidates; } void setParent(QCon a_newParent) { i_parent = a_newParent; } /** * @param obj * @param removeExisting */ QCon shareParent(Object obj, BooleanByRef removeExisting) { // virtual return null; } /** * @param claxx * @param removeExisting */ QConClass shareParentForClass(ReflectClass claxx, BooleanByRef removeExisting) { // virtual return null; } public Constraint smaller() { throw notSupported(); } protected Object streamLock() { return i_trans.container().lock(); } void unmarshall(final Transaction a_trans) { if (i_trans != null) { return; } i_trans = a_trans; unmarshallParent(a_trans); unmarshallJoins(a_trans); unmarshallChildren(a_trans); } private void unmarshallParent(final Transaction a_trans) { if (i_parent != null) { i_parent.unmarshall(a_trans); } } private void unmarshallChildren(final Transaction a_trans) { Iterator4 i = iterateChildren(); while(i.moveNext()){ ((QCon)i.current()).unmarshall(a_trans); } } private void unmarshallJoins(final Transaction a_trans) { if (hasJoins()) { Iterator4 i = iterateJoins(); while (i.moveNext()) { ((QCon) i.current()).unmarshall(a_trans); } } } public void visit(Object obj) { InternalCandidate candidate = (InternalCandidate) obj; visit1(candidate.getRoot(), this, evaluate(candidate)); } void visit(InternalCandidate root, boolean res) { visit1(root, this, i_evaluator.not(res)); } void visit1(InternalCandidate root, QCon reason, boolean res) { // The a_reason parameter makes it eays to distinguish // between calls from above (a_reason == this) and below. if (hasJoins()) { // this should probably be on the Join Iterator4 i = iterateJoins(); while (i.moveNext()) { root.evaluate(new QPending((QConJoin) i.current(), this, res)); } } else { if (!res) { doNotInclude(root); } } } final void visitOnNull(final InternalCandidate a_root) { // TODO: It may be more efficient to rule out // all possible keepOnNull issues when starting // evaluation. if (Debug4.queries) { System.out.println("QCon.visitOnNull " + id()); } Iterator4 i = iterateChildren(); while(i.moveNext()){ ((QCon)i.current()).visitOnNull(a_root); } if (visitSelfOnNull()) { visit(a_root, isNullConstraint()); } } boolean visitSelfOnNull() { return true; } public QE evaluator() { return i_evaluator; } public void setProcessedByIndex(QCandidates candidates) { internalSetProcessedByIndex(candidates); } protected void internalSetProcessedByIndex(QCandidates candidates) { _processedByIndex = true; if(i_joins != null){ Iterator4 i = i_joins.iterator(); while(i.moveNext()){ ((QConJoin)i.current()).setProcessedByIndex(candidates); } } } public boolean processedByIndex(){ return _processedByIndex; } public int childrenCount(){ return List4.size(_children); } public int id() { return i_id; } }