/* 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.fieldindex;
import com.db4o.foundation.*;
import com.db4o.internal.query.processor.*;
public class IndexedNodeCollector {
private final Collection4 _nodes;
private final Hashtable4 _nodeCache;
public IndexedNodeCollector(QCandidates candidates) {
_nodes = new Collection4();
_nodeCache = new Hashtable4();
collectIndexedNodes(candidates);
}
public Iterator4 getNodes() {
return _nodes.iterator();
}
private void collectIndexedNodes(QCandidates candidates) {
collectIndexedNodes(candidates.iterateConstraints());
implicitlyAndJoinsOnSameField();
}
private void implicitlyAndJoinsOnSameField() {
final Object[] nodes = _nodes.toArray();
for (int i = 0; i < nodes.length; i++) {
Object node = nodes[i];
if (node instanceof OrIndexedLeaf) {
OrIndexedLeaf current = (OrIndexedLeaf) node;
OrIndexedLeaf other = findJoinOnSameFieldAtSameLevel(current);
if (null != other) {
nodes[Arrays4.indexOfIdentity(nodes, other)] = null;
collectImplicitAnd(current.getConstraint(), current, other);
}
}
}
}
private OrIndexedLeaf findJoinOnSameFieldAtSameLevel(OrIndexedLeaf join) {
final Iterator4 i = _nodes.iterator();
while (i.moveNext()) {
if (i.current() == join) {
continue;
}
if (i.current() instanceof OrIndexedLeaf) {
OrIndexedLeaf current = (OrIndexedLeaf) i.current();
if (current.getIndex() == join.getIndex()
&& parentConstraint(current) == parentConstraint(join)) {
return current;
}
}
}
return null;
}
private Object parentConstraint(OrIndexedLeaf node) {
return node.getConstraint().parent();
}
private void collectIndexedNodes(final Iterator4 qcons) {
while (qcons.moveNext()) {
QCon qcon = (QCon)qcons.current();
if (isCached(qcon)) {
continue;
}
if (isLeaf(qcon)) {
if (qcon.canLoadByIndex() && qcon.canBeIndexLeaf()) {
final QConObject conObject = (QConObject) qcon;
if (conObject.hasJoins()) {
collectJoinedNode(conObject);
} else {
collectStandaloneNode(conObject);
}
}
} else {
if (!qcon.hasJoins()) {
collectIndexedNodes(qcon.iterateChildren());
}
}
}
}
private boolean isCached(QCon qcon) {
return null != _nodeCache.get(qcon);
}
private void collectStandaloneNode(final QConObject conObject) {
IndexedLeaf existing = findLeafOnSameField(conObject);
if (existing != null) {
collectImplicitAnd(conObject, existing, new IndexedLeaf(conObject));
} else {
_nodes.add(new IndexedLeaf(conObject));
}
}
private void collectJoinedNode(QConObject constraintWithJoins) {
Collection4 joins = collectTopLevelJoins(constraintWithJoins);
if (!canJoinsBeSearchedByIndex(joins)) {
return;
}
if (1 == joins.size()) {
_nodes.add(nodeForConstraint((QCon)joins.singleElement()));
return;
}
collectImplicitlyAndingJoins(joins, constraintWithJoins);
}
private boolean allHaveSamePath(Collection4 leaves) {
final Iterator4 i = leaves.iterator();
i.moveNext();
QCon first = (QCon)i.current();
while (i.moveNext()) {
if (!haveSamePath(first, (QCon)i.current())) {
return false;
}
}
return true;
}
private boolean haveSamePath(QCon x, QCon y) {
if (x == y) {
return true;
}
if (!x.onSameFieldAs(y)) {
return false;
}
if (!x.hasParent()) {
return !y.hasParent();
}
return haveSamePath(x.parent(), y.parent());
}
private Collection4 collectLeaves(Collection4 joins) {
Collection4 leaves = new Collection4();
collectLeaves(leaves, joins);
return leaves;
}
private void collectLeaves(Collection4 leaves, Collection4 joins) {
final Iterator4 i = joins.iterator();
while (i.moveNext()) {
final QConJoin join = ((QConJoin)i.current());
collectLeavesFromJoin(leaves, join);
}
}
private void collectLeavesFromJoin(Collection4 leaves, QConJoin join) {
collectLeavesFromJoinConstraint(leaves, join.constraint1());
collectLeavesFromJoinConstraint(leaves, join.constraint2());
}
private void collectLeavesFromJoinConstraint(Collection4 leaves, QCon constraint) {
if (constraint instanceof QConJoin) {
collectLeavesFromJoin(leaves, (QConJoin) constraint);
} else {
if (!leaves.containsByIdentity(constraint)) {
leaves.add(constraint);
}
}
}
private boolean canJoinsBeSearchedByIndex(Collection4 joins) {
Collection4 leaves = collectLeaves(joins);
return allHaveSamePath(leaves)
&& allCanBeSearchedByIndex(leaves);
}
private boolean allCanBeSearchedByIndex(Collection4 leaves) {
final Iterator4 i = leaves.iterator();
while (i.moveNext()) {
final QCon leaf = ((QCon)i.current());
if (!leaf.canLoadByIndex()) {
return false;
}
}
return true;
}
private void collectImplicitlyAndingJoins(Collection4 joins, QConObject constraintWithJoins) {
final Iterator4 i = joins.iterator();
i.moveNext();
IndexedNodeWithRange last = nodeForConstraint((QCon)i.current());
while (i.moveNext()) {
final IndexedNodeWithRange node = nodeForConstraint((QCon)i.current());
last = new AndIndexedLeaf(constraintWithJoins, node, last);
_nodes.add(last);
}
}
private Collection4 collectTopLevelJoins(QConObject constraintWithJoins) {
Collection4 joins = new Collection4();
collectTopLevelJoins(joins, constraintWithJoins);
return joins;
}
private void collectTopLevelJoins(Collection4 joins, QCon constraintWithJoins) {
final Iterator4 i = constraintWithJoins.iterateJoins();
while (i.moveNext()) {
QConJoin join = (QConJoin)i.current();
if (!join.hasJoins()) {
if (!joins.containsByIdentity(join)) {
joins.add(join);
}
} else {
collectTopLevelJoins(joins, join);
}
}
}
private IndexedNodeWithRange newNodeForConstraint(QConJoin join) {
final IndexedNodeWithRange c1 = nodeForConstraint(join.constraint1());
final IndexedNodeWithRange c2 = nodeForConstraint(join.constraint2());
if (join.isOr()) {
return new OrIndexedLeaf(findLeafForJoin(join), c1, c2);
}
return new AndIndexedLeaf(join.constraint1(), c1, c2);
}
private QCon findLeafForJoin(QConJoin join) {
if (join.constraint1() instanceof QConObject) {
return join.constraint1();
}
QCon con = join.constraint2();
if (con instanceof QConObject) {
return con;
}
return findLeafForJoin((QConJoin)con);
}
private IndexedNodeWithRange nodeForConstraint(QCon con) {
IndexedNodeWithRange node = (IndexedNodeWithRange) _nodeCache.get(con);
if (null != node || _nodeCache.containsKey(con)) {
return node;
}
node = newNodeForConstraint(con);
_nodeCache.put(con, node);
return node;
}
private IndexedNodeWithRange newNodeForConstraint(QCon con) {
if (con instanceof QConJoin) {
return newNodeForConstraint((QConJoin)con);
}
return new IndexedLeaf((QConObject)con);
}
private void collectImplicitAnd(final QCon constraint, IndexedNodeWithRange x, final IndexedNodeWithRange y) {
_nodes.remove(x);
_nodes.remove(y);
_nodes.add(new AndIndexedLeaf(constraint, x, y));
}
private IndexedLeaf findLeafOnSameField(QConObject conObject) {
final Iterator4 i = _nodes.iterator();
while (i.moveNext()) {
if (i.current() instanceof IndexedLeaf) {
IndexedLeaf leaf = (IndexedLeaf)i.current();
if (conObject.onSameFieldAs(leaf.constraint())) {
return leaf;
}
}
}
return null;
}
private boolean isLeaf(QCon qcon) {
return !qcon.hasChildren();
}
}