View Javadoc

1   /*
2    * $Id$
3    *
4    * The contents of this file are subject to the Mozilla Public License 
5    * Version 1.1 (the "License"); you may not use this file except in 
6    * compliance with the License. You may obtain a copy of the License at 
7    * http://www.mozilla.org/MPL/ 
8    *
9    * Software distributed under the License is distributed on an "AS IS" basis, 
10   * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
11   * for the specific language governing rights and limitations under the License.
12   *
13   * The Original Code is XML Hammer code. (org.xmlhammer.*)
14   *
15   * The Initial Developer of the Original Code is Edwin Dankert. Portions created 
16   * by the Initial Developer are Copyright (C) 2005 - 2006 the Initial Developer. 
17   * All Rights Reserved.
18   *
19   * Contributor(s): Edwin Dankert <edankert@gmail.com>
20   */
21  
22  package org.xmlhammer.gui.xslt;
23  
24  import java.io.ByteArrayInputStream;
25  import java.io.ByteArrayOutputStream;
26  import java.io.File;
27  import java.io.FileOutputStream;
28  import java.io.IOException;
29  import java.io.OutputStream;
30  import java.io.PrintStream;
31  import java.net.MalformedURLException;
32  import java.net.URI;
33  import java.util.ArrayList;
34  import java.util.List;
35  
36  import javax.xml.parsers.DocumentBuilder;
37  import javax.xml.parsers.DocumentBuilderFactory;
38  import javax.xml.parsers.ParserConfigurationException;
39  import javax.xml.parsers.SAXParser;
40  import javax.xml.transform.Source;
41  import javax.xml.transform.Transformer;
42  import javax.xml.transform.TransformerConfigurationException;
43  import javax.xml.transform.TransformerException;
44  import javax.xml.transform.TransformerFactory;
45  import javax.xml.transform.dom.DOMSource;
46  import javax.xml.transform.sax.SAXSource;
47  import javax.xml.transform.stream.StreamResult;
48  import javax.xml.transform.stream.StreamSource;
49  
50  import org.apache.log4j.Logger;
51  import org.bounce.util.URIUtils;
52  import org.xml.sax.InputSource;
53  import org.xml.sax.SAXException;
54  import org.xml.sax.SAXParseException;
55  import org.xmlhammer.DefaultErrorHandler;
56  import org.xmlhammer.Module;
57  import org.xmlhammer.ResultModel;
58  import org.xmlhammer.DefaultErrorHandler.Error;
59  import org.xmlhammer.DefaultErrorHandler.Fatal;
60  import org.xmlhammer.DefaultErrorHandler.Message;
61  import org.xmlhammer.DefaultErrorHandler.Problem;
62  import org.xmlhammer.DefaultErrorHandler.Warning;
63  import org.xmlhammer.gui.status.StatusModel;
64  import org.xmlhammer.model.preferences.Preferences;
65  import org.xmlhammer.model.project.Parameter;
66  import org.xmlhammer.model.project.ParameterisedSource;
67  import org.xmlhammer.model.project.Project;
68  import org.xmlhammer.model.tools.xslt.Associated;
69  import org.xmlhammer.model.tools.xslt.OutputProperty;
70  import org.xmlhammer.model.tools.xslt.XSLT.Transform;
71  
72  /***
73   * Put comment...
74   * 
75   * @version $Revision$, $Date$
76   * @author Edwin Dankert <edankert@gmail.com>
77   */
78  public class XSLTModule extends Module {
79      private SAXParser parser = null;
80      private DocumentBuilder builder = null;
81      private TransformerFactory factory = null;
82      private ArrayList<Transformer> transformers = null;
83      private Associated associated = null;
84      private Transform transform = null;
85  
86      public XSLTModule(Preferences preferences, Project project, Logger logger, boolean logSettings) throws SAXException, ParserConfigurationException, TransformerConfigurationException, TransformerException {
87          super(preferences, project, logger, logSettings);
88  
89          if ("dom".equals(getProject().getParser().getType())) {
90              DocumentBuilderFactory factory = getDocumentBuilderFactory();
91              builder = factory.newDocumentBuilder();
92          } else {
93              parser = getSAXParser();
94          }
95          
96          factory = getTransformerFactory();
97          
98          transform = project.getXSLT().getTransform();
99          if (transform.getAssociated() != null) {
100             associated = transform.getAssociated();
101             return;
102         } 
103         
104         if (associated == null) {
105             transformers = getTransformers(logger);
106         }
107     }
108 
109     /***
110      * Sets-up an XML Validator Module.
111      * 
112      * @param preferences the global preferences.
113      * @param project the project specific properties;
114      * @throws ParserConfigurationException 
115      * @throws SAXException 
116      */
117 	public XSLTModule(Preferences preferences, Project project, Logger logger) throws SAXException, ParserConfigurationException, TransformerConfigurationException, TransformerException {
118         this(preferences, project, logger, true);
119 	}
120     
121     private void setOutputProperties(Transformer transformer) {
122         if (transformer != null) {
123             List<OutputProperty> properties = getProject().getXSLT().getOutputProperties().getOutputProperty();
124             for (OutputProperty property : properties) {
125                 if (property.isActive()) {
126                     transformer.setOutputProperty(property.getName(), property.getValue());
127                 }
128             }
129         }
130     }
131     
132     public boolean isResultEnabled() {
133         return true;
134     }
135 
136     /***
137      * Execute the XPath expressio.
138      * @throws MalformedURLException 
139      * @throws ParserConfigurationException 
140      */
141     @Override
142     public void execute(StatusModel status, ResultModel result, URI sourceURI, URI resultURI) {
143         PrintStream err = System.err;
144         PrintStream out = System.out;
145         DefaultErrorHandler errorHandler = new DefaultErrorHandler(null);
146 
147         if (sourceURI != null && resultURI != null) {
148             // Tell that we're loading classes and parsing, so the time it
149 			// takes to do this doesn't get confused with the time to do
150 			// the actual query and serialization.
151             InputSource in = new InputSource(sourceURI.toString());
152 
153             System.setErr(new PrintStream(new LogOutputStream("err", errorHandler), true));
154             System.setOut(new PrintStream(new LogOutputStream("out", errorHandler), true));
155 
156             try {
157                 FileOutputStream resultOuputStream = new FileOutputStream(new File(resultURI));
158 
159                 // TODO handle SAXTransformerFactory stuff ...
160                 if ( "dom".equals(getProject().getParser().getType())) {
161                     builder.setErrorHandler(errorHandler);
162                     builder.setEntityResolver(getCatalogResolver());
163                     DOMSource source = new DOMSource(builder.parse( in), in.getSystemId());
164                     
165                     if (associated != null) {
166                         transformers = new ArrayList<Transformer>();
167                         Source style = factory.getAssociatedStylesheet(source, associated.getMedia(), associated.getTitle(), associated.getCharset());
168                         Transformer transformer = null;
169                         
170                         try {
171                             transformer = getTransformer(result, sourceURI, style, associated.getParameter());
172                         } catch (TransformerException e) {
173                             return;
174                         }
175 
176                         setOutputProperties(transformer);
177 
178                         transformers.add(transformer);
179                     }
180                     
181                     getLogger().info( "transform: "+URIUtils.toString(sourceURI));
182 
183                     for (Transformer transformer : transformers) {
184                         transformer.setErrorListener(errorHandler);
185                         StreamResult stream = null;
186                         
187                         if (transformer == transformers.get(transformers.size()-1)) {
188                             stream = new StreamResult(resultOuputStream);
189                         } else {
190                             stream = new StreamResult(new ByteArrayOutputStream());
191                         }
192 
193                         transformer.transform(source, stream);
194 
195                         if (stream.getOutputStream() instanceof ByteArrayOutputStream) {
196                             in.setByteStream(new ByteArrayInputStream(((ByteArrayOutputStream)stream.getOutputStream()).toByteArray()));
197                             source = new DOMSource(builder.parse( in), in.getSystemId());
198                         }
199                     }
200                 } else { // sax is the only other one.
201                     parser.getXMLReader().setErrorHandler(errorHandler);
202                     parser.getXMLReader().setEntityResolver(getCatalogResolver());
203                     SAXSource source = new SAXSource(parser.getXMLReader(), in);
204 
205                     if (associated != null) {
206                         transformers = new ArrayList<Transformer>();
207                         Source style = factory.getAssociatedStylesheet(source, associated.getMedia(), associated.getTitle(), associated.getCharset());
208 
209                         Transformer transformer = null;
210                         
211                         try {
212                             transformer = getTransformer(result, sourceURI, style, associated.getParameter());
213                         } catch (TransformerException e) {
214                             return;
215                         }
216                         
217                         setOutputProperties(transformer);
218 
219                         transformers.add(transformer);
220                     }
221 
222                     getLogger().info( "transform: "+URIUtils.toString(sourceURI));
223 
224                     for (Transformer transformer : transformers) {
225                         transformer.setErrorListener(errorHandler);
226                         StreamResult stream = null;
227                         
228                         if (transformer == transformers.get(transformers.size()-1)) {
229                             stream = new StreamResult(resultOuputStream);
230                         } else {
231                             stream = new StreamResult(new ByteArrayOutputStream());
232                         }
233                         
234                         transformer.transform(source, stream);
235 
236                         if (stream.getOutputStream() instanceof ByteArrayOutputStream) {
237                             in.setByteStream(new ByteArrayInputStream(((ByteArrayOutputStream)stream.getOutputStream()).toByteArray()));
238                         }
239                     }
240                 }
241                 
242                 resultOuputStream.flush();
243                 resultOuputStream.close();
244                 
245             } catch ( SAXException e) {
246                 if ( e instanceof SAXParseException) {
247                     try {
248                         errorHandler.fatalError( (SAXParseException)e);
249                     } catch ( Exception x) {}
250                 }
251             } catch (IOException e) {
252                 errorHandler.fatalError(e);
253             } catch (TransformerException e) {
254                 errorHandler.fatalError(e);
255             } finally {
256                 System.setErr(err);
257                 System.setOut(out);
258             }
259 
260             ArrayList<Problem> list = errorHandler.getProblems();
261             if ( list.size() > 0) {
262     			for (Problem problem : list) {
263                     if (problem instanceof Warning) {
264                         if (problem.getException() instanceof SAXParseException) {
265                             logWarning(sourceURI, (SAXParseException)problem.getException());
266 
267                             if (result != null) {
268                                 result.addWarning(sourceURI, (SAXParseException)problem.getException());
269                             }
270                         } else {
271                             logWarning(sourceURI, (TransformerException)problem.getException());
272 
273                             if (result != null) {
274                                 result.addWarning(sourceURI, (TransformerException)problem.getException());
275                             }
276                         }
277                     } else if (problem instanceof Error) {
278                         if (problem.getException() instanceof SAXParseException) {
279                             logError(sourceURI, (SAXParseException)problem.getException());
280 
281                             if (result != null) {
282                                 result.addError(sourceURI, (SAXParseException)problem.getException());
283                             }
284                         } else {
285                             logError(sourceURI, (TransformerException)problem.getException());
286 
287                             if (result != null) {
288                                 result.addError(sourceURI, (TransformerException)problem.getException());
289                             }
290                         }
291                     } else if (problem instanceof Fatal) {
292                         logFatal(sourceURI, problem.getException());
293                         
294                         if (result != null) {
295                             Exception exception = problem.getException();
296                             if ( exception instanceof IOException) {
297                                 result.addFatal(sourceURI, (IOException)problem.getException());
298                             } else if ( exception instanceof SAXParseException) {
299                                 result.addFatal(sourceURI, (SAXParseException)problem.getException());
300                             } else {
301                                 result.addFatal(sourceURI, (TransformerException)problem.getException());
302                             }
303                         }
304                     } else if ( problem instanceof Message) {
305                         getLogger().info( "\t[message] "+((Message)problem).getMessage());
306                         
307                         if (result != null) {
308                             result.addMessage(sourceURI, ((Message)problem).getType(), ((Message)problem).getMessage());
309                         }
310                     }
311     			}
312             } else {
313                 getLogger().info( "\t[processed] Document Processed");
314 
315                 if (result != null) {
316                     result.addValid(sourceURI);
317                 }
318             }
319 		}
320 	}
321     
322     private ArrayList<Transformer> getTransformers(Logger logger) throws TransformerException {
323         if (transformers == null) {
324             transformers = new ArrayList<Transformer>();
325 
326             if (transform.getStylesheets() != null) {
327                 List<ParameterisedSource> stylesheets = transform.getStylesheets().getParameterisedSource();
328                 for (ParameterisedSource stylesheet: stylesheets) {
329                     Transformer transformer = getTransformer(null, null, new StreamSource(stylesheet.getSrc()), stylesheet.getParameter());
330         
331                     transformers.add(transformer);
332                 }
333                 
334                 setOutputProperties(transformers.get(transformers.size()-1));
335             } else {
336                 try {
337                     Transformer transformer = factory.newTransformer();
338                     transformers.add(transformer);
339                     setOutputProperties(transformer);
340                 } catch (TransformerConfigurationException e) {
341                     // this should never happen but if it does, we should just report it ...
342                     e.printStackTrace();
343                 }
344             }
345         }
346     
347         return transformers;
348     }
349 
350     private Transformer getTransformer(ResultModel result, URI base, Source source, List<Parameter> parameters) throws TransformerException {
351         Transformer transformer = null;
352         TransformerException exception = null;
353         
354         if (source != null) {
355             getLogger().info( "validate stylesheet: "+source.getSystemId());
356 
357             DefaultErrorHandler errorHandler = new DefaultErrorHandler(URIUtils.createURI(source.getSystemId()));
358 
359             try {
360                 factory.setErrorListener(errorHandler);
361                 transformer = factory.newTransformer(source);
362 
363                 for (Parameter parameter : parameters) {
364                     if (parameter.isActive()) {
365                         getLogger().info("\t[parameter] "+parameter.getName()+" = "+parameter.getValue());
366                         transformer.setParameter(parameter.getName(), parameter.getValue());
367                     }
368                 }
369             } catch (TransformerException e) {
370                 errorHandler.fatalError(e);
371                 exception = e;
372             }
373  
374             ArrayList<Problem> list = errorHandler.getProblems();
375             if ( list.size() > 0) {
376                 for ( Problem problem : list) {
377                     if ( problem instanceof Warning) {
378                         logWarning(base, (TransformerException)problem.getException());
379                         
380                         if (result != null) {
381                             result.addWarning(base, (TransformerException)problem.getException());
382                         }
383                     } else if ( problem instanceof Error) {
384                         logError(base, (TransformerException)problem.getException());
385                         
386                         if (result != null) {
387                             result.addError(base, (TransformerException)problem.getException());
388                         }
389                     } else if ( problem instanceof Fatal) {
390                         logFatal(base, problem.getException());
391                         
392                         if (result != null) {
393                             if (problem.getException() instanceof IOException) {
394                                 result.addFatal(base, (IOException)problem.getException());
395                             } else {
396                                 result.addFatal(base, (TransformerException)problem.getException());
397                             }
398                         }
399                     }
400                 }
401             } else {
402                 getLogger().info( "\t[valid] Valid Stylesheet");
403             }
404         }
405         
406         if (exception != null) {
407             throw exception;
408         }
409         
410         return transformer;
411     }
412 
413     class LogOutputStream extends OutputStream {
414         private StringBuffer buffer = new StringBuffer();
415         private DefaultErrorHandler handler = null;
416         private String type = null;
417         
418         public LogOutputStream(String type, DefaultErrorHandler handler) {
419             this.handler = handler;
420             this.type = type;
421         }
422 
423         public void write(int b) {
424             buffer.append( (char)b);
425         }
426     
427         public void flush () {
428             String message = buffer.toString().trim();
429             if (message.length() > 0) {
430                 handler.message(type, message);
431             }
432             
433             buffer = new StringBuffer();
434         }
435     }
436 }