Pack200CompressorInputStream.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one
  3.  * or more contributor license agreements.  See the NOTICE file
  4.  * distributed with this work for additional information
  5.  * regarding copyright ownership.  The ASF licenses this file
  6.  * to you under the Apache License, Version 2.0 (the
  7.  * "License"); you may not use this file except in compliance
  8.  * with the License.  You may obtain a copy of the License at
  9.  *
  10.  * http://www.apache.org/licenses/LICENSE-2.0
  11.  *
  12.  * Unless required by applicable law or agreed to in writing,
  13.  * software distributed under the License is distributed on an
  14.  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15.  * KIND, either express or implied.  See the License for the
  16.  * specific language governing permissions and limitations
  17.  * under the License.
  18.  */

  19. package org.apache.commons.compress.compressors.pack200;

  20. import java.io.File;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.io.UncheckedIOException;
  24. import java.util.Map;
  25. import java.util.jar.JarOutputStream;

  26. import org.apache.commons.compress.compressors.CompressorInputStream;
  27. import org.apache.commons.compress.java.util.jar.Pack200;

  28. /**
  29.  * An input stream that decompresses from the Pack200 format to be read as any other stream.
  30.  *
  31.  * <p>
  32.  * The {@link CompressorInputStream#getCount getCount} and {@link CompressorInputStream#getBytesRead getBytesRead} methods always return 0.
  33.  * </p>
  34.  *
  35.  * @NotThreadSafe
  36.  * @since 1.3
  37.  */
  38. public class Pack200CompressorInputStream extends CompressorInputStream {

  39.     private static final byte[] CAFE_DOOD = { (byte) 0xCA, (byte) 0xFE, (byte) 0xD0, (byte) 0x0D };
  40.     private static final int SIG_LENGTH = CAFE_DOOD.length;

  41.     /**
  42.      * Checks if the signature matches what is expected for a pack200 file (0xCAFED00D).
  43.      *
  44.      * @param signature the bytes to check
  45.      * @param length    the number of bytes to check
  46.      * @return true, if this stream is a pack200 compressed stream, false otherwise
  47.      */
  48.     public static boolean matches(final byte[] signature, final int length) {
  49.         if (length < SIG_LENGTH) {
  50.             return false;
  51.         }

  52.         for (int i = 0; i < SIG_LENGTH; i++) {
  53.             if (signature[i] != CAFE_DOOD[i]) {
  54.                 return false;
  55.             }
  56.         }

  57.         return true;
  58.     }

  59.     private final InputStream originalInputStream;

  60.     private final AbstractStreamBridge abstractStreamBridge;

  61.     /**
  62.      * Decompresses the given file, caching the decompressed data in memory.
  63.      *
  64.      * @param file the file to decompress
  65.      * @throws IOException if reading fails
  66.      */
  67.     public Pack200CompressorInputStream(final File file) throws IOException {
  68.         this(file, Pack200Strategy.IN_MEMORY);
  69.     }

  70.     /**
  71.      * Decompresses the given file, caching the decompressed data in memory and using the given properties.
  72.      *
  73.      * @param file       the file to decompress
  74.      * @param properties Pack200 properties to use
  75.      * @throws IOException if reading fails
  76.      */
  77.     public Pack200CompressorInputStream(final File file, final Map<String, String> properties) throws IOException {
  78.         this(file, Pack200Strategy.IN_MEMORY, properties);
  79.     }

  80.     /**
  81.      * Decompresses the given file using the given strategy to cache the results.
  82.      *
  83.      * @param file the file to decompress
  84.      * @param mode the strategy to use
  85.      * @throws IOException if reading fails
  86.      */
  87.     public Pack200CompressorInputStream(final File file, final Pack200Strategy mode) throws IOException {
  88.         this(null, file, mode, null);
  89.     }

  90.     /**
  91.      * Decompresses the given file using the given strategy to cache the results and the given properties.
  92.      *
  93.      * @param file       the file to decompress
  94.      * @param mode       the strategy to use
  95.      * @param properties Pack200 properties to use
  96.      * @throws IOException if reading fails
  97.      */
  98.     public Pack200CompressorInputStream(final File file, final Pack200Strategy mode, final Map<String, String> properties) throws IOException {
  99.         this(null, file, mode, properties);
  100.     }

  101.     /**
  102.      * Decompresses the given stream, caching the decompressed data in memory.
  103.      *
  104.      * <p>
  105.      * When reading from a file the File-arg constructor may provide better performance.
  106.      * </p>
  107.      *
  108.      * @param inputStream the InputStream from which this object should be created
  109.      * @throws IOException if reading fails
  110.      */
  111.     public Pack200CompressorInputStream(final InputStream inputStream) throws IOException {
  112.         this(inputStream, Pack200Strategy.IN_MEMORY);
  113.     }

  114.     private Pack200CompressorInputStream(final InputStream inputStream, final File file, final Pack200Strategy mode, final Map<String, String> properties)
  115.             throws IOException {
  116.         this.originalInputStream = inputStream;
  117.         this.abstractStreamBridge = mode.newStreamBridge();
  118.         try (JarOutputStream jarOut = new JarOutputStream(abstractStreamBridge)) {
  119.             final Pack200.Unpacker unpacker = Pack200.newUnpacker();
  120.             if (properties != null) {
  121.                 unpacker.properties().putAll(properties);
  122.             }
  123.             if (file == null) {
  124.                 unpacker.unpack(inputStream, jarOut);
  125.             } else {
  126.                 unpacker.unpack(file, jarOut);
  127.             }
  128.         }
  129.     }

  130.     /**
  131.      * Decompresses the given stream, caching the decompressed data in memory and using the given properties.
  132.      *
  133.      * <p>
  134.      * When reading from a file the File-arg constructor may provide better performance.
  135.      * </p>
  136.      *
  137.      * @param inputStream the InputStream from which this object should be created
  138.      * @param properties  Pack200 properties to use
  139.      * @throws IOException if reading fails
  140.      */
  141.     public Pack200CompressorInputStream(final InputStream inputStream, final Map<String, String> properties) throws IOException {
  142.         this(inputStream, Pack200Strategy.IN_MEMORY, properties);
  143.     }

  144.     /**
  145.      * Decompresses the given stream using the given strategy to cache the results.
  146.      *
  147.      * <p>
  148.      * When reading from a file the File-arg constructor may provide better performance.
  149.      * </p>
  150.      *
  151.      * @param inputStream the InputStream from which this object should be created
  152.      * @param mode        the strategy to use
  153.      * @throws IOException if reading fails
  154.      */
  155.     public Pack200CompressorInputStream(final InputStream inputStream, final Pack200Strategy mode) throws IOException {
  156.         this(inputStream, null, mode, null);
  157.     }

  158.     /**
  159.      * Decompresses the given stream using the given strategy to cache the results and the given properties.
  160.      *
  161.      * <p>
  162.      * When reading from a file the File-arg constructor may provide better performance.
  163.      * </p>
  164.      *
  165.      * @param inputStream the InputStream from which this object should be created
  166.      * @param mode        the strategy to use
  167.      * @param properties  Pack200 properties to use
  168.      * @throws IOException if reading fails
  169.      */
  170.     public Pack200CompressorInputStream(final InputStream inputStream, final Pack200Strategy mode, final Map<String, String> properties) throws IOException {
  171.         this(inputStream, null, mode, properties);
  172.     }

  173.     @SuppressWarnings("resource") // Does not allocate
  174.     @Override
  175.     public int available() throws IOException {
  176.         return getInputStream().available();
  177.     }

  178.     @Override
  179.     public void close() throws IOException {
  180.         try {
  181.             abstractStreamBridge.stop();
  182.         } finally {
  183.             if (originalInputStream != null) {
  184.                 originalInputStream.close();
  185.             }
  186.         }
  187.     }

  188.     private InputStream getInputStream() throws IOException {
  189.         return abstractStreamBridge.getInputStream();
  190.     }

  191.     @SuppressWarnings("resource") // Does not allocate
  192.     @Override
  193.     public synchronized void mark(final int limit) {
  194.         try {
  195.             getInputStream().mark(limit);
  196.         } catch (final IOException ex) {
  197.             throw new UncheckedIOException(ex); // NOSONAR
  198.         }
  199.     }

  200.     @SuppressWarnings("resource") // Does not allocate
  201.     @Override
  202.     public boolean markSupported() {
  203.         try {
  204.             return getInputStream().markSupported();
  205.         } catch (final IOException ex) { // NOSONAR
  206.             return false;
  207.         }
  208.     }

  209.     @SuppressWarnings("resource") // Does not allocate
  210.     @Override
  211.     public int read() throws IOException {
  212.         return getInputStream().read();
  213.     }

  214.     @SuppressWarnings("resource") // Does not allocate
  215.     @Override
  216.     public int read(final byte[] b) throws IOException {
  217.         return getInputStream().read(b);
  218.     }

  219.     @SuppressWarnings("resource") // Does not allocate
  220.     @Override
  221.     public int read(final byte[] b, final int off, final int count) throws IOException {
  222.         return getInputStream().read(b, off, count);
  223.     }

  224.     @SuppressWarnings("resource") // Does not allocate
  225.     @Override
  226.     public synchronized void reset() throws IOException {
  227.         getInputStream().reset();
  228.     }

  229.     @SuppressWarnings("resource") // Does not allocate
  230.     @Override
  231.     public long skip(final long count) throws IOException {
  232.         return org.apache.commons.io.IOUtils.skip(getInputStream(), count);
  233.     }
  234. }