    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one
    003 * or more contributor license agreements.  See the NOTICE file
    004 * distributed with this work for additional information
    005 * regarding copyright ownership.  The ASF licenses this file
    006 * to you under the Apache License, Version 2.0 (the
    007 * "License"); you may not use this file except in compliance
    008 * with the License.  You may obtain a copy of the License at
    009 *
    010 * http://www.apache.org/licenses/LICENSE-2.0
    011 *
    012 * Unless required by applicable law or agreed to in writing,
    013 * software distributed under the License is distributed on an
    014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015 * KIND, either express or implied.  See the License for the
    016 * specific language governing permissions and limitations
    017 * under the License.
    018 */
    019package org.apache.commons.compress.compressors.lzma;
    020
    021import java.util.HashMap;
    022import java.util.Map;
    023import org.apache.commons.compress.compressors.FileNameUtil;
    024import org.apache.commons.compress.utils.OsgiUtils;
    025
    026/**
    027 * Utility code for the lzma compression format.
    028 * @ThreadSafe
    029 * @since 1.10
    030 */
    031public class LZMAUtils {
    032
    033    private static final FileNameUtil fileNameUtil;
    034
    035    /**
    036     * LZMA Header Magic Bytes begin a LZMA file.
    037     */
    038    private static final byte[] HEADER_MAGIC = {
    039        (byte) 0x5D, 0, 0
    040    };
    041
    042    enum CachedAvailability {
    043        DONT_CACHE, CACHED_AVAILABLE, CACHED_UNAVAILABLE
    044    }
    045
    046    private static volatile CachedAvailability cachedLZMAAvailability;
    047
    048    static {
    049        final Map<String, String> uncompressSuffix = new HashMap<>();
    050        uncompressSuffix.put(".lzma", "");
    051        uncompressSuffix.put("-lzma", "");
    052        fileNameUtil = new FileNameUtil(uncompressSuffix, ".lzma");
    053        cachedLZMAAvailability = CachedAvailability.DONT_CACHE;
    054        setCacheLZMAAvailablity(!OsgiUtils.isRunningInOsgiEnvironment());
    055    }
    056
    057    /** Private constructor to prevent instantiation of this utility class. */
    058    private LZMAUtils() {
    059    }
    060
    061    /**
    062     * Checks if the signature matches what is expected for a .lzma file.
    063     *
    064     * @param   signature     the bytes to check
    065     * @param   length        the number of bytes to check
    066     * @return  true if signature matches the .lzma magic bytes, false otherwise
    067     */
    068    public static boolean matches(final byte[] signature, final int length) {
    069        if (length < HEADER_MAGIC.length) {
    070            return false;
    071        }
    072
    073        for (int i = 0; i < HEADER_MAGIC.length; ++i) {
    074            if (signature[i] != HEADER_MAGIC[i]) {
    075                return false;
    076            }
    077        }
    078
    079        return true;
    080    }
    081
    082    /**
    083     * Are the classes required to support LZMA compression available?
    084     * @return true if the classes required to support LZMA
    085     * compression are available
    086     */
    087    public static boolean isLZMACompressionAvailable() {
    088        final CachedAvailability cachedResult = cachedLZMAAvailability;
    089        if (cachedResult != CachedAvailability.DONT_CACHE) {
    090            return cachedResult == CachedAvailability.CACHED_AVAILABLE;
    091        }
    092        return internalIsLZMACompressionAvailable();
    093    }
    094
    095    private static boolean internalIsLZMACompressionAvailable() {
    096        try {
    097            LZMACompressorInputStream.matches(null, 0);
    098            return true;
    099        } catch (final NoClassDefFoundError error) { // NOSONAR
    100            return false;
    101        }
    102    }
    103
    104    /**
    105     * Detects common lzma suffixes in the given file name.
    106     *
    107     * @param fileName name of a file
    108     * @return {@code true} if the file name has a common lzma suffix,
    109     *         {@code false} otherwise
    110     */
    111    public static boolean isCompressedFilename(final String fileName) {
    112        return fileNameUtil.isCompressedFilename(fileName);
    113    }
    114
    115    /**
    116     * Maps the given name of a lzma-compressed file to the name that
    117     * the file should have after uncompression.  Any file names with
    118     * the generic ".lzma" suffix (or any other generic lzma suffix)
    119     * is mapped to a name without that suffix. If no lzma suffix is
    120     * detected, then the file name is returned unmapped.
    121     *
    122     * @param fileName name of a file
    123     * @return name of the corresponding uncompressed file
    124     */
    125    public static String getUncompressedFilename(final String fileName) {
    126        return fileNameUtil.getUncompressedFilename(fileName);
    127    }
    128
    129    /**
    130     * Maps the given file name to the name that the file should have after
    131     * compression with lzma.
    132     *
    133     * @param fileName name of a file
    134     * @return name of the corresponding compressed file
    135     */
    136    public static String getCompressedFilename(final String fileName) {
    137        return fileNameUtil.getCompressedFilename(fileName);
    138    }
    139
    140    /**
    141     * Whether to cache the result of the LZMA check.
    142     *
    143     * <p>This defaults to {@code false} in an OSGi environment and {@code true} otherwise.</p>
    144     * @param doCache whether to cache the result
    145     */
    146    public static void setCacheLZMAAvailablity(final boolean doCache) {
    147        if (!doCache) {
    148            cachedLZMAAvailability = CachedAvailability.DONT_CACHE;
    149        } else if (cachedLZMAAvailability == CachedAvailability.DONT_CACHE) {
    150            final boolean hasLzma = internalIsLZMACompressionAvailable();
    151            cachedLZMAAvailability = hasLzma ? CachedAvailability.CACHED_AVAILABLE // NOSONAR
    152                : CachedAvailability.CACHED_UNAVAILABLE;
    153        }
    154    }
    155
    156    // only exists to support unit tests
    157    static CachedAvailability getCachedLZMAAvailability() {
    158        return cachedLZMAAvailability;
    159    }
    160}