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; 023 024import org.apache.commons.compress.compressors.FileNameUtil; 025import org.apache.commons.compress.utils.OsgiUtils; 026 027/** 028 * Utility code for the LZMA compression format. 029 * 030 * @ThreadSafe 031 * @since 1.10 032 */ 033public class LZMAUtils { 034 035 enum CachedAvailability { 036 DONT_CACHE, CACHED_AVAILABLE, CACHED_UNAVAILABLE 037 } 038 039 private static final FileNameUtil fileNameUtil; 040 041 /** 042 * LZMA Header Magic Bytes begin a LZMA file. 043 */ 044 private static final byte[] HEADER_MAGIC = { (byte) 0x5D, 0, 0 }; 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 // only exists to support unit tests 058 static CachedAvailability getCachedLZMAAvailability() { 059 return cachedLZMAAvailability; 060 } 061 062 /** 063 * Maps the given file name to the name that the file should have after compression with LZMA. 064 * 065 * @param fileName name of a file 066 * @return name of the corresponding compressed file 067 * @deprecated Use {@link #getCompressedFileName(String)}. 068 */ 069 @Deprecated 070 public static String getCompressedFilename(final String fileName) { 071 return fileNameUtil.getCompressedFileName(fileName); 072 } 073 074 /** 075 * Maps the given file name to the name that the file should have after compression with LZMA. 076 * 077 * @param fileName name of a file 078 * @return name of the corresponding compressed file 079 * @since 1.25.0 080 */ 081 public static String getCompressedFileName(final String fileName) { 082 return fileNameUtil.getCompressedFileName(fileName); 083 } 084 085 /** 086 * Maps the given name of a LZMA-compressed file to the name that the file should have after uncompression. Any file names with the generic ".lzma" suffix 087 * (or any other generic LZMA suffix) is mapped to a name without that suffix. If no LZMA suffix is detected, then the file name is returned unmapped. 088 * 089 * @param fileName name of a file 090 * @return name of the corresponding uncompressed file 091 * @deprecated Use {@link #getUncompressedFileName(String)}. 092 */ 093 @Deprecated 094 public static String getUncompressedFilename(final String fileName) { 095 return fileNameUtil.getUncompressedFileName(fileName); 096 } 097 098 /** 099 * Maps the given name of a LZMA-compressed file to the name that the file should have after uncompression. Any file names with the generic ".lzma" suffix 100 * (or any other generic LZMA suffix) is mapped to a name without that suffix. If no LZMA suffix is detected, then the file name is 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 110 private static boolean internalIsLZMACompressionAvailable() { 111 try { 112 LZMACompressorInputStream.matches(null, 0); 113 return true; 114 } catch (final NoClassDefFoundError error) { // NOSONAR 115 return false; 116 } 117 } 118 119 /** 120 * Detects common LZMA suffixes in the given file name. 121 * 122 * @param fileName name of a file 123 * @return {@code true} if the file name has a common LZMA suffix, {@code false} otherwise 124 * @deprecated Use {@link #isCompressedFileName(String)}. 125 */ 126 @Deprecated 127 public static boolean isCompressedFilename(final String fileName) { 128 return fileNameUtil.isCompressedFileName(fileName); 129 } 130 131 /** 132 * Detects common LZMA suffixes in the given file name. 133 * 134 * @param fileName name of a file 135 * @return {@code true} if the file name has a common LZMA suffix, {@code false} otherwise 136 * @since 1.25.0 137 */ 138 public static boolean isCompressedFileName(final String fileName) { 139 return fileNameUtil.isCompressedFileName(fileName); 140 } 141 142 /** 143 * Are the classes required to support LZMA compression available? 144 * 145 * @return true if the classes required to support LZMA compression are available 146 */ 147 public static boolean isLZMACompressionAvailable() { 148 final CachedAvailability cachedResult = cachedLZMAAvailability; 149 if (cachedResult != CachedAvailability.DONT_CACHE) { 150 return cachedResult == CachedAvailability.CACHED_AVAILABLE; 151 } 152 return internalIsLZMACompressionAvailable(); 153 } 154 155 /** 156 * Checks if the signature matches what is expected for a .lzma file. 157 * 158 * @param signature the bytes to check 159 * @param length the number of bytes to check 160 * @return true if signature matches the .lzma magic bytes, false otherwise 161 */ 162 public static boolean matches(final byte[] signature, final int length) { 163 if (length < HEADER_MAGIC.length) { 164 return false; 165 } 166 167 for (int i = 0; i < HEADER_MAGIC.length; ++i) { 168 if (signature[i] != HEADER_MAGIC[i]) { 169 return false; 170 } 171 } 172 173 return true; 174 } 175 176 /** 177 * Whether to cache the result of the LZMA check. 178 * 179 * <p> 180 * This defaults to {@code false} in an OSGi environment and {@code true} otherwise. 181 * </p> 182 * 183 * @param doCache whether to cache the result 184 */ 185 public static void setCacheLZMAAvailablity(final boolean doCache) { 186 if (!doCache) { 187 cachedLZMAAvailability = CachedAvailability.DONT_CACHE; 188 } else if (cachedLZMAAvailability == CachedAvailability.DONT_CACHE) { 189 final boolean hasLzma = internalIsLZMACompressionAvailable(); 190 cachedLZMAAvailability = hasLzma ? CachedAvailability.CACHED_AVAILABLE // NOSONAR 191 : CachedAvailability.CACHED_UNAVAILABLE; 192 } 193 } 194 195 /** Private constructor to prevent instantiation of this utility class. */ 196 private LZMAUtils() { 197 } 198}