mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			806 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			806 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Java
		
	
	
	
/* TransformerImpl.java --
 | 
						|
   Copyright (C) 2004,2005,2006 Free Software Foundation, Inc.
 | 
						|
 | 
						|
This file is part of GNU Classpath.
 | 
						|
 | 
						|
GNU Classpath is free software; you can redistribute it and/or modify
 | 
						|
it under the terms of the GNU General Public License as published by
 | 
						|
the Free Software Foundation; either version 2, or (at your option)
 | 
						|
any later version.
 | 
						|
 | 
						|
GNU Classpath is distributed in the hope that it will be useful, but
 | 
						|
WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
General Public License for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU General Public License
 | 
						|
along with GNU Classpath; see the file COPYING.  If not, write to the
 | 
						|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | 
						|
02110-1301 USA.
 | 
						|
 | 
						|
Linking this library statically or dynamically with other modules is
 | 
						|
making a combined work based on this library.  Thus, the terms and
 | 
						|
conditions of the GNU General Public License cover the whole
 | 
						|
combination.
 | 
						|
 | 
						|
As a special exception, the copyright holders of this library give you
 | 
						|
permission to link this library with independent modules to produce an
 | 
						|
executable, regardless of the license terms of these independent
 | 
						|
modules, and to copy and distribute the resulting executable under
 | 
						|
terms of your choice, provided that you also meet, for each linked
 | 
						|
independent module, the terms and conditions of the license of that
 | 
						|
module.  An independent module is a module which is not derived from
 | 
						|
or based on this library.  If you modify this library, you may extend
 | 
						|
this exception to your version of the library, but you are not
 | 
						|
obligated to do so.  If you do not wish to do so, delete this
 | 
						|
exception statement from your version. */
 | 
						|
 | 
						|
package gnu.xml.transform;
 | 
						|
 | 
						|
import gnu.java.lang.CPStringBuilder;
 | 
						|
 | 
						|
import java.io.BufferedOutputStream;
 | 
						|
import java.io.FileOutputStream;
 | 
						|
import java.io.IOException;
 | 
						|
import java.io.OutputStream;
 | 
						|
import java.io.UnsupportedEncodingException;
 | 
						|
import java.io.Writer;
 | 
						|
import java.net.MalformedURLException;
 | 
						|
import java.net.UnknownServiceException;
 | 
						|
import java.net.URL;
 | 
						|
import java.net.URLConnection;
 | 
						|
import java.util.Collection;
 | 
						|
import java.util.Iterator;
 | 
						|
import java.util.LinkedList;
 | 
						|
import java.util.List;
 | 
						|
import java.util.Properties;
 | 
						|
import java.util.StringTokenizer;
 | 
						|
import javax.xml.namespace.QName;
 | 
						|
import javax.xml.transform.ErrorListener;
 | 
						|
import javax.xml.transform.OutputKeys;
 | 
						|
import javax.xml.transform.Result;
 | 
						|
import javax.xml.transform.Source;
 | 
						|
import javax.xml.transform.Transformer;
 | 
						|
import javax.xml.transform.TransformerConfigurationException;
 | 
						|
import javax.xml.transform.TransformerException;
 | 
						|
import javax.xml.transform.URIResolver;
 | 
						|
import javax.xml.transform.dom.DOMSource;
 | 
						|
import javax.xml.transform.dom.DOMResult;
 | 
						|
import javax.xml.transform.sax.SAXResult;
 | 
						|
import javax.xml.transform.stream.StreamResult;
 | 
						|
import org.w3c.dom.Document;
 | 
						|
import org.w3c.dom.DocumentType;
 | 
						|
import org.w3c.dom.DOMImplementation;
 | 
						|
import org.w3c.dom.Node;
 | 
						|
import org.w3c.dom.Text;
 | 
						|
import org.xml.sax.ContentHandler;
 | 
						|
import org.xml.sax.SAXException;
 | 
						|
import org.xml.sax.ext.LexicalHandler;
 | 
						|
import gnu.xml.dom.DomDoctype;
 | 
						|
import gnu.xml.dom.DomDocument;
 | 
						|
import gnu.xml.dom.ls.WriterOutputStream;
 | 
						|
 | 
						|
/**
 | 
						|
 * The transformation process for a given stylesheet.
 | 
						|
 *
 | 
						|
 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
 | 
						|
 */
 | 
						|
class TransformerImpl
 | 
						|
  extends Transformer
 | 
						|
{
 | 
						|
 | 
						|
  final TransformerFactoryImpl factory;
 | 
						|
  final Stylesheet stylesheet;
 | 
						|
  URIResolver uriResolver;
 | 
						|
  ErrorListener errorListener;
 | 
						|
  Properties outputProperties;
 | 
						|
 | 
						|
  TransformerImpl(TransformerFactoryImpl factory,
 | 
						|
                  Stylesheet stylesheet,
 | 
						|
                  Properties outputProperties)
 | 
						|
    throws TransformerConfigurationException
 | 
						|
  {
 | 
						|
    this.factory = factory;
 | 
						|
    uriResolver = factory.userResolver;
 | 
						|
    errorListener = factory.userListener;
 | 
						|
    this.stylesheet = stylesheet;
 | 
						|
    this.outputProperties = outputProperties;
 | 
						|
    if (stylesheet != null)
 | 
						|
      {
 | 
						|
        // Set up parameter context for this transformer
 | 
						|
        stylesheet.bindings.push(Bindings.PARAM);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  public void transform(Source xmlSource, Result outputTarget)
 | 
						|
    throws TransformerException
 | 
						|
  {
 | 
						|
    // Get the source tree
 | 
						|
    DOMSource source;
 | 
						|
    synchronized (factory.resolver)
 | 
						|
      {
 | 
						|
        factory.resolver.setUserResolver(uriResolver);
 | 
						|
        factory.resolver.setUserListener(errorListener);
 | 
						|
        source = factory.resolver.resolveDOM(xmlSource, null, null);
 | 
						|
      }
 | 
						|
    Node context = source.getNode();
 | 
						|
    Document doc = (context instanceof Document) ? (Document) context :
 | 
						|
      context.getOwnerDocument();
 | 
						|
    if (doc instanceof DomDocument)
 | 
						|
      {
 | 
						|
        // Suppress mutation events
 | 
						|
        ((DomDocument) doc).setBuilding(true);
 | 
						|
      }
 | 
						|
    // Get the result tree
 | 
						|
    Node parent = null, nextSibling = null;
 | 
						|
    if (outputTarget instanceof DOMResult)
 | 
						|
      {
 | 
						|
        DOMResult dr = (DOMResult) outputTarget;
 | 
						|
        parent = dr.getNode();
 | 
						|
        nextSibling = dr.getNextSibling();
 | 
						|
 | 
						|
        Document rdoc = (parent instanceof Document) ? (Document) parent :
 | 
						|
          parent.getOwnerDocument();
 | 
						|
        if (rdoc instanceof DomDocument)
 | 
						|
          {
 | 
						|
            // Suppress mutation events and allow multiple root elements
 | 
						|
            DomDocument drdoc = (DomDocument) rdoc;
 | 
						|
            drdoc.setBuilding(true);
 | 
						|
            drdoc.setCheckWellformedness(false);
 | 
						|
          }
 | 
						|
      }
 | 
						|
    boolean created = false;
 | 
						|
    // Transformation
 | 
						|
    if (stylesheet != null)
 | 
						|
      {
 | 
						|
        if (parent == null)
 | 
						|
          {
 | 
						|
            // Create a new document to hold the result
 | 
						|
            DomDocument resultDoc = new DomDocument();
 | 
						|
            resultDoc.setBuilding(true);
 | 
						|
            resultDoc.setCheckWellformedness(false);
 | 
						|
            parent = resultDoc;
 | 
						|
            created = true;
 | 
						|
          }
 | 
						|
        // Make a copy of the source node, and strip it
 | 
						|
        context = context.cloneNode(true);
 | 
						|
        strip(stylesheet, context);
 | 
						|
        // XSLT transformation
 | 
						|
        try
 | 
						|
          {
 | 
						|
            // Set output properties in the underlying stylesheet
 | 
						|
            ((TransformerOutputProperties) outputProperties).apply();
 | 
						|
            stylesheet.initTopLevelVariables(context);
 | 
						|
            TemplateNode t = stylesheet.getTemplate(null, context, false);
 | 
						|
            if (t != null)
 | 
						|
              {
 | 
						|
                stylesheet.current = context;
 | 
						|
                t.apply(stylesheet, null, context, 1, 1, parent, nextSibling);
 | 
						|
              }
 | 
						|
          }
 | 
						|
        catch (TransformerException e)
 | 
						|
          {
 | 
						|
            // Done transforming, reset document
 | 
						|
            if (doc instanceof DomDocument)
 | 
						|
              ((DomDocument) doc).setBuilding(false);
 | 
						|
            throw e;
 | 
						|
          }
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        // Identity transform
 | 
						|
        Node clone = context.cloneNode(true);
 | 
						|
        if (context.getNodeType() != Node.DOCUMENT_NODE)
 | 
						|
          {
 | 
						|
            Document resultDoc;
 | 
						|
            if (parent == null)
 | 
						|
              {
 | 
						|
                // Create a new document to hold the result
 | 
						|
                DomDocument rd = new DomDocument();
 | 
						|
                rd.setBuilding(true);
 | 
						|
                rd.setCheckWellformedness(false);
 | 
						|
                parent = resultDoc = rd;
 | 
						|
                created = true;
 | 
						|
              }
 | 
						|
            else
 | 
						|
              {
 | 
						|
                resultDoc = (parent instanceof Document) ?
 | 
						|
                  (Document) parent :
 | 
						|
                  parent.getOwnerDocument();
 | 
						|
              }
 | 
						|
            Document sourceDoc = context.getOwnerDocument();
 | 
						|
            if (sourceDoc != resultDoc)
 | 
						|
              clone = resultDoc.adoptNode(clone);
 | 
						|
            if (nextSibling != null)
 | 
						|
              parent.insertBefore(clone, nextSibling);
 | 
						|
            else
 | 
						|
              parent.appendChild(clone);
 | 
						|
          }
 | 
						|
        else
 | 
						|
          {
 | 
						|
            // Cannot append document to another tree
 | 
						|
            parent = clone;
 | 
						|
            created = true;
 | 
						|
          }
 | 
						|
      }
 | 
						|
    String method = outputProperties.getProperty(OutputKeys.METHOD);
 | 
						|
    int outputMethod = "html".equals(method) ? Stylesheet.OUTPUT_HTML :
 | 
						|
      "text".equals(method) ? Stylesheet.OUTPUT_TEXT :
 | 
						|
      Stylesheet.OUTPUT_XML;
 | 
						|
    String encoding = outputProperties.getProperty(OutputKeys.ENCODING);
 | 
						|
    String publicId = outputProperties.getProperty(OutputKeys.DOCTYPE_PUBLIC);
 | 
						|
    String systemId = outputProperties.getProperty(OutputKeys.DOCTYPE_SYSTEM);
 | 
						|
    String version = outputProperties.getProperty(OutputKeys.VERSION);
 | 
						|
    boolean omitXmlDeclaration =
 | 
						|
      "yes".equals(outputProperties.getProperty(OutputKeys.OMIT_XML_DECLARATION));
 | 
						|
    boolean standalone =
 | 
						|
      "yes".equals(outputProperties.getProperty(OutputKeys.STANDALONE));
 | 
						|
    String mediaType = outputProperties.getProperty(OutputKeys.MEDIA_TYPE);
 | 
						|
    String cdataSectionElements =
 | 
						|
      outputProperties.getProperty(OutputKeys.CDATA_SECTION_ELEMENTS);
 | 
						|
    boolean indent =
 | 
						|
      "yes".equals(outputProperties.getProperty(OutputKeys.INDENT));
 | 
						|
    if (created && parent instanceof DomDocument)
 | 
						|
      {
 | 
						|
        // Discover document element
 | 
						|
        DomDocument resultDoc = (DomDocument) parent;
 | 
						|
        Node root = resultDoc.getDocumentElement();
 | 
						|
        // Add doctype if specified
 | 
						|
        if (publicId != null || systemId != null)
 | 
						|
          {
 | 
						|
            if (root != null)
 | 
						|
              {
 | 
						|
                // We must know the name of the root element to
 | 
						|
                // create the document type
 | 
						|
                DocumentType doctype = new DomDoctype(resultDoc,
 | 
						|
                                                      root.getNodeName(),
 | 
						|
                                                      publicId,
 | 
						|
                                                      systemId);
 | 
						|
                resultDoc.insertBefore(doctype, root);
 | 
						|
              }
 | 
						|
          }
 | 
						|
        resultDoc.setBuilding(false);
 | 
						|
        resultDoc.setCheckWellformedness(true);
 | 
						|
      }
 | 
						|
    else if (publicId != null || systemId != null)
 | 
						|
      {
 | 
						|
        switch (parent.getNodeType())
 | 
						|
          {
 | 
						|
          case Node.DOCUMENT_NODE:
 | 
						|
          case Node.DOCUMENT_FRAGMENT_NODE:
 | 
						|
            Document resultDoc = (parent instanceof Document) ?
 | 
						|
              (Document) parent :
 | 
						|
              parent.getOwnerDocument();
 | 
						|
            DOMImplementation impl = resultDoc.getImplementation();
 | 
						|
            Node root = resultDoc.getDocumentElement();
 | 
						|
            if (root != null)
 | 
						|
              {
 | 
						|
                DocumentType doctype =
 | 
						|
                  impl.createDocumentType(root.getNodeName(),
 | 
						|
                                          publicId,
 | 
						|
                                          systemId);
 | 
						|
                resultDoc.insertBefore(doctype, root);
 | 
						|
              }
 | 
						|
          }
 | 
						|
      }
 | 
						|
    if (version != null)
 | 
						|
      parent.setUserData("version", version, stylesheet);
 | 
						|
    if (omitXmlDeclaration)
 | 
						|
      parent.setUserData("omit-xml-declaration", "yes", stylesheet);
 | 
						|
    if (standalone)
 | 
						|
      parent.setUserData("standalone", "yes", stylesheet);
 | 
						|
    if (mediaType != null)
 | 
						|
      parent.setUserData("media-type", mediaType, stylesheet);
 | 
						|
    if (cdataSectionElements != null)
 | 
						|
      {
 | 
						|
        List list = new LinkedList();
 | 
						|
        StringTokenizer st = new StringTokenizer(cdataSectionElements);
 | 
						|
        while (st.hasMoreTokens())
 | 
						|
          {
 | 
						|
            String name = st.nextToken();
 | 
						|
            String localName = name;
 | 
						|
            String uri = null;
 | 
						|
            String prefix = null;
 | 
						|
            int ci = name.indexOf(':');
 | 
						|
            if (ci != -1)
 | 
						|
              {
 | 
						|
                // Use namespaces defined on xsl:output node to resolve
 | 
						|
                // namespaces for QName
 | 
						|
                prefix = name.substring(0, ci);
 | 
						|
                localName = name.substring(ci + 1);
 | 
						|
                uri = stylesheet.output.lookupNamespaceURI(prefix);
 | 
						|
              }
 | 
						|
            list.add(new QName(uri, localName, prefix));
 | 
						|
          }
 | 
						|
        if (!list.isEmpty())
 | 
						|
          {
 | 
						|
            Document resultDoc = (parent instanceof Document) ?
 | 
						|
              (Document) parent :
 | 
						|
              parent.getOwnerDocument();
 | 
						|
            convertCdataSectionElements(resultDoc, parent, list);
 | 
						|
          }
 | 
						|
      }
 | 
						|
    if (indent)
 | 
						|
      {
 | 
						|
        if (created && parent instanceof DomDocument)
 | 
						|
          {
 | 
						|
            DomDocument domDoc = (DomDocument) parent;
 | 
						|
            domDoc.setBuilding(true);
 | 
						|
            domDoc.setCheckWellformedness(false);
 | 
						|
          }
 | 
						|
        parent.normalize();
 | 
						|
        if (stylesheet != null)
 | 
						|
          strip(stylesheet, parent);
 | 
						|
        Document resultDoc = (parent instanceof Document) ?
 | 
						|
          (Document) parent :
 | 
						|
          parent.getOwnerDocument();
 | 
						|
        reindent(resultDoc, parent, 0);
 | 
						|
        if (created && parent instanceof DomDocument)
 | 
						|
          {
 | 
						|
            DomDocument domDoc = (DomDocument) parent;
 | 
						|
            domDoc.setBuilding(false);
 | 
						|
            domDoc.setCheckWellformedness(true);
 | 
						|
          }
 | 
						|
      }
 | 
						|
    // Render result to the target device
 | 
						|
    if (outputTarget instanceof DOMResult)
 | 
						|
      {
 | 
						|
        if (created)
 | 
						|
          {
 | 
						|
            DOMResult dr = (DOMResult) outputTarget;
 | 
						|
            dr.setNode(parent);
 | 
						|
            dr.setNextSibling(null);
 | 
						|
          }
 | 
						|
      }
 | 
						|
    else if (outputTarget instanceof StreamResult)
 | 
						|
      {
 | 
						|
        StreamResult sr = (StreamResult) outputTarget;
 | 
						|
        IOException ex = null;
 | 
						|
        try
 | 
						|
          {
 | 
						|
            writeStreamResult(parent, sr, outputMethod, encoding);
 | 
						|
          }
 | 
						|
        catch (UnsupportedEncodingException e)
 | 
						|
          {
 | 
						|
            try
 | 
						|
              {
 | 
						|
                writeStreamResult(parent, sr, outputMethod, "UTF-8");
 | 
						|
              }
 | 
						|
            catch (IOException e2)
 | 
						|
              {
 | 
						|
                ex = e2;
 | 
						|
              }
 | 
						|
          }
 | 
						|
        catch (IOException e)
 | 
						|
          {
 | 
						|
            ex = e;
 | 
						|
          }
 | 
						|
        if (ex != null)
 | 
						|
          {
 | 
						|
            if (errorListener != null)
 | 
						|
              errorListener.error(new TransformerException(ex));
 | 
						|
            else
 | 
						|
              ex.printStackTrace(System.err);
 | 
						|
          }
 | 
						|
      }
 | 
						|
    else if (outputTarget instanceof SAXResult)
 | 
						|
      {
 | 
						|
        SAXResult sr = (SAXResult) outputTarget;
 | 
						|
        try
 | 
						|
          {
 | 
						|
            ContentHandler ch = sr.getHandler();
 | 
						|
            LexicalHandler lh = sr.getLexicalHandler();
 | 
						|
            if (lh == null && ch instanceof LexicalHandler)
 | 
						|
              lh = (LexicalHandler) ch;
 | 
						|
            SAXSerializer serializer = new SAXSerializer();
 | 
						|
            serializer.serialize(parent, ch, lh);
 | 
						|
          }
 | 
						|
        catch (SAXException e)
 | 
						|
          {
 | 
						|
            if (errorListener != null)
 | 
						|
              errorListener.error(new TransformerException(e));
 | 
						|
            else
 | 
						|
              e.printStackTrace(System.err);
 | 
						|
          }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Strip whitespace from the source tree.
 | 
						|
   */
 | 
						|
  static boolean strip(Stylesheet stylesheet, Node node)
 | 
						|
    throws TransformerConfigurationException
 | 
						|
  {
 | 
						|
    short nt = node.getNodeType();
 | 
						|
    if (nt == Node.ENTITY_REFERENCE_NODE)
 | 
						|
      {
 | 
						|
        // Replace entity reference with its content
 | 
						|
        Node parent = node.getParentNode();
 | 
						|
        Node nextSibling = node.getNextSibling();
 | 
						|
        Node child = node.getFirstChild();
 | 
						|
        while (child != null)
 | 
						|
          {
 | 
						|
            Node next = child.getNextSibling();
 | 
						|
            node.removeChild(child);
 | 
						|
            if (nextSibling != null)
 | 
						|
              parent.insertBefore(child, nextSibling);
 | 
						|
            else
 | 
						|
              parent.appendChild(child);
 | 
						|
            child = next;
 | 
						|
          }
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
    if (nt == Node.TEXT_NODE || nt == Node.CDATA_SECTION_NODE)
 | 
						|
      {
 | 
						|
        // Denormalize text into whitespace and non-whitespace nodes
 | 
						|
        String text = node.getNodeValue();
 | 
						|
        String[] tokens = tokenizeWhitespace(text);
 | 
						|
        if (tokens.length > 1)
 | 
						|
          {
 | 
						|
            node.setNodeValue(tokens[0]);
 | 
						|
            Node parent = node.getParentNode();
 | 
						|
            Node nextSibling = node.getNextSibling();
 | 
						|
            Document doc = node.getOwnerDocument();
 | 
						|
            for (int i = 1; i < tokens.length; i++)
 | 
						|
              {
 | 
						|
                Node newChild = (nt == Node.CDATA_SECTION_NODE) ?
 | 
						|
                  doc.createCDATASection(tokens[i]) :
 | 
						|
                  doc.createTextNode(tokens[i]);
 | 
						|
                if (nextSibling != null)
 | 
						|
                  parent.insertBefore(newChild, nextSibling);
 | 
						|
                else
 | 
						|
                  parent.appendChild(newChild);
 | 
						|
              }
 | 
						|
          }
 | 
						|
        return !stylesheet.isPreserved((Text) node, true);
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        Node child = node.getFirstChild();
 | 
						|
        while (child != null)
 | 
						|
          {
 | 
						|
            boolean remove = strip(stylesheet, child);
 | 
						|
            Node next = child.getNextSibling();
 | 
						|
            if (remove)
 | 
						|
              node.removeChild(child);
 | 
						|
            child = next;
 | 
						|
          }
 | 
						|
      }
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Tokenize the specified text into contiguous whitespace-only and
 | 
						|
   * non-whitespace chunks.
 | 
						|
   */
 | 
						|
  private static String[] tokenizeWhitespace(String text)
 | 
						|
  {
 | 
						|
    int len = text.length();
 | 
						|
    int start = 0, end = len - 1;
 | 
						|
    // Find index of text start
 | 
						|
    for (int i = 0; i < len; i++)
 | 
						|
      {
 | 
						|
        char c = text.charAt(i);
 | 
						|
        boolean whitespace = (c == ' ' || c == '\n' || c == '\t' || c == '\r');
 | 
						|
        if (whitespace)
 | 
						|
          start++;
 | 
						|
        else
 | 
						|
          break;
 | 
						|
      }
 | 
						|
    if (start == end) // all whitespace
 | 
						|
      return new String[] { text };
 | 
						|
    // Find index of text end
 | 
						|
    for (int i = end; i > start; i--)
 | 
						|
      {
 | 
						|
        char c = text.charAt(i);
 | 
						|
        boolean whitespace = (c == ' ' || c == '\n' || c == '\t' || c == '\r');
 | 
						|
        if (whitespace)
 | 
						|
          end--;
 | 
						|
        else
 | 
						|
          break;
 | 
						|
      }
 | 
						|
    if (start == 0 && end == len - 1) // all non-whitespace
 | 
						|
      return new String[] { text };
 | 
						|
    // whitespace, then text, then whitespace
 | 
						|
    String[] ret = (start > 0 && end < len - 1) ?
 | 
						|
      new String[3] : new String[2];
 | 
						|
    int i = 0;
 | 
						|
    if (start > 0)
 | 
						|
      ret[i++] = text.substring(0, start);
 | 
						|
    ret[i++] = text.substring(start, end + 1);
 | 
						|
    if (end < len - 1)
 | 
						|
      ret[i++] = text.substring(end + 1);
 | 
						|
    return ret;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Obtain a suitable output stream for writing the result to,
 | 
						|
   * and use the StreamSerializer to write the result tree to the stream.
 | 
						|
   */
 | 
						|
  void writeStreamResult(Node node, StreamResult sr, int outputMethod,
 | 
						|
                         String encoding)
 | 
						|
    throws IOException
 | 
						|
  {
 | 
						|
    OutputStream out = null;
 | 
						|
    boolean created = false;
 | 
						|
    try
 | 
						|
      {
 | 
						|
        out = sr.getOutputStream();
 | 
						|
        if (out == null)
 | 
						|
          {
 | 
						|
            Writer writer = sr.getWriter();
 | 
						|
            if (writer != null)
 | 
						|
              out = new WriterOutputStream(writer);
 | 
						|
          }
 | 
						|
        if (out == null)
 | 
						|
          {
 | 
						|
            String systemId = sr.getSystemId();
 | 
						|
            try
 | 
						|
              {
 | 
						|
                URL url = new URL(systemId);
 | 
						|
                URLConnection connection = url.openConnection();
 | 
						|
                // We need to call setDoInput(false), because our
 | 
						|
                // implementation of the file protocol allows writing
 | 
						|
                // (unlike Sun), but it will fail with a FileNotFoundException
 | 
						|
                // if we also open the connection for input and the output
 | 
						|
                // file doesn't yet exist.
 | 
						|
                connection.setDoInput(false);
 | 
						|
                connection.setDoOutput(true);
 | 
						|
                out = connection.getOutputStream();
 | 
						|
              }
 | 
						|
            catch (MalformedURLException e)
 | 
						|
              {
 | 
						|
                out = new FileOutputStream(systemId);
 | 
						|
              }
 | 
						|
            catch (UnknownServiceException e)
 | 
						|
              {
 | 
						|
                URL url = new URL(systemId);
 | 
						|
                out = new FileOutputStream(url.getPath());
 | 
						|
              }
 | 
						|
            created = true;
 | 
						|
          }
 | 
						|
        out = new BufferedOutputStream(out);
 | 
						|
        StreamSerializer serializer =
 | 
						|
          new StreamSerializer(outputMethod, encoding, null);
 | 
						|
        if (stylesheet != null)
 | 
						|
          {
 | 
						|
            Collection celem = stylesheet.outputCdataSectionElements;
 | 
						|
            serializer.setCdataSectionElements(celem);
 | 
						|
          }
 | 
						|
        serializer.serialize(node, out);
 | 
						|
        out.flush();
 | 
						|
      }
 | 
						|
    finally
 | 
						|
      {
 | 
						|
        try
 | 
						|
          {
 | 
						|
            if (out != null && created)
 | 
						|
              out.close();
 | 
						|
          }
 | 
						|
        catch (IOException e)
 | 
						|
          {
 | 
						|
            if (errorListener != null)
 | 
						|
              {
 | 
						|
                try
 | 
						|
                  {
 | 
						|
                    errorListener.error(new TransformerException(e));
 | 
						|
                  }
 | 
						|
                catch (TransformerException e2)
 | 
						|
                  {
 | 
						|
                    e2.printStackTrace(System.err);
 | 
						|
                  }
 | 
						|
              }
 | 
						|
            else
 | 
						|
              e.printStackTrace(System.err);
 | 
						|
          }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  void copyChildren(Document dstDoc, Node src, Node dst)
 | 
						|
  {
 | 
						|
    Node srcChild = src.getFirstChild();
 | 
						|
    while (srcChild != null)
 | 
						|
      {
 | 
						|
        Node dstChild = dstDoc.adoptNode(srcChild);
 | 
						|
        dst.appendChild(dstChild);
 | 
						|
        srcChild = srcChild.getNextSibling();
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  public void setParameter(String name, Object value)
 | 
						|
  {
 | 
						|
    if (stylesheet != null)
 | 
						|
      stylesheet.bindings.set(new QName(null, name), value, Bindings.PARAM);
 | 
						|
  }
 | 
						|
 | 
						|
  public Object getParameter(String name)
 | 
						|
  {
 | 
						|
    if (stylesheet != null)
 | 
						|
      return stylesheet.bindings.get(new QName(null, name), null, 1, 1);
 | 
						|
    return null;
 | 
						|
  }
 | 
						|
 | 
						|
  public void clearParameters()
 | 
						|
  {
 | 
						|
    if (stylesheet != null)
 | 
						|
      {
 | 
						|
        stylesheet.bindings.pop(Bindings.PARAM);
 | 
						|
        stylesheet.bindings.push(Bindings.PARAM);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  public void setURIResolver(URIResolver resolver)
 | 
						|
  {
 | 
						|
    uriResolver = resolver;
 | 
						|
  }
 | 
						|
 | 
						|
  public URIResolver getURIResolver()
 | 
						|
  {
 | 
						|
    return uriResolver;
 | 
						|
  }
 | 
						|
 | 
						|
  public void setOutputProperties(Properties oformat)
 | 
						|
    throws IllegalArgumentException
 | 
						|
  {
 | 
						|
    if (oformat == null)
 | 
						|
      outputProperties.clear();
 | 
						|
    else
 | 
						|
      outputProperties.putAll(oformat);
 | 
						|
  }
 | 
						|
 | 
						|
  public Properties getOutputProperties()
 | 
						|
  {
 | 
						|
    return (Properties) outputProperties.clone();
 | 
						|
  }
 | 
						|
 | 
						|
  public void setOutputProperty(String name, String value)
 | 
						|
    throws IllegalArgumentException
 | 
						|
  {
 | 
						|
    outputProperties.put(name, value);
 | 
						|
  }
 | 
						|
 | 
						|
  public String getOutputProperty(String name)
 | 
						|
    throws IllegalArgumentException
 | 
						|
  {
 | 
						|
    return outputProperties.getProperty(name);
 | 
						|
  }
 | 
						|
 | 
						|
  public void setErrorListener(ErrorListener listener)
 | 
						|
  {
 | 
						|
    errorListener = listener;
 | 
						|
  }
 | 
						|
 | 
						|
  public ErrorListener getErrorListener()
 | 
						|
  {
 | 
						|
    return errorListener;
 | 
						|
  }
 | 
						|
 | 
						|
  static final String INDENT_WHITESPACE = "  ";
 | 
						|
 | 
						|
  /*
 | 
						|
   * Apply indent formatting to the given tree.
 | 
						|
   */
 | 
						|
  void reindent(Document doc, Node node, int offset)
 | 
						|
  {
 | 
						|
    if (node.hasChildNodes())
 | 
						|
      {
 | 
						|
        boolean markupContent = false;
 | 
						|
        boolean textContent = false;
 | 
						|
        List children = new LinkedList();
 | 
						|
        Node ctx = node.getFirstChild();
 | 
						|
        while (ctx != null)
 | 
						|
          {
 | 
						|
            switch (ctx.getNodeType())
 | 
						|
              {
 | 
						|
              case Node.ELEMENT_NODE:
 | 
						|
              case Node.PROCESSING_INSTRUCTION_NODE:
 | 
						|
              case Node.DOCUMENT_TYPE_NODE:
 | 
						|
                markupContent = true;
 | 
						|
                break;
 | 
						|
              case Node.TEXT_NODE:
 | 
						|
              case Node.CDATA_SECTION_NODE:
 | 
						|
              case Node.ENTITY_REFERENCE_NODE:
 | 
						|
              case Node.COMMENT_NODE:
 | 
						|
                textContent = true;
 | 
						|
                break;
 | 
						|
              }
 | 
						|
            children.add(ctx);
 | 
						|
            ctx = ctx.getNextSibling();
 | 
						|
          }
 | 
						|
        if (markupContent)
 | 
						|
          {
 | 
						|
            if (textContent)
 | 
						|
              {
 | 
						|
                // XXX handle mixed content differently?
 | 
						|
              }
 | 
						|
            int nodeType = node.getNodeType();
 | 
						|
            if (nodeType == Node.DOCUMENT_NODE)
 | 
						|
              {
 | 
						|
                for (Iterator i = children.iterator(); i.hasNext(); )
 | 
						|
                  {
 | 
						|
                    ctx = (Node) i.next();
 | 
						|
                    reindent(doc, ctx, offset);
 | 
						|
                  }
 | 
						|
              }
 | 
						|
            else
 | 
						|
              {
 | 
						|
                CPStringBuilder buf = new CPStringBuilder();
 | 
						|
                buf.append('\n');
 | 
						|
                for (int i = 0; i < offset + 1; i++)
 | 
						|
                  buf.append(INDENT_WHITESPACE);
 | 
						|
                String ws = buf.toString();
 | 
						|
                for (Iterator i = children.iterator(); i.hasNext(); )
 | 
						|
                  {
 | 
						|
                    ctx = (Node) i.next();
 | 
						|
                    node.insertBefore(doc.createTextNode(ws), ctx);
 | 
						|
                    reindent(doc, ctx, offset + 1);
 | 
						|
                  }
 | 
						|
                buf = new CPStringBuilder();
 | 
						|
                buf.append('\n');
 | 
						|
                for (int i = 0; i < offset; i++)
 | 
						|
                  buf.append(INDENT_WHITESPACE);
 | 
						|
                ws = buf.toString();
 | 
						|
                node.appendChild(doc.createTextNode(ws));
 | 
						|
              }
 | 
						|
          }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Converts the text node children of any cdata-section-elements in the
 | 
						|
   * tree to CDATA section nodes.
 | 
						|
   */
 | 
						|
  void convertCdataSectionElements(Document doc, Node node, List list)
 | 
						|
  {
 | 
						|
    if (node.getNodeType() == Node.ELEMENT_NODE)
 | 
						|
      {
 | 
						|
        boolean match = false;
 | 
						|
        for (Iterator i = list.iterator(); i.hasNext(); )
 | 
						|
          {
 | 
						|
            QName qname = (QName) i.next();
 | 
						|
            if (match(qname, node))
 | 
						|
              {
 | 
						|
                match = true;
 | 
						|
                break;
 | 
						|
              }
 | 
						|
          }
 | 
						|
        if (match)
 | 
						|
          {
 | 
						|
            Node ctx = node.getFirstChild();
 | 
						|
            while (ctx != null)
 | 
						|
              {
 | 
						|
                if (ctx.getNodeType() == Node.TEXT_NODE)
 | 
						|
                  {
 | 
						|
                    Node cdata = doc.createCDATASection(ctx.getNodeValue());
 | 
						|
                    node.replaceChild(cdata, ctx);
 | 
						|
                    ctx = cdata;
 | 
						|
                  }
 | 
						|
                ctx = ctx.getNextSibling();
 | 
						|
              }
 | 
						|
          }
 | 
						|
      }
 | 
						|
    Node ctx = node.getFirstChild();
 | 
						|
    while (ctx != null)
 | 
						|
      {
 | 
						|
        if (ctx.hasChildNodes())
 | 
						|
          convertCdataSectionElements(doc, ctx, list);
 | 
						|
        ctx = ctx.getNextSibling();
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  boolean match(QName qname, Node node)
 | 
						|
  {
 | 
						|
    String ln1 = qname.getLocalPart();
 | 
						|
    String ln2 = node.getLocalName();
 | 
						|
    if (ln2 == null)
 | 
						|
      return ln1.equals(node.getNodeName());
 | 
						|
    else
 | 
						|
      {
 | 
						|
        String uri1 = qname.getNamespaceURI();
 | 
						|
        String uri2 = node.getNamespaceURI();
 | 
						|
        return (uri1.equals(uri2) && ln1.equals(ln2));
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
}
 |