/* 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.config.*;
import com.db4o.foundation.*;
import com.db4o.internal.*;
import com.db4o.internal.handlers.*;
import com.db4o.marshall.*;
import com.db4o.query.*;
import com.db4o.reflect.*;
/**
* Object constraint on queries
*
* @exclude
*/
public class QConObject extends QCon implements FieldFilterable {
// the constraining object
@decaf.Public
private Object i_object;
// cache for the db4o object ID
@decaf.Public
private int i_objectID;
// the YapClass
transient ClassMetadata _classMetadata;
// needed for marshalling the request
@decaf.Public
private int i_classMetadataID;
@decaf.Public
private QField i_field;
transient PreparedComparison _preparedComparison;
@decaf.Public
private ObjectAttribute i_attributeProvider;
private transient boolean _checkClassMetadataOnly = false;
public QConObject() {
// C/S only
}
public QConObject(Transaction a_trans, QCon a_parent, QField a_field,
Object a_object) {
super(a_trans);
i_parent = a_parent;
if (a_object instanceof Compare) {
a_object = ((Compare) a_object).compare();
}
i_object = a_object;
i_field = a_field;
}
private void associateYapClass(Transaction a_trans, Object a_object) {
if (a_object == null) {
//It seems that we need not result the following field
//i_object = null;
//i_comparator = Null.INSTANCE;
//i_classMetadata = null;
// FIXME: Setting the YapClass to null will prevent index use
// If the field is typed we can guess the right one with the
// following line. However this does break some SODA test cases.
// Revisit!
// if(i_field != null){
// i_classMetadata = i_field.getYapClass();
// }
} else {
_classMetadata = a_trans.container()
.produceClassMetadata(a_trans.reflector().forObject(a_object));
if (_classMetadata != null) {
i_object = _classMetadata.getComparableObject(a_object);
if (a_object != i_object) {
i_attributeProvider = _classMetadata.config().queryAttributeProvider();
_classMetadata = a_trans.container().produceClassMetadata(a_trans.reflector().forObject(i_object));
}
if (_classMetadata != null) {
_classMetadata.collectConstraints(a_trans, this, i_object,
new Visitor4() {
public void visit(Object obj) {
addConstraint((QCon) obj);
}
});
} else {
associateYapClass(a_trans, null);
}
} else {
associateYapClass(a_trans, null);
}
}
}
public boolean canBeIndexLeaf(){
return i_object == null || ((_classMetadata != null && _classMetadata.isValueType()) || evaluator().identity());
}
public boolean canLoadByIndex(){
if(i_field == null){
return false;
}
if(i_field._fieldMetadata == null){
return false;
}
if(! i_field._fieldMetadata.hasIndex()){
return false;
}
if (!i_evaluator.supportsIndex()) {
return false;
}
return i_field._fieldMetadata.canLoadByIndex();
}
boolean evaluate(InternalCandidate candidate) {
try {
return candidate.evaluate(this, i_evaluator);
} catch (Exception e) {
if (Debug4.atHome) {
e.printStackTrace();
}
return false;
}
}
void evaluateEvaluationsExec(final QCandidates a_candidates,
boolean rereadObject) {
if (i_field.isQueryLeaf()) {
boolean hasEvaluation = false;
Iterator4 i = iterateChildren();
while (i.moveNext()) {
if (i.current() instanceof QConEvaluation) {
hasEvaluation = true;
break;
}
}
if (hasEvaluation) {
Iterator4 j = iterateChildren();
while (j.moveNext()) {
((QCon) j.current()).evaluateEvaluationsExec(a_candidates,false);
}
}
}
}
void evaluateSelf() {
if(DTrace.enabled){
DTrace.EVALUATE_SELF.log(id());
}
if (_classMetadata != null) {
if (!(_classMetadata instanceof PrimitiveTypeMetadata)) {
if (!i_evaluator.identity() && (_classMetadata.typeHandler() instanceof StandardReferenceTypeHandler) ) {
_checkClassMetadataOnly = true;
}
Object transactionalObject = _classMetadata.wrapWithTransactionContext(transaction(), i_object);
_preparedComparison = _classMetadata.prepareComparison(context(), transactionalObject);
}
}
super.evaluateSelf();
_checkClassMetadataOnly = false;
}
private Context context() {
return transaction().context();
}
void collect(QCandidates a_candidates) {
if (i_field.isClass()) {
a_candidates.filter(i_field, i_candidates);
}
}
void evaluateSimpleExec(QCandidates a_candidates) {
// TODO: The following can be skipped if we used the index on
// this field to load the objects, if hasOrdering() is false
if (i_field.isQueryLeaf() || isNullConstraint()) {
prepareComparison(i_field);
a_candidates.filter(i_field, this);
}
}
PreparedComparison prepareComparison(InternalCandidate candidate){
if(_preparedComparison != null){
return _preparedComparison;
}
return candidate.prepareComparison(container(), i_object);
}
ClassMetadata getYapClass() {
return _classMetadata;
}
public QField getField() {
return i_field;
}
int getObjectID() {
if (i_objectID == 0) {
i_objectID = i_trans.container().getID(i_trans, i_object);
if (i_objectID == 0) {
i_objectID = -1;
}
}
return i_objectID;
}
public boolean hasObjectInParentPath(Object obj) {
if (obj == i_object) {
return true;
}
return super.hasObjectInParentPath(obj);
}
public int identityID() {
if (i_evaluator.identity()) {
int id = getObjectID();
if (id != 0) {
if( !(i_evaluator instanceof QENot) ){
return id;
}
}
}
return 0;
}
boolean isNullConstraint() {
return i_object == null;
}
void log(String indent) {
if (Debug4.queries) {
super.log(indent);
}
}
String logObject() {
if (Debug4.queries) {
if (i_object != null) {
return i_object.toString();
}
return "[NULL]";
}
return "";
}
void marshall() {
super.marshall();
getObjectID();
if (_classMetadata != null) {
i_classMetadataID = _classMetadata.getID();
}
}
public boolean onSameFieldAs(QCon other){
if(! (other instanceof QConObject)){
return false;
}
return i_field == ((QConObject)other).i_field;
}
void prepareComparison(QField a_field) {
if (isNullConstraint() & !a_field.isArray()) {
_preparedComparison = Null.INSTANCE;
} else {
_preparedComparison = a_field.prepareComparison(context(), i_object);
}
}
@Override
QCon shareParent(Object a_object, BooleanByRef removeExisting) {
if(i_parent == null){
return null;
}
Object obj = i_field.coerce(a_object);
if(obj == No4.INSTANCE){
return null;
}
return i_parent.addSharedConstraint(i_field, obj);
}
@Override
QConClass shareParentForClass(ReflectClass a_class, BooleanByRef removeExisting) {
if(i_parent == null){
return null;
}
QConClass newConstraint = new QConClass(i_trans, i_parent,i_field, a_class);
i_parent.addConstraint(newConstraint);
return newConstraint;
}
final Object translate(Object candidate) {
if (i_attributeProvider != null) {
i_candidates.i_trans.container().activate(i_candidates.i_trans, candidate);
return i_attributeProvider.attribute(candidate);
}
return candidate;
}
void unmarshall(Transaction trans) {
if (i_trans != null) {
return;
}
super.unmarshall(trans);
if (i_object == null) {
_preparedComparison = Null.INSTANCE;
}
if (i_classMetadataID != 0) {
_classMetadata = trans.container().classMetadataForID(i_classMetadataID);
}
if (i_field != null) {
i_field.unmarshall(trans);
}
if(i_objectID > 0){
Object obj = trans.container().tryGetByID(trans, i_objectID);
if(obj != null){
i_object = obj;
}
}
}
public void visit(Object obj) {
InternalCandidate qc = (InternalCandidate) obj;
boolean res = true;
boolean processed = false;
if (_checkClassMetadataOnly) {
ClassMetadata yc = qc.classMetadata();
if (yc != null) {
res = i_evaluator.not(_classMetadata.getHigherHierarchy(yc) == _classMetadata);
processed = true;
}
}
if (!processed) {
res = evaluate(qc);
}
visit1(qc.getRoot(), this, res);
}
@Override
public void filter(QField field, ParentCandidate candidate) {
candidate.useField(field);
boolean res = true;
boolean processed = false;
if (_checkClassMetadataOnly) {
ClassMetadata classMetadata = candidate.classMetadata();
if (classMetadata != null) {
res = i_evaluator.not(_classMetadata.getHigherHierarchy(classMetadata) == _classMetadata);
processed = true;
}
}
if (!processed) {
res = evaluate(candidate);
}
visit1(candidate.getRoot(), this, res);
}
public Constraint contains() {
synchronized (streamLock()) {
i_evaluator = i_evaluator.add(new QEContains(true));
return this;
}
}
public Constraint equal() {
synchronized (streamLock()) {
i_evaluator = i_evaluator.add(new QEEqual());
return this;
}
}
public Object getObject() {
return i_object;
}
public Constraint greater() {
synchronized (streamLock()) {
i_evaluator = i_evaluator.add(new QEGreater());
return this;
}
}
public Constraint identity() {
synchronized (streamLock()) {
if(i_object==null) {
return this;
}
getObjectID();
i_evaluator = i_evaluator.add(new QEIdentity());
return this;
}
}
public Constraint byExample() {
synchronized (streamLock()) {
associateYapClass(i_trans, i_object);
return this;
}
}
/*
* if the i_object is stored in db4o, set the evaluation mode as identity,
* otherwise, set the evaluation mode as example.
*/
void setEvaluationMode() {
if ((i_object == null) || evaluationModeAlreadySet()) {
return;
}
int id = getObjectID();
if (id < 0) {
byExample();
} else {
_classMetadata = i_trans.container().produceClassMetadata(
i_trans.reflector().forObject(i_object));
identity();
}
}
boolean evaluationModeAlreadySet(){
return _classMetadata != null;
}
public Constraint like() {
synchronized (streamLock()) {
i_evaluator = i_evaluator.add(new QEContains(false));
return this;
}
}
public Constraint smaller() {
synchronized (streamLock()) {
i_evaluator = i_evaluator.add(new QESmaller());
return this;
}
}
public Constraint startsWith(boolean caseSensitive) {
synchronized (streamLock()) {
i_evaluator = i_evaluator.add(new QEStartsWith(caseSensitive));
return this;
}
}
public Constraint endsWith(boolean caseSensitive) {
synchronized (streamLock()) {
i_evaluator = i_evaluator.add(new QEEndsWith(caseSensitive));
return this;
}
}
public String toString() {
String str = "QConObject ";
if (i_object != null) {
str += i_object.toString();
}
return str;
}
@Override
protected void internalSetProcessedByIndex(QCandidates candidates) {
super.internalSetProcessedByIndex(candidates);
if(i_field == null){
return;
}
FieldMetadata fieldMetadata = i_field.getFieldMetadata();
if(! fieldMetadata.isVirtual()){
candidates.wasLoadedFromClassFieldIndex(true);
}
}
@Override
protected boolean canResolveByFieldIndex() {
return false;
}
}