/*
* $Id: DOMBuilder.java 472634 2006-11-08 20:43:55Z jycli $
*/
package org.apache.xml.utils;
import java.util.Stack;
import java.util.Vector;
import org.apache.xml.res.XMLErrorResources;
import org.apache.xml.res.XMLMessages;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Text;
import org.w3c.dom.CDATASection;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.ext.LexicalHandler;
/**
* This class takes SAX events (in addition to some extra events
* that SAX doesn't handle yet) and adds the result to a document
* or document fragment.
* @xsl.usage general
*/
public class DOMBuilder
implements ContentHandler, LexicalHandler
{
/** Root document */
public Document m_doc;
/** Current node */
protected Node m_currentNode = null;
/** The root node */
protected Node m_root = null;
/** The next sibling node */
protected Node m_nextSibling = null;
/** First node of document fragment or null if not a DocumentFragment */
public DocumentFragment m_docFrag = null;
/** Vector of element nodes */
protected Stack m_elemStack = new Stack();
/** Namespace support */
protected Vector m_prefixMappings = new Vector();
/**
* DOMBuilder instance constructor... it will add the DOM nodes
* to the document fragment.
*
* @param doc Root document
* @param node Current node
*/
public DOMBuilder(Document doc, Node node)
{
m_doc = doc;
m_currentNode = m_root = node;
if (node instanceof Element)
m_elemStack.push(node);
}
/**
* DOMBuilder instance constructor... it will add the DOM nodes
* to the document fragment.
*
* @param doc Root document
* @param docFrag Document fragment
*/
public DOMBuilder(Document doc, DocumentFragment docFrag)
{
m_doc = doc;
m_docFrag = docFrag;
}
/**
* DOMBuilder instance constructor... it will add the DOM nodes
* to the document.
*
* @param doc Root document
*/
public DOMBuilder(Document doc)
{
m_doc = doc;
}
/**
* Get the root document or DocumentFragment of the DOM being created.
*
* @return The root document or document fragment if not null
*/
public Node getRootDocument()
{
return (null != m_docFrag) ? (Node) m_docFrag : (Node) m_doc;
}
/**
* Get the root node of the DOM tree.
*/
public Node getRootNode()
{
return m_root;
}
/**
* Get the node currently being processed.
*
* @return the current node being processed
*/
public Node getCurrentNode()
{
return m_currentNode;
}
/**
* Set the next sibling node, which is where the result nodes
* should be inserted before.
*
* @param nextSibling the next sibling node.
*/
public void setNextSibling(Node nextSibling)
{
m_nextSibling = nextSibling;
}
/**
* Return the next sibling node.
*
* @return the next sibling node.
*/
public Node getNextSibling()
{
return m_nextSibling;
}
/**
* Return null since there is no Writer for this class.
*
* @return null
*/
public java.io.Writer getWriter()
{
return null;
}
/**
* Append a node to the current container.
*
* @param newNode New node to append
*/
protected void append(Node newNode) throws org.xml.sax.SAXException
{
Node currentNode = m_currentNode;
if (null != currentNode)
{
if (currentNode == m_root && m_nextSibling != null)
currentNode.insertBefore(newNode, m_nextSibling);
else
currentNode.appendChild(newNode);
// System.out.println(newNode.getNodeName());
}
else if (null != m_docFrag)
{
if (m_nextSibling != null)
m_docFrag.insertBefore(newNode, m_nextSibling);
else
m_docFrag.appendChild(newNode);
}
else
{
boolean ok = true;
short type = newNode.getNodeType();
if (type == Node.TEXT_NODE)
{
String data = newNode.getNodeValue();
if ((null != data) && (data.trim().length() > 0))
{
throw new org.xml.sax.SAXException(
XMLMessages.createXMLMessage(
XMLErrorResources.ER_CANT_OUTPUT_TEXT_BEFORE_DOC, null)); //"Warning: can't output text before document element! Ignoring...");
}
ok = false;
}
else if (type == Node.ELEMENT_NODE)
{
if (m_doc.getDocumentElement() != null)
{
ok = false;
throw new org.xml.sax.SAXException(
XMLMessages.createXMLMessage(
XMLErrorResources.ER_CANT_HAVE_MORE_THAN_ONE_ROOT, null)); //"Can't have more than one root on a DOM!");
}
}
if (ok)
{
if (m_nextSibling != null)
m_doc.insertBefore(newNode, m_nextSibling);
else
m_doc.appendChild(newNode);
}
}
}
/**
* Receive an object for locating the origin of SAX document events.
*
* <p>SAX parsers are strongly encouraged (though not absolutely
* required) to supply a locator: if it does so, it must supply
* the locator to the application by invoking this method before
* invoking any of the other methods in the ContentHandler
* interface.</p>
*
* <p>The locator allows the application to determine the end
* position of any document-related event, even if the parser is
* not reporting an error. Typically, the application will
* use this information for reporting its own errors (such as
* character content that does not match an application's
* business rules). The information returned by the locator
* is probably not sufficient for use with a search engine.</p>
*
* <p>Note that the locator will return correct information only
* during the invocation of the events in this interface. The
* application should not attempt to use it at any other time.</p>
*
* @param locator An object that can return the location of
* any SAX document event.
* @see org.xml.sax.Locator
*/
public void setDocumentLocator(Locator locator)
{
// No action for the moment.
}
/**
* Receive notification of the beginning of a document.
*
* <p>The SAX parser will invoke this method only once, before any
* other methods in this interface or in DTDHandler (except for
* setDocumentLocator).</p>
*/
public void startDocument() throws org.xml.sax.SAXException
{
// No action for the moment.
}
/**
* Receive notification of the end of a document.
*
* <p>The SAX parser will invoke this method only once, and it will
* be the last method invoked during the parse. The parser shall
* not invoke this method until it has either abandoned parsing
* (because of an unrecoverable error) or reached the end of
* input.</p>
*/
public void endDocument() throws org.xml.sax.SAXException
{
// No action for the moment.
}
/**
* Receive notification of the beginning of an element.
*
* <p>The Parser will invoke this method at the beginning of every
* element in the XML document; there will be a corresponding
* endElement() event for every startElement() event (even when the
* element is empty). All of the element's content will be
* reported, in order, before the corresponding endElement()
* event.</p>
*
* <p>If the element name has a namespace prefix, the prefix will
* still be attached. Note that the attribute list provided will
* contain only attributes with explicit values (specified or
* defaulted): #IMPLIED attributes will be omitted.</p>
*
*
* @param ns The namespace of the node
* @param localName The local part of the qualified name
* @param name The element name.
* @param atts The attributes attached to the element, if any.
* @see #