1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 /* $Id: InputHandler.java 1805173 2017-08-16 10:50:04Z ssteiner $ */
19 
20 package org.apache.fop.cli;
21 
22 import java.io.File;
23 import java.io.FileNotFoundException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.lang.reflect.InvocationTargetException;
27 import java.util.Vector;
28 
29 import javax.xml.parsers.ParserConfigurationException;
30 import javax.xml.parsers.SAXParserFactory;
31 import javax.xml.transform.ErrorListener;
32 import javax.xml.transform.Result;
33 import javax.xml.transform.Source;
34 import javax.xml.transform.Transformer;
35 import javax.xml.transform.TransformerException;
36 import javax.xml.transform.TransformerFactory;
37 import javax.xml.transform.URIResolver;
38 import javax.xml.transform.sax.SAXResult;
39 import javax.xml.transform.sax.SAXSource;
40 import javax.xml.transform.stream.StreamResult;
41 import javax.xml.transform.stream.StreamSource;
42 
43 import org.xml.sax.EntityResolver;
44 import org.xml.sax.InputSource;
45 import org.xml.sax.SAXException;
46 import org.xml.sax.XMLReader;
47 
48 import org.apache.commons.logging.Log;
49 import org.apache.commons.logging.LogFactory;
50 
51 import org.apache.fop.ResourceEventProducer;
52 import org.apache.fop.apps.FOPException;
53 import org.apache.fop.apps.FOUserAgent;
54 import org.apache.fop.apps.Fop;
55 import org.apache.fop.render.awt.viewer.Renderable;
56 
57 /**
58  * Class for handling files input from command line
59  * either with XML and XSLT files (and optionally xsl
60  * parameters) or FO File input alone.
61  */
62 public class InputHandler implements ErrorListener, Renderable {
63 
64     /** original source file */
65     protected File sourcefile;
66     private File stylesheet;  // for XML/XSLT usage
67     private Vector xsltParams; // for XML/XSLT usage
68     private EntityResolver entityResolver;
69     private URIResolver uriResolver;
70 
71     /** the logger */
72     protected Log log = LogFactory.getLog(InputHandler.class);
73 
74     /**
75      * Constructor for XML->XSLT->FO input
76      *
77      * @param xmlfile XML file
78      * @param xsltfile XSLT file
79      * @param params Vector of command-line parameters (name, value,
80      *      name, value, ...) for XSL stylesheet, null if none
81      */
InputHandler(File xmlfile, File xsltfile, Vector params)82     public InputHandler(File xmlfile, File xsltfile, Vector params) {
83         sourcefile  = xmlfile;
84         stylesheet = xsltfile;
85         xsltParams = params;
86     }
87 
88     /**
89      * Constructor for FO input
90      * @param fofile the file to read the FO document.
91      */
InputHandler(File fofile)92     public InputHandler(File fofile) {
93         sourcefile = fofile;
94     }
95 
96     /**
97      * Generate a document, given an initialized Fop object
98      * @param userAgent the user agent
99      * @param outputFormat the output format to generate (MIME type, see MimeConstants)
100      * @param out the output stream to write the generated output to (may be null if not applicable)
101      * @throws FOPException in case of an error during processing
102      */
renderTo(FOUserAgent userAgent, String outputFormat, OutputStream out)103     public void renderTo(FOUserAgent userAgent, String outputFormat, OutputStream out)
104                 throws FOPException {
105 
106         Fop fop;
107         if (out != null) {
108             fop = userAgent.newFop(outputFormat, out);
109         } else {
110             fop = userAgent.newFop(outputFormat);
111         }
112 
113         // Resulting SAX events (the generated FO) must be piped through to FOP
114         Result res = new SAXResult(fop.getDefaultHandler());
115 
116         transformTo(res);
117     }
118 
119     /** {@inheritDoc} */
renderTo(FOUserAgent userAgent, String outputFormat)120     public void renderTo(FOUserAgent userAgent, String outputFormat) throws FOPException {
121         renderTo(userAgent, outputFormat, null);
122     }
123 
124     /**
125      * In contrast to render(Fop) this method only performs the XSLT stage and saves the
126      * intermediate XSL-FO file to the output file.
127      * @param out OutputStream to write the transformation result to.
128      * @throws FOPException in case of an error during processing
129      */
transformTo(OutputStream out)130     public void transformTo(OutputStream out) throws FOPException {
131         Result res = new StreamResult(out);
132         transformTo(res);
133     }
134 
135     /**
136      * Creates a Source for the main input file. Processes XInclude if
137      * available in the XML parser.
138      *
139      * @return the Source for the main input file
140      */
createMainSource()141     protected Source createMainSource() {
142         Source source;
143         InputStream in;
144         String uri;
145         if (this.sourcefile != null) {
146             try {
147                 in = new java.io.FileInputStream(this.sourcefile);
148                 uri = this.sourcefile.toURI().toASCIIString();
149             } catch (FileNotFoundException e) {
150                 //handled elsewhere
151                 return new StreamSource(this.sourcefile);
152             }
153         } else {
154             in = System.in;
155             uri = null;
156         }
157         try {
158             InputSource is = new InputSource(in);
159             is.setSystemId(uri);
160             XMLReader xr = getXMLReader();
161             if (entityResolver != null) {
162                 xr.setEntityResolver(entityResolver);
163             }
164             source = new SAXSource(xr, is);
165         } catch (SAXException e) {
166             if (this.sourcefile != null) {
167                 source = new StreamSource(this.sourcefile);
168             } else {
169                 source = new StreamSource(in, uri);
170             }
171         } catch (ParserConfigurationException e) {
172             if (this.sourcefile != null) {
173                 source = new StreamSource(this.sourcefile);
174             } else {
175                 source = new StreamSource(in, uri);
176             }
177         }
178         return source;
179     }
180 
181     /**
182      * Creates a catalog resolver and uses it for XML parsing and XSLT URI resolution.
183      * Tries the Apache Commons Resolver, and if unsuccessful,
184      * tries the same built into Java 6.
185      * @param userAgent the user agent instance
186      */
createCatalogResolver(FOUserAgent userAgent)187     public void createCatalogResolver(FOUserAgent userAgent) {
188         String[] classNames = new String[] {
189                 "org.apache.xml.resolver.tools.CatalogResolver",
190                 "com.sun.org.apache.xml.internal.resolver.tools.CatalogResolver"};
191         ResourceEventProducer eventProducer
192             = ResourceEventProducer.Provider.get(userAgent.getEventBroadcaster());
193         Class resolverClass = null;
194         for (int i = 0; i < classNames.length && resolverClass == null; ++i) {
195             try {
196                 resolverClass = Class.forName(classNames[i]);
197             } catch (ClassNotFoundException e) {
198                 // No worries
199             }
200         }
201         if (resolverClass == null) {
202             eventProducer.catalogResolverNotFound(this);
203             return;
204         }
205         try {
206             entityResolver = (EntityResolver) resolverClass.getDeclaredConstructor().newInstance();
207             uriResolver = (URIResolver) resolverClass.getDeclaredConstructor().newInstance();
208         } catch (InstantiationException e) {
209             log.error("Error creating the catalog resolver: " + e.getMessage());
210             eventProducer.catalogResolverNotCreated(this, e.getMessage());
211         } catch (IllegalAccessException e) {
212             log.error("Error creating the catalog resolver: " + e.getMessage());
213             eventProducer.catalogResolverNotCreated(this, e.getMessage());
214         } catch (NoSuchMethodException e) {
215             log.error("Error creating the catalog resolver: " + e.getMessage());
216             eventProducer.catalogResolverNotCreated(this, e.getMessage());
217         } catch (InvocationTargetException e) {
218             log.error("Error creating the catalog resolver: " + e.getMessage());
219             eventProducer.catalogResolverNotCreated(this, e.getMessage());
220         }
221     }
222 
223     /**
224      * Creates a Source for the selected stylesheet.
225      *
226      * @return the Source for the selected stylesheet or null if there's no stylesheet
227      */
createXSLTSource()228     protected Source createXSLTSource() {
229         Source xslt = null;
230         if (this.stylesheet != null) {
231             if (entityResolver != null) {
232                 try {
233                     InputSource is = new InputSource(this.stylesheet.getPath());
234                     XMLReader xr = getXMLReader();
235                     xr.setEntityResolver(entityResolver);
236                     xslt = new SAXSource(xr, is);
237                 } catch (SAXException e) {
238                     // return StreamSource
239                 } catch (ParserConfigurationException e) {
240                     // return StreamSource
241                 }
242             }
243             if (xslt == null) {
244                 xslt = new StreamSource(this.stylesheet);
245             }
246         }
247         return xslt;
248     }
249 
getXMLReader()250     private XMLReader getXMLReader() throws ParserConfigurationException, SAXException {
251         SAXParserFactory spf = SAXParserFactory.newInstance();
252         spf.setFeature("http://xml.org/sax/features/namespaces", true);
253         spf.setFeature("http://apache.org/xml/features/xinclude", true);
254         spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
255         XMLReader xr = spf.newSAXParser().getXMLReader();
256         return xr;
257     }
258 
259     /**
260      * Transforms the input document to the input format expected by FOP using XSLT.
261      * @param result the Result object where the result of the XSL transformation is sent to
262      * @throws FOPException in case of an error during processing
263      */
transformTo(Result result)264     protected void transformTo(Result result) throws FOPException {
265         try {
266             // Setup XSLT
267             TransformerFactory factory = TransformerFactory.newInstance();
268             Transformer transformer;
269 
270             Source xsltSource = createXSLTSource();
271             if (xsltSource == null) {   // FO Input
272                 transformer = factory.newTransformer();
273             } else {    // XML/XSLT input
274                 transformer = factory.newTransformer(xsltSource);
275 
276                 // Set the value of parameters, if any, defined for stylesheet
277                 if (xsltParams != null) {
278                     for (int i = 0; i < xsltParams.size(); i += 2) {
279                         transformer.setParameter((String) xsltParams.elementAt(i),
280                             (String) xsltParams.elementAt(i + 1));
281                     }
282                 }
283                 if (uriResolver != null) {
284                     transformer.setURIResolver(uriResolver);
285                 }
286             }
287             transformer.setErrorListener(this);
288 
289             // Create a SAXSource from the input Source file
290             Source src = createMainSource();
291 
292             // Start XSLT transformation and FOP processing
293             transformer.transform(src, result);
294 
295         } catch (Exception e) {
296             throw new FOPException(e);
297         }
298     }
299 
300     // --- Implementation of the ErrorListener interface ---
301 
302     /**
303      * {@inheritDoc}
304      */
warning(TransformerException exc)305     public void warning(TransformerException exc) {
306         log.warn(exc.getLocalizedMessage());
307     }
308 
309     /**
310      * {@inheritDoc}
311      */
error(TransformerException exc)312     public void error(TransformerException exc) {
313         log.error(exc.toString());
314     }
315 
316     /**
317      * {@inheritDoc}
318      */
fatalError(TransformerException exc)319     public void fatalError(TransformerException exc)
320             throws TransformerException {
321         throw exc;
322     }
323 
324 }
325