package uk.co.demon.mcdowella.algorithms;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Code by A.G.McDowell. This was written for fun, and has not been
* independently tested, so use at your own risk! Feel free to copy,
* spindle, mutilate, or whatever - but some acknowledgement would
* be nice.
*
* This class, and its inner classes, can be used to store
* attribute-value information, where the nodes owning the attributes
* form a tree. The value associated with an attribute at a particular
* node is the value explicitly stored at that node, or, if no such
* attribute is explicitly stored there, at the lowest possible ancestor
* of that node. In other words, if the node you want doesn't contain
* the desired attribute, look in its parent, or its parent's parent,
* and so on.
*
* Done naively, this would take time for each query linear
* in the distance from the queried node to the parent holding the
* desired information, and so, in the worst case, could grow with
* the size of the tree. However, queries
* can be answered in logarithmic time using only linear space if we
* are allowed to build an index from the tree to be queried, in
* linear time. This class represents such an index.
*
* This class assigns each node a number, writing that into the node.
* To refer to a node during a query, you pass in the number. After
* this class has been built, you can discard the nodes, if you want.
* The same is true of attributes. The number assigned to a node is
* in fact a numbering in depth first search order, starting at zero,
* and the calls to assign numbers to nodes are made in depth first
* search order.
*
* This implementation numbers the nodes of the tree in depth first
* search order, and traverses the tree in that order. While doing
* this, it uses a stack to keep track of the current value of
* each attribute at a node, and the values it will revert to when the
* implementation retreats up the tree. This allows it to write down
* the numbers of
* the nodes at which the value changes. It answers a query by doing
* a binary search on this list to find the highest number in this
* list ≤ the node we are querying on. Each occurrence of an explicit
* attribute setting in the tree causes at most two entries to be made
* in the list of node occurrences, so the size of our array - and the
* time taken to produce it - is at most linear in the size of the
* tree.
*
* Objects of this class are immutable once built, but have links
* to mutable objects (such as the Node and Attribute classes). These
* objects should not normally be modified while the TreeDefault is
* in use. Under these circumstances, thread safety should not be
* a problem. However, this code contains no synchronisation calls.
*/
public class TreeDefault
{
/** Return the value associated with the given attribute
* at the numbered node quoted, or some ancestor of it, or null
* if no such value can be found.
*/
public Object getValue(int nodeNumber, int attributeNumber)
{
// array of node numbers for this attribute
AttributeOccurrence[] ao = attributeArray[attributeNumber];
// find the highest numbered node ≤ the given node number -
// this is the last change seen if we traverse the tree from
// the start to the given node
int first = 0;
int past = ao.length;
Object maybe = null;
while (first < past)
{
// Probe value is >= first and < past
// (check: worst possible cases are (0, 1) => 0 and
// (1, 2) => 1
int probe = (first + past) >> 1;
AttributeOccurrence sample = ao[probe];
int num = sample.nodeNumber;
if (num > nodeNumber)
{ // probe is too far along
past = probe;
}
else if (num < nodeNumber)
{ // may be a match
maybe = sample.value;
first = probe + 1;
}
else
{ // exact match
return sample.value;
}
// If here, distance betwen first and past has decreased
}
return maybe;
}
/** Utility routine to fetch the value associated with a given
* DefaultAttribute from a given DefaultNode
*/
public Object getValueWithDefaults(DefaultNode node,
DefaultAttribute attribute)
{
int atNum = attribute.getNumber();
if (atNum == -1)
{ // never seen in tree
return null;
}
return getValue(node.getNumber(), attribute.getNumber());
}
/** This interface represents a Node */
public interface Node
{
/** Will be called to assign to it its number */
void setNumber(int num);
/** Should return an iterator providing objects implementing
* java.util.Map.Entry - these are the attributes and values
* at this node. The key, which is the attribute, must
* implement the interface Attribute.
*/
Iterator getAttributeValueIterator();
/** should return an iterator providing its child nodes */
Iterator getChildrenIterator();
}
/** this interface represents an Attribute */
public interface Attribute
{
/** set a number identifying this attribute */
void setNumber(int num);
/** must return the last value set by setNumber(). Need not
* return any particular value before setNumber() is called, but
* must not throw an exception. However, attributes never
* encountered in the tree are never numbered, so you may wish
* find some way of distinguishing them for yourself. The
* number used is a 1-up number starting from 0, so all attribute
* numbers we assign should be ≥ 0.
*/
int getNumber();
}
/** This class holds the info we need about an occurrence of
* an attribute
*/
private static class AttributeOccurrence
{
/** The number of the node at which this value comes into force.
* This could either be an explicit set, or the result of
* the value reverting after returning up from a node at which
* an explicit set was made.
*/
final int nodeNumber;
/** The associated value */
final Object value;
AttributeOccurrence(int number, Object v)
{
nodeNumber = number;
value = v;
}
}
/** This class holds the info we need about an attribute while
* we build our main datastructures.
*/
private static class AttributeInfo
{
/** Attribute in question */
final Attribute thisAttribute;
/** List of AttributeOccurrences, in order */
final List occurrenceList;
/** Stack of AttributeOccurrences seen in nodes directly above
* the current node
*/
final List stackedOccurrences;
AttributeInfo(Attribute at)
{
thisAttribute = at;
occurrenceList = new ArrayList();
stackedOccurrences = new ArrayList();
}
}
/** This is the attribute occurrence information in the form used
* to answer queries. attributeInfo[atNo] holds an array of
* AttributeInfo structures for the attribute numbered atNo.
* They are held in order of nodeNumber (and also created in this
* order, so they don't need to be sorted).
*/
private final AttributeOccurrence[][] attributeArray;
/** Use this to number the nodes in depth first search order */
private int nextNodeNumber = 0;
/** List of Attributes encountered, numbered in the order in which
* they are encountered. Set to null on exit from constructor.
*/
private List attributeInfoList = new ArrayList();
/** This is the information we need for our explicit recursion
* stack
*/
private static class RecursionStackEntry
{
final Node n;
final Iterator i;
RecursionStackEntry(Node theNode, Iterator it)
{
n = theNode;
i = it;
}
}
/** Add an attribute to the end of an attributeInfo list and
* replace any attribute already there with the same number. This
* can happen on exit if a child and its parent both set the
* same attribute, or on entry if two siblings set the same
* attribute.
*/
private static void addAttributeOccurrence(List attributeInfoList,
AttributeOccurrence ao)
{
int pos = attributeInfoList.size() - 1;
if (pos > 0)
{
AttributeOccurrence current =
(AttributeOccurrence)attributeInfoList.get(pos);
if (current.nodeNumber == ao.nodeNumber)
{
attributeInfoList.remove(pos);
}
}
attributeInfoList.add(ao);
}
/** This gets called when we enter a node in depth first search */
private void handleNodeEntry(RecursionStackEntry rse)
{
Node n = rse.n;
int thisNumber = nextNodeNumber++;
n.setNumber(thisNumber);
for (Iterator i = n.getAttributeValueIterator(); i.hasNext();)
{
Map.Entry me = (Map.Entry)i.next();
Attribute attribute = (Attribute)me.getKey();
int atNumber = attribute.getNumber();
AttributeInfo ai;
if ((atNumber >= attributeInfoList.size()) ||
(atNumber < 0) ||
(((AttributeInfo)attributeInfoList.get(atNumber)).
thisAttribute != attribute))
{ // have not seen this attribute before
attribute.setNumber(attributeInfoList.size());
ai = new AttributeInfo(attribute);
attributeInfoList.add(ai);
}
else
{
ai = (AttributeInfo)attributeInfoList.get(atNumber);
}
AttributeOccurrence occurrence =
new AttributeOccurrence(thisNumber, me.getValue());
addAttributeOccurrence(ai.occurrenceList, occurrence);
ai.stackedOccurrences.add(occurrence);
}
}
/** This gets called when we exit a node in depth first search
*/
private void handleNodeExit(RecursionStackEntry rse)
{
Node n = rse.n;
for (Iterator i = n.getAttributeValueIterator(); i.hasNext();)
{
Attribute at = (Attribute)((Map.Entry)i.next()).getKey();
AttributeInfo ai =
(AttributeInfo)attributeInfoList.get(at.getNumber());
// remove our value from the list of stacked occurrences
int pos = ai.stackedOccurrences.size() - 1;
ai.stackedOccurrences.remove(pos--);
// Find the previous occurrence, if any
Object value = null;
if (pos >= 0)
{
AttributeOccurrence prev =
(AttributeOccurrence)ai.stackedOccurrences.get(pos);
value = prev.value;
}
// Add an attributeOccurrence for the next node, cancelling
// out the value added here
addAttributeOccurrence(ai.occurrenceList,
new AttributeOccurrence(nextNodeNumber, value));
}
}
/** Construct from the root of a tree of nodes */
public TreeDefault(Node root)
{
if (root != null)
{
// The first stage is a depth-first search of the tree. By
// keeping a stack of values per attribute, we can keep track
// of the values assigned to each attribute at each node as we
// traverse the tree, and note down in ArrayLists where these
// values change. For this depth-first search we use an explicit
// stack, rather than recursion, because we cannot control the
// depth of the tree, which determines the depth of the recursion.
List stack = new ArrayList();
RecursionStackEntry rse = new RecursionStackEntry(root,
root.getChildrenIterator());
handleNodeEntry(rse);
for (;;)
{
if (rse.i.hasNext())
{
Node lowerNode = (Node)rse.i.next();
RecursionStackEntry lower = new RecursionStackEntry(
lowerNode, lowerNode.getChildrenIterator());
handleNodeEntry(lower);
if (lower.i.hasNext())
{
stack.add(rse);
rse = lower;
}
else
{
handleNodeExit(lower);
}
}
else
{
handleNodeExit(rse);
int len = stack.size();
if (len <= 0)
{
break;
}
len--;
rse = (RecursionStackEntry)stack.remove(len);
}
}
}
// The last stage is to populate attributeArray from
// attributeInfoList
attributeArray =
new AttributeOccurrence[attributeInfoList.size()][];
for (int i = 0; i < attributeArray.length; i++)
{
AttributeInfo ai = (AttributeInfo)attributeInfoList.get(i);
List l = ai.occurrenceList;
AttributeOccurrence[] ao = new AttributeOccurrence[l.size()];
attributeArray[i] = (AttributeOccurrence[])l.toArray(ao);
}
// no longer need attributeInfoList
attributeInfoList = null;
}
/** Utility implementation of Node */
public static class DefaultNode implements Node
{
private int number;
public void setNumber(int num)
{
number = num;
}
/** returns the number assigned to this node */
public int getNumber()
{
return number;
}
private final Map valueByAttribute = new HashMap();
private final List children = new ArrayList();
/** Add a child to this node */
public void addChild(Node n)
{
children.add(n);
}
/** Associate an attribute-value pair with this node */
public void put(Attribute attribute, Object value)
{
valueByAttribute.put(attribute, value);
}
public Iterator getAttributeValueIterator()
{
return valueByAttribute.entrySet().iterator();
}
public Iterator getChildrenIterator()
{
return children.iterator();
}
/** return an unmodifiable copy of the Map of attribute-value
* settings held at this node (just this node, so does not
* say anything about attribute-value settings held at its
* parents).
*/
public Map getUnmodifiableAttributeMap()
{
return Collections.unmodifiableMap(valueByAttribute);
}
}
/** Utility implementation of Attribute */
public static class DefaultAttribute implements Attribute
{
/** Use -1 to mean "never seen" */
private int number = -1;
public void setNumber(int num)
{
number = num;
}
public int getNumber()
{
return number;
}
}
/** For debugging only */
void dump()
{
for (int i = 0; i < attributeArray.length; i++)
{
System.out.println("Attribute " + i);
AttributeOccurrence[] oca = attributeArray[i];
for (int j = 0; j < oca.length; j++)
{
AttributeOccurrence here = oca[j];
System.out.println("Value at " + here.nodeNumber + " is " +
here.value);
}
}
}
}