public int selectShardContents(Shard shard)
throws DissectionException {
// Completed nodes should never be visited again.
if (completed) {
throw new DissectionException(
exceptionLocalizer.format("completed-node-visited"));
}
// Get the available space. Check it to see whether there is any space
// available at all for this node.
int availableSpace = shard.getAvailableSpace();
if (availableSpace < 0) {
return NODE_CANNOT_FIT;
}
// Check to see if the whole node fits if we have not already added this
// node to a shard. If this has been added to a shard then the
// firstShard will have been modified otherwise it will still be set to
// its initial value.
if (!inAnyShard()) {
// If the cost of this node and its parts is variable then
// we must visitElement all the parts.
// If this node must check the cost, or the cost is not variable so
// we can check it easily then do so.
boolean variable = costIsVariable();
if (mustCheckCost || !variable) {
// If the size is variable then we need to track changes to
// the shards SharedContentUsagesImpl in case we need to undo them.
SharedContentUsages table = null;
SharedContentUsages.ChangeSet oldChangeSet = null;
if (variable) {
table = shard.getSharedContentUsages();
// If the ChangeSet has not been created then create it
// now.
if (changeSet == null) {
changeSet = table.createChangeSet();
}
// Push this node's ChangeSet and remember the current
// one if any to restore later.
oldChangeSet = table.pushChangeSet(changeSet);
}
// Calculate the cost, this will update the shard's
// SharedContentUsagesImpl.
int cost = calculateCost(shard);
if (logger.isDebugEnabled()) {
logger.debug("Node " + this + " would cost " + cost
+ " to add to shard " + shard
+ " which has " + availableSpace
+ " units of space available");
}
// Check to see whether this node and all its parts will fit
// in the current shard. If the cost is infinite then it will
// not fit in no matter what, even if the available space is
// also infinite.
if (cost != Cost.INFINITE && cost < availableSpace) {
if (logger.isDebugEnabled()) {
logger.debug("Node " + this
+ " completely fits in shard " + shard);
}
// The cost of this node is less than the space available
// so add this node and all its parts to the shard and
// mark this node as complete.
addToShard(shard, true);
// Reduce the available space in the shard by the cost of
// this node.
shard.decrementAvailableSpace(cost);
// If the cost is variable then we need to remove the
// ChangeSet we added to the SharedContentUsagesImpl without
// undoing the changes.
if (variable) {
table.popChangeSet(oldChangeSet);
}
return ADDED_NODE;
}
// If the cost is variable then we need to remove the ChangeSet
// that was added from the SharedContentUsagesImpl, undoing the
// changes in the process.
if (variable) {
table.popChangeSet(oldChangeSet, true);
}
// This node is too big to add into the shard so we will need
// to split it.
if (logger.isDebugEnabled()) {
logger.debug("Cost " + cost
+ " is greater than the available space "
+ availableSpace + " left in shard " + shard);
}
// Drop through to the dissection.
} else {
// Either this node or one of its parts has a variable cost and
// we do not have to calculate the cost. Instead we will
// visitElement the parts of this node directly.
return selectVariableShardContents(shard);
}
}
// If the shard is empty then this node must be dissected otherwise
// we will end up with empty shards and possibly never finish generating
// the page.
boolean mustDissect = shard.isEmpty();
// Attempt to dissect the node.
int result = dissectNode(shard, mustDissect);
switch (result) {
case NODE_CANNOT_FIT:
if (mustDissect) {
throw new DissectionException(
exceptionLocalizer.format(
"disect-empty-shard-error"));
}
// This node cannot fit.
if (logger.isDebugEnabled()) {