2 /************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2008 by Sun Microsystems, Inc.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * $RCSfile: XSLTransformer.java,v $
13 * This file is part of OpenOffice.org.
15 * OpenOffice.org is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU Lesser General Public License version 3
17 * only, as published by the Free Software Foundation.
19 * OpenOffice.org is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License version 3 for more details
23 * (a copy is included in the LICENSE file that accompanied this code).
25 * You should have received a copy of the GNU Lesser General Public License
26 * version 3 along with OpenOffice.org. If not, see
27 * <http://www.openoffice.org/license.html>
28 * for a copy of the LGPLv3 License.
30 ************************************************************************///Standard Java classes
31 import java
.io
.BufferedInputStream
;
32 import java
.io
.BufferedOutputStream
;
34 import java
.io
.FileOutputStream
;
35 import java
.io
.InputStream
;
36 import java
.io
.PrintStream
;
37 import java
.io
.StringReader
;
39 import java
.net
.URLConnection
;
40 import java
.util
.Enumeration
;
41 import java
.util
.Hashtable
;
42 import java
.util
.Vector
;
44 // Imported TraX classes
45 import javax
.xml
.parsers
.SAXParserFactory
;
46 import javax
.xml
.transform
.Source
;
47 import javax
.xml
.transform
.Transformer
;
48 import javax
.xml
.transform
.TransformerFactory
;
49 import javax
.xml
.transform
.URIResolver
;
50 import javax
.xml
.transform
.sax
.SAXSource
;
51 import javax
.xml
.transform
.stream
.StreamResult
;
52 import javax
.xml
.transform
.stream
.StreamSource
;
53 import org
.xml
.sax
.EntityResolver
;
54 import org
.xml
.sax
.InputSource
;
55 import org
.xml
.sax
.SAXException
;
56 import org
.xml
.sax
.XMLReader
;
58 //StarOffice Interfaces and UNO
59 import com
.sun
.star
.beans
.NamedValue
;
60 import com
.sun
.star
.comp
.loader
.FactoryHelper
;
61 import com
.sun
.star
.io
.XActiveDataControl
;
62 import com
.sun
.star
.io
.XActiveDataSink
;
63 import com
.sun
.star
.io
.XActiveDataSource
;
64 import com
.sun
.star
.io
.XInputStream
;
65 import com
.sun
.star
.io
.XOutputStream
;
66 import com
.sun
.star
.io
.XSeekable
;
67 import com
.sun
.star
.io
.XStreamListener
;
68 import com
.sun
.star
.lang
.XInitialization
;
69 import com
.sun
.star
.lang
.XMultiServiceFactory
;
70 import com
.sun
.star
.lang
.XServiceInfo
;
71 import com
.sun
.star
.lang
.XServiceName
;
72 import com
.sun
.star
.lang
.XSingleServiceFactory
;
73 import com
.sun
.star
.lang
.XTypeProvider
;
74 import com
.sun
.star
.registry
.XRegistryKey
;
75 import com
.sun
.star
.uno
.AnyConverter
;
76 import com
.sun
.star
.uno
.Type
;
77 import com
.sun
.star
.uno
.UnoRuntime
;
80 import com
.sun
.star
.lib
.uno
.adapter
.XInputStreamToInputStreamAdapter
;
81 import com
.sun
.star
.lib
.uno
.adapter
.XOutputStreamToOutputStreamAdapter
;
83 /** This outer class provides an inner class to implement the service
84 * description, a method to instantiate the
85 * component on demand (__getServiceFactory()), and a method to give
86 * information about the component (__writeRegistryServiceInfo()).
88 public class XSLTransformer
89 implements XTypeProvider
, XServiceName
, XServiceInfo
, XActiveDataSink
,
90 XActiveDataSource
, XActiveDataControl
, XInitialization
, URIResolver
, EntityResolver
{
93 * This component provides java based XSL transformations
94 * A SAX based interface is not feasible when crossing language bordes
95 * since too much time would be wasted by bridging the events between environments
96 * example: 190 pages document, 82000 events 8seconds transform 40(!) sec. bridging
99 private XInputStream m_xis
;
100 private XOutputStream m_xos
; // private static HashMap templatecache;
101 private static final int STREAM_BUFFER_SIZE
= 4000;
102 private static final String STATSPROP
= "XSLTransformer.statsfile";
103 private static PrintStream statsp
;
104 private String stylesheeturl
;
105 private String targeturl
;
106 private String targetbaseurl
;
107 private String sourceurl
;
108 private String sourcebaseurl
;
109 private String pubtype
= new String();
110 private String systype
= new String(); // processing thread
111 private Thread t
; // listeners
112 private Vector listeners
= new Vector(); //
113 private XMultiServiceFactory svcfactory
; // cache for transformations by stylesheet
114 private static Hashtable transformers
= new Hashtable();
115 // struct for cached stylesheets
116 private static class Transformation
{
118 public Transformer transformer
;
121 // Resolve URIs to an empty source
122 public Source
resolve(String href
, String base
) {
123 return new StreamSource(new StringReader(""));
126 public InputSource
resolveEntity(String publicId
, String systemId
) throws SAXException
, java
.io
.IOException
{
127 return new InputSource(new StringReader(""));
129 // --- Initialization ---
130 public XSLTransformer(XMultiServiceFactory msf
) {
134 public void initialize(Object
[] values
) throws com
.sun
.star
.uno
.Exception
{
135 // some configurable debugging
136 String statsfilepath
= null;
137 if ((statsfilepath
= System
.getProperty(STATSPROP
)) != null) {
139 File statsfile
= new File(statsfilepath
);
140 statsp
= new PrintStream(new FileOutputStream(statsfile
.getPath(), false));
141 } catch (java
.lang
.Exception e
) {
142 System
.err
.println("XSLTransformer: could not open statsfile'" + statsfilepath
+ "'");
143 System
.err
.println(" " + e
.getClass().getName() + ": " + e
.getMessage());
144 System
.err
.println(" output disabled");
148 // reading the values
149 NamedValue nv
= null;
150 debug("The transformation's parameters as 'name = value' pairs:\n");
152 for (int i
= 0; i
< values
.length
; i
++) {
153 nv
= (NamedValue
) AnyConverter
.toObject(new Type(NamedValue
.class), values
[i
]);
155 if (nv
.Name
!= null && !nv
.Name
.equals("")) {
156 debug(nv
.Name
+ " = " + nv
.Value
);
159 if (nv
.Name
.equals("StylesheetURL")) {
160 stylesheeturl
= (String
) AnyConverter
.toObject(
161 new Type(String
.class), nv
.Value
);
162 } else if (nv
.Name
.equals("SourceURL")) {
163 sourceurl
= (String
) AnyConverter
.toObject(
164 new Type(String
.class), nv
.Value
);
165 } else if (nv
.Name
.equals("TargetURL")) {
166 targeturl
= (String
) AnyConverter
.toObject(
167 new Type(String
.class), nv
.Value
);
168 } else if (nv
.Name
.equals("SourceBaseURL")) {
169 sourcebaseurl
= (String
) AnyConverter
.toObject(
170 new Type(String
.class), nv
.Value
);
171 } else if (nv
.Name
.equals("TargetBaseURL")) {
172 targetbaseurl
= (String
) AnyConverter
.toObject(
173 new Type(String
.class), nv
.Value
);
174 } else if (nv
.Name
.equals("SystemType")) {
175 systype
= (String
) AnyConverter
.toObject(
176 new Type(String
.class), nv
.Value
);
177 } else if (nv
.Name
.equals("PublicType")) {
178 pubtype
= (String
) AnyConverter
.toObject(
179 new Type(String
.class), nv
.Value
);
184 // --- XActiveDataSink xistream = aStream;
185 public void setInputStream(XInputStream aStream
) {
189 public com
.sun
.star
.io
.XInputStream
getInputStream() {
193 // --- XActiveDataSource
194 public void setOutputStream(XOutputStream aStream
) {
198 public com
.sun
.star
.io
.XOutputStream
getOutputStream() {
202 // --- XActiveDataControl
203 public void addListener(XStreamListener aListener
) {
204 if (aListener
!= null && !listeners
.contains(aListener
)) {
205 listeners
.add(aListener
);
209 public void removeListener(XStreamListener aListener
) {
210 if (aListener
!= null) {
211 listeners
.removeElement(aListener
);
216 public void start() {
223 // Local variabes used outside try block in finally block
224 InputStream is
= null;
225 Source source
= null;
226 BufferedOutputStream os
= null;
227 PrintStream origOut
= System
.out
;
228 PrintStream origErr
= System
.err
;
229 if (statsp
!= null) {
230 System
.setErr(statsp
);
231 System
.setOut(statsp
);
234 debug("\n\nStarting transformation...");
236 // Set up context class loader for SAXParserFactory and
237 // TransformerFactory calls below:
238 setContextClassLoader(this.getClass().getClassLoader());
240 for (Enumeration e
= listeners
.elements(); e
.hasMoreElements();) {
241 XStreamListener l
= (XStreamListener
) e
.nextElement();
245 XSeekable xseek
= (XSeekable
) UnoRuntime
.queryInterface(XSeekable
.class, m_xis
);
250 is
= new BufferedInputStream(
251 new XInputStreamToInputStreamAdapter(m_xis
));
252 //Source xmlsource = new StreamSource(xmlinput);
253 SAXParserFactory spf
= SAXParserFactory
.newInstance();
254 spf
.setValidating(false);
255 spf
.setNamespaceAware(true);
256 XMLReader xmlReader
= spf
.newSAXParser().getXMLReader();
257 xmlReader
.setEntityResolver(XSLTransformer
.this);
258 source
= new SAXSource(xmlReader
, new InputSource(is
));
260 // in order to help performance and to remedy a a possible memory
261 // leak in xalan, where it seems, that Transformer instances cannot
262 // be reclaimed though they are no longer referenced here, we use
263 // a cache of weak references to transformers created for specific
264 // style sheet URLs see also #i48384#
266 Transformer transformer
= null;
267 Transformation transformation
= null;
268 // File stylefile = new File(new URI(stylesheeturl));
271 URL uStyle
= new URL(stylesheeturl
);
272 URLConnection c
= uStyle
.openConnection();
273 lastmod
= c
.getLastModified();
274 } catch (java
.lang
.Exception ex
) {
275 // lastmod will remain at 0;
276 if (statsp
!= null) {
277 statsp
.println(ex
.getClass().getName() + ": " + ex
.getMessage());
278 ex
.printStackTrace(statsp
);
282 synchronized (transformers
) {
283 java
.lang
.ref
.WeakReference ref
= null;
284 // try to get the transformer reference from the cache
285 if ((ref
= (java
.lang
.ref
.WeakReference
) transformers
.get(stylesheeturl
)) == null ||
286 (transformation
= ((Transformation
) ref
.get())) == null ||
287 ((Transformation
) ref
.get()).lastmod
< lastmod
) {
288 // we cannot find a valid reference for this stylesheet
289 // or the stylsheet was updated
291 transformers
.remove(stylesheeturl
);
293 // create new transformer for this stylesheet
294 TransformerFactory tfactory
= TransformerFactory
.newInstance();
295 debug("TransformerFactory is '" + tfactory
.getClass().getName() + "'");
296 transformer
= tfactory
.newTransformer(new StreamSource(stylesheeturl
));
297 transformer
.setOutputProperty("encoding", "UTF-8");
298 // transformer.setURIResolver(XSLTransformer.this);
300 // store the transformation into the cache
301 transformation
= new Transformation();
302 transformation
.lastmod
= lastmod
;
303 transformation
.transformer
= transformer
;
304 ref
= new java
.lang
.ref
.WeakReference(transformation
);
305 transformers
.put(stylesheeturl
, ref
);
308 transformer
= transformation
.transformer
;
310 // invalid to set 'null' as parameter as 'null' is not a valid Java object
311 if (sourceurl
!= null) {
312 transformer
.setParameter("sourceURL", sourceurl
);
314 if (sourcebaseurl
!= null) {
315 transformer
.setParameter("sourceBaseURL", sourcebaseurl
);
317 if (targeturl
!= null) {
318 transformer
.setParameter("targetURL", targeturl
);
320 if (targetbaseurl
!= null) {
321 transformer
.setParameter("targetBaseURL", targetbaseurl
);
323 if (pubtype
!= null) {
324 transformer
.setParameter("publicType", pubtype
);
326 if (systype
!= null) {
327 transformer
.setParameter("systemType", systype
);
329 if (svcfactory
!= null) {
330 transformer
.setParameter("XMultiServiceFactory", svcfactory
);
332 os
= new BufferedOutputStream(
333 new XOutputStreamToOutputStreamAdapter(m_xos
));
334 StreamResult sr
= new StreamResult(os
);
335 long tstart
= System
.currentTimeMillis();
336 transformer
.transform(source
, sr
);
337 debug("finished transformation in " + (System
.currentTimeMillis() - tstart
) + "ms");
339 } catch (java
.lang
.Throwable ex
) {
340 // notify any listeners about close
341 for (Enumeration e
= listeners
.elements(); e
.hasMoreElements();) {
343 XStreamListener l
= (XStreamListener
) e
.nextElement();
344 l
.error(new com
.sun
.star
.uno
.Exception(ex
.getClass().getName() + ": " + ex
.getMessage()));
346 if (statsp
!= null) {
347 statsp
.println(ex
.getClass().getName() + ": " + ex
.getMessage());
348 ex
.printStackTrace(statsp
);
351 // dereference input buffer
366 } catch (java
.lang
.Throwable ex
) {
367 if (statsp
!= null) {
368 statsp
.println(ex
.getClass().getName() + ": " + ex
.getMessage());
369 ex
.printStackTrace(statsp
);
373 // resetting standard input/error streams from logfile to default
374 if (statsp
!= null) {
375 System
.setErr(origErr
);
376 System
.setOut(origOut
);
378 // try to release references asap...
383 // notify any listeners about close
384 if (listeners
!= null) {
385 for (Enumeration e
= listeners
.elements(); e
.hasMoreElements();) {
386 XStreamListener l
= (XStreamListener
) e
.nextElement();
394 } /* a statsfile have to be created as precondition to use this function */
397 private static final void debug(String s
) {
398 if (statsp
!= null) {
403 public void terminate() {
405 debug("terminate called");
408 for (Enumeration e
= listeners
.elements(); e
.hasMoreElements();) {
409 XStreamListener l
= (XStreamListener
) e
.nextElement();
413 } catch (java
.lang
.Exception ex
) {
414 if (statsp
!= null) {
415 statsp
.println(ex
.getClass().getName() + ": " + ex
.getMessage());
416 ex
.printStackTrace(statsp
);
419 } // --- component management interfaces... ---
420 private final static String _serviceName
= "com.sun.star.comp.JAXTHelper";
422 // Implement methods from interface XTypeProvider
423 public byte[] getImplementationId() {
424 byte[] byteReturn
= {};
425 byteReturn
= new String("" + this.hashCode()).getBytes();
429 public com
.sun
.star
.uno
.Type
[] getTypes() {
430 Type
[] typeReturn
= {};
432 typeReturn
= new Type
[]{
433 new Type(XTypeProvider
.class),
434 new Type(XServiceName
.class),
435 new Type(XServiceInfo
.class),
436 new Type(XActiveDataSource
.class),
437 new Type(XActiveDataSink
.class),
438 new Type(XActiveDataControl
.class),
439 new Type(XInitialization
.class)
441 } catch (java
.lang
.Exception exception
) {
447 // --- Implement method from interface XServiceName ---
448 public String
getServiceName() {
449 return (_serviceName
);
452 // --- Implement methods from interface XServiceInfo ---
453 public boolean supportsService(String stringServiceName
) {
454 return (stringServiceName
.equals(_serviceName
));
457 public String
getImplementationName() {
458 return (XSLTransformer
.class.getName());
461 public String
[] getSupportedServiceNames() {
462 String
[] stringSupportedServiceNames
= {_serviceName
};
463 return stringSupportedServiceNames
;
466 // --- component registration methods ---
467 public static XSingleServiceFactory
__getServiceFactory(
468 String implName
, XMultiServiceFactory multiFactory
, XRegistryKey regKey
) {
469 XSingleServiceFactory xSingleServiceFactory
= null;
470 if (implName
.equals(XSLTransformer
.class.getName())) {
471 xSingleServiceFactory
= FactoryHelper
.getServiceFactory(XSLTransformer
.class,
472 _serviceName
, multiFactory
, regKey
);
474 return xSingleServiceFactory
;
477 public static boolean __writeRegistryServiceInfo(XRegistryKey regKey
) {
478 return FactoryHelper
.writeRegistryServiceInfo(XSLTransformer
.class.getName(),
479 _serviceName
, regKey
);