| /* |
| * 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.xalan.stree; |
| |
| import org.apache.xml.utils.DOMBuilder; |
| import org.apache.xml.utils.XMLCharacterRecognizer; |
| |
| import org.w3c.dom.Document; |
| import org.w3c.dom.DocumentFragment; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.Element; |
| |
| import org.xml.sax.Attributes; |
| |
| import javax.xml.transform.TransformerException; |
| |
| /** |
| * <meta name="usage" content="internal"/> |
| * 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 StreeDOMBuilder extends DOMBuilder |
| { |
| |
| /** This is the current text node that we don't append until the first |
| * non-text event occurs following a characters event, so the threaded |
| * transformer doesn't try and use it before it's time! |
| * WARNING: Do NOT do a getNodeValue() or the like on this node while |
| * it is accumulating text, as that will cause a string to be made, and |
| * no more text will be obtained by the node! */ |
| TextImpl m_text_buffer = null; |
| |
| /** Source document node */ |
| protected DocumentImpl m_docImpl; |
| |
| /** |
| * State of the source tree indicating whether the last event |
| * was a characters event. |
| */ |
| private boolean m_previousIsText = false; |
| |
| /** Indicate whether running in Debug mode */ |
| private static final boolean DEBUG = false; |
| |
| /** |
| * StreeDOMBuilder instance constructor... it will add the DOM nodes |
| * to the document fragment. |
| * |
| * @param doc Root node of DOM being created |
| * @param node Node currently being processed |
| */ |
| public StreeDOMBuilder(Document doc, Node node) |
| { |
| |
| super(doc, node); |
| |
| m_docImpl = (DocumentImpl) doc; |
| } |
| |
| /** |
| * StreeDOMBuilder instance constructor... it will add the DOM nodes |
| * to the document fragment. |
| * |
| * @param doc Root node of DOM being created |
| * @param docFrag Document fragment node of DOM being created |
| */ |
| public StreeDOMBuilder(Document doc, DocumentFragment docFrag) |
| { |
| |
| super(doc, docFrag); |
| |
| m_docImpl = (DocumentImpl) doc; |
| } |
| |
| /** |
| * StreeDOMBuilder instance constructor... it will add the DOM nodes |
| * to the document. |
| * |
| * @param doc Root node of DOM being created |
| */ |
| public StreeDOMBuilder(Document doc) |
| { |
| |
| super(doc); |
| |
| m_docImpl = (DocumentImpl) doc; |
| } |
| |
| /** |
| * 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) |
| { |
| m_docImpl.setIDAttribute(id, elem); |
| } |
| |
| /** |
| * Receive notification of the beginning of an element. |
| * |
| * |
| * @param ns namespace URL of the element |
| * @param localName local part of qualified name of the element |
| * @param name The element type name. |
| * @param atts The attributes attached to the element, if any. |
| * @see org.apache.xml.utils.DOMBuilder#startElement(String, String, String, Attributes) |
| * |
| * @throws org.xml.sax.SAXException |
| */ |
| public void startElement( |
| String ns, String localName, String name, Attributes atts) |
| throws org.xml.sax.SAXException |
| { |
| setPreviousIsText(false); |
| ElementImpl elem; |
| |
| if ((null == ns) || (ns.length() == 0)) |
| elem = (ElementImpl)m_doc.createElement(name); |
| else |
| elem = (ElementImpl)m_doc.createElementNS(ns, name); |
| |
| // if you do the append here, the element might be accessed before |
| // the attributes are added. |
| // append(elem); |
| |
| // But, in order for the document order stuff to be done correctly, we |
| // have to set the uid here, before the attributes are counted. |
| elem.m_uid = ++((DocImpl)m_doc).m_docOrderCount; |
| elem.m_level = (short) (((DocImpl)m_doc).m_level + 1); |
| |
| 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); |
| |
| if(attrNS == null) |
| attrNS = ""; // defensive, shouldn't have to do this. |
| |
| // System.out.println("attrNS: "+attrNS+", localName: "+atts.getQName(i) |
| // +", qname: "+atts.getQName(i)+", value: "+atts.getValue(i)); |
| // Crimson won't let us set an xmlns: attribute on the DOM. |
| if ((attrNS.length() == 0) || atts.getQName(i).startsWith("xmlns:")) |
| elem.setAttribute(atts.getQName(i), atts.getValue(i)); |
| else |
| { |
| |
| // elem.setAttributeNS(atts.getURI(i), atts.getLocalName(i), atts.getValue(i)); |
| elem.setAttributeNS(attrNS, atts.getQName(i), atts.getValue(i)); |
| } |
| } |
| } |
| |
| append(elem); |
| |
| m_elemStack.push(elem); |
| |
| m_currentNode = elem; |
| } |
| |
| /** |
| * Receive notification of the end of an element. |
| * |
| * @param ns namespace URL of the element |
| * @param localName local part of qualified name of the element |
| * @param name The element type name |
| * |
| * @throws org.xml.sax.SAXException |
| */ |
| public void endElement(String ns, String localName, String name) |
| throws org.xml.sax.SAXException |
| { |
| setPreviousIsText(false); |
| // ((Parent)getCurrentNode()).setComplete(true); |
| super.endElement(ns, localName, name); |
| } |
| |
| /** |
| * Receive notification of character data. |
| * |
| * |
| * @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. |
| * |
| * @throws org.xml.sax.SAXException |
| * |
| * @throws org.xml.sax.SAXException |
| */ |
| public void characters(char ch[], int start, int length) |
| throws org.xml.sax.SAXException |
| { |
| if(DEBUG) |
| { |
| System.out.print("SourceTreeDOMBuilder#characters: "); |
| int n = start+length; |
| for (int i = start; i < n; i++) |
| { |
| if(Character.isWhitespace(ch[i])) |
| System.out.print("\\"+((int)ch[i])); |
| else |
| System.out.print(ch[i]); |
| } |
| System.out.println(""); |
| } |
| |
| if (getPreviousIsText()) |
| appendAccumulatedText(m_text_buffer, ch, start, length); |
| else |
| { |
| // if (m_inCData) |
| // { |
| // CDATA SECTIONS DON'T REALLY WORK IN THE STREE. I WOULD |
| // LEAVE THIS, BUT THE APPEND MODE MAKES IT STRANGE... |
| // m_text_buffer = (new CDATASectionImpl(m_docImpl, ch, start, |
| // length)); |
| // } |
| // else |
| m_text_buffer = (new TextImpl(m_docImpl, ch, start, length)); |
| |
| setPreviousIsText(true); |
| } |
| |
| } |
| |
| /** |
| * Receive notification of ignorable whitespace in element content. |
| * |
| * |
| * @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. |
| * |
| * @throws org.xml.sax.SAXException |
| * |
| * @throws org.xml.sax.SAXException |
| */ |
| public void ignorableWhitespace(char ch[], int start, int length) |
| throws org.xml.sax.SAXException |
| { |
| |
| if (getPreviousIsText()) |
| appendAccumulatedText(m_text_buffer, ch, start, length); |
| else |
| m_text_buffer = new TextImpl(m_docImpl, ch, start, length); |
| |
| setPreviousIsText(true); |
| } |
| |
| /** |
| * 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 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. |
| * |
| * @throws org.xml.sax.SAXException |
| * |
| * @throws org.xml.sax.SAXException |
| */ |
| public void charactersRaw(char ch[], int start, int length) |
| throws org.xml.sax.SAXException |
| { |
| |
| setPreviousIsText(false); |
| append(m_doc.createProcessingInstruction("xslt-next-is-raw", |
| "formatter-to-dom")); |
| append(new TextImpl(m_docImpl, ch, start, length)); |
| } |
| |
| /** |
| * Report an XML comment anywhere in the document. |
| * |
| * |
| * @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. |
| * |
| * @throws org.xml.sax.SAXException |
| * |
| * @throws org.xml.sax.SAXException |
| */ |
| public void comment(char ch[], int start, int length) |
| throws org.xml.sax.SAXException |
| { |
| setPreviousIsText(false); |
| append(new CommentImpl(m_docImpl, ch, start, length)); |
| } |
| |
| |
| /** |
| * Receive notification of ignorable whitespace in element content. |
| * |
| * @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 processingInstruction(String target, String data) |
| throws org.xml.sax.SAXException |
| { |
| setPreviousIsText(false); |
| super.processingInstruction(target, data); |
| } |
| |
| /** |
| * Set the state of the source tree to indicate whether the last event |
| * was a characters event. |
| * |
| * |
| * @param isText True if last event was a characters event |
| * |
| * |
| * @throws org.xml.sax.SAXException |
| */ |
| public void setPreviousIsText(boolean isText) throws org.xml.sax.SAXException |
| { |
| |
| if (m_previousIsText && !isText) |
| { |
| if (!( m_docImpl.m_sourceTreeHandler.getShouldStripWhitespace() |
| && m_text_buffer.isWhitespace() )) |
| { |
| append(m_text_buffer); |
| } |
| |
| m_text_buffer = null; |
| } |
| |
| m_previousIsText = isText; |
| } |
| |
| /** |
| * 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 |
| { |
| if(DEBUG) |
| { |
| System.out.println("SourceTreeDOMBuilder#endDocument"); |
| } |
| super.endDocument(); |
| setPreviousIsText(false); |
| } |
| |
| |
| /** |
| * Get the state of the source tree indicating whether the last event |
| * was a characters event. |
| * |
| * |
| * @return True if last event was a characters event |
| * |
| */ |
| boolean getPreviousIsText() |
| { |
| return m_previousIsText; |
| } |
| |
| /** |
| * Append the text from this characters event to the previous text. |
| * |
| * |
| * @param textNode Text node created by previous characters event. |
| * |
| * @param currentNode The current node. |
| * @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. |
| * |
| * |
| * @throws org.xml.sax.SAXException |
| */ |
| void appendAccumulatedText( |
| Node currentNode, char ch[], int start, int length) |
| throws org.xml.sax.SAXException |
| { |
| |
| // Currently, we don't call this unless there _is_ a text |
| // outstanding... I think... but let's be paranoid. |
| if (m_text_buffer == null) |
| { |
| m_text_buffer = new TextImpl(m_docImpl, ch, start, length); |
| |
| setPreviousIsText(true); |
| } |
| else |
| ((TextImpl) m_text_buffer).appendText(ch, start, length); |
| |
| if(DEBUG) |
| { |
| System.out.print("SourceTreeDOMBuilder#appendAccumulatedText: "); |
| int n = start+length; |
| for (int i = start; i < n; i++) |
| { |
| if(Character.isWhitespace(ch[i])) |
| System.out.print("\\"+((int)ch[i])); |
| else |
| System.out.print(ch[i]); |
| } |
| System.out.println(""); |
| } |
| |
| |
| } |
| } |