XZUtils.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.xz;

  20. import java.util.HashMap;
  21. import java.util.Map;

  22. import org.apache.commons.compress.compressors.FileNameUtil;
  23. import org.apache.commons.compress.utils.OsgiUtils;

  24. /**
  25.  * Utility code for the XZ compression format.
  26.  *
  27.  * @ThreadSafe
  28.  * @since 1.4
  29.  */
  30. public class XZUtils {

  31.     enum CachedAvailability {
  32.         DONT_CACHE, CACHED_AVAILABLE, CACHED_UNAVAILABLE
  33.     }

  34.     private static final FileNameUtil fileNameUtil;

  35.     /**
  36.      * XZ Header Magic Bytes begin a XZ file.
  37.      *
  38.      * <p>
  39.      * This is a copy of {@code org.tukaani.xz.XZ.HEADER_MAGIC} in XZ for Java version 1.5.
  40.      * </p>
  41.      */
  42.     private static final byte[] HEADER_MAGIC = { (byte) 0xFD, '7', 'z', 'X', 'Z', '\0' };

  43.     private static volatile CachedAvailability cachedXZAvailability;

  44.     static {
  45.         final Map<String, String> uncompressSuffix = new HashMap<>();
  46.         uncompressSuffix.put(".txz", ".tar");
  47.         uncompressSuffix.put(".xz", "");
  48.         uncompressSuffix.put("-xz", "");
  49.         fileNameUtil = new FileNameUtil(uncompressSuffix, ".xz");
  50.         cachedXZAvailability = CachedAvailability.DONT_CACHE;
  51.         setCacheXZAvailablity(!OsgiUtils.isRunningInOsgiEnvironment());
  52.     }

  53.     // only exists to support unit tests
  54.     static CachedAvailability getCachedXZAvailability() {
  55.         return cachedXZAvailability;
  56.     }

  57.     /**
  58.      * Maps the given file name to the name that the file should have after compression with xz. Common file types with custom suffixes for compressed versions
  59.      * are automatically detected and correctly mapped. For example the name "package.tar" is mapped to "package.txz". If no custom mapping is applicable, then
  60.      * the default ".xz" suffix is appended to the file name.
  61.      *
  62.      * @param fileName name of a file
  63.      * @return name of the corresponding compressed file
  64.      * @deprecated Use {@link #getCompressedFileName(String)}.
  65.      */
  66.     @Deprecated
  67.     public static String getCompressedFilename(final String fileName) {
  68.         return fileNameUtil.getCompressedFileName(fileName);
  69.     }

  70.     /**
  71.      * Maps the given file name to the name that the file should have after compression with xz. Common file types with custom suffixes for compressed versions
  72.      * are automatically detected and correctly mapped. For example the name "package.tar" is mapped to "package.txz". If no custom mapping is applicable, then
  73.      * the default ".xz" suffix is appended to the file name.
  74.      *
  75.      * @param fileName name of a file
  76.      * @return name of the corresponding compressed file
  77.      * @since 1.25.0
  78.      */
  79.     public static String getCompressedFileName(final String fileName) {
  80.         return fileNameUtil.getCompressedFileName(fileName);
  81.     }

  82.     /**
  83.      * Maps the given name of a xz-compressed file to the name that the file should have after uncompression. Commonly used file type specific suffixes like
  84.      * ".txz" are automatically detected and correctly mapped. For example the name "package.txz" is mapped to "package.tar". And any file names with the
  85.      * generic ".xz" suffix (or any other generic xz suffix) is mapped to a name without that suffix. If no xz suffix is detected, then the file name is
  86.      * returned unmapped.
  87.      *
  88.      * @param fileName name of a file
  89.      * @return name of the corresponding uncompressed file
  90.      * @deprecated Use {@link #getUncompressedFileName(String)}.
  91.      */
  92.     @Deprecated
  93.     public static String getUncompressedFilename(final String fileName) {
  94.         return fileNameUtil.getUncompressedFileName(fileName);
  95.     }

  96.     /**
  97.      * Maps the given name of a xz-compressed file to the name that the file should have after uncompression. Commonly used file type specific suffixes like
  98.      * ".txz" are automatically detected and correctly mapped. For example the name "package.txz" is mapped to "package.tar". And any file names with the
  99.      * generic ".xz" suffix (or any other generic xz suffix) is mapped to a name without that suffix. If no xz suffix is detected, then the file name is
  100.      * returned unmapped.
  101.      *
  102.      * @param fileName name of a file
  103.      * @return name of the corresponding uncompressed file
  104.      * @since 1.25.0
  105.      */
  106.     public static String getUncompressedFileName(final String fileName) {
  107.         return fileNameUtil.getUncompressedFileName(fileName);
  108.     }

  109.     private static boolean internalIsXZCompressionAvailable() {
  110.         try {
  111.             XZCompressorInputStream.matches(null, 0);
  112.             return true;
  113.         } catch (final NoClassDefFoundError error) { // NOSONAR
  114.             return false;
  115.         }
  116.     }

  117.     /**
  118.      * Detects common xz suffixes in the given file name.
  119.      *
  120.      * @param fileName name of a file
  121.      * @return {@code true} if the file name has a common xz suffix, {@code false} otherwise
  122.      * @deprecated Use {@link #isCompressedFileName(String)}.
  123.      */
  124.     @Deprecated
  125.     public static boolean isCompressedFilename(final String fileName) {
  126.         return fileNameUtil.isCompressedFileName(fileName);
  127.     }

  128.     /**
  129.      * Detects common xz suffixes in the given file name.
  130.      *
  131.      * @param fileName name of a file
  132.      * @return {@code true} if the file name has a common xz suffix, {@code false} otherwise
  133.      * @since 1.25.0
  134.      */
  135.     public static boolean isCompressedFileName(final String fileName) {
  136.         return fileNameUtil.isCompressedFileName(fileName);
  137.     }

  138.     /**
  139.      * Are the classes required to support XZ compression available?
  140.      *
  141.      * @since 1.5
  142.      * @return true if the classes required to support XZ compression are available
  143.      */
  144.     public static boolean isXZCompressionAvailable() {
  145.         final CachedAvailability cachedResult = cachedXZAvailability;
  146.         if (cachedResult != CachedAvailability.DONT_CACHE) {
  147.             return cachedResult == CachedAvailability.CACHED_AVAILABLE;
  148.         }
  149.         return internalIsXZCompressionAvailable();
  150.     }

  151.     /**
  152.      * Checks if the signature matches what is expected for a .xz file.
  153.      *
  154.      * <p>
  155.      * This is more or less a copy of the version found in {@link XZCompressorInputStream} but doesn't depend on the presence of XZ for Java.
  156.      * </p>
  157.      *
  158.      * @param signature the bytes to check
  159.      * @param length    the number of bytes to check
  160.      * @return true if signature matches the .xz magic bytes, false otherwise
  161.      * @since 1.9
  162.      */
  163.     public static boolean matches(final byte[] signature, final int length) {
  164.         if (length < HEADER_MAGIC.length) {
  165.             return false;
  166.         }

  167.         for (int i = 0; i < HEADER_MAGIC.length; ++i) {
  168.             if (signature[i] != HEADER_MAGIC[i]) {
  169.                 return false;
  170.             }
  171.         }

  172.         return true;
  173.     }

  174.     /**
  175.      * Whether to cache the result of the XZ for Java check.
  176.      *
  177.      * <p>
  178.      * This defaults to {@code false} in an OSGi environment and {@code true} otherwise.
  179.      * </p>
  180.      *
  181.      * @param doCache whether to cache the result
  182.      * @since 1.9
  183.      */
  184.     public static void setCacheXZAvailablity(final boolean doCache) {
  185.         if (!doCache) {
  186.             cachedXZAvailability = CachedAvailability.DONT_CACHE;
  187.         } else if (cachedXZAvailability == CachedAvailability.DONT_CACHE) {
  188.             final boolean hasXz = internalIsXZCompressionAvailable();
  189.             cachedXZAvailability = hasXz ? CachedAvailability.CACHED_AVAILABLE // NOSONAR
  190.                     : CachedAvailability.CACHED_UNAVAILABLE;
  191.         }
  192.     }

  193.     /** Private constructor to prevent instantiation of this utility class. */
  194.     private XZUtils() {
  195.     }
  196. }