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}