/* 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.internal.classindex.*;
import com.db4o.internal.diagnostic.*;
import com.db4o.internal.fieldindex.*;
import com.db4o.internal.handlers.*;
import com.db4o.internal.marshall.*;
import com.db4o.typehandlers.*;
/**
* @exclude
*/
public final class QCandidates implements /*Visitor4, */ FieldFilterable {
// Transaction necessary as reference to stream
public final LocalTransaction i_trans;
public QueryResultCandidates _result;
// collection of all constraints
private List4 _constraints;
// possible class information
ClassMetadata _classMetadata;
// possible field information
private QField _field;
// current executing constraint, only set where needed
QCon _currentConstraint;
private IDGenerator _idGenerator;
private boolean _loadedFromClassIndex;
private boolean _loadedFromClassFieldIndex;
private boolean _isTopLevel;
QCandidates(LocalTransaction a_trans, ClassMetadata a_classMetadata, QField a_field, boolean isTopLevel) {
_result = new QueryResultCandidates(this);
_isTopLevel = isTopLevel;
i_trans = a_trans;
_classMetadata = a_classMetadata;
_field = a_field;
if (a_field == null
|| a_field._fieldMetadata == null
|| !(a_field._fieldMetadata.getHandler() instanceof StandardReferenceTypeHandler)
) {
return;
}
ClassMetadata yc = ((StandardReferenceTypeHandler) a_field._fieldMetadata.getHandler()).classMetadata();
if (_classMetadata == null) {
_classMetadata = yc;
} else {
yc = _classMetadata.getHigherOrCommonHierarchy(yc);
if (yc != null) {
_classMetadata = yc;
}
}
}
public boolean isTopLevel(){
return _isTopLevel;
}
public InternalCandidate add(InternalCandidate candidate) {
if(Debug4.queries){
String msg = "Candidate added ID: " + candidate.id();
InternalCandidate root = candidate.getRoot();
if(root != null){
msg += " root: " + root.id();
}
System.out.println(msg);
}
_result.add(candidate);
if(((QCandidateBase)candidate)._size == 0){
// This means that the candidate was already present
// and QCandidate does not allow duplicates.
// In this case QCandidate#isDuplicateOf will have
// placed the existing QCandidate in the i_root
// variable of the new candidate. We return it here:
return candidate.getRoot();
}
return candidate;
}
void addConstraint(QCon a_constraint) {
_constraints = new List4(_constraints, a_constraint);
}
public InternalCandidate readSubCandidate(QueryingReadContext context, TypeHandler4 handler){
ObjectID objectID = ObjectID.NOT_POSSIBLE;
try {
int offset = context.offset();
if(handler instanceof ReadsObjectIds){
objectID = ((ReadsObjectIds)handler).readObjectID(context);
}
if(objectID.isValid()){
return new QCandidate(this, null, objectID._id);
}
if(objectID == ObjectID.NOT_POSSIBLE){
context.seek(offset);
Object obj = context.read(handler);
if(obj != null){
int id = context.container().getID(context.transaction(), obj);
if(id == 0) {
return new QPrimitiveCandidate(this, obj);
}
QCandidate candidate = new QCandidate(this, obj, id);
candidate.classMetadata(context.container().classMetadataForObject(obj));
return candidate;
}
}
} catch (Exception e) {
// FIXME: Catchall
}
return null;
}
void collect(final QCandidates a_candidates) {
Iterator4 i = iterateConstraints();
while(i.moveNext()){
QCon qCon = (QCon)i.current();
setCurrentConstraint(qCon);
qCon.collect(a_candidates);
}
setCurrentConstraint(null);
}
void execute() {
if(DTrace.enabled){
DTrace.QUERY_PROCESS.log();
}
final FieldIndexProcessorResult result = processFieldIndexes();
if(result.foundIndex()){
_result.fieldIndexProcessorResult(result);
}else{
loadFromClassIndex();
}
evaluate();
}
public Iterator4 executeSnapshot(Collection4 executionPath){
IntIterator4 indexIterator = new IntIterator4Adaptor(iterateIndex(processFieldIndexes()));
Tree idRoot = TreeInt.addAll(null, indexIterator);
Iterator4 snapshotIterator = new TreeKeyIterator(idRoot);
Iterator4 singleObjectQueryIterator = singleObjectSodaProcessor(snapshotIterator);
return mapIdsToExecutionPath(singleObjectQueryIterator, executionPath);
}
private Iterator4 singleObjectSodaProcessor(Iterator4 indexIterator){
return Iterators.map(indexIterator, new Function4() {
public Object apply(Object current) {
int id = ((Integer)current).intValue();
QCandidateBase candidate = new QCandidate(QCandidates.this, null, id);
_result.singleCandidate(candidate);
evaluate();
if(! candidate.include()){
return Iterators.SKIP;
}
return current;
}
});
}
public Iterator4 executeLazy(Collection4 executionPath){
Iterator4 indexIterator = iterateIndex(processFieldIndexes());
Iterator4 singleObjectQueryIterator = singleObjectSodaProcessor(indexIterator);
return mapIdsToExecutionPath(singleObjectQueryIterator, executionPath);
}
private Iterator4 iterateIndex (FieldIndexProcessorResult result ){
if(result.noMatch()){
return Iterators.EMPTY_ITERATOR;
}
if(result.foundIndex()){
return result.iterateIDs();
}
if(!_classMetadata.hasClassIndex()) {
return Iterators.EMPTY_ITERATOR;
}
return BTreeClassIndexStrategy.iterate(_classMetadata, i_trans);
}
private Iterator4 mapIdsToExecutionPath(Iterator4 singleObjectQueryIterator, Collection4 executionPath) {
if(executionPath == null){
return singleObjectQueryIterator;
}
Iterator4 res = singleObjectQueryIterator;
Iterator4 executionPathIterator = executionPath.iterator();
while(executionPathIterator.moveNext()){
final String fieldName = (String) executionPathIterator.current();
res = Iterators.concat(Iterators.map(res, new Function4() {
public Object apply(Object current) {
int id = ((Integer)current).intValue();
CollectIdContext context = CollectIdContext.forID(i_trans, id);
if(context == null){
return Iterators.SKIP;
}
context.classMetadata().collectIDs(context, fieldName);
return new TreeKeyIterator(context.ids());
}
}));
}
return res;
}
public ObjectContainerBase stream() {
return i_trans.container();
}
public int classIndexEntryCount() {
return _classMetadata.indexEntryCount(i_trans);
}
private FieldIndexProcessorResult processFieldIndexes() {
if(_constraints == null){
return FieldIndexProcessorResult.NO_INDEX_FOUND;
}
return new FieldIndexProcessor(this).run();
}
void evaluate() {
if (_constraints == null) {
return;
}
forEachConstraint(new Procedure4() {
public void apply(Object arg) {
QCon qCon = (QCon)arg;
qCon.setCandidates(QCandidates.this);
qCon.evaluateSelf();
}
});
forEachConstraint(new Procedure4() {
public void apply(Object arg) {
((QCon)arg).evaluateSimpleChildren();
}
});
forEachConstraint(new Procedure4() {
public void apply(Object arg) {
((QCon)arg).evaluateEvaluations();
}
});
forEachConstraint(new Procedure4() {
public void apply(Object arg) {
((QCon)arg).evaluateCreateChildrenCandidates();
}
});
forEachConstraint(new Procedure4() {
public void apply(Object arg) {
((QCon)arg).evaluateCollectChildren();
}
});
forEachConstraint(new Procedure4() {
public void apply(Object arg) {
((QCon)arg).evaluateChildren();
}
});
}
private void forEachConstraint(Procedure4 proc){
Iterator4 i = iterateConstraints();
while(i.moveNext()){
QCon constraint = (QCon)i.current();
if(! constraint.processedByIndex()){
proc.apply(constraint);
}
}
}
boolean isEmpty() {
final boolean[] ret = new boolean[] { true };
traverse(new Visitor4() {
public void visit(Object obj) {
if (((InternalCandidate) obj).include()) {
ret[0] = false;
}
}
});
return ret[0];
}
boolean filter(Visitor4 visitor) {
return _result.filter(visitor);
}
boolean filter(QField field, FieldFilterable filterable) {
return _result.filter(field, filterable);
}
int generateCandidateId(){
if(_idGenerator == null){
_idGenerator = new IDGenerator();
}
return - _idGenerator.next();
}
public Iterator4 iterateConstraints(){
if(_constraints == null){
return Iterators.EMPTY_ITERATOR;
}
return new Iterator4Impl(_constraints);
}
void loadFromClassIndex() {
if (!isEmpty()) {
return;
}
_result.loadFromClassIndex(_classMetadata.index());
DiagnosticProcessor dp = i_trans.container()._handlers.diagnosticProcessor();
if (dp.enabled() && !isClassOnlyQuery()){
dp.loadedFromClassIndex(_classMetadata);
}
_loadedFromClassIndex = true;
}
void setCurrentConstraint(QCon a_constraint) {
_currentConstraint = a_constraint;
}
void traverse(Visitor4 visitor) {
_result.traverse(visitor);
}
void traverseIds(IntVisitor visitor) {
_result.traverseIds(visitor);
}
// FIXME: This method should go completely.
// We changed the code to create the QCandidates graph in two steps:
// (1) call fitsIntoExistingConstraintHierarchy to determine whether
// or not we need more QCandidates objects
// (2) add all constraints
// This method tries to do both in one, which results in missing
// constraints. Not all are added to all QCandiates.
// Right methodology is in
// QQueryBase#createCandidateCollection
// and
// QQueryBase#createQCandidatesList
boolean tryAddConstraint(QCon a_constraint) {
if (_field != null) {
QField qf = a_constraint.getField();
if (qf != null) {
if (_field.name()!=null&&!_field.name().equals(qf.name())) {
return false;
}
}
}
if (_classMetadata == null || a_constraint.isNullConstraint()) {
addConstraint(a_constraint);
return true;
}
ClassMetadata yc = a_constraint.getYapClass();
if (yc != null) {
yc = _classMetadata.getHigherOrCommonHierarchy(yc);
if (yc != null) {
_classMetadata = yc;
addConstraint(a_constraint);
return true;
}
}
addConstraint(a_constraint);
return false;
}
// public void visit(Object a_tree) {
// final QCandidate parent = (QCandidate) a_tree;
// if (parent.createChild(this)) {
// return;
// }
//
// // No object found.
// // All children constraints are necessarily false.
// // Check immediately.
// Iterator4 i = iterateConstraints();
// while(i.moveNext()){
// ((QCon)i.current()).visitOnNull(parent.getRoot());
// }
//
// }
@Override
public void filter(QField field, ParentCandidate parent) {
if (parent.createChild(field, this)) {
return;
}
// No object found.
// All children constraints are necessarily false.
// Check immediately.
Iterator4 i = iterateConstraints();
while(i.moveNext()){
((QCon)i.current()).visitOnNull(parent.getRoot());
}
}
public String toString() {
final StringBuffer sb = new StringBuffer();
_result.traverse(new Visitor4() {
public void visit(Object obj) {
QCandidateBase candidate = (QCandidateBase) obj;
sb.append(" ");
sb.append(candidate._key);
}
});
return sb.toString();
}
public final Transaction transaction(){
return i_trans;
}
public boolean wasLoadedFromClassIndex(){
return _loadedFromClassIndex;
}
public boolean wasLoadedFromClassFieldIndex(){
return _loadedFromClassFieldIndex;
}
public void wasLoadedFromClassFieldIndex(boolean flag){
_loadedFromClassFieldIndex = flag;
}
public boolean fitsIntoExistingConstraintHierarchy(QCon constraint) {
if (_field != null) {
QField qf = constraint.getField();
if (qf != null) {
if (_field.name()!=null&&!_field.name().equals(qf.name())) {
return false;
}
}
}
if (_classMetadata == null || constraint.isNullConstraint()) {
return true;
}
ClassMetadata classMetadata = constraint.getYapClass();
if (classMetadata == null) {
return false;
}
classMetadata = _classMetadata.getHigherOrCommonHierarchy(classMetadata);
if (classMetadata == null) {
return false;
}
_classMetadata = classMetadata;
return true;
}
private boolean isClassOnlyQuery() {
if(_constraints._next != null) {
return false;
}
if(!(_constraints._element instanceof QConClass)) {
return false;
}
return !((QCon)_constraints._element).hasChildren();
}
}