Coverage Report - org.crosswire.common.xml.TransformingSAXEventProvider
 
Classes in this File Line Coverage Branch Coverage Complexity
TransformingSAXEventProvider
0%
0/78
0%
0/20
1.7
TransformingSAXEventProvider$TemplateInfo
0%
0/6
N/A
1.7
 
 1  
 /**
 2  
  * Distribution License:
 3  
  * JSword is free software; you can redistribute it and/or modify it under
 4  
  * the terms of the GNU Lesser General Public License, version 2.1 or later
 5  
  * as published by the Free Software Foundation. This program is distributed
 6  
  * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 7  
  * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 8  
  * See the GNU Lesser General Public License for more details.
 9  
  *
 10  
  * The License is available on the internet at:
 11  
  *      http://www.gnu.org/copyleft/lgpl.html
 12  
  * or by writing to:
 13  
  *      Free Software Foundation, Inc.
 14  
  *      59 Temple Place - Suite 330
 15  
  *      Boston, MA 02111-1307, USA
 16  
  *
 17  
  * © CrossWire Bible Society, 2005 - 2016
 18  
  *
 19  
  */
 20  
 package org.crosswire.common.xml;
 21  
 
 22  
 import java.io.IOException;
 23  
 import java.io.InputStream;
 24  
 import java.net.URI;
 25  
 import java.util.HashMap;
 26  
 import java.util.Map;
 27  
 import java.util.Properties;
 28  
 
 29  
 import javax.xml.transform.ErrorListener;
 30  
 import javax.xml.transform.Result;
 31  
 import javax.xml.transform.Source;
 32  
 import javax.xml.transform.Templates;
 33  
 import javax.xml.transform.Transformer;
 34  
 import javax.xml.transform.TransformerConfigurationException;
 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.StreamSource;
 41  
 
 42  
 import org.crosswire.common.util.IOUtil;
 43  
 import org.crosswire.common.util.NetUtil;
 44  
 import org.slf4j.Logger;
 45  
 import org.slf4j.LoggerFactory;
 46  
 import org.xml.sax.ContentHandler;
 47  
 import org.xml.sax.SAXException;
 48  
 
 49  
 /**
 50  
  * A SAXEventProvider that gets its output data from an XSL stylesheet and
 51  
  * another SAXEventProvider (supplying input XML).
 52  
  * 
 53  
  * @see gnu.lgpl.License The GNU Lesser General Public License for details.
 54  
  * @author Joe Walker
 55  
  */
 56  
 public class TransformingSAXEventProvider extends Transformer implements SAXEventProvider {
 57  
     /**
 58  
      * Simple ctor
 59  
      * 
 60  
      * @param xsluri the URI of XSL
 61  
      * @param xmlsep a SAX Event Provider for an XML document
 62  
      */
 63  0
     public TransformingSAXEventProvider(URI xsluri, SAXEventProvider xmlsep) {
 64  0
         this.xsluri = xsluri;
 65  0
         this.xmlsep = xmlsep;
 66  0
         this.outputs = new Properties();
 67  0
         this.params = new HashMap<String, Object>();
 68  0
     }
 69  
 
 70  
     /**
 71  
      * Compile the XSL or retrieve it from the cache
 72  
      * 
 73  
      * @return the template information
 74  
      * @throws TransformerConfigurationException when there is a problem with configuring the transformer
 75  
      * @throws IOException when there is an I/O error
 76  
      */
 77  
     private TemplateInfo getTemplateInfo() throws TransformerConfigurationException, IOException {
 78  
         // we may have one cached
 79  0
         TemplateInfo tinfo = txers.get(xsluri);
 80  
 
 81  0
         long modtime = -1;
 82  0
         if (TransformingSAXEventProvider.developmentMode) {
 83  0
             if (tinfo != null) {
 84  0
                 modtime = NetUtil.getLastModified(xsluri);
 85  
 
 86  
                 // But check it is up to date
 87  0
                 if (modtime > tinfo.getModtime()) {
 88  0
                     txers.remove(xsluri);
 89  0
                     tinfo = null;
 90  0
                     log.debug("updated style, re-caching. xsl={}", xsluri);
 91  
                 }
 92  
             }
 93  
         }
 94  
 
 95  0
         if (tinfo == null) {
 96  0
             log.debug("generating templates for {}", xsluri);
 97  
 
 98  0
             InputStream xslStream = null;
 99  
             try {
 100  0
                 xslStream = NetUtil.getInputStream(xsluri);
 101  0
                 if (transfact == null) {
 102  0
                     transfact = TransformerFactory.newInstance();
 103  
                 }
 104  0
                 Templates templates = transfact.newTemplates(new StreamSource(xslStream));
 105  
 
 106  0
                 if (modtime == -1) {
 107  0
                     modtime = NetUtil.getLastModified(xsluri);
 108  
                 }
 109  
 
 110  0
                 tinfo = new TemplateInfo(templates, modtime);
 111  
 
 112  0
                 txers.put(xsluri, tinfo);
 113  
             } finally {
 114  0
                 IOUtil.close(xslStream);
 115  0
             }
 116  
         }
 117  
 
 118  0
         return tinfo;
 119  
     }
 120  
 
 121  
     /* (non-Javadoc)
 122  
      * @see javax.xml.transform.Transformer#transform(javax.xml.transform.Source, javax.xml.transform.Result)
 123  
      */
 124  
     @Override
 125  
     public void transform(Source xmlSource, Result outputTarget) throws TransformerException {
 126  
         TemplateInfo tinfo;
 127  
         try {
 128  0
             tinfo = getTemplateInfo();
 129  0
         } catch (IOException e) {
 130  0
             throw new TransformerException(e);
 131  0
         }
 132  
 
 133  0
         Transformer transformer = tinfo.getTemplates().newTransformer();
 134  
 
 135  0
         for (Object obj : outputs.keySet()) {
 136  0
             String key = (String) obj;
 137  0
             String val = getOutputProperty(key);
 138  0
             transformer.setOutputProperty(key, val);
 139  0
         }
 140  
 
 141  0
         for (String key : params.keySet()) {
 142  0
             Object val = params.get(key);
 143  0
             transformer.setParameter(key, val);
 144  0
         }
 145  
 
 146  0
         if (errors != null) {
 147  0
             transformer.setErrorListener(errors);
 148  
         }
 149  
 
 150  0
         if (resolver != null) {
 151  0
             transformer.setURIResolver(resolver);
 152  
         }
 153  
 
 154  0
         transformer.transform(xmlSource, outputTarget);
 155  0
     }
 156  
 
 157  
     /* (non-Javadoc)
 158  
      * @see org.crosswire.common.xml.SAXEventProvider#provideSAXEvents(org.xml.sax.ContentHandler)
 159  
      */
 160  
     public void provideSAXEvents(ContentHandler handler) throws SAXException {
 161  
         try {
 162  0
             Source xmlSource = new SAXSource(new SAXEventProviderXMLReader(xmlsep), new SAXEventProviderInputSource());
 163  
 
 164  0
             SAXResult outputTarget = new SAXResult(handler);
 165  
 
 166  0
             transform(xmlSource, outputTarget);
 167  0
         } catch (TransformerException ex) {
 168  0
             throw new SAXException(ex);
 169  0
         }
 170  0
     }
 171  
 
 172  
     /* (non-Javadoc)
 173  
      * @see javax.xml.transform.Transformer#getErrorListener()
 174  
      */
 175  
     @Override
 176  
     public ErrorListener getErrorListener() {
 177  0
         return errors;
 178  
     }
 179  
 
 180  
     /* (non-Javadoc)
 181  
      * @see javax.xml.transform.Transformer#setErrorListener(javax.xml.transform.ErrorListener)
 182  
      */
 183  
     @Override
 184  
     public void setErrorListener(ErrorListener errors) throws IllegalArgumentException {
 185  0
         this.errors = errors;
 186  0
     }
 187  
 
 188  
     /* (non-Javadoc)
 189  
      * @see javax.xml.transform.Transformer#getURIResolver()
 190  
      */
 191  
     @Override
 192  
     public URIResolver getURIResolver() {
 193  0
         return resolver;
 194  
     }
 195  
 
 196  
     /* (non-Javadoc)
 197  
      * @see javax.xml.transform.Transformer#setURIResolver(javax.xml.transform.URIResolver)
 198  
      */
 199  
     @Override
 200  
     public void setURIResolver(URIResolver resolver) {
 201  0
         this.resolver = resolver;
 202  0
     }
 203  
 
 204  
     /* (non-Javadoc)
 205  
      * @see javax.xml.transform.Transformer#getOutputProperties()
 206  
      */
 207  
     @Override
 208  
     public Properties getOutputProperties() {
 209  0
         return outputs;
 210  
     }
 211  
 
 212  
     /* (non-Javadoc)
 213  
      * @see javax.xml.transform.Transformer#setOutputProperties(java.util.Properties)
 214  
      */
 215  
     @Override
 216  
     public void setOutputProperties(Properties outputs) throws IllegalArgumentException {
 217  0
         this.outputs = outputs;
 218  0
     }
 219  
 
 220  
     /* (non-Javadoc)
 221  
      * @see javax.xml.transform.Transformer#getOutputProperty(java.lang.String)
 222  
      */
 223  
     @Override
 224  
     public String getOutputProperty(String name) throws IllegalArgumentException {
 225  0
         return outputs.getProperty(name);
 226  
     }
 227  
 
 228  
     /* (non-Javadoc)
 229  
      * @see javax.xml.transform.Transformer#setOutputProperty(java.lang.String, java.lang.String)
 230  
      */
 231  
     @Override
 232  
     public void setOutputProperty(String name, String value) throws IllegalArgumentException {
 233  0
         outputs.setProperty(name, value);
 234  0
     }
 235  
 
 236  
     /* (non-Javadoc)
 237  
      * @see javax.xml.transform.Transformer#clearParameters()
 238  
      */
 239  
     @Override
 240  
     public void clearParameters() {
 241  0
         params.clear();
 242  0
     }
 243  
 
 244  
     /* (non-Javadoc)
 245  
      * @see javax.xml.transform.Transformer#getParameter(java.lang.String)
 246  
      */
 247  
     @Override
 248  
     public Object getParameter(String name) {
 249  0
         return params.get(name);
 250  
     }
 251  
 
 252  
     /* (non-Javadoc)
 253  
      * @see javax.xml.transform.Transformer#setParameter(java.lang.String, java.lang.Object)
 254  
      */
 255  
     @Override
 256  
     public void setParameter(String name, Object value) {
 257  0
         params.put(name, value);
 258  0
     }
 259  
 
 260  
     /**
 261  
      * In development mode the style sheet is checked for modifications before use and if so, it is recompiled.
 262  
      * 
 263  
      * @param developmentMode the developmentMode to set
 264  
      */
 265  
     public static void setDevelopmentMode(boolean developmentMode) {
 266  0
         TransformingSAXEventProvider.developmentMode = developmentMode;
 267  0
     }
 268  
 
 269  
     /**
 270  
      * In development mode the style sheet is checked for modifications before use and if so, it is recompiled.
 271  
      * 
 272  
      * @return the developmentMode
 273  
      */
 274  
     public static boolean isDevelopmentMode() {
 275  0
         return developmentMode;
 276  
     }
 277  
 
 278  
     /**
 279  
      * A simple class to link modification times to Templates objects
 280  
      */
 281  
     private static class TemplateInfo {
 282  
         /**
 283  
          * Simple ctor
 284  
          * 
 285  
          * @param templates the compiled XSL
 286  
          * @param modtime the time the XSL was last modified
 287  
          */
 288  0
         TemplateInfo(Templates templates, long modtime) {
 289  0
             this.templates = templates;
 290  0
             this.modtime = modtime;
 291  0
         }
 292  
 
 293  
         /**
 294  
          * The transformer
 295  
          * 
 296  
          * @return the compiled XSL
 297  
          */
 298  
         Templates getTemplates() {
 299  0
             return templates;
 300  
         }
 301  
 
 302  
         /**
 303  
          * The time the xsl file was last modified
 304  
          * 
 305  
          * @return when the xsl was last modified.
 306  
          */
 307  
         long getModtime() {
 308  0
             return modtime;
 309  
         }
 310  
 
 311  
         private Templates templates;
 312  
         private long modtime;
 313  
     }
 314  
 
 315  
     /**
 316  
      * In development mode the style sheet is checked for modifications before use and if so, it is recompiled.
 317  
      */
 318  
     private static boolean developmentMode;
 319  
 
 320  
     /**
 321  
      * The remembered ErrorListener because the transformer has not been created
 322  
      */
 323  
     private ErrorListener errors;
 324  
 
 325  
     /**
 326  
      * The remembered URIResolver because the transformer has not been created
 327  
      */
 328  
     private URIResolver resolver;
 329  
 
 330  
     /**
 331  
      * The remembered OutputProperties because the transformer has not been
 332  
      * created
 333  
      */
 334  
     private Properties outputs;
 335  
 
 336  
     /**
 337  
      * The remembered Parameters because the transformer has not been created
 338  
      */
 339  
     private Map<String, Object> params;
 340  
 
 341  
     /**
 342  
      * The XSL stylesheet
 343  
      */
 344  
     private URI xsluri;
 345  
 
 346  
     /**
 347  
      * The XML input source
 348  
      */
 349  
     private SAXEventProvider xmlsep;
 350  
 
 351  
     /**
 352  
      * How we get the transformer objects
 353  
      */
 354  
     private TransformerFactory transfact;
 355  
 
 356  
     /**
 357  
      * A cache of transformers
 358  
      */
 359  0
     private static Map<URI, TemplateInfo> txers = new HashMap<URI, TemplateInfo>();
 360  
 
 361  
     /**
 362  
      * The log stream
 363  
      */
 364  0
     private static final Logger log = LoggerFactory.getLogger(TransformingSAXEventProvider.class);
 365  
 }