001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: PipeDocument.java 468639 2006-10-28 06:52:33Z minchau $
020     */
021    package org.apache.xalan.lib;
022    
023    import java.io.FileNotFoundException;
024    import java.io.FileOutputStream;
025    import java.io.IOException;
026    import java.util.Properties;
027    import java.util.Vector;
028    
029    import javax.xml.transform.Templates;
030    import javax.xml.transform.Transformer;
031    import javax.xml.transform.TransformerConfigurationException;
032    import javax.xml.transform.TransformerException;
033    import javax.xml.transform.TransformerFactory;
034    import javax.xml.transform.sax.SAXResult;
035    import javax.xml.transform.sax.SAXTransformerFactory;
036    import javax.xml.transform.sax.TransformerHandler;
037    import javax.xml.transform.stream.StreamSource;
038    
039    import org.apache.xalan.extensions.XSLProcessorContext;
040    import org.apache.xalan.templates.AVT;
041    import org.apache.xalan.templates.ElemExtensionCall;
042    import org.apache.xalan.templates.ElemLiteralResult;
043    import org.apache.xalan.transformer.TransformerImpl;
044    import org.apache.xml.utils.SystemIDResolver;
045    import org.apache.xpath.XPathContext;
046    
047    import org.w3c.dom.Element;
048    import org.w3c.dom.Node;
049    import org.w3c.dom.NodeList;
050    
051    import org.xml.sax.SAXException;
052    import org.xml.sax.SAXNotRecognizedException;
053    import org.xml.sax.XMLReader;
054    import org.xml.sax.helpers.XMLReaderFactory;
055    /**
056     */
057    // Imported Serializer classes
058    import org.apache.xml.serializer.Serializer;
059    import org.apache.xml.serializer.SerializerFactory;
060    
061    /**
062     * PipeDocument is a Xalan extension element to set stylesheet params and pipes an XML 
063     * document through a series of 1 or more stylesheets.
064     * PipeDocument is invoked from a stylesheet as the {@link #pipeDocument pipeDocument extension element}.
065     * 
066     * It is accessed by specifying a namespace URI as follows:
067     * <pre>
068     *    xmlns:pipe="http://xml.apache.org/xalan/PipeDocument"
069     * </pre>
070     *
071     * @author Donald Leslie
072     */
073    public class PipeDocument
074    {
075    /**
076     * Extension element for piping an XML document through a series of 1 or more transformations.
077     * 
078     * <pre>Common usage pattern: A stylesheet transforms a listing of documents to be
079     * transformed into a TOC. For each document in the listing calls the pipeDocument
080     * extension element to pipe that document through a series of 1 or more stylesheets 
081     * to the desired output document.
082     * 
083     * Syntax:
084     * &lt;xsl:stylesheet version="1.0"
085     *                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
086     *                xmlns:pipe="http://xml.apache.org/xalan/PipeDocument"
087     *                extension-element-prefixes="pipe"&gt;
088     * ...
089     * &lt;pipe:pipeDocument   source="source.xml" target="target.xml"&gt;
090     *   &lt;stylesheet href="ss1.xsl"&gt;
091     *     &lt;param name="param1" value="value1"/&gt;
092     *   &lt;/stylesheet&gt;
093     *   &lt;stylesheet href="ss2.xsl"&gt;
094     *     &lt;param name="param1" value="value1"/&gt;
095     *     &lt;param name="param2" value="value2"/&gt;
096     *   &lt;/stylesheet&gt;
097     *   &lt;stylesheet href="ss1.xsl"/&gt;     
098     * &lt;/pipe:pipeDocument&gt;
099     * 
100     * Notes:</pre>
101     * <ul>
102     *   <li>The base URI for the source attribute is the XML "listing" document.<li/>
103     *   <li>The target attribute is taken as is (base is the current user directory).<li/>
104     *   <li>The stylsheet containg the extension element is the base URI for the
105     *   stylesheet hrefs.<li/>
106     * </ul>
107     */
108      public void pipeDocument(XSLProcessorContext context, ElemExtensionCall elem)
109              throws TransformerException, TransformerConfigurationException, 
110             SAXException, IOException, FileNotFoundException          
111      {
112    
113          SAXTransformerFactory saxTFactory = (SAXTransformerFactory) TransformerFactory.newInstance();
114          
115          // XML doc to transform.
116          String source =  elem.getAttribute("source", 
117                                             context.getContextNode(),
118                                             context.getTransformer());
119          TransformerImpl transImpl = context.getTransformer();
120    
121          //Base URI for input doc, so base for relative URI to XML doc to transform.
122          String baseURLOfSource = transImpl.getBaseURLOfSource();
123          // Absolute URI for XML doc to transform.
124          String absSourceURL = SystemIDResolver.getAbsoluteURI(source, baseURLOfSource);      
125    
126          // Transformation target
127          String target =  elem.getAttribute("target", 
128                                             context.getContextNode(),
129                                             context.getTransformer());
130          
131          XPathContext xctxt = context.getTransformer().getXPathContext();
132          int xt = xctxt.getDTMHandleFromNode(context.getContextNode());
133     
134          // Get System Id for stylesheet; to be used to resolve URIs to other stylesheets.
135          String sysId = elem.getSystemId();
136          
137          NodeList ssNodes = null;
138          NodeList paramNodes = null;
139          Node ssNode = null;
140          Node paramNode = null;
141          if (elem.hasChildNodes())
142          {
143            ssNodes = elem.getChildNodes();        
144            // Vector to contain TransformerHandler for each stylesheet.
145            Vector vTHandler = new Vector(ssNodes.getLength());
146            
147            // The child nodes of an extension element node are instances of
148            // ElemLiteralResult, which requires does not fully support the standard
149            // Node interface. Accordingly, some special handling is required (see below)
150            // to get attribute values.
151            for (int i = 0; i < ssNodes.getLength(); i++)
152            {
153              ssNode = ssNodes.item(i);
154              if (ssNode.getNodeType() == Node.ELEMENT_NODE
155                  && ((Element)ssNode).getTagName().equals("stylesheet")
156                  && ssNode instanceof ElemLiteralResult)
157              {
158                AVT avt = ((ElemLiteralResult)ssNode).getLiteralResultAttribute("href");
159                String href = avt.evaluate(xctxt,xt, elem);
160                String absURI = SystemIDResolver.getAbsoluteURI(href, sysId);
161                Templates tmpl = saxTFactory.newTemplates(new StreamSource(absURI));
162                TransformerHandler tHandler = saxTFactory.newTransformerHandler(tmpl);
163                Transformer trans = tHandler.getTransformer();
164                
165                // AddTransformerHandler to vector
166                vTHandler.addElement(tHandler);
167    
168                paramNodes = ssNode.getChildNodes();
169                for (int j = 0; j < paramNodes.getLength(); j++)
170                {
171                  paramNode = paramNodes.item(j);
172                  if (paramNode.getNodeType() == Node.ELEMENT_NODE 
173                      && ((Element)paramNode).getTagName().equals("param")
174                      && paramNode instanceof ElemLiteralResult)
175                  {
176                     avt = ((ElemLiteralResult)paramNode).getLiteralResultAttribute("name");
177                     String pName = avt.evaluate(xctxt,xt, elem);
178                     avt = ((ElemLiteralResult)paramNode).getLiteralResultAttribute("value");
179                     String pValue = avt.evaluate(xctxt,xt, elem);
180                     trans.setParameter(pName, pValue);
181                   } 
182                 }
183               }
184             }
185             usePipe(vTHandler, absSourceURL, target);
186           }
187      }
188      /**
189       * Uses a Vector of TransformerHandlers to pipe XML input document through
190       * a series of 1 or more transformations. Called by {@link #pipeDocument}.
191       * 
192       * @param vTHandler Vector of Transformation Handlers (1 per stylesheet).
193       * @param source absolute URI to XML input
194       * @param target absolute path to transformation output.
195       */
196      public void usePipe(Vector vTHandler, String source, String target)
197              throws TransformerException, TransformerConfigurationException, 
198                     FileNotFoundException, IOException, SAXException, SAXNotRecognizedException
199      {
200        XMLReader reader = XMLReaderFactory.createXMLReader();
201        TransformerHandler tHFirst = (TransformerHandler)vTHandler.firstElement();
202        reader.setContentHandler(tHFirst);
203        reader.setProperty("http://xml.org/sax/properties/lexical-handler", tHFirst);
204        for (int i = 1; i < vTHandler.size(); i++)
205        {
206          TransformerHandler tHFrom = (TransformerHandler)vTHandler.elementAt(i-1);
207          TransformerHandler tHTo = (TransformerHandler)vTHandler.elementAt(i);
208          tHFrom.setResult(new SAXResult(tHTo));      
209        }
210        TransformerHandler tHLast = (TransformerHandler)vTHandler.lastElement();
211        Transformer trans = tHLast.getTransformer();
212        Properties outputProps = trans.getOutputProperties();
213        Serializer serializer = SerializerFactory.getSerializer(outputProps);
214        
215        FileOutputStream out = new FileOutputStream(target);
216        try 
217        {
218          serializer.setOutputStream(out);
219          tHLast.setResult(new SAXResult(serializer.asContentHandler()));
220          reader.parse(source);
221        }
222        finally 
223        {
224          // Always clean up the FileOutputStream,
225          // even if an exception was thrown in the try block
226          if (out != null)
227            out.close();
228        }    
229      }
230    }