Index: /trunk/gsdl3/src/java/org/greenstone/gsdl3/gs3build/xpointer/IdentifierXPointer.java
===================================================================
--- /trunk/gsdl3/src/java/org/greenstone/gsdl3/gs3build/xpointer/IdentifierXPointer.java	(revision 6290)
+++ /trunk/gsdl3/src/java/org/greenstone/gsdl3/gs3build/xpointer/IdentifierXPointer.java	(revision 6290)
@@ -0,0 +1,59 @@
+package org.greenstone.gsdl3.gs3build.xpointer;
+
+import org.w3c.dom.*;
+import org.apache.xpath.*;
+
+public class IdentifierXPointer extends XPointer
+{ NodeList nodes;
+
+  public IdentifierXPointer(Document document, String xpointer)
+  { super(document, xpointer);
+
+    this.xpointer = XPointer.expandXPath(this.xpointer);
+
+    this.nodes = this.getNodes();
+  }
+
+  private NodeList getNodes()
+  {
+    try {
+      NodeList nodes = XPathAPI.selectNodeList(this.document, this.xpointer, 
+					       this.document.getDocumentElement());
+      return nodes;
+    }
+    catch (Exception ex)
+    { System.out.println(ex.toString());
+    }
+    return null;
+  }
+
+  public Node getStartNode()
+  { return this.nodes.item(0);
+  }
+
+  private void recurseStringify(NodeList nodes, StringBuffer buffer)
+  { 
+    for (int n = 0; n < nodes.getLength(); n ++) {
+      Node node = nodes.item(n);
+
+      // append the actual node itself
+      this.printNode(node, buffer, false);
+
+      // then process its children...
+      if (node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
+	NodeList children = node.getChildNodes();
+	this.recurseStringify(children, buffer);
+      }
+
+      // do the close node
+      this.printNode(node, buffer, true);
+    }
+  }
+
+  public String toString()
+  {
+    StringBuffer reply = new StringBuffer();
+    this.recurseStringify(this.nodes, reply);
+    return reply.toString();
+  }
+}
Index: /trunk/gsdl3/src/java/org/greenstone/gsdl3/gs3build/xpointer/RangeToXPointer.java
===================================================================
--- /trunk/gsdl3/src/java/org/greenstone/gsdl3/gs3build/xpointer/RangeToXPointer.java	(revision 6290)
+++ /trunk/gsdl3/src/java/org/greenstone/gsdl3/gs3build/xpointer/RangeToXPointer.java	(revision 6290)
@@ -0,0 +1,69 @@
+package org.greenstone.gsdl3.gs3build.xpointer;
+
+import org.w3c.dom.*;
+import org.apache.xpath.*;
+
+public class RangeToXPointer extends XPointer
+{
+  public static final String RANGE_TO = "range-to(";
+  NodeList startNodes;
+  NodeList endNodes;
+
+  public RangeToXPointer(Document document, String xpointer)
+  { super(document, xpointer);
+
+    String fromPointer, toPointer;
+
+    int rangeAt = xpointer.indexOf(RANGE_TO);
+
+    fromPointer = xpointer.substring(0, rangeAt-1);
+    toPointer = xpointer.substring(rangeAt+RANGE_TO.length(), xpointer.length()-1);
+
+    fromPointer = XPointer.expandXPath(fromPointer);
+    toPointer   = XPointer.expandXPath(toPointer);
+    //    System.out.println(fromPointer + " " + toPointer);
+    
+    this.startNodes = this.getNodes(fromPointer);
+    this.endNodes = this.getNodes(toPointer);
+    
+    //    System.out.println(this.startNodes.item(0).toString() + " " + this.endNodes.item(0).toString());
+  }
+
+  public Node getStartNode()
+  { return this.startNodes.item(0);
+  }
+
+  private NodeList getNodes(String pointer)
+  {
+    try {
+      NodeList nodes = XPathAPI.selectNodeList(document, pointer, document.getDocumentElement());
+      return nodes;
+    }
+    catch (Exception ex)
+    { System.out.println(ex.toString());
+    }
+    return null;
+  }
+
+  private void recurseStringify(Node at, Node end, StringBuffer reply)
+  {
+    while (at != end && at != null) {
+      this.printNode(at, reply, false);
+      at = this.getNextNode(at, reply);
+      //      System.out.println(at + " " + end);
+    }
+  }
+
+  public String toString()
+  { StringBuffer reply = new StringBuffer();
+
+    // skip the first node
+    Node firstNode = this.startNodes.item(0);
+    firstNode = this.getNextNode(firstNode, reply);
+    
+    // actually, we don't, as otherwise the indexed copy of 
+    // the document would be incomplete...
+    this.recurseStringify(this.startNodes.item(0), this.endNodes.item(0), reply);
+    return reply.toString();
+  }
+}
Index: /trunk/gsdl3/src/java/org/greenstone/gsdl3/gs3build/xpointer/XPointer.java
===================================================================
--- /trunk/gsdl3/src/java/org/greenstone/gsdl3/gs3build/xpointer/XPointer.java	(revision 6290)
+++ /trunk/gsdl3/src/java/org/greenstone/gsdl3/gs3build/xpointer/XPointer.java	(revision 6290)
@@ -0,0 +1,247 @@
+package org.greenstone.gsdl3.gs3build.xpointer;
+
+import java.io.File; 
+
+import java.net.URL;
+
+import javax.xml.parsers.*;
+
+import org.w3c.dom.*;
+import org.apache.xpath.*;
+import org.w3c.tidy.Tidy;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import org.greenstone.gsdl3.gs3build.doctypes.HTMLDocumentTools;
+import org.greenstone.gsdl3.gs3build.util.HTMLTidy;
+
+public abstract class XPointer
+{
+  Document document;  
+  String   xpointer;
+
+  public XPointer(org.w3c.dom.Document document, String xpointer)
+  { this.document = document;
+    this.xpointer = xpointer;
+    // now process the type of description
+  }
+
+  public static String expandXPath(String xpath)
+  {
+    int          dashAt;
+    String       pathElement;
+    StringBuffer expandedPtr = new StringBuffer();
+
+    if (xpath.startsWith("//")) {
+      xpath = xpath.substring(2);
+      expandedPtr.append("//");
+    }
+    else if (xpath.startsWith("/")) {
+      xpath = xpath.substring(1);
+      expandedPtr.append("/");
+    }
+
+    while (xpath.length() > 0) {
+      dashAt = xpath.indexOf('/');
+      if (dashAt >= 0) {
+	pathElement = xpath.substring(0, dashAt);
+	xpath = xpath.substring(dashAt+1);
+      }
+      else {
+	pathElement = xpath;
+	xpath = "";
+      }
+
+      int c = 0;
+      while (c < pathElement.length()) {
+	if (!Character.isDigit(pathElement.charAt(c))) 
+	  break;
+	c ++;
+      }
+
+      if (c == pathElement.length()) {
+	expandedPtr.append("*[");
+	expandedPtr.append(pathElement);
+	expandedPtr.append("]");
+      }
+      else {
+	expandedPtr.append(pathElement);
+      }
+
+      if (dashAt >= 0) {
+	expandedPtr.append('/');
+      }
+    }
+    xpath = expandedPtr.toString();
+
+    return xpath;
+  }
+
+  public static XPointer processXPointer(Document document, String xpointer)
+  { // do a "cheap and dirty" detection of range-to
+    if (xpointer.indexOf("range-to") >= 0) {
+      return new RangeToXPointer(document, xpointer);
+    } 
+    return new IdentifierXPointer(document, xpointer);
+  }
+
+  public static XPointer processXPointer(Document document, URL url)
+  { String reference = url.getRef();
+
+    reference.trim();
+    if (reference.startsWith("xpointer(")) {
+      reference = reference.substring(9, reference.length() - 1);
+    }
+    else {
+      return null;
+    }
+    //    System.out.println(reference);
+    
+    return processXPointer(document, reference);
+  }
+
+  public static void printNode(Node node, StringBuffer to, boolean close)
+  {
+    if (node.getNodeType() == org.w3c.dom.Node.TEXT_NODE) {
+      if (!close) {
+	to.append(node.getNodeValue());
+      }
+    }
+    else if (node.getNodeType() == org.w3c.dom.Node.ENTITY_NODE) {
+      if (!close) {
+	to.append("&");
+	to.append(node.getNodeValue());
+	to.append(";");
+      }
+    }
+    else if (node.getNodeType() == org.w3c.dom.Node.COMMENT_NODE) {
+      if (!close) {
+	to.append("<!-- ");
+	to.append(node.getNodeValue());
+	to.append(" -->");
+      }
+    }
+    else if (node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
+      boolean hasChildren = (node.getChildNodes().getLength() > 0);
+
+      if (close && !hasChildren) {
+	return;
+      }
+      to.append("<");
+      if (close) {
+	to.append("/");
+      }
+      to.append(node.getNodeName());
+
+      if (!close) {
+	NamedNodeMap attributes = ((Element) node).getAttributes();
+	for (int a = 0; a < attributes.getLength(); a ++) {
+	  Node attributeNode = attributes.item(a);
+	  to.append(" ");
+	  to.append(attributeNode.getNodeName());
+	  
+	  String value = attributeNode.getNodeValue();
+	  if (value != null && value.length() > 0) {
+	    to.append("=\"");
+	    to.append(value);
+	    to.append("\"");
+	  }
+	}
+      }
+
+      if (!hasChildren) {
+	to.append(" /");
+      }
+      to.append(">");
+    }
+  }
+
+  public static Node getNextNode(Node currentNode, StringBuffer buffer)
+  { Node reply;
+
+    // Try for children only the once...
+    NodeList children = currentNode.getChildNodes();
+    for (int c = 0; c < children.getLength(); c++) {
+      Node childNode = children.item(c);
+
+      if (childNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE ||
+	  childNode.getNodeType() == org.w3c.dom.Node.COMMENT_NODE ||
+	  childNode.getNodeType() == org.w3c.dom.Node.TEXT_NODE ||
+	  childNode.getNodeType() == org.w3c.dom.Node.ENTITY_NODE) {
+	return childNode;
+      }
+    }
+
+    // Repeat looking for a sibling & going up until done...
+    do {
+      reply = currentNode.getNextSibling();
+      if (reply != null) {
+	return reply;
+      }
+      
+      currentNode = currentNode.getParentNode();
+      if (buffer != null && currentNode != null) {
+	printNode(currentNode, buffer, true);
+      }
+    } while (currentNode != null);
+
+    return reply;
+  }
+
+  public abstract Node getStartNode();
+
+
+  public static void main(String args[])
+  {	
+    try
+    { File file = new File(args[0]);
+
+      Document document;
+      if (args[0].endsWith(".html") || args[0].endsWith(".htm")) {
+	Tidy tidy = new Tidy();
+	tidy.setMakeClean(true);
+	tidy.setXmlTags(true);
+	document = tidy.parseDOM(new java.io.FileInputStream(file), null);
+
+	HTMLTidy tidier = new HTMLTidy(file);
+	document = tidier.getDocument();
+
+	HTMLDocumentTools docTools = new HTMLDocumentTools(document);
+	docTools.findSections();
+	System.out.println("Sections found");
+      }
+      else {
+	DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+	DocumentBuilder builder = factory.newDocumentBuilder();
+	document = builder.parse(file);
+      }
+      
+      XPointer pointer;
+      if (args.length == 2) {
+	pointer = XPointer.processXPointer(document, args[1]);
+      }
+      else {
+	pointer = XPointer.processXPointer(document, args[1]+"/range-to("+args[2]+")");
+      }
+      System.out.println(pointer.toString());
+    }
+    catch (FactoryConfigurationError e) {
+      System.err.println(e);
+    }
+    catch (ParserConfigurationException ex) {
+      System.err.println(ex);
+    }
+    catch (SAXException ex) {
+      System.err.println(ex);
+    }
+    catch (java.io.IOException ex) {
+      System.err.println(ex);
+    }
+  }
+}
+
+
+
+
