1 /*
   2  * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * EfficientStreamingTransformer.java
  28  *
  29  * Created on July 29, 2002, 3:49 PM
  30  */
  31 
  32 package com.sun.xml.internal.messaging.saaj.util.transform;
  33 
  34 import java.io.*;
  35 
  36 import java.net.URISyntaxException;
  37 import javax.xml.transform.dom.DOMSource;
  38 import javax.xml.transform.dom.DOMResult;
  39 import javax.xml.transform.stream.StreamResult;
  40 import javax.xml.transform.stream.StreamSource;
  41 
  42 import org.w3c.dom.Document;
  43 
  44 import com.sun.xml.internal.messaging.saaj.util.XMLDeclarationParser;
  45 import com.sun.xml.internal.messaging.saaj.util.FastInfosetReflection;
  46 import java.net.URI;
  47 import javax.xml.transform.Transformer;
  48 import javax.xml.transform.TransformerException;
  49 import javax.xml.transform.TransformerFactory;
  50 
  51 /**
  52  * This class is a proxy for a Transformer object with optimizations
  53  * for certain cases. If source and result are of type stream, then
  54  * bytes are simply copied whenever possible (note that this assumes
  55  * that the input is well formed). In addition, it provides support for
  56  * FI using native DOM parsers and serializers.
  57  *
  58  * @author Panos Kougiouris panos@acm.org
  59  * @author Santiago.PericasGeertsen@sun.com
  60  *
  61  */
  62 public class EfficientStreamingTransformer
  63     extends javax.xml.transform.Transformer {
  64 
  65   //static final String version;
  66   //static final String vendor;
  67   // removing static : security issue : CR 6813167Z
  68   private final TransformerFactory transformerFactory = TransformerFactory.newInstance();
  69 
  70   /**
  71   removing support for Java 1.4 and 1.3 : CR6658158
  72   static {
  73         version = System.getProperty("java.vm.version");
  74         vendor = System.getProperty("java.vm.vendor");
  75         if (vendor.startsWith("Sun") &&
  76             (version.startsWith("1.4") || version.startsWith("1.3"))) {
  77             transformerFactory =
  78                 new com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl();
  79         }
  80   }*/
  81 
  82     /**
  83      * TransformerFactory instance.
  84      */
  85 
  86     /**
  87      * Underlying XSLT transformer.
  88      */
  89     private Transformer m_realTransformer = null;
  90 
  91     /**
  92      * Undelying FI DOM parser.
  93      */
  94     private Object m_fiDOMDocumentParser = null;
  95 
  96     /**
  97      * Underlying FI DOM serializer.
  98      */
  99     private Object m_fiDOMDocumentSerializer = null;
 100 
 101     private EfficientStreamingTransformer() {
 102     }
 103 
 104     private void materialize() throws TransformerException {
 105         if (m_realTransformer == null) {
 106             m_realTransformer = transformerFactory.newTransformer();
 107         }
 108     }
 109 
 110     @Override
 111     public void clearParameters() {
 112         if (m_realTransformer != null)
 113             m_realTransformer.clearParameters();
 114     }
 115 
 116     @Override
 117     public javax.xml.transform.ErrorListener getErrorListener() {
 118         try {
 119             materialize();
 120             return m_realTransformer.getErrorListener();
 121         } catch (TransformerException e) {
 122             // will be caught later
 123         }
 124         return null;
 125     }
 126 
 127     @Override
 128     public java.util.Properties getOutputProperties() {
 129         try {
 130             materialize();
 131             return m_realTransformer.getOutputProperties();
 132         } catch (TransformerException e) {
 133             // will be caught later
 134         }
 135         return null;
 136     }
 137 
 138     @Override
 139     public String getOutputProperty(String str)
 140         throws java.lang.IllegalArgumentException {
 141         try {
 142             materialize();
 143             return m_realTransformer.getOutputProperty(str);
 144         } catch (TransformerException e) {
 145             // will be caught later
 146         }
 147         return null;
 148     }
 149 
 150     @Override
 151     public Object getParameter(String str) {
 152         try {
 153             materialize();
 154             return m_realTransformer.getParameter(str);
 155         } catch (TransformerException e) {
 156             // will be caught later
 157         }
 158         return null;
 159     }
 160 
 161     @Override
 162     public javax.xml.transform.URIResolver getURIResolver() {
 163         try {
 164             materialize();
 165             return m_realTransformer.getURIResolver();
 166         } catch (TransformerException e) {
 167             // will be caught later
 168         }
 169         return null;
 170     }
 171 
 172     @Override
 173     public void setErrorListener(
 174         javax.xml.transform.ErrorListener errorListener)
 175         throws java.lang.IllegalArgumentException {
 176         try {
 177             materialize();
 178             m_realTransformer.setErrorListener(errorListener);
 179         } catch (TransformerException e) {
 180             // will be caught later
 181         }
 182     }
 183 
 184     @Override
 185     public void setOutputProperties(java.util.Properties properties)
 186         throws java.lang.IllegalArgumentException {
 187         try {
 188             materialize();
 189             m_realTransformer.setOutputProperties(properties);
 190         } catch (TransformerException e) {
 191             // will be caught later
 192         }
 193     }
 194 
 195     @Override
 196     public void setOutputProperty(String str, String str1)
 197         throws java.lang.IllegalArgumentException {
 198         try {
 199             materialize();
 200             m_realTransformer.setOutputProperty(str, str1);
 201         } catch (TransformerException e) {
 202             // will be caught later
 203         }
 204     }
 205 
 206     @Override
 207     public void setParameter(String str, Object obj) {
 208         try {
 209             materialize();
 210             m_realTransformer.setParameter(str, obj);
 211         } catch (TransformerException e) {
 212             // will be caught later
 213         }
 214     }
 215 
 216     @Override
 217     public void setURIResolver(javax.xml.transform.URIResolver uRIResolver) {
 218         try {
 219             materialize();
 220             m_realTransformer.setURIResolver(uRIResolver);
 221         } catch (TransformerException e) {
 222             // will be caught later
 223         }
 224     }
 225 
 226     private InputStream getInputStreamFromSource(StreamSource s)
 227         throws TransformerException {
 228 
 229         InputStream stream = s.getInputStream();
 230         if (stream != null)
 231             return stream;
 232 
 233         if (s.getReader() != null)
 234             return null;
 235 
 236         String systemId = s.getSystemId();
 237         if (systemId != null) {
 238             try {
 239                 String fileURL = systemId;
 240 
 241                 if (systemId.startsWith("file:///"))
 242                 {
 243                     /*
 244                      systemId is:
 245                      file:///<drive>:/some/path/file.xml
 246                      or
 247                      file:///some/path/file.xml
 248                     */
 249 
 250                     String absolutePath = systemId.substring(7);
 251                     /*
 252                      /<drive>:/some/path/file.xml
 253                      or
 254                      /some/path/file.xml
 255                     */
 256 
 257                     boolean hasDriveDesignator = absolutePath.indexOf(":") > 0;
 258                     if (hasDriveDesignator) {
 259                       String driveDesignatedPath = absolutePath.substring(1);
 260                       /*
 261                       <drive>:/some/path/file.xml */
 262                       fileURL = driveDesignatedPath;
 263                     }
 264                     else {
 265                       /*
 266                       /some/path/file.xml */
 267                       fileURL = absolutePath;
 268                     }
 269                 }
 270                 //return new FileInputStream(fileURL);
 271                 try {
 272                     return new FileInputStream(new File(new URI(fileURL)));
 273                 } catch (URISyntaxException ex) {
 274                     throw new TransformerException(ex);
 275                 }
 276             } catch (IOException e) {
 277                 throw new TransformerException(e.toString());
 278             }
 279         }
 280 
 281         throw new TransformerException("Unexpected StreamSource object");
 282     }
 283 
 284     //------------------------------------------------------------------------
 285 
 286     @Override
 287     public void transform(
 288         javax.xml.transform.Source source,
 289         javax.xml.transform.Result result)
 290         throws javax.xml.transform.TransformerException
 291     {
 292         // StreamSource -> StreamResult
 293         if ((source instanceof StreamSource)
 294             && (result instanceof StreamResult)) {
 295             try {
 296                 StreamSource streamSource = (StreamSource) source;
 297                 InputStream is = getInputStreamFromSource(streamSource);
 298 
 299                 OutputStream os = ((StreamResult) result).getOutputStream();
 300                 if (os == null)
 301                     // TODO: We might want to fix this if it were to be used beyond
 302                     // XmlDataContentHandler that we know uses only OutputStream
 303                     throw new TransformerException("Unexpected StreamResult object contains null OutputStream");
 304 
 305                 if (is != null) {
 306                     if (is.markSupported())
 307                         is.mark(Integer.MAX_VALUE);
 308                     int num;
 309                     byte[] b = new byte[8192];
 310                     while ((num = is.read(b)) != -1) {
 311                         os.write(b, 0, num);
 312                     }
 313                     if (is.markSupported())
 314                         is.reset();
 315                     return;
 316                 }
 317 
 318                 Reader reader = streamSource.getReader();
 319                 if (reader != null) {
 320 
 321                     if (reader.markSupported())
 322                         reader.mark(Integer.MAX_VALUE);
 323 
 324                     PushbackReader pushbackReader = new PushbackReader(reader, 4096);
 325                     //some size to unread <?xml ....?>
 326                     XMLDeclarationParser ev =
 327                         new XMLDeclarationParser(pushbackReader);
 328                     try {
 329                         ev.parse();
 330                     } catch (Exception ex) {
 331                         throw new TransformerException(
 332                             "Unable to run the JAXP transformer on a stream "
 333                                 + ex.getMessage());
 334                     }
 335                     Writer writer =
 336                         new OutputStreamWriter(os /*, ev.getEncoding()*/);
 337                     ev.writeTo(writer);         // doesn't write any, if no header
 338 
 339                     int num;
 340                     char[] ac = new char[8192];
 341                     while ((num = pushbackReader.read(ac)) != -1) {
 342                         writer.write(ac, 0, num);
 343                     }
 344                     writer.flush();
 345 
 346                     if (reader.markSupported())
 347                         reader.reset();
 348                     return;
 349                 }
 350             } catch (IOException e) {
 351                 e.printStackTrace();
 352                 throw new TransformerException(e.toString());
 353             }
 354 
 355             throw new TransformerException("Unexpected StreamSource object");
 356         }
 357         // FastInfosetSource -> DOMResult
 358         else if (FastInfosetReflection.isFastInfosetSource(source)
 359                 && (result instanceof DOMResult))
 360         {
 361             try {
 362                 // Use reflection to avoid a static dep with FI
 363                 if (m_fiDOMDocumentParser == null) {
 364                     m_fiDOMDocumentParser = FastInfosetReflection.DOMDocumentParser_new();
 365                 }
 366 
 367                 // m_fiDOMDocumentParser.parse(document, source.getInputStream())
 368                 FastInfosetReflection.DOMDocumentParser_parse(
 369                     m_fiDOMDocumentParser,
 370                     (Document) ((DOMResult) result).getNode(),
 371                     FastInfosetReflection.FastInfosetSource_getInputStream(source));
 372 
 373                 // We're done!
 374                 return;
 375             }
 376             catch (Exception e) {
 377                 throw new TransformerException(e);
 378             }
 379         }
 380         // DOMSource -> FastInfosetResult
 381         else if ((source instanceof DOMSource)
 382                 && FastInfosetReflection.isFastInfosetResult(result))
 383         {
 384             try {
 385                 // Use reflection to avoid a static dep with FI
 386                 if (m_fiDOMDocumentSerializer == null) {
 387                     m_fiDOMDocumentSerializer = FastInfosetReflection.DOMDocumentSerializer_new();
 388                 }
 389 
 390                 // m_fiDOMDocumentSerializer.setOutputStream(result.getOutputStream())
 391                 FastInfosetReflection.DOMDocumentSerializer_setOutputStream(
 392                     m_fiDOMDocumentSerializer,
 393                     FastInfosetReflection.FastInfosetResult_getOutputStream(result));
 394 
 395                 // m_fiDOMDocumentSerializer.serialize(node)
 396                 FastInfosetReflection.DOMDocumentSerializer_serialize(
 397                     m_fiDOMDocumentSerializer,
 398                     ((DOMSource) source).getNode());
 399 
 400                 // We're done!
 401                 return;
 402             }
 403             catch (Exception e) {
 404                 throw new TransformerException(e);
 405             }
 406         }
 407 
 408         // All other cases -- use transformer object
 409 
 410         materialize();
 411         m_realTransformer.transform(source, result);
 412     }
 413 
 414     /**
 415      * Threadlocal to hold a Transformer instance for this thread.
 416      * CR : 6813167
 417      */
 418     //private static ThreadLocal effTransformer = new ThreadLocal();
 419 
 420     /**
 421      * Return Transformer instance for this thread, allocating a new one if
 422      * necessary. Note that this method does not clear global parameters,
 423      * properties or any other data set on a previously used transformer.
 424      *
 425      * @return Transformer instance
 426      */
 427     public static Transformer newTransformer() {
 428         //CR : 6813167
 429         /*Transformer tt = (Transformer) effTransformer.get();
 430         if (tt == null) {
 431             effTransformer.set(tt = new EfficientStreamingTransformer());
 432         }
 433         return tt;*/
 434         return new EfficientStreamingTransformer();
 435     }
 436 
 437 }