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 }