com.db4o.QCandidate Maven / Gradle / Ivy
The newest version!
/* Copyright (C) 2004 - 2005 db4objects Inc. http://www.db4o.com
This file is part of the db4o open source object database.
db4o is free software; you can redistribute it and/or modify it under
the terms of version 2 of the GNU General Public License as published
by the Free Software Foundation and as clarified by db4objects' GPL
interpretation policy, available at
http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
Suite 350, San Mateo, CA 94403, USA.
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, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
package com.db4o;
import com.db4o.config.*;
import com.db4o.foundation.*;
import com.db4o.query.*;
import com.db4o.reflect.*;
/**
* Represents an actual object in the database. Forms a tree structure,
* indexed by id. Can have dependents that are doNotInclude'd in the
* query result when this is doNotInclude'd.
*
*/
public class QCandidate extends TreeInt implements Candidate, Orderable {
// db4o ID is stored in i_key;
// db4o byte stream storing the object
YapReader i_bytes;
final QCandidates i_candidates;
// Dependant candidates
private List4 i_dependants;
// whether to include in the result set
// may use id for optimisation ???
boolean i_include = true;
private Object i_member;
// Comparable
Orderable i_order;
// Possible pending joins on children
Tree i_pendingJoins;
// The evaluation root to compare all ORs
private QCandidate i_root;
// the YapClass of this object
YapClass i_yapClass;
// temporary yapField and member for one field during evaluation
YapField i_yapField; // null denotes null object
private QCandidate() {
super(0);
i_candidates = null;
// dummy constructor to get "this" out of declaration for C#
}
QCandidate(QCandidates candidates, Object obj, int id, boolean include) {
super(id);
if(DTrace.enabled){
DTrace.CREATE_CANDIDATE.log(id);
}
i_candidates = candidates;
i_order = this;
i_member = obj;
i_include = include;
}
void addDependant(QCandidate a_candidate) {
i_dependants = new List4(i_dependants, a_candidate);
}
private void checkInstanceOfCompare() {
if (i_member instanceof Compare) {
i_member = ((Compare)i_member).compare();
YapFile stream = getStream();
i_yapClass = stream.getYapClass(stream.reflector().forObject(i_member), false);
i_key = (int)stream.getID(i_member);
i_bytes = stream.readReaderByID(getTransaction(), i_key);
}
}
///
///
///
public int compare(Tree a_to) {
return i_order.compareTo(((QCandidate)a_to).i_order);
}
public int compareTo(Object a_object) {
return i_key - ((TreeInt)a_object).i_key;
}
boolean createChild(final QCandidates a_candidates) {
if (!i_include) {
return false;
}
QCandidate candidate = null;
if (i_yapField != null) {
TypeHandler4 handler = i_yapField.getHandler();
if (handler != null) {
final YapReader[] arrayBytes = { i_bytes };
final TypeHandler4 arrayWrapper =
handler.readArrayWrapper(getTransaction(), arrayBytes);
if (arrayWrapper != null) {
final int offset = arrayBytes[0]._offset;
boolean outerRes = true;
// The following construct is worse than not ideal.
// For each constraint it completely reads the
// underlying structure again. The structure could b
// kept fairly easy. TODO: Optimize!
Iterator4 i = a_candidates.iterateConstraints();
while (i.hasNext()) {
QCon qcon = (QCon)i.next();
QField qf = qcon.getField();
if (qf == null || qf.i_name.equals(i_yapField.getName())) {
QCon tempParent = qcon.i_parent;
qcon.setParent(null);
final QCandidates candidates =
new QCandidates(a_candidates.i_trans, null, qf);
candidates.addConstraint(qcon);
qcon.setCandidates(candidates);
arrayWrapper.readCandidates(arrayBytes[0], candidates);
arrayBytes[0]._offset = offset;
final boolean isNot = qcon.isNot();
if (isNot) {
qcon.removeNot();
}
candidates.evaluate();
final Tree[] pending = new Tree[1];
final boolean[] innerRes = { isNot };
candidates.traverse(new Visitor4() {
public void visit(Object obj) {
QCandidate cand = (QCandidate)obj;
if (cand.include()) {
innerRes[0] = !isNot;
}
// Collect all pending subresults.
if(cand.i_pendingJoins != null){
cand.i_pendingJoins.traverse(new Visitor4() {
public void visit(Object a_object) {
QPending newPending = (QPending)a_object;
// We need to change
// the
// constraint here, so
// our
// pending collector
// uses
// the right
// comparator.
newPending.changeConstraint();
QPending oldPending =
(QPending)Tree.find(
pending[0],
newPending);
if (oldPending != null) {
// We only keep one
// pending result
// for
// all array
// elements.
// and memorize,
// whether we had a
// true or a false
// result.
// or both.
if (oldPending.i_result
!= newPending.i_result) {
oldPending.i_result = QPending.BOTH;
}
} else {
pending[0] =
Tree.add(pending[0], newPending);
}
}
});
}
}
});
if (isNot) {
qcon.not();
}
// In case we had pending subresults, we
// need to communicate
// them up to our root.
if (pending[0] != null) {
pending[0].traverse(new Visitor4() {
public void visit(Object a_object) {
getRoot().evaluate((QPending)a_object);
}
});
}
if (!innerRes[0]) {
if (Deploy.debugQueries) {
System.out.println(
" Array evaluation false. Constraint:"
+ qcon.i_id);
}
// Again this could be double triggering.
//
// We want to clean up the "No route"
// at some stage.
qcon.visit(getRoot(), qcon.i_evaluator.not(false));
outerRes = false;
}
qcon.setParent(tempParent);
}
}
return outerRes;
}
// We may get simple types here too, if the YapField was null
// in the higher level simple evaluation. Evaluate these
// immediately.
if (handler.getType() == YapConst.TYPE_SIMPLE) {
a_candidates.i_currentConstraint.visit(this);
return true;
}
}
}
if (candidate == null) {
candidate = readSubCandidate(a_candidates);
if (candidate == null) {
return false;
}
}
// fast early check for YapClass
if (a_candidates.i_yapClass != null && a_candidates.i_yapClass.isStrongTyped()) {
if (i_yapField != null) {
TypeHandler4 handler = i_yapField.getHandler();
if (handler != null && (handler.getType() == YapConst.TYPE_CLASS)) {
YapClass yc = (YapClass)handler;
if (yc instanceof YapClassAny) {
yc = candidate.readYapClass();
}
if (!yc.canHold(a_candidates.i_yapClass.classReflector())) {
return false;
}
}
}
}
addDependant(a_candidates.addByIdentity(candidate));
return true;
}
void doNotInclude() {
i_include = false;
if (i_dependants != null) {
Iterator4 i = new Iterator4Impl(i_dependants);
i_dependants = null;
while (i.hasNext()) {
((QCandidate)i.next()).doNotInclude();
}
}
}
public boolean duplicates() {
return i_order.hasDuplicates();
}
boolean evaluate(final QConObject a_constraint, final QE a_evaluator) {
if(a_evaluator.identity()){
return a_evaluator.evaluate(a_constraint, this, null);
}
if (i_member == null) {
i_member = value();
}
return a_evaluator.evaluate(a_constraint, this, a_constraint.translate(i_member));
}
boolean evaluate(QPending a_pending) {
if (Deploy.debugQueries) {
System.out.println(
"Pending arrived Join: "
+ a_pending.i_join.i_id
+ " Constraint:"
+ a_pending.i_constraint.i_id
+ " res:"
+ a_pending.i_result);
}
QPending oldPending = (QPending)Tree.find(i_pendingJoins, a_pending);
if (oldPending == null) {
a_pending.changeConstraint();
i_pendingJoins = Tree.add(i_pendingJoins, a_pending);
return true;
} else {
i_pendingJoins = i_pendingJoins.removeNode(oldPending);
oldPending.i_join.evaluatePending(this, oldPending, a_pending, a_pending.i_result);
return false;
}
}
ReflectClass classReflector(){
readYapClass();
if (i_yapClass == null) {
return null;
}
return i_yapClass.classReflector();
}
/// ******
public ObjectContainer objectContainer(){
return getStream();
}
public Object getObject() {
Object obj = value(true);
if(obj instanceof YapReader) {
/* CHANGED (pr) */
YapReader reader=(YapReader)obj;
int offset=reader._offset;
obj = reader.toString(getTransaction());
reader._offset=offset;
}
return obj;
}
QCandidate getRoot() {
return i_root == null ? this : i_root;
}
private YapFile getStream() {
return getTransaction().i_file;
}
private Transaction getTransaction() {
return i_candidates.i_trans;
}
public boolean hasDuplicates() {
// Subcandidates are evaluated along with their constraints
// in one big QCandidates object. The tree can have duplicates
// so evaluation can be cascaded up to different roots.
return i_root != null;
}
public void hintOrder(int a_order, boolean a_major) {
i_order = new Order();
i_order.hintOrder(a_order, a_major);
}
public boolean include() {
return i_include;
}
/**
* For external interface use only. Call doNotInclude() internally so
* dependancies can be checked.
*/
public void include(boolean flag) {
// TODO:
// Internal and external flag may need to be handled seperately.
i_include = flag;
}
void isDuplicateOf(Tree a_tree) {
i_size = 0;
i_root = (QCandidate)a_tree;
}
private ReflectClass memberClass(){
return getTransaction().reflector().forObject(i_member);
}
YapComparable prepareComparison(YapStream a_stream, Object a_constraint) {
if (i_yapField != null) {
return i_yapField.prepareComparison(a_constraint);
}
if (i_yapClass == null) {
YapClass yc = null;
if (i_bytes != null) {
yc = a_stream.getYapClass(a_stream.reflector().forObject(a_constraint), true);
} else {
if (i_member != null) {
yc = a_stream.getYapClass(a_stream.reflector().forObject(i_member), false);
}
}
if (yc != null) {
if (i_member != null && i_member.getClass().isArray()) {
TypeHandler4 ydt = (TypeHandler4)yc.prepareComparison(a_constraint);
if (a_stream.reflector().array().isNDimensional(memberClass())) {
YapArrayN yan = new YapArrayN(a_stream, ydt, false);
return yan;
} else {
YapArray ya = new YapArray(a_stream, ydt, false);
return ya;
}
} else {
return yc.prepareComparison(a_constraint);
}
}
return null;
} else {
return i_yapClass.prepareComparison(a_constraint);
}
}
private void read() {
if (i_include) {
if (i_bytes == null) {
if (i_key > 0) {
if(DTrace.enabled){
DTrace.CANDIDATE_READ.log(i_key);
}
i_bytes = getStream().readReaderByID(getTransaction(), i_key);
if (i_bytes == null) {
i_include = false;
}
} else {
i_include = false;
}
}
}
}
private QCandidate readSubCandidate(QCandidates candidateCollection) {
int id = 0;
read();
if (i_bytes != null) {
final int offset = i_bytes._offset;
try {
id = i_bytes.readInt();
} catch (Exception e) {
return null;
}
i_bytes._offset = offset;
if (id != 0) {
QCandidate candidate = new QCandidate(candidateCollection, null, id, true);
candidate.i_root = getRoot();
return candidate;
}
}
return null;
}
private void readThis(boolean a_activate) {
read();
Transaction trans = getTransaction();
if (trans != null) {
i_member = trans.i_stream.getByID1(trans, i_key);
if (i_member != null && (a_activate || i_member instanceof Compare)) {
trans.i_stream.activate1(trans, i_member);
checkInstanceOfCompare();
}
}
}
YapClass readYapClass() {
if (i_yapClass == null) {
read();
if (i_bytes != null) {
i_bytes._offset = 0;
if (Deploy.debug) {
i_bytes.readBegin(0, YapConst.YAPOBJECT);
}
YapStream stream = getStream();
i_yapClass = stream.getYapClass(i_bytes.readInt());
if(i_yapClass != null){
if ( stream.i_handlers.ICLASS_COMPARE.isAssignableFrom(i_yapClass.classReflector())){
readThis(false);
}
}
}
}
return i_yapClass;
}
public String toString() {
if(! Debug4.prettyToStrings){
return super.toString();
}
String str = "QCandidate ";
if (i_yapClass != null) {
str += "\n YapClass " + i_yapClass.getName();
}
if (i_yapField != null) {
str += "\n YapField " + i_yapField.getName();
}
if (i_member != null) {
str += "\n Member " + i_member.toString();
}
if (i_root != null) {
str += "\n rooted by:\n";
str += i_root.toString();
} else {
str += "\n ROOT";
}
return str;
}
void useField(QField a_field) {
read();
if (i_bytes == null) {
i_yapField = null;
} else {
readYapClass();
i_member = null;
if (a_field == null) {
i_yapField = null;
} else {
if(i_yapClass == null){
i_yapField = null;
}else{
i_yapField = a_field.getYapField(i_yapClass);
if (i_yapField == null | ! i_yapClass.findOffset(i_bytes, i_yapField)) {
if (i_yapClass.holdsAnyClass()) {
i_yapField = null;
} else {
i_yapField = new YapFieldNull();
}
}
}
}
}
}
Object value() {
return value(false);
}
// TODO: This is only used for Evaluations. Handling may need
// to be different for collections also.
Object value(boolean a_activate) {
if (i_member == null) {
if (i_yapField == null) {
readThis(a_activate);
} else {
int offset = i_bytes._offset;
try {
i_member = i_yapField.readQuery(getTransaction(), i_bytes);
} catch (CorruptionException ce) {
i_member = null;
}
i_bytes._offset = offset;
checkInstanceOfCompare();
}
}
return i_member;
}
}