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 * <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">
088 * ...
089 * <pipe:pipeDocument source="source.xml" target="target.xml">
090 * <stylesheet href="ss1.xsl">
091 * <param name="param1" value="value1"/>
092 * </stylesheet>
093 * <stylesheet href="ss2.xsl">
094 * <param name="param1" value="value1"/>
095 * <param name="param2" value="value2"/>
096 * </stylesheet>
097 * <stylesheet href="ss1.xsl"/>
098 * </pipe:pipeDocument>
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 }