- /*
- * The Apache Software License, Version 1.1
- *
- *
- * Copyright (c) 1999 The Apache Software Foundation. All rights
- * reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * 3. The end-user documentation included with the redistribution,
- * if any, must include the following acknowledgment:
- * "This product includes software developed by the
- * Apache Software Foundation (http://www.apache.org/)."
- * Alternately, this acknowledgment may appear in the software itself,
- * if and wherever such third-party acknowledgments normally appear.
- *
- * 4. The names "Xalan" and "Apache Software Foundation" must
- * not be used to endorse or promote products derived from this
- * software without prior written permission. For written
- * permission, please contact apache@apache.org.
- *
- * 5. Products derived from this software may not be called "Apache",
- * nor may "Apache" appear in their name, without prior written
- * permission of the Apache Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation and was
- * originally based on software copyright (c) 1999, Lotus
- * Development Corporation., http://www.lotus.com. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- */
- package org.apache.xml.utils;
-
- import org.apache.xalan.res.XSLMessages;
- import org.apache.xpath.res.XPATHErrorResources;
- import org.apache.xml.utils.NodeVector;
- import java.util.Stack;
-
- import org.xml.sax.ext.LexicalHandler;
- import org.xml.sax.ContentHandler;
- import org.xml.sax.Locator;
- import org.xml.sax.Attributes;
-
- import org.w3c.dom.*; // we pretty much use everything in the DOM here.
-
- /**
- * <meta name="usage" content="general"/>
- * 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.
- */
- public class DOMBuilder
- implements ContentHandler, LexicalHandler
- {
-
- /** Root document */
- public Document m_doc;
-
- /** Current node */
- protected Node m_currentNode = 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();
-
- /**
- * 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 = 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 node of the DOM being created. This
- * is either a Document or a DocumentFragment.
- *
- * @return The root document or document fragment if not null
- */
- public Node getRootNode()
- {
- return (null != m_docFrag) ? (Node) m_docFrag : (Node) m_doc;
- }
-
- /**
- * Get the node currently being processed.
- *
- * @return the current node being processed
- */
- public Node getCurrentNode()
- {
- return m_currentNode;
- }
-
- /**
- * 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)
- {
- currentNode.appendChild(newNode);
-
- // System.out.println(newNode.getNodeName());
- }
- else if (null != m_docFrag)
- {
- 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(
- XSLMessages.createXPATHMessage(
- XPATHErrorResources.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)
- {
- throw new org.xml.sax.SAXException(
- XSLMessages.createXPATHMessage(
- XPATHErrorResources.ER_CANT_HAVE_MORE_THAN_ONE_ROOT, null)); //"Can't have more than one root on a DOM!");
- }
- }
-
- if (ok)
- 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 #endElement
- * @see org.xml.sax.Attributes
- */
- public void startElement(
- String ns, String localName, String name, Attributes atts)
- throws org.xml.sax.SAXException
- {
-
- Element elem;
-
- // Note that the namespace-aware call must be used to correctly
- // construct a Level 2 DOM, even for non-namespaced nodes.
- if ((null == ns) || (ns.length() == 0))
- elem = m_doc.createElementNS(null,name);
- else
- elem = m_doc.createElementNS(ns, name);
-
- append(elem);
-
- try
- {
- int nAtts = atts.getLength();
-
- if (0 != nAtts)
- {
- for (int i = 0; i < nAtts; i++)
- {
-
- //System.out.println("type " + atts.getType(i) + " name " + atts.getLocalName(i) );
- // First handle a possible ID attribute
- if (atts.getType(i).equalsIgnoreCase("ID"))
- setIDAttribute(atts.getValue(i), elem);
-
- String attrNS = atts.getURI(i);
-
- String attrQName = atts.getQName(i);
- if((attrQName.equals("xmlns") || attrQName.startsWith("xmlns:")) )
- {
- elem.setAttributeNS("http://www.w3.org/2000/xmlns/",attrQName, atts.getValue(i));
- }
- else
- {
- elem.setAttributeNS(atts.getURI(i),attrQName, atts.getValue(i));
- }
- }
- }
-
- // append(elem);
-
- m_elemStack.push(elem);
-
- m_currentNode = elem;
-
- // append(elem);
- }
- catch(java.lang.Exception de)
- {
- // de.printStackTrace();
- throw new org.xml.sax.SAXException(de);
- }
-
- }
-
- /**
- * Receive notification of the end of an element.
- *
- * <p>The SAX parser will invoke this method at the end of every
- * element in the XML document; there will be a corresponding
- * startElement() event for every endElement() event (even when the
- * element is empty).</p>
- *
- * <p>If the element name has a namespace prefix, the prefix will
- * still be attached to the name.</p>
- *
- *
- * @param ns the namespace of the element
- * @param localName The local part of the qualified name of the element
- * @param name The element name
- */
- public void endElement(String ns, String localName, String name)
- throws org.xml.sax.SAXException
- {
- m_elemStack.pop();
- m_currentNode = m_elemStack.isEmpty() ? null : (Node)m_elemStack.peek();
- }
-
- /**
- * Set an ID string to node association in the ID table.
- *
- * @param id The ID string.
- * @param elem The associated ID.
- */
- public void setIDAttribute(String id, Element elem)
- {
-
- // Do nothing. This method is meant to be overiden.
- }
-
- /**
- * Receive notification of character data.
- *
- * <p>The Parser will call this method to report each chunk of
- * character data. SAX parsers may return all contiguous character
- * data in a single chunk, or they may split it into several
- * chunks; however, all of the characters in any single event
- * must come from the same external entity, so that the Locator
- * provides useful information.</p>
- *
- * <p>The application must not attempt to read from the array
- * outside of the specified range.</p>
- *
- * <p>Note that some parsers will report whitespace using the
- * ignorableWhitespace() method rather than this one (validating
- * parsers must do so).</p>
- *
- * @param ch The characters from the XML document.
- * @param start The start position in the array.
- * @param length The number of characters to read from the array.
- * @see #ignorableWhitespace
- * @see org.xml.sax.Locator
- */
- public void characters(char ch[], int start, int length) throws org.xml.sax.SAXException
- {
- if(isOutsideDocElem()
- && org.apache.xml.utils.XMLCharacterRecognizer.isWhiteSpace(ch, start, length))
- return; // avoid DOM006 Hierarchy request error
-
- if (m_inCData)
- {
- cdata(ch, start, length);
-
- return;
- }
-
- String s = new String(ch, start, length);
- Text text = m_doc.createTextNode(s);
-
- append(text);
- }
-
- /**
- * If available, when the disable-output-escaping attribute is used,
- * output raw text without escaping. A PI will be inserted in front
- * of the node with the name "lotusxsl-next-is-raw" and a value of
- * "formatter-to-dom".
- *
- * @param ch Array containing the characters
- * @param start Index to start of characters in the array
- * @param length Number of characters in the array
- */
- public void charactersRaw(char ch[], int start, int length)
- throws org.xml.sax.SAXException
- {
- if(isOutsideDocElem()
- && org.apache.xml.utils.XMLCharacterRecognizer.isWhiteSpace(ch, start, length))
- return; // avoid DOM006 Hierarchy request error
-
-
- String s = new String(ch, start, length);
-
- append(m_doc.createProcessingInstruction("xslt-next-is-raw",
- "formatter-to-dom"));
- append(m_doc.createTextNode(s));
- }
-
- /**
- * Report the beginning of an entity.
- *
- * The start and end of the document entity are not reported.
- * The start and end of the external DTD subset are reported
- * using the pseudo-name "[dtd]". All other events must be
- * properly nested within start/end entity events.
- *
- * @param name The name of the entity. If it is a parameter
- * entity, the name will begin with '%'.
- * @see #endEntity
- * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
- * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
- */
- public void startEntity(String name) throws org.xml.sax.SAXException
- {
-
- // Almost certainly the wrong behavior...
- // entityReference(name);
- }
-
- /**
- * Report the end of an entity.
- *
- * @param name The name of the entity that is ending.
- * @see #startEntity
- */
- public void endEntity(String name) throws org.xml.sax.SAXException{}
-
- /**
- * Receive notivication of a entityReference.
- *
- * @param name name of the entity reference
- */
- public void entityReference(String name) throws org.xml.sax.SAXException
- {
- append(m_doc.createEntityReference(name));
- }
-
- /**
- * Receive notification of ignorable whitespace in element content.
- *
- * <p>Validating Parsers must use this method to report each chunk
- * of ignorable whitespace (see the W3C XML 1.0 recommendation,
- * section 2.10): non-validating parsers may also use this method
- * if they are capable of parsing and using content models.</p>
- *
- * <p>SAX parsers may return all contiguous whitespace in a single
- * chunk, or they may split it into several chunks; however, all of
- * the characters in any single event must come from the same
- * external entity, so that the Locator provides useful
- * information.</p>
- *
- * <p>The application must not attempt to read from the array
- * outside of the specified range.</p>
- *
- * @param ch The characters from the XML document.
- * @param start The start position in the array.
- * @param length The number of characters to read from the array.
- * @see #characters
- */
- public void ignorableWhitespace(char ch[], int start, int length)
- throws org.xml.sax.SAXException
- {
- if(isOutsideDocElem())
- return; // avoid DOM006 Hierarchy request error
-
- String s = new String(ch, start, length);
-
- append(m_doc.createTextNode(s));
- }
-
- /**
- * Tell if the current node is outside the document element.
- *
- * @return true if the current node is outside the document element.
- */
- private boolean isOutsideDocElem()
- {
- return (null == m_docFrag) && m_elemStack.size() == 0 && (null == m_currentNode || m_currentNode.getNodeType() == Node.DOCUMENT_NODE);
- }
-
- /**
- * Receive notification of a processing instruction.
- *
- * <p>The Parser will invoke this method once for each processing
- * instruction found: note that processing instructions may occur
- * before or after the main document element.</p>
- *
- * <p>A SAX parser should never report an XML declaration (XML 1.0,
- * section 2.8) or a text declaration (XML 1.0, section 4.3.1)
- * using this method.</p>
- *
- * @param target The processing instruction target.
- * @param data The processing instruction data, or null if
- * none was supplied.
- */
- public void processingInstruction(String target, String data)
- throws org.xml.sax.SAXException
- {
- append(m_doc.createProcessingInstruction(target, data));
- }
-
- /**
- * Report an XML comment anywhere in the document.
- *
- * This callback will be used for comments inside or outside the
- * document element, including comments in the external DTD
- * subset (if read).
- *
- * @param ch An array holding the characters in the comment.
- * @param start The starting position in the array.
- * @param length The number of characters to use from the array.
- */
- public void comment(char ch[], int start, int length) throws org.xml.sax.SAXException
- {
- append(m_doc.createComment(new String(ch, start, length)));
- }
-
- /** Flag indicating that we are processing a CData section */
- protected boolean m_inCData = false;
-
- /**
- * Report the start of a CDATA section.
- *
- * @see #endCDATA
- */
- public void startCDATA() throws org.xml.sax.SAXException
- {
- m_inCData = true;
- }
-
- /**
- * Report the end of a CDATA section.
- *
- * @see #startCDATA
- */
- public void endCDATA() throws org.xml.sax.SAXException
- {
- m_inCData = false;
- }
-
- /**
- * Receive notification of cdata.
- *
- * <p>The Parser will call this method to report each chunk of
- * character data. SAX parsers may return all contiguous character
- * data in a single chunk, or they may split it into several
- * chunks; however, all of the characters in any single event
- * must come from the same external entity, so that the Locator
- * provides useful information.</p>
- *
- * <p>The application must not attempt to read from the array
- * outside of the specified range.</p>
- *
- * <p>Note that some parsers will report whitespace using the
- * ignorableWhitespace() method rather than this one (validating
- * parsers must do so).</p>
- *
- * @param ch The characters from the XML document.
- * @param start The start position in the array.
- * @param length The number of characters to read from the array.
- * @see #ignorableWhitespace
- * @see org.xml.sax.Locator
- */
- public void cdata(char ch[], int start, int length) throws org.xml.sax.SAXException
- {
- if(isOutsideDocElem()
- && org.apache.xml.utils.XMLCharacterRecognizer.isWhiteSpace(ch, start, length))
- return; // avoid DOM006 Hierarchy request error
-
- String s = new String(ch, start, length);
-
- append(m_doc.createCDATASection(s));
- }
-
- /**
- * Report the start of DTD declarations, if any.
- *
- * Any declarations are assumed to be in the internal subset
- * unless otherwise indicated.
- *
- * @param name The document type name.
- * @param publicId The declared public identifier for the
- * external DTD subset, or null if none was declared.
- * @param systemId The declared system identifier for the
- * external DTD subset, or null if none was declared.
- * @see #endDTD
- * @see #startEntity
- */
- public void startDTD(String name, String publicId, String systemId)
- throws org.xml.sax.SAXException
- {
-
- // Do nothing for now.
- }
-
- /**
- * Report the end of DTD declarations.
- *
- * @see #startDTD
- */
- public void endDTD() throws org.xml.sax.SAXException
- {
-
- // Do nothing for now.
- }
-
- /**
- * Begin the scope of a prefix-URI Namespace mapping.
- *
- * <p>The information from this event is not necessary for
- * normal Namespace processing: the SAX XML reader will
- * automatically replace prefixes for element and attribute
- * names when the http://xml.org/sax/features/namespaces
- * feature is true (the default).</p>
- *
- * <p>There are cases, however, when applications need to
- * use prefixes in character data or in attribute values,
- * where they cannot safely be expanded automatically; the
- * start/endPrefixMapping event supplies the information
- * to the application to expand prefixes in those contexts
- * itself, if necessary.</p>
- *
- * <p>Note that start/endPrefixMapping events are not
- * guaranteed to be properly nested relative to each-other:
- * all startPrefixMapping events will occur before the
- * corresponding startElement event, and all endPrefixMapping
- * events will occur after the corresponding endElement event,
- * but their order is not guaranteed.</p>
- *
- * @param prefix The Namespace prefix being declared.
- * @param uri The Namespace URI the prefix is mapped to.
- * @see #endPrefixMapping
- * @see #startElement
- */
- public void startPrefixMapping(String prefix, String uri)
- throws org.xml.sax.SAXException
- {
-
- /*
- // Not sure if this is needed or wanted
- // Also, it fails in the stree.
- if((null != m_currentNode)
- && (m_currentNode.getNodeType() == Node.ELEMENT_NODE))
- {
- String qname;
- if(((null != prefix) && (prefix.length() == 0))
- || (null == prefix))
- qname = "xmlns";
- else
- qname = "xmlns:"+prefix;
-
- Element elem = (Element)m_currentNode;
- String val = elem.getAttribute(qname); // Obsolete, should be DOM2...?
- if(val == null)
- {
- elem.setAttributeNS("http://www.w3.org/XML/1998/namespace",
- qname, uri);
- }
- }
- */
- }
-
- /**
- * End the scope of a prefix-URI mapping.
- *
- * <p>See startPrefixMapping for details. This event will
- * always occur after the corresponding endElement event,
- * but the order of endPrefixMapping events is not otherwise
- * guaranteed.</p>
- *
- * @param prefix The prefix that was being mapping.
- * @see #startPrefixMapping
- * @see #endElement
- */
- public void endPrefixMapping(String prefix) throws org.xml.sax.SAXException{}
-
- /**
- * Receive notification of a skipped entity.
- *
- * <p>The Parser will invoke this method once for each entity
- * skipped. Non-validating processors may skip entities if they
- * have not seen the declarations (because, for example, the
- * entity was declared in an external DTD subset). All processors
- * may skip external entities, depending on the values of the
- * http://xml.org/sax/features/external-general-entities and the
- * http://xml.org/sax/features/external-parameter-entities
- * properties.</p>
- *
- * @param name The name of the skipped entity. If it is a
- * parameter entity, the name will begin with '%'.
- */
- public void skippedEntity(String name) throws org.xml.sax.SAXException{}
- }