package ca.umontreal.iro.rali;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
/**
* Data structure for a DOM tree limited to a single path from the root to a single bottom node.
* The bottom node can have many children but in this case, no new children will be appended to it.
*
* @author Guy Lapalme, Université de Montréal, 2009
*
*/
/**
* @author Guy Lapalme, Université de Montréal, 2009
*
*/
public class SpineDocument {
/**
* to create a DOM document builder
*/
private DocumentBuilderFactory factory;
/**
* to create a DOM document
*/
private DocumentBuilder builder;
/**
* To keep the skeleton document
*/
private Document doc;
/**
* Bottom node of the skeleton document
*/
private Node bottom;
SpineDocument() throws ParserConfigurationException{
// create spine document to create elements
factory = DocumentBuilderFactory.newInstance();
builder = factory.newDocumentBuilder();
doc = builder.newDocument();
bottom = null;
}
protected Document getDoc(){
return doc;
}
protected Node getBottom(){
return bottom;
}
/**
* Insert a node n
at the bottom of the spine document
* Initialize the spine document if necessary
* Modify the bottom
global variable
*
* @param n node to add at the bottom
* @throws DOMException
* @throws XMLStreamException
*/
protected void addBottom(Node n){
if(bottom==null){
bottom=doc.appendChild(n);
for(Map.Entrye:Options.nsContext.getAllPrefixesURI())
doc.getDocumentElement().setAttributeNS(Options.XML_NS, "xmlns:"+e.getKey(), e.getValue());
} else
bottom=bottom.appendChild(n);
}
/**
* remove the bottom node (and all its children) by modifying the bottom
global variable
*/
protected void removeBottom(){
bottom=(Node)bottom.getParentNode();
if (bottom!=null)
for(Node c= bottom.getFirstChild();c!=null;c=bottom.getFirstChild()){
bottom.removeChild(bottom.getFirstChild());
}
}
/**
* set the bottom element to its parent
*/
protected void upBottom(){
bottom=(Node)bottom.getParentNode();
}
/**
* remove all nodes (and its children) from bottom up to (and including) n
* @param n
*/
protected void removeUpTo(Node n){
while(bottom!=n)removeBottom();
removeBottom();
}
/**
* count the number of nodes in the document
* useful for debugging memory leaks...
* @return number of nodes
*/
protected int nbNodes(){
return nbNodes(doc.getDocumentElement());
}
/**
* count the number of nodes starting from a given node
* @param n the node to start the count from
* @return number of nodes
*/
protected int nbNodes(Node n){
int res=1;
for(Node c=n.getFirstChild();c!=null;c=c.getNextSibling())
res+=nbNodes(c);
return res;
}
// create string for accessing the leaf node of the document rooted at "node"
/**
* Creates a string representation of the skeleton document starting from a given node
*
* @param node node of the skeleton document
* @return String showing the path expression of the leaf of the skeleton document
*/
protected String buildXPath(Node node){
if(node==null)return "";
StringBuilder sb=new StringBuilder();
if(node.getNodeType()==Node.ELEMENT_NODE){
sb.append("/"); // output element name with the appropriate namespace
String uri=node.getNamespaceURI();
if(uri!=null){
String prefix = node.getPrefix();
if(prefix==null){
prefix=Options.nsContext.getOrGenPrefix(uri);
if(prefix.length()>0)sb.append(prefix).append(":");
} else
Options.nsContext.addPrefixURI(prefix, uri);
}
sb.append(node.getNodeName());
if(Options.outAttr || Options.outAttrVal){ // output attributes
NamedNodeMap attrs=node.getAttributes();
int nbAttrs = attrs.getLength();
for(int i=0;i