/* * 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.transformer; import java.util.Enumeration; import org.apache.xalan.templates.Stylesheet; import org.apache.xalan.templates.StylesheetRoot; import org.apache.xalan.templates.ElemTemplate; import org.apache.xalan.templates.ElemTemplateElement; import org.apache.xalan.trace.TraceManager; import org.apache.xalan.trace.GenerateEvent; import org.apache.xml.utils.MutableAttrListImpl; import org.apache.xml.utils.QName; import org.apache.xml.utils.TreeWalker; import org.apache.xml.utils.ObjectPool; import org.apache.xml.utils.XMLCharacterRecognizer; import org.apache.xpath.objects.XObject; import org.apache.xpath.XPathContext; import org.apache.xml.dtm.DTM; import org.apache.xml.dtm.DTMIterator; import org.apache.xml.dtm.DTMFilter; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.ext.LexicalHandler; import org.xml.sax.helpers.NamespaceSupport; import org.xml.sax.Locator; import javax.xml.transform.TransformerException; import javax.xml.transform.ErrorListener; import javax.xml.transform.Transformer; /** * This class is a layer between the direct calls to the result * tree content handler, and the transformer. For one thing, * we have to delay the call to * getContentHandler().startElement(name, atts) because of the * xsl:attribute and xsl:copy calls. In other words, * the attributes have to be fully collected before you * can call startElement. */ public class ResultTreeHandler extends QueuedEvents implements ContentHandler, LexicalHandler, TransformState, org.apache.xml.dtm.ref.dom2dtm.DOM2DTM.CharacterNodeHandler { /** Indicate whether running in Debug mode */ private static final boolean DEBUG = false; /** * Null constructor for object pooling. */ public ResultTreeHandler(){} /** * Create a new result tree handler. The real content * handler will be the ContentHandler passed as an argument. * * @param transformer non-null transformer instance * @param realHandler Content Handler instance */ public ResultTreeHandler(TransformerImpl transformer, ContentHandler realHandler) { init(transformer, realHandler); } /** * Initializer method. * * @param transformer non-null transformer instance * @param realHandler Content Handler instance */ public void init(TransformerImpl transformer, ContentHandler realHandler) { m_transformer = transformer; // m_startDoc.setTransformer(m_transformer); TraceManager tracer = transformer.getTraceManager(); if ((null != tracer) && tracer.hasTraceListeners()) m_tracer = tracer; else m_tracer = null; // m_startDoc.setTraceManager(m_tracer); m_contentHandler = realHandler; // m_startDoc.setContentHandler(m_contentHandler); if (m_contentHandler instanceof LexicalHandler) m_lexicalHandler = (LexicalHandler) m_contentHandler; else m_lexicalHandler = null; m_isTransformClient = (m_contentHandler instanceof TransformerClient); m_cloner = new ClonerToResultTree(transformer, this); // The stylesheet is set at a rather late stage, so I do // this here, though it would probably be better done elsewhere. if (null != m_transformer) m_stylesheetRoot = m_transformer.getStylesheet(); pushDocumentEvent(); // not pending yet. } /** * Bottleneck the startDocument event. * * @throws org.xml.sax.SAXException */ public void startDocument() throws org.xml.sax.SAXException{} /** * Bottleneck the endDocument event. This may be called * more than once in order to make sure the pending start * document is called. * * @throws org.xml.sax.SAXException */ public void endDocument() throws org.xml.sax.SAXException { flushPending(true); if (!m_docEnded) { m_contentHandler.endDocument(); if (null != m_tracer) { GenerateEvent ge = new GenerateEvent(m_transformer, GenerateEvent.EVENTTYPE_ENDDOCUMENT, null); m_tracer.fireGenerateEvent(ge); } m_docEnded = true; m_docPending = false; } } /** * Bottleneck the startElement event. This is used to "pend" an * element, so that attributes can still be added to it before * the real "startElement" is called on the result tree listener. * * @param ns Namespace URI of element * @param localName Local part of qname of element * @param name Name of element * @param atts List of attributes for the element * * @throws org.xml.sax.SAXException */ public void startElement( String ns, String localName, String name, Attributes atts) throws org.xml.sax.SAXException { if (DEBUG) { if (m_elemIsPending) System.out.println("(ResultTreeHandler#startElement - pended: " + m_url + "#" + m_localName); System.out.println("ResultTreeHandler#startElement: " + ns + "#" + localName); // if(null == ns) // { // (new RuntimeException(localName+" has a null namespace!")).printStackTrace(); // } } if(m_docPending) checkForSerializerSwitch(ns, localName); flushPending(true); if (!m_nsContextPushed) { if (DEBUG) System.out.println( "ResultTreeHandler#startElement - push(startElement)"); m_nsSupport.pushContext(); } if (ns != null) ensurePrefixIsDeclared(ns, name); m_name = name; m_url = ns; m_localName = localName; if (null != atts) m_attributes.addAttributes(atts); m_elemIsPending = true; m_elemIsEnded = false; if(m_isTransformClient && (null != m_transformer)) { m_snapshot.m_currentElement = m_transformer.getCurrentElement(); m_snapshot.m_currentTemplate = m_transformer.getCurrentTemplate(); m_snapshot.m_matchedTemplate = m_transformer.getMatchedTemplate(); int currentNodeHandle = m_transformer.getCurrentNode(); DTM dtm = m_transformer.getXPathContext().getDTM(currentNodeHandle); m_snapshot.m_currentNode = dtm.getNode(currentNodeHandle); m_snapshot.m_matchedNode = m_transformer.getMatchedNode(); m_snapshot.m_contextNodeList = m_transformer.getContextNodeList(); // TODO: Need to clone } // initQSE(m_startElement); m_eventCount++; } /** * Bottleneck the endElement event. * * @param ns Namespace URI of element * @param localName Local part of qname of element * @param name Name of element * * @throws org.xml.sax.SAXException */ public void endElement(String ns, String localName, String name) throws org.xml.sax.SAXException { if (DEBUG) { if (m_elemIsPending) System.out.println("(ResultTreeHandler#endElement - pended: " + m_url + "#" + m_localName); System.out.println("ResultTreeHandler#endElement: " + ns + "#" + localName); } flushPending(true); m_contentHandler.endElement(ns, localName, name); if (null != m_tracer) { GenerateEvent ge = new GenerateEvent(m_transformer, GenerateEvent.EVENTTYPE_ENDELEMENT, name); m_tracer.fireGenerateEvent(ge); } sendEndPrefixMappings(); popEvent(); if (DEBUG) System.out.println("ResultTreeHandler#startElement pop: " + localName); m_nsSupport.popContext(); } /** Indicate whether a namespace context was pushed */ boolean m_nsContextPushed = false; /** * 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. * @throws org.xml.sax.SAXException The client may throw * an exception during processing. * @see #endPrefixMapping * @see #startElement */ public void startPrefixMapping(String prefix, String uri) throws org.xml.sax.SAXException { startPrefixMapping(prefix, uri, true); } /** * Begin the scope of a prefix-URI Namespace mapping. * * * @param prefix The Namespace prefix being declared. * @param uri The Namespace URI the prefix is mapped to. * @param shouldFlush Indicate whether pending events needs * to be flushed first * * @throws org.xml.sax.SAXException The client may throw * an exception during processing. */ public void startPrefixMapping( String prefix, String uri, boolean shouldFlush) throws org.xml.sax.SAXException { if (shouldFlush) flushPending(false); if (!m_nsContextPushed) { if (DEBUG) System.out.println( "ResultTreeHandler#startPrefixMapping push(startPrefixMapping: " + prefix + ")"); m_nsSupport.pushContext(); m_nsContextPushed = true; } if (null == prefix) prefix = ""; // bit-o-hack, that that's OK String existingURI = m_nsSupport.getURI(prefix); if (null == existingURI) existingURI = ""; if (null == uri) uri = ""; if (!existingURI.equals(uri)) { if (DEBUG) { System.out.println("ResultTreeHandler#startPrefixMapping Prefix: " + prefix); System.out.println("ResultTreeHandler#startPrefixMapping uri: " + uri); } m_nsSupport.declarePrefix(prefix, 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. * @throws org.xml.sax.SAXException The client may throw * an exception during processing. * @see #startPrefixMapping * @see #endElement */ public void endPrefixMapping(String prefix) throws org.xml.sax.SAXException{} /** * Bottleneck the characters event. * * @param ch Array of characters to process * @param start start of characters in the array * @param length Number of characters in the array * * @throws org.xml.sax.SAXException */ public void characters(char ch[], int start, int length) throws org.xml.sax.SAXException { // It would be nice to suppress all whitespace before the // first element, but this is going to cause potential problems with // text serialization and with text entities (right term?). // So this really needs to be done at the serializer level. /*if (m_startDoc.isPending && XMLCharacterRecognizer.isWhiteSpace(ch, start, length)) return;*/ if (DEBUG) { System.out.print("ResultTreeHandler#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(""); } flushPending(true); m_contentHandler.characters(ch, start, length); if (null != m_tracer) { GenerateEvent ge = new GenerateEvent(m_transformer, GenerateEvent.EVENTTYPE_CHARACTERS, ch, start, length); m_tracer.fireGenerateEvent(ge); } } public void characters(org.w3c.dom.Node node) throws org.xml.sax.SAXException { flushPending(true); if(m_isTransformClient) m_snapshot.m_currentNode = node; String data = node.getNodeValue(); char [] ch = data.toCharArray(); int length = data.length(); m_contentHandler.characters(ch, 0, length); if (null != m_tracer) { GenerateEvent ge = new GenerateEvent(m_transformer, GenerateEvent.EVENTTYPE_CHARACTERS, ch, 0, length); m_tracer.fireGenerateEvent(ge); } if(m_isTransformClient) m_snapshot.m_currentNode = null; } /** * Bottleneck the ignorableWhitespace event. * * @param ch Array of characters to process * @param start start of characters in the array * @param length Number of characters in the array * * @throws org.xml.sax.SAXException */ public void ignorableWhitespace(char ch[], int start, int length) throws org.xml.sax.SAXException { if (m_docPending && XMLCharacterRecognizer.isWhiteSpace(ch, start, length)) return; flushPending(true); m_contentHandler.ignorableWhitespace(ch, start, length); if (null != m_tracer) { GenerateEvent ge = new GenerateEvent(m_transformer, GenerateEvent.EVENTTYPE_IGNORABLEWHITESPACE, ch, start, length); m_tracer.fireGenerateEvent(ge); } } /** * Bottleneck the processingInstruction event. * * @param target Processing instruction target name * @param data Processing instruction data * * @throws org.xml.sax.SAXException */ public void processingInstruction(String target, String data) throws org.xml.sax.SAXException { flushPending(true); m_contentHandler.processingInstruction(target, data); if (null != m_tracer) { GenerateEvent ge = new GenerateEvent(m_transformer, GenerateEvent.EVENTTYPE_PI, target, data); m_tracer.fireGenerateEvent(ge); } } /** * Bottleneck the comment event. * * @param data Comment data * * @throws org.xml.sax.SAXException */ public void comment(String data) throws org.xml.sax.SAXException { flushPending(true); if (null != m_lexicalHandler) { m_lexicalHandler.comment(data.toCharArray(), 0, data.length()); } if (null != m_tracer) { GenerateEvent ge = new GenerateEvent(m_transformer, GenerateEvent.EVENTTYPE_COMMENT, data); m_tracer.fireGenerateEvent(ge); } } /** * Bottleneck the comment event. * * @param ch Character array with comment data * @param start start of characters in the array * @param length number of characters in the array * * @throws org.xml.sax.SAXException */ public void comment(char ch[], int start, int length) throws org.xml.sax.SAXException { flushPending(true); if (null != m_lexicalHandler) { m_lexicalHandler.comment(ch, start, length); } if (null != m_tracer) { GenerateEvent ge = new GenerateEvent(m_transformer, GenerateEvent.EVENTTYPE_COMMENT, new String(ch, start, length)); m_tracer.fireGenerateEvent(ge); } } /** * Entity reference event. * * @param name Name of entity * * @throws org.xml.sax.SAXException */ public void entityReference(String name) throws org.xml.sax.SAXException { flushPending(true); if (null != m_lexicalHandler) { m_lexicalHandler.startEntity(name); m_lexicalHandler.endEntity(name); } if (null != m_tracer) { GenerateEvent ge = new GenerateEvent(m_transformer, GenerateEvent.EVENTTYPE_ENTITYREF, name); m_tracer.fireGenerateEvent(ge); } } /** * Start an entity. * * @param name Name of the entity * * @throws org.xml.sax.SAXException */ public void startEntity(String name) throws org.xml.sax.SAXException { flushPending(true); if (null != m_lexicalHandler) { m_lexicalHandler.startEntity(name); } } /** * End an entity. * * @param name Name of the entity * * @throws org.xml.sax.SAXException */ public void endEntity(String name) throws org.xml.sax.SAXException { flushPending(true); if (null != m_lexicalHandler) { m_lexicalHandler.endEntity(name); } if (null != m_tracer) { GenerateEvent ge = new GenerateEvent(m_transformer, GenerateEvent.EVENTTYPE_ENTITYREF, name); m_tracer.fireGenerateEvent(ge); } } /** * Start the DTD. * * @param s1 The document type name. * @param s2 The declared public identifier for the * external DTD subset, or null if none was declared. * @param s3 The declared system identifier for the * external DTD subset, or null if none was declared. * * @throws org.xml.sax.SAXException */ public void startDTD(String s1, String s2, String s3) throws org.xml.sax.SAXException { flushPending(true); if (null != m_lexicalHandler) { m_lexicalHandler.startDTD(s1, s2, s3); } } /** * End the DTD. * * @throws org.xml.sax.SAXException */ public void endDTD() throws org.xml.sax.SAXException { flushPending(true); if (null != m_lexicalHandler) { m_lexicalHandler.endDTD(); } } /** * Start the CDATACharacters. * * @throws org.xml.sax.SAXException */ public void startCDATA() throws org.xml.sax.SAXException { flushPending(true); if (null != m_lexicalHandler) { m_lexicalHandler.startCDATA(); } } /** * End the CDATA characters. * * @throws org.xml.sax.SAXException */ public void endCDATA() throws org.xml.sax.SAXException { flushPending(true); if (null != m_lexicalHandler) { m_lexicalHandler.endCDATA(); } } /** * 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 '%'. * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. */ public void skippedEntity(String name) throws org.xml.sax.SAXException{} /** * Set whether Namespace declarations have been added to * this element * * * @param b Flag indicating whether Namespace declarations * have been added to this element */ public void setNSDeclsHaveBeenAdded(boolean b) { m_nsDeclsHaveBeenAdded = b; } /** * Flush the event. * * @throws TransformerException * * @throws org.xml.sax.SAXException */ void flushDocEvent() throws org.xml.sax.SAXException { if (m_docPending) { m_contentHandler.startDocument(); if (null != m_tracer) { GenerateEvent ge = new GenerateEvent(m_transformer, GenerateEvent.EVENTTYPE_STARTDOCUMENT); m_tracer.fireGenerateEvent(ge); } if (m_contentHandler instanceof TransformerClient) { ((TransformerClient) m_contentHandler).setTransformState(this); } m_docPending = false; } } /** * Flush the event. * * @throws SAXException */ void flushElem() throws org.xml.sax.SAXException { if (m_elemIsPending) { if (null != m_name) { m_contentHandler.startElement(m_url, m_localName, m_name, m_attributes); if(null != m_tracer) { GenerateEvent ge = new GenerateEvent(m_transformer, GenerateEvent.EVENTTYPE_STARTELEMENT); m_tracer.fireGenerateEvent(ge); } if(m_isTransformClient) m_snapshot.m_currentNode = null; } m_elemIsPending = false; m_attributes.clear(); m_nsDeclsHaveBeenAdded = false; m_name = null; m_url = null; m_localName = null; m_namespaces = null; // super.flush(); } } /** * Flush the pending element. * * @throws org.xml.sax.SAXException */ public final void flushPending() throws org.xml.sax.SAXException { flushPending(true); } /** * Flush the pending element. * * @param type Event type * * NEEDSDOC @param flushPrefixes * * @throws org.xml.sax.SAXException */ public final void flushPending(boolean flushPrefixes) throws org.xml.sax.SAXException { if (flushPrefixes && m_docPending) { flushDocEvent(); } if (m_elemIsPending) { if (!m_nsDeclsHaveBeenAdded) addNSDeclsToAttrs(); sendStartPrefixMappings(); if (DEBUG) { System.out.println("ResultTreeHandler#flushPending - start flush: " + m_name); } flushElem(); if (DEBUG) { System.out.println( "ResultTreeHandler#flushPending - after flush, isPending: " + m_elemIsPending); } m_nsContextPushed = false; } } /** * Given a result tree fragment, walk the tree and * output it to the result stream. * * @param obj Result tree fragment object * @param support XPath context for the result tree fragment * * @throws org.xml.sax.SAXException */ public void outputResultTreeFragment(XObject obj, XPathContext support) throws org.xml.sax.SAXException { int doc = obj.rtf(); DTM dtm = support.getDTM(doc); for (int n = dtm.getFirstChild(doc); DTM.NULL != n; n = dtm.getNextSibling(n)) { flushPending(true); // I think. dtm.dispatchToEvents(n, this); } } /** * To fullfill the FormatterListener interface... no action * for the moment. * * @param locator Document locator */ public void setDocumentLocator(Locator locator){} /** * This function checks to make sure a given prefix is really * declared. It might not be, because it may be an excluded prefix. * If it's not, it still needs to be declared at this point. * TODO: This needs to be done at an earlier stage in the game... -sb * * @param ns Namespace URI of the element * @param rawName Raw name of element (with prefix) * * @throws org.xml.sax.SAXException */ public void ensurePrefixIsDeclared(String ns, String rawName) throws org.xml.sax.SAXException { if (ns != null && ns.length() > 0) { int index; String prefix = (index = rawName.indexOf(":")) < 0 ? "" : rawName.substring(0, index); if (null != prefix) { String foundURI = m_nsSupport.getURI(prefix); if ((null == foundURI) ||!foundURI.equals(ns)) { startPrefixMapping(prefix, ns, false); } } } } /** * This function checks to make sure a given prefix is really * declared. It might not be, because it may be an excluded prefix. * If it's not, it still needs to be declared at this point. * TODO: This needs to be done at an earlier stage in the game... -sb * * @param ns Namespace URI of the element * @param rawName Raw name of element (with prefix) * * NEEDSDOC @param dtm * NEEDSDOC @param namespace * * @throws org.xml.sax.SAXException */ public void ensureNamespaceDeclDeclared(DTM dtm, int namespace) throws org.xml.sax.SAXException { String uri = dtm.getNodeValue(namespace); String prefix = dtm.getNodeNameX(namespace); if ((uri != null && uri.length() > 0) && (null != prefix)) { String foundURI = m_nsSupport.getURI(prefix); if ((null == foundURI) ||!foundURI.equals(uri)) { startPrefixMapping(prefix, uri, false); } } } /** * Add the attributes that have been declared to the attribute list. * (Seems like I shouldn't have to do this...) * * @throws org.xml.sax.SAXException */ protected void sendStartPrefixMappings() throws org.xml.sax.SAXException { Enumeration prefixes = m_nsSupport.getDeclaredPrefixes(); ContentHandler handler = m_contentHandler; while (prefixes.hasMoreElements()) { String prefix = (String) prefixes.nextElement(); handler.startPrefixMapping(prefix, m_nsSupport.getURI(prefix)); } } /** * Add the attributes that have been declared to the attribute list. * (Seems like I shouldn't have to do this...) * * @throws org.xml.sax.SAXException */ protected void sendEndPrefixMappings() throws org.xml.sax.SAXException { Enumeration prefixes = m_nsSupport.getDeclaredPrefixes(); ContentHandler handler = m_contentHandler; while (prefixes.hasMoreElements()) { String prefix = (String) prefixes.nextElement(); handler.endPrefixMapping(prefix); } } /** * Check to see if we should switch serializers based on the * first output element being an HTML element. * * @param ns Namespace URI of the element * @param localName Local part of name of the element * * @throws org.xml.sax.SAXException */ private void checkForSerializerSwitch(String ns, String localName) throws org.xml.sax.SAXException { try { if (m_docPending) { SerializerSwitcher.switchSerializerIfHTML(m_transformer, ns, localName); } } catch (TransformerException te) { throw new org.xml.sax.SAXException(te); } } /** * Add the attributes that have been declared to the attribute list. * (Seems like I shouldn't have to do this...) */ public void addNSDeclsToAttrs() { Enumeration prefixes = m_nsSupport.getDeclaredPrefixes(); while (prefixes.hasMoreElements()) { String prefix = (String) prefixes.nextElement(); boolean isDefault = (prefix.length() == 0); String name; if (isDefault) { //prefix = "xml"; name = "xmlns"; } else name = "xmlns:" + prefix; String uri = m_nsSupport.getURI(prefix); if (null == uri) uri = ""; m_attributes.addAttribute("http://www.w3.org/2000/xmlns/", prefix, name, "CDATA", uri); } m_nsDeclsHaveBeenAdded = true; } /** * Copy <KBD>xmlns:</KBD> attributes in if not already in scope. * * @param src Source Node * NEEDSDOC @param type * NEEDSDOC @param dtm * * @throws TransformerException */ public void processNSDecls(int src, int type, DTM dtm) throws TransformerException { try { if (type == DTM.ELEMENT_NODE) { for (int namespace = dtm.getFirstNamespaceNode(src, true); DTM.NULL != namespace; namespace = dtm.getNextNamespaceNode(src, namespace, true)) { // String prefix = dtm.getPrefix(namespace); String prefix = dtm.getNodeNameX(namespace); String desturi = getURI(prefix); String srcURI = dtm.getNodeValue(namespace); if (!srcURI.equalsIgnoreCase(desturi)) { this.startPrefixMapping(prefix, srcURI, false); } } } } catch (org.xml.sax.SAXException se) { throw new TransformerException(se); } } /** * Given a prefix, return the namespace, * * @param prefix Given prefix name * * @return Namespace associated with the given prefix, or null */ public String getURI(String prefix) { return m_nsSupport.getURI(prefix); } /** * Given a namespace, try and find a prefix. * * @param namespace Given namespace URI * * @return Prefix name associated with namespace URI */ public String getPrefix(String namespace) { // This Enumeration business may be too slow for our purposes... Enumeration enum = m_nsSupport.getPrefixes(); while (enum.hasMoreElements()) { String prefix = (String) enum.nextElement(); if (m_nsSupport.getURI(prefix).equals(namespace)) return prefix; } return null; } /** * Get the NamespaceSupport object. * * @return NamespaceSupport object. */ public NamespaceSupport getNamespaceSupport() { return m_nsSupport; } // /** // * Override QueuedEvents#initQSE. // * // * @param qse Give queued Sax event // */ // protected void initQSE(QueuedSAXEvent qse) // { // // // qse.setContentHandler(m_contentHandler); // // qse.setTransformer(m_transformer); // // qse.setTraceManager(m_tracer); // } /** * Return the current content handler. * * @return The current content handler, or null if none * has been registered. * @see #setContentHandler */ public final ContentHandler getContentHandler() { return m_contentHandler; } /** * Set the current content handler. * * * @param ch Content Handler to be set * @return The current content handler, or null if none * has been registered. * @see #getContentHandler */ public void setContentHandler(ContentHandler ch) { m_contentHandler = ch; m_isTransformClient = (m_contentHandler instanceof TransformerClient); if (m_contentHandler instanceof LexicalHandler) m_lexicalHandler = (LexicalHandler) m_contentHandler; else m_lexicalHandler = null; reInitEvents(); } /** * Get a unique namespace value. * * @return a unique namespace value to be used with a * fabricated prefix */ public int getUniqueNSValue() { return m_uniqueNSValue++; } /** * Get new unique namespace prefix. * * @return Unique fabricated prefix. */ public String getNewUniqueNSPrefix() { return S_NAMESPACEPREFIX + String.valueOf(getUniqueNSValue()); } /** * Get the pending attributes. We have to delay the call to * m_flistener.startElement(name, atts) because of the * xsl:attribute and xsl:copy calls. In other words, * the attributes have to be fully collected before you * can call startElement. * * @return the pending attributes. */ public MutableAttrListImpl getPendingAttributes() { return m_attributes; } /** * Add an attribute to the end of the list. * * <p>Do not pass in xmlns decls to this function! * * <p>For the sake of speed, this method does no checking * to see if the attribute is already in the list: that is * the responsibility of the application.</p> * * @param uri The Namespace URI, or the empty string if * none is available or Namespace processing is not * being performed. * @param localName The local name, or the empty string if * Namespace processing is not being performed. * @param rawName The raw XML 1.0 name, or the empty string * if raw names are not available. * @param type The attribute type as a string. * @param value The attribute value. * * @throws TransformerException */ public void addAttribute( String uri, String localName, String rawName, String type, String value) throws TransformerException { if (!m_nsDeclsHaveBeenAdded) addNSDeclsToAttrs(); if (null == uri) // defensive, should not really need this. uri = ""; try { if (!rawName.equals("xmlns")) // don't handle xmlns default namespace. ensurePrefixIsDeclared(uri, rawName); } catch (org.xml.sax.SAXException se) { throw new TransformerException(se); } if (DEBUG) System.out.println("ResultTreeHandler#addAttribute Adding attr: " + localName + ", " + uri); if (!isDefinedNSDecl(rawName, value)) m_attributes.addAttribute(uri, localName, rawName, type, value); } /** * Return whether or not a namespace declaration is defined * * * @param rawName Raw name of namespace element * @param value URI of given namespace * * @return True if the namespace is already defined in list of * namespaces */ public boolean isDefinedNSDecl(String rawName, String value) { if (rawName.equals("xmlns") || rawName.startsWith("xmlns:")) { int index; String prefix = (index = rawName.indexOf(":")) < 0 ? "" : rawName.substring(0, index); String definedURI = m_nsSupport.getURI(prefix); if (null != definedURI) { if (definedURI.equals(value)) { return true; } else return false; } else return false; } else return false; } /** * Returns whether a namespace is defined * * * @param attr Namespace attribute node * * @return True if the namespace is already defined in * list of namespaces */ public boolean isDefinedNSDecl(int attr) { DTM dtm = m_transformer.getXPathContext().getDTM(attr); if (DTM.NAMESPACE_NODE == dtm.getNodeType(attr)) { // String prefix = dtm.getPrefix(attr); String prefix = dtm.getNodeNameX(attr); String uri = getURI(prefix); if ((null != uri) && uri.equals(dtm.getStringValue(attr))) return true; } return false; } /** * Returns whether a namespace is defined * * * @param attr Namespace attribute node * @param dtm The DTM that owns attr. * * @return True if the namespace is already defined in * list of namespaces */ public boolean isDefinedNSDecl(int attr, DTM dtm) { if (DTM.NAMESPACE_NODE == dtm.getNodeType(attr)) { // String prefix = dtm.getPrefix(attr); String prefix = dtm.getNodeNameX(attr); String uri = getURI(prefix); if ((null != uri) && uri.equals(dtm.getStringValue(attr))) return true; } return false; } /** * Copy an DOM attribute to the created output element, executing * attribute templates as need be, and processing the xsl:use * attribute. * * @param attr Attribute node to add to result tree * * @throws TransformerException */ public void addAttribute(int attr) throws TransformerException { DTM dtm = m_transformer.getXPathContext().getDTM(attr); if (isDefinedNSDecl(attr, dtm)) return; String ns = dtm.getNamespaceURI(attr); if (ns == null) ns = ""; // %OPT% ...can I just store the node handle? addAttribute(ns, dtm.getLocalName(attr), dtm.getNodeName(attr), "CDATA", dtm.getNodeValue(attr)); } // end copyAttributeToTarget method /** * Copy DOM attributes to the result element. * * @param src Source node with the attributes * * @throws TransformerException */ public void addAttributes(int src) throws TransformerException { DTM dtm = m_transformer.getXPathContext().getDTM(src); for (int node = dtm.getFirstAttribute(src); DTM.NULL != node; node = dtm.getNextAttribute(node)) { addAttribute(node); } } /** * Tell if an element is pending, to be output to the result tree. * * @return True if an element is pending */ public final boolean isElementPending() { return m_elemIsPending; } /** * Retrieves the stylesheet element that produced * the SAX event. * * <p>Please note that the ElemTemplateElement returned may * be in a default template, and thus may not be * defined in the stylesheet.</p> * * @return the stylesheet element that produced the SAX event. */ public ElemTemplateElement getCurrentElement() { if (m_elemIsPending) return m_snapshot.m_currentElement; else return m_transformer.getCurrentElement(); } /** * This method retrieves the current context node * in the source tree. * * @return the current context node in the source tree. */ public org.w3c.dom.Node getCurrentNode() { if (m_snapshot.m_currentNode != null) { return m_snapshot.m_currentNode; } else { DTM dtm = m_transformer.getXPathContext().getDTM(m_transformer.getCurrentNode()); return dtm.getNode(m_transformer.getCurrentNode()); } } /** * This method retrieves the xsl:template * that is in effect, which may be a matched template * or a named template. * * <p>Please note that the ElemTemplate returned may * be a default template, and thus may not have a template * defined in the stylesheet.</p> * * @return the xsl:template that is in effect */ public ElemTemplate getCurrentTemplate() { if (m_elemIsPending) return m_snapshot.m_currentTemplate; else return m_transformer.getCurrentTemplate(); } /** * This method retrieves the xsl:template * that was matched. Note that this may not be * the same thing as the current template (which * may be from getCurrentElement()), since a named * template may be in effect. * * <p>Please note that the ElemTemplate returned may * be a default template, and thus may not have a template * defined in the stylesheet.</p> * * @return the xsl:template that was matched. */ public ElemTemplate getMatchedTemplate() { if (m_elemIsPending) return m_snapshot.m_matchedTemplate; else return m_transformer.getMatchedTemplate(); } /** * Retrieves the node in the source tree that matched * the template obtained via getMatchedTemplate(). * * @return the node in the source tree that matched * the template obtained via getMatchedTemplate(). */ public org.w3c.dom.Node getMatchedNode() { if (m_elemIsPending) { DTM dtm = m_transformer.getXPathContext().getDTM(m_snapshot.m_matchedNode); return dtm.getNode(m_snapshot.m_matchedNode); } else { DTM dtm = m_transformer.getXPathContext().getDTM(m_transformer.getMatchedNode()); return dtm.getNode(m_transformer.getMatchedNode()); } } /** * Get the current context node list. * * @return the current context node list. */ public org.w3c.dom.traversal.NodeIterator getContextNodeList() { if (m_elemIsPending) { return new org.apache.xml.dtm.ref.DTMNodeIterator(m_snapshot.m_contextNodeList); } else return new org.apache.xml.dtm.ref.DTMNodeIterator(m_transformer.getContextNodeList()); } /** * Get the TrAX Transformer object in effect. * * @return the TrAX Transformer object in effect. */ public Transformer getTransformer() { return m_transformer; } boolean m_isTransformClient = false; /** * Use the SAX2 helper class to track result namespaces. */ NamespaceSupport m_nsSupport = new NamespaceSupport(); /** * The transformer object. */ private TransformerImpl m_transformer; /** * The content handler. May be null, in which * case, we'll defer to the content handler in the * transformer. */ private ContentHandler m_contentHandler; /** The LexicalHandler */ private LexicalHandler m_lexicalHandler; /** * The root of a linked set of stylesheets. */ private StylesheetRoot m_stylesheetRoot = null; /** * This is used whenever a unique namespace is needed. */ private int m_uniqueNSValue = 0; /** Prefix used to create unique prefix names */ private static final String S_NAMESPACEPREFIX = "ns"; /** * This class clones nodes to the result tree. */ public ClonerToResultTree m_cloner; /** * Trace manager for debug support. */ private TraceManager m_tracer; private QueuedStateSnapshot m_snapshot = new QueuedStateSnapshot(); // These are passed to flushPending, to help it decide if it // should really flush. class QueuedStateSnapshot { /** * The stylesheet element that produced the SAX event. */ ElemTemplateElement m_currentElement; /** * The current context node in the source tree. */ org.w3c.dom.Node m_currentNode; /** * The xsl:template that is in effect, which may be a matched template * or a named template. */ ElemTemplate m_currentTemplate; /** * The xsl:template that was matched. */ ElemTemplate m_matchedTemplate; /** * The node in the source tree that matched * the template obtained via getMatchedTemplate(). */ int m_matchedNode; /** * The current context node list. */ DTMIterator m_contextNodeList; } }