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