| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| package com.sun.star.comp.xsltfilter; |
| |
| |
| |
| //Standard Java classes |
| import java.io.BufferedInputStream; |
| import java.io.BufferedOutputStream; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.InputStream; |
| import java.io.PrintStream; |
| import java.io.StringReader; |
| import java.net.URL; |
| import java.net.URLConnection; |
| import java.util.Enumeration; |
| import java.util.Hashtable; |
| import java.util.Vector; |
| |
| // Imported TraX classes |
| import javax.xml.parsers.SAXParserFactory; |
| import javax.xml.transform.Source; |
| import javax.xml.transform.Transformer; |
| import javax.xml.transform.TransformerFactory; |
| import javax.xml.transform.URIResolver; |
| import javax.xml.transform.sax.SAXSource; |
| import javax.xml.transform.stream.StreamResult; |
| import javax.xml.transform.stream.StreamSource; |
| import org.xml.sax.EntityResolver; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.XMLReader; |
| |
| //StarOffice Interfaces and UNO |
| import com.sun.star.beans.NamedValue; |
| import com.sun.star.comp.loader.FactoryHelper; |
| import com.sun.star.io.XActiveDataControl; |
| import com.sun.star.io.XActiveDataSink; |
| import com.sun.star.io.XActiveDataSource; |
| import com.sun.star.io.XInputStream; |
| import com.sun.star.io.XOutputStream; |
| import com.sun.star.io.XSeekable; |
| import com.sun.star.io.XStreamListener; |
| import com.sun.star.lang.XInitialization; |
| import com.sun.star.lang.XMultiServiceFactory; |
| import com.sun.star.lang.XServiceInfo; |
| import com.sun.star.lang.XServiceName; |
| import com.sun.star.lang.XSingleServiceFactory; |
| import com.sun.star.lang.XTypeProvider; |
| import com.sun.star.registry.XRegistryKey; |
| import com.sun.star.uno.AnyConverter; |
| import com.sun.star.uno.Type; |
| import com.sun.star.uno.UnoRuntime; |
| |
| //Uno to java Adaptor |
| import com.sun.star.lib.uno.adapter.XInputStreamToInputStreamAdapter; |
| import com.sun.star.lib.uno.adapter.XOutputStreamToOutputStreamAdapter; |
| import javax.xml.transform.Templates; |
| |
| import net.sf.saxon.FeatureKeys; |
| |
| /** This outer class provides an inner class to implement the service |
| * description and a method to instantiate the |
| * component on demand (__getServiceFactory()). |
| */ |
| public class XSLTransformer |
| implements XTypeProvider, XServiceName, XServiceInfo, XActiveDataSink, |
| XActiveDataSource, XActiveDataControl, XInitialization, URIResolver, EntityResolver { |
| |
| /** |
| * This component provides java based XSL transformations |
| * A SAX based interface is not feasible when crossing language bordes |
| * since too much time would be wasted by bridging the events between environments |
| * example: 190 pages document, 82000 events 8seconds transform 40(!) sec. bridging |
| * |
| */ |
| private XInputStream m_xis; |
| private XOutputStream m_xos; // private static HashMap templatecache; |
| private static final String STATSPROP = "XSLTransformer.statsfile"; |
| private static PrintStream statsp; |
| private String stylesheeturl; |
| private String targeturl; |
| private String targetbaseurl; |
| private String sourceurl; |
| private String sourcebaseurl; |
| private String pubtype = new String(); |
| private String systype = new String(); // processing thread |
| private Thread t; // listeners |
| private Vector listeners = new Vector(); // |
| private XMultiServiceFactory svcfactory; // cache for transformations by stylesheet |
| private static Hashtable xsltReferences = new Hashtable(); |
| // struct for cached stylesheets |
| private static class Transformation { |
| |
| public Templates cachedXSLT; |
| public long lastmod; |
| } |
| // Resolve URIs to an empty source |
| public Source resolve(String href, String base) { |
| return new StreamSource(new StringReader("")); |
| } |
| |
| public InputSource resolveEntity(String publicId, String systemId) throws SAXException, java.io.IOException { |
| return new InputSource(new StringReader("")); |
| } |
| // --- Initialization --- |
| public XSLTransformer(XMultiServiceFactory msf) { |
| svcfactory = msf; |
| } |
| |
| public void initialize(Object[] values) throws com.sun.star.uno.Exception { |
| // some configurable debugging |
| String statsfilepath = null; |
| if ((statsfilepath = System.getProperty(STATSPROP)) != null) { |
| try { |
| File statsfile = new File(statsfilepath); |
| statsp = new PrintStream(new FileOutputStream(statsfile.getPath(), false)); |
| } catch (java.lang.Exception e) { |
| System.err.println("XSLTransformer: could not open statsfile'" + statsfilepath + "'"); |
| System.err.println(" " + e.getClass().getName() + ": " + e.getMessage()); |
| System.err.println(" output disabled"); |
| } |
| } |
| |
| // reading the values |
| NamedValue nv = null; |
| debug("The transformation's parameters as 'name = value' pairs:\n"); |
| |
| for (int i = 0; i < values.length; i++) { |
| nv = (NamedValue) AnyConverter.toObject(new Type(NamedValue.class), values[i]); |
| |
| if (nv.Name != null && !nv.Name.equals("")) { |
| debug(nv.Name + " = " + nv.Value); |
| } |
| |
| if (nv.Name.equals("StylesheetURL")) { |
| stylesheeturl = (String) AnyConverter.toObject( |
| new Type(String.class), nv.Value); |
| } else if (nv.Name.equals("SourceURL")) { |
| sourceurl = (String) AnyConverter.toObject( |
| new Type(String.class), nv.Value); |
| } else if (nv.Name.equals("TargetURL")) { |
| targeturl = (String) AnyConverter.toObject( |
| new Type(String.class), nv.Value); |
| } else if (nv.Name.equals("SourceBaseURL")) { |
| sourcebaseurl = (String) AnyConverter.toObject( |
| new Type(String.class), nv.Value); |
| } else if (nv.Name.equals("TargetBaseURL")) { |
| targetbaseurl = (String) AnyConverter.toObject( |
| new Type(String.class), nv.Value); |
| } else if (nv.Name.equals("SystemType")) { |
| systype = (String) AnyConverter.toObject( |
| new Type(String.class), nv.Value); |
| } else if (nv.Name.equals("PublicType")) { |
| pubtype = (String) AnyConverter.toObject( |
| new Type(String.class), nv.Value); |
| } |
| } |
| } |
| |
| // --- XActiveDataSink xistream = aStream; |
| public void setInputStream(XInputStream aStream) { |
| m_xis = aStream; |
| } |
| |
| public com.sun.star.io.XInputStream getInputStream() { |
| return m_xis; |
| } |
| |
| // --- XActiveDataSource |
| public void setOutputStream(XOutputStream aStream) { |
| m_xos = aStream; |
| } |
| |
| public com.sun.star.io.XOutputStream getOutputStream() { |
| return m_xos; |
| } |
| |
| // --- XActiveDataControl |
| public void addListener(XStreamListener aListener) { |
| if (aListener != null && !listeners.contains(aListener)) { |
| listeners.add(aListener); |
| } |
| } |
| |
| public void removeListener(XStreamListener aListener) { |
| if (aListener != null) { |
| listeners.removeElement(aListener); |
| } |
| |
| } |
| |
| public void start() { |
| // notify listeners |
| t = new Thread() { |
| |
| @Override |
| public void run() { |
| |
| // Local variabes used outside try block in finally block |
| InputStream is = null; |
| Source source = null; |
| BufferedOutputStream os = null; |
| PrintStream origOut = System.out; |
| PrintStream origErr = System.err; |
| if (statsp != null) { |
| System.setErr(statsp); |
| System.setOut(statsp); |
| } |
| try { |
| debug("\n\nStarting transformation..."); |
| |
| // Set up context class loader for SAXParserFactory and |
| // TransformerFactory calls below: |
| setContextClassLoader(this.getClass().getClassLoader()); |
| |
| for (Enumeration e = listeners.elements(); e.hasMoreElements();) { |
| XStreamListener l = (XStreamListener) e.nextElement(); |
| l.started(); |
| } |
| |
| XSeekable xseek = (XSeekable) UnoRuntime.queryInterface(XSeekable.class, m_xis); |
| if (xseek != null) { |
| xseek.seek(0); |
| } |
| |
| is = new BufferedInputStream( |
| new XInputStreamToInputStreamAdapter(m_xis)); |
| //Source xmlsource = new StreamSource(xmlinput); |
| SAXParserFactory spf = SAXParserFactory.newInstance(); |
| spf.setValidating(false); |
| spf.setNamespaceAware(true); |
| XMLReader xmlReader = spf.newSAXParser().getXMLReader(); |
| xmlReader.setEntityResolver(XSLTransformer.this); |
| source = new SAXSource(xmlReader, new InputSource(is)); |
| |
| // in order to help performance and to remedy a a possible memory |
| // leak in xalan, where it seems, that Transformer instances cannot |
| // be reclaimed though they are no longer referenced here, we use |
| // a cache of weak references (ie. xsltReferences) created for specific |
| // style sheet URLs see also #i48384# |
| |
| Templates xsltTemplate = null; |
| Transformer transformer = null; |
| Transformation transformation = null; |
| // File stylefile = new File(new URI(stylesheeturl)); |
| long lastmod = 0; |
| try { |
| URL uStyle = new URL(stylesheeturl); |
| URLConnection c = uStyle.openConnection(); |
| lastmod = c.getLastModified(); |
| } catch (java.lang.Exception ex) { |
| // lastmod will remain at 0; |
| if (statsp != null) { |
| statsp.println(ex.getClass().getName() + ": " + ex.getMessage()); |
| ex.printStackTrace(statsp); |
| } |
| } |
| |
| synchronized (xsltReferences) { |
| java.lang.ref.WeakReference ref = null; |
| // try to get the xsltTemplate reference from the cache |
| if ((ref = (java.lang.ref.WeakReference) xsltReferences.get(stylesheeturl)) == null || |
| (transformation = ((Transformation) ref.get())) == null || |
| ((Transformation) ref.get()).lastmod < lastmod) { |
| // we cannot find a valid reference for this stylesheet |
| // or the stylsheet was updated |
| if (ref != null) { |
| xsltReferences.remove(stylesheeturl); |
| } |
| // create new xsltTemplate for this stylesheet |
| TransformerFactory tfactory = TransformerFactory.newInstance(); |
| debug("TransformerFactory is '" + tfactory.getClass().getName() + "'"); |
| // some external saxons (Debian, Ubuntu, ...) have this disabled |
| // per default |
| tfactory.setAttribute(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS, new Boolean(true)); |
| xsltTemplate = tfactory.newTemplates(new StreamSource(stylesheeturl)); |
| |
| // store the transformation into the cache |
| transformation = new Transformation(); |
| transformation.lastmod = lastmod; |
| transformation.cachedXSLT = xsltTemplate; |
| ref = new java.lang.ref.WeakReference(transformation); |
| xsltReferences.put(stylesheeturl, ref); |
| } |
| } |
| xsltTemplate = transformation.cachedXSLT; |
| transformer = xsltTemplate.newTransformer(); |
| transformer.setOutputProperty("encoding", "UTF-8"); |
| // transformer.setURIResolver(XSLTransformer.this); |
| |
| // invalid to set 'null' as parameter as 'null' is not a valid Java object |
| if (sourceurl != null) { |
| transformer.setParameter("sourceURL", sourceurl); |
| } |
| if (sourcebaseurl != null) { |
| transformer.setParameter("sourceBaseURL", sourcebaseurl); |
| } |
| if (targeturl != null) { |
| transformer.setParameter("targetURL", targeturl); |
| } |
| if (targetbaseurl != null) { |
| transformer.setParameter("targetBaseURL", targetbaseurl); |
| } |
| if (pubtype != null) { |
| transformer.setParameter("publicType", pubtype); |
| } |
| if (systype != null) { |
| transformer.setParameter("systemType", systype); |
| } |
| if (svcfactory != null) { |
| transformer.setParameter("XMultiServiceFactory", svcfactory); |
| } |
| os = new BufferedOutputStream( |
| new XOutputStreamToOutputStreamAdapter(m_xos)); |
| StreamResult sr = new StreamResult(os); |
| long tstart = System.currentTimeMillis(); |
| transformer.transform(source, sr); |
| debug("finished transformation in " + (System.currentTimeMillis() - tstart) + "ms"); |
| |
| } catch (java.lang.Throwable ex) { |
| // notify any listeners about close |
| for (Enumeration e = listeners.elements(); e.hasMoreElements();) { |
| |
| XStreamListener l = (XStreamListener) e.nextElement(); |
| l.error(new com.sun.star.uno.Exception(ex.getClass().getName() + ": " + ex.getMessage())); |
| } |
| if (statsp != null) { |
| statsp.println(ex.getClass().getName() + ": " + ex.getMessage()); |
| ex.printStackTrace(statsp); |
| } |
| } finally { |
| // dereference input buffer |
| source = null; |
| try { |
| if (is != null) { |
| is.close(); |
| } |
| } catch (java.lang.Throwable ex) { |
| if (statsp != null) { |
| statsp.println(ex.getClass().getName() + ": " + ex.getMessage()); |
| ex.printStackTrace(statsp); |
| } |
| } |
| try { |
| if (os != null) { |
| os.close(); |
| } |
| } catch (java.lang.Throwable ex) { |
| if (statsp != null) { |
| statsp.println(ex.getClass().getName() + ": " + ex.getMessage()); |
| ex.printStackTrace(statsp); |
| } |
| } |
| try { |
| if (m_xis != null) { |
| m_xis.closeInput(); |
| } |
| } catch (java.lang.Throwable ex) { |
| if (statsp != null) { |
| statsp.println(ex.getClass().getName() + ": " + ex.getMessage()); |
| ex.printStackTrace(statsp); |
| } |
| } |
| try { |
| if (m_xos != null) { |
| m_xos.closeOutput(); |
| } |
| } catch (java.lang.Throwable ex) { |
| if (statsp != null) { |
| statsp.println(ex.getClass().getName() + ": " + ex.getMessage()); |
| ex.printStackTrace(statsp); |
| } |
| } |
| |
| // resetting standard input/error streams from logfile to default |
| if (statsp != null) { |
| System.setErr(origErr); |
| System.setOut(origOut); |
| } |
| // try to release references asap... |
| m_xos = null; |
| m_xis = null; |
| is = null; |
| os = null; |
| // notify any listeners about close |
| if (listeners != null) { |
| for (Enumeration e = listeners.elements(); e.hasMoreElements();) { |
| XStreamListener l = (XStreamListener) e.nextElement(); |
| l.closed(); |
| } |
| } |
| } |
| } |
| }; |
| t.start(); |
| } /* a statsfile have to be created as precondition to use this function */ |
| |
| |
| private static final void debug(String s) { |
| if (statsp != null) { |
| statsp.println(s); |
| } |
| } |
| |
| public void terminate() { |
| try { |
| debug("terminate called"); |
| if (t.isAlive()) { |
| t.interrupt(); |
| for (Enumeration e = listeners.elements(); e.hasMoreElements();) { |
| XStreamListener l = (XStreamListener) e.nextElement(); |
| l.terminated(); |
| } |
| } |
| } catch (java.lang.Exception ex) { |
| if (statsp != null) { |
| statsp.println(ex.getClass().getName() + ": " + ex.getMessage()); |
| ex.printStackTrace(statsp); |
| } |
| } |
| } // --- component management interfaces... --- |
| private final static String _serviceName = "com.sun.star.comp.JAXTHelper"; |
| |
| // Implement methods from interface XTypeProvider |
| public byte[] getImplementationId() { |
| byte[] byteReturn = {}; |
| byteReturn = new String("" + this.hashCode()).getBytes(); |
| return (byteReturn); |
| } |
| |
| public com.sun.star.uno.Type[] getTypes() { |
| Type[] typeReturn = {}; |
| try { |
| typeReturn = new Type[]{ |
| new Type(XTypeProvider.class), |
| new Type(XServiceName.class), |
| new Type(XServiceInfo.class), |
| new Type(XActiveDataSource.class), |
| new Type(XActiveDataSink.class), |
| new Type(XActiveDataControl.class), |
| new Type(XInitialization.class) |
| }; |
| } catch (java.lang.Exception exception) { |
| } |
| |
| return (typeReturn); |
| } |
| |
| // --- Implement method from interface XServiceName --- |
| public String getServiceName() { |
| return (_serviceName); |
| } |
| |
| // --- Implement methods from interface XServiceInfo --- |
| public boolean supportsService(String stringServiceName) { |
| return (stringServiceName.equals(_serviceName)); |
| } |
| |
| public String getImplementationName() { |
| return (XSLTransformer.class.getName()); |
| } |
| |
| public String[] getSupportedServiceNames() { |
| String[] stringSupportedServiceNames = {_serviceName}; |
| return stringSupportedServiceNames; |
| } |
| |
| // --- component registration methods --- |
| public static XSingleServiceFactory __getServiceFactory( |
| String implName, XMultiServiceFactory multiFactory, XRegistryKey regKey) { |
| XSingleServiceFactory xSingleServiceFactory = null; |
| if (implName.indexOf("XSLTransformer") != -1) { |
| xSingleServiceFactory = FactoryHelper.getServiceFactory(XSLTransformer.class, |
| _serviceName, multiFactory, regKey); |
| } |
| return xSingleServiceFactory; |
| } |
| } |