Index: /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/AbstractBasicDocument.java
===================================================================
--- /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/AbstractBasicDocument.java	(revision 26044)
+++ /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/AbstractBasicDocument.java	(revision 26045)
@@ -19,4 +19,6 @@
 package org.greenstone.gsdl3.util;
 
+import java.util.ArrayList;
+
 import org.apache.log4j.*;
 import org.w3c.dom.Document;
@@ -24,4 +26,10 @@
 
 public abstract class AbstractBasicDocument {
+
+  /** info types */
+	protected static final String INFO_NUM_SIBS = "numSiblings";
+	protected static final String INFO_NUM_CHILDREN = "numChildren";
+	protected static final String INFO_SIB_POS = "siblingPosition";
+	protected static final String INFO_DOC_TYPE = "documentType";
 
     /** XML element for describe requests - the container doc */
@@ -59,4 +67,16 @@
     }
 
+	/**
+	 * adds all the children of doc_id to the doc element, and if
+	 * recursive=true, adds all their children as well
+	 */
+  abstract public void addDescendants(Element doc, String doc_id, boolean recursive);
+  
+
+	/**
+	 * adds all the siblings of current_id to the parent element. returns the
+	 * new current element
+	 */
+  abstract public Element addSiblings(Element parent_node, String parent_id, String current_id);
 
     /** returns the node type of the specified node.
@@ -97,6 +117,29 @@
     abstract public boolean hasChildren(String node_id);
     
+	/**
+	 * returns the structural information asked for. info_type may be one of
+	 * INFO_NUM_SIBS, INFO_NUM_CHILDREN, INFO_SIB_POS, INFO_DOC_TYPE
+	 */
+	abstract public String getStructureInfo(String doc_id, String info_type);
+
+  abstract public int getNumChildren(String node_id) ;
+  /** returns a list of the child ids in order, null if no children
+   */
+  abstract public ArrayList<String> getChildrenIds(String node_id);
     /** returns true if the node has a parent 
      */
     abstract public boolean hasParent(String node_id);
+
+	/**
+	 * returns the node id of the parent node, null if no parent 
+	 */
+  abstract public String getParentId(String node_id);
+
+  /** 
+   * returns the node id of the root node of the document containing node_id
+   */
+  abstract public String getRootId(String node_id);
+
+  /** returns the list of sibling ids, including the specified node_id */
+  abstract public ArrayList<String> getSiblingIds(String node_id);
 }
Index: /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/BasicDocument.java
===================================================================
--- /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/BasicDocument.java	(revision 26044)
+++ /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/BasicDocument.java	(revision 26045)
@@ -18,4 +18,6 @@
  */
 package org.greenstone.gsdl3.util;
+
+import java.util.ArrayList;
 
 import org.apache.log4j.*;
@@ -39,4 +41,23 @@
     }
 
+	/**
+	 * returns the structural information asked for. info_type may be one of
+	 * INFO_NUM_SIBS, INFO_NUM_CHILDREN, INFO_SIB_POS, INFO_DOC_TYPE
+	 */
+  public String getStructureInfo(String doc_id, String info_type) {
+    if (info_type.equals(INFO_NUM_SIBS)) {
+      return "0";
+    }
+    if (info_type.equals(INFO_NUM_CHILDREN)) {
+      return "0";
+    }
+    if (info_type.equals(INFO_SIB_POS)) {
+      return "-1";
+    }
+    if (info_type.equals(INFO_DOC_TYPE)) {
+      return getDocType(doc_id);
+    }
+    return null;
+  }
     /** returns the document type of the doc that the specified node 
 	belongs to. should be one of 
@@ -62,5 +83,13 @@
 	return false;
     }
-
+  public int getNumChildren(String node_id) {
+    return 0;
+  }
+  /** returns a list of the child ids in order, null if no children
+   * default implementation: return null
+   */
+  public ArrayList<String> getChildrenIds(String node_id) {
+    return null;
+  }
     /** returns true if the node has a parent 
      * default implementation returns false, over ride if documents can be 
@@ -69,3 +98,43 @@
 	return false;
     }
+
+	/**
+	 * returns the node id of the parent node, null if no parent 
+	 * default implementation return null
+	 */
+  public String getParentId(String node_id) {
+    return null;
+  }
+
+  /** 
+   * returns the node id of the root node of the document containing node_id
+   * default implementation: return node_id
+   */
+  public String getRootId(String node_id) {
+    return node_id;
+  }
+	/**
+	 * adds all the children of doc_id to the doc element, and if
+	 * recursive=true, adds all their children as well
+	 * default implementation: do nothing
+	 */
+  public void addDescendants(Element doc, String doc_id, boolean recursive) {
+    return;
+  }
+
+	/**
+	 * adds all the siblings of current_id to the parent element. returns the
+	 * new current element
+	 */
+  public Element addSiblings(Element parent_node, String parent_id, String current_id)
+  {
+    return null;
+  }
+
+  /** returns the list of sibling ids, including the specified node_id */
+  public ArrayList<String> getSiblingIds(String node_id) {
+    return null;
+  }
+
+ 
 }
Index: /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/BasicDocumentDatabase.java
===================================================================
--- /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/BasicDocumentDatabase.java	(revision 26044)
+++ /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/BasicDocumentDatabase.java	(revision 26045)
@@ -36,7 +36,9 @@
 import java.util.Map;
 import java.util.Set;
+import java.util.StringTokenizer;
 import java.util.Iterator;
 import java.io.File;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.*;
 
@@ -136,4 +138,67 @@
     }
     
+	/**
+	 * returns the structural information asked for. info_type may be one of
+	 * INFO_NUM_SIBS, INFO_NUM_CHILDREN, INFO_SIB_POS, INFO_DOC_TYPE
+	 */
+  public String getStructureInfo(String doc_id, String info_type){
+		String value = "";
+		if (info_type.equals(INFO_NUM_SIBS))
+		{
+			String parent_id = OID.getParent(doc_id);
+			if (parent_id.equals(doc_id))
+			{
+				value = "0";
+			}
+			else
+			{
+				value = String.valueOf(getNumChildren(parent_id));
+			}
+			return value;
+		}
+
+		if (info_type.equals(INFO_NUM_CHILDREN))
+		{
+			return String.valueOf(getNumChildren(doc_id));
+		}
+
+		if (info_type.equals(INFO_SIB_POS))
+		{
+			String parent_id = OID.getParent(doc_id);
+			if (parent_id.equals(doc_id))
+			{
+				return "-1";
+			}
+
+			DBInfo info = this.coll_db.getInfo(parent_id);
+			if (info == null)
+			{
+				return "-1";
+			}
+
+			String contains = info.getInfo("contains");
+			contains = StringUtils.replace(contains, "\"", parent_id);
+			String[] children = contains.split(";");
+			for (int i = 0; i < children.length; i++)
+			{
+				String child_id = children[i];
+				if (child_id.equals(doc_id))
+				{
+					return String.valueOf(i + 1); // make it from 1 to length
+
+				}
+			}
+
+			return "-1";
+		}
+		if (info_type.equals(INFO_DOC_TYPE))
+	
+		{
+		  return getDocType(doc_id);
+		}
+		return null;
+
+  }
+
     /** returns true if the node has child nodes */
     public boolean hasChildren(String node_id){
@@ -149,4 +214,46 @@
     }
     
+  public int getNumChildren(String node_id) {
+		DBInfo info = this.coll_db.getInfo(node_id);
+		if (info == null)
+		{
+			return 0;
+		}
+		String contains = info.getInfo("contains");
+		if (contains.equals(""))
+		{
+			return 0;
+		}
+		String[] children = contains.split(";");
+		return children.length;
+
+
+  }
+  /** returns a list of the child ids in order, null if no children
+   */
+  public ArrayList<String> getChildrenIds(String node_id) {
+		DBInfo info = this.coll_db.getInfo(node_id);
+		if (info == null)
+		{
+			return null;
+		}
+
+		String contains = info.getInfo("contains");
+		if (contains.equals(""))
+		{
+			return null;
+		}
+		ArrayList<String> children = new ArrayList<String>();
+		StringTokenizer st = new StringTokenizer(contains, ";");
+		while (st.hasMoreTokens())
+		{
+			String child_id = StringUtils.replace(st.nextToken(), "\"", node_id);
+			children.add(child_id);
+		}
+		return children;
+
+
+  }
+
     /** returns true if the node has a parent */
     public boolean hasParent(String node_id){
@@ -157,5 +264,24 @@
 	return true;
     }
+	/**
+	 * returns the node id of the parent node, null if no parent 
+	 */
+  public String getParentId(String node_id) {
+		String parent = OID.getParent(node_id);
+		if (parent.equals(node_id))
+		{
+			return null;
+		}
+		return parent;
+
+  }
     
+  /** 
+   * returns the node id of the root node of the document containing node_id
+   */
+  public String getRootId(String node_id) {
+    return OID.getTop(node_id);
+  }
+
    /** convert indexer internal id to Greenstone oid */
     public String internalNum2OID(long docnum)
@@ -169,4 +295,64 @@
     }
 
+
+	/**
+	 * adds all the children of doc_id to the doc element, and if
+	 * recursive=true, adds all their children as well
+	 */
+	public void addDescendants(Element doc, String doc_id, boolean recursive)
+	{
+		ArrayList<String> child_ids = getChildrenIds(doc_id);
+		if (child_ids == null)
+			return;
+		for (int i = 0; i < child_ids.size(); i++)
+		{
+			String child_id = child_ids.get(i);
+			Element child_elem = createDocNode(child_id);
+			doc.appendChild(child_elem);
+			if (recursive && (!child_elem.getAttribute(GSXML.NODE_TYPE_ATT).equals(GSXML.NODE_TYPE_LEAF) || child_elem.getAttribute(GSXML.DOC_TYPE_ATT).equals(GSXML.DOC_TYPE_PAGED)))
+			{
+				addDescendants(child_elem, child_id, recursive);
+			}
+		}
+	}
+
+	/**
+	 * adds all the siblings of current_id to the parent element. returns the
+	 * new current element
+	 */
+	public Element addSiblings(Element parent_node, String parent_id, String current_id)
+	{
+		Element current_node = GSXML.getFirstElementChild(parent_node);//(Element)parent_node.getFirstChild();
+		if (current_node == null)
+		{
+			// create a sensible error message
+			logger.error(" there should be a first child.");
+			return null;
+		}
+		// remove the current child,- will add it in later in its correct place
+		parent_node.removeChild(current_node);
+
+		// add in all the siblings,
+		addDescendants(parent_node, parent_id, false);
+
+		// find the node that is now the current node
+		// this assumes that the new node that was created is the same as 
+		// the old one that was removed - we may want to replace the new one 
+		// with the old one.
+		Element new_current = GSXML.getNamedElement(parent_node, current_node.getNodeName(), GSXML.NODE_ID_ATT, current_id);
+		return new_current;
+	}
+
+  /** returns the list of sibling ids, including the specified node_id */
+  public ArrayList<String> getSiblingIds(String node_id){
+		String parent_id = getParentId(node_id);
+		if (parent_id == null)
+		{
+			return null;
+		}
+		return getChildrenIds(parent_id);
+
+  }
+
 }
 
