merge the formfield patch from ooo-build
[ooovba.git] / filter / source / xsltfilter / XSLTransformer.java
blob7dfffedb07feeabef912c391bac74cca7a8d975c
2 /************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * Copyright 2008 by Sun Microsystems, Inc.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * $RCSfile: XSLTransformer.java,v $
11 * $Revision: 1.20 $
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;
33 import java.io.File;
34 import java.io.FileOutputStream;
35 import java.io.InputStream;
36 import java.io.PrintStream;
37 import java.io.StringReader;
38 import java.net.URL;
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;
79 //Uno to java Adaptor
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 {
92 /**
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;
119 public long lastmod;
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) {
131 svcfactory = 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) {
138 try {
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) {
186 m_xis = aStream;
189 public com.sun.star.io.XInputStream getInputStream() {
190 return m_xis;
193 // --- XActiveDataSource
194 public void setOutputStream(XOutputStream aStream) {
195 m_xos = aStream;
198 public com.sun.star.io.XOutputStream getOutputStream() {
199 return m_xos;
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() {
217 // notify listeners
218 t = new Thread() {
220 @Override
221 public void run() {
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);
233 try {
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();
242 l.started();
245 XSeekable xseek = (XSeekable) UnoRuntime.queryInterface(XSeekable.class, m_xis);
246 if (xseek != null) {
247 xseek.seek(0);
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));
269 long lastmod = 0;
270 try {
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
290 if (ref != null) {
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);
350 } finally {
351 // dereference input buffer
352 source = null;
353 try {
354 if (is != null) {
355 is.close();
357 if (os != null) {
358 os.close();
360 if (m_xis != null) {
361 m_xis.closeInput();
363 if (m_xos != null) {
364 m_xos.closeOutput();
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...
379 m_xos = null;
380 m_xis = null;
381 is = null;
382 os = null;
383 // notify any listeners about close
384 if (listeners != null) {
385 for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
386 XStreamListener l = (XStreamListener) e.nextElement();
387 l.closed();
393 t.start();
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) {
399 statsp.println(s);
403 public void terminate() {
404 try {
405 debug("terminate called");
406 if (t.isAlive()) {
407 t.interrupt();
408 for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
409 XStreamListener l = (XStreamListener) e.nextElement();
410 l.terminated();
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();
426 return (byteReturn);
429 public com.sun.star.uno.Type[] getTypes() {
430 Type[] typeReturn = {};
431 try {
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) {
444 return (typeReturn);
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);