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 * https://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.lzma;
20
21 import java.util.HashMap;
22 import java.util.Map;
23
24 import org.apache.commons.compress.compressors.FileNameUtil;
25 import org.apache.commons.compress.utils.OsgiUtils;
26
27 /**
28 * Utility code for the LZMA compression format.
29 *
30 * @ThreadSafe
31 * @since 1.10
32 */
33 public class LZMAUtils {
34
35 enum CachedAvailability {
36 DONT_CACHE, CACHED_AVAILABLE, CACHED_UNAVAILABLE
37 }
38
39 private static final FileNameUtil fileNameUtil;
40
41 /**
42 * LZMA Header Magic Bytes begin a LZMA file.
43 */
44 private static final byte[] HEADER_MAGIC = { (byte) 0x5D, 0, 0 };
45
46 private static volatile CachedAvailability cachedLZMAAvailability;
47
48 static {
49 final Map<String, String> uncompressSuffix = new HashMap<>();
50 uncompressSuffix.put(".lzma", "");
51 uncompressSuffix.put("-lzma", "");
52 fileNameUtil = new FileNameUtil(uncompressSuffix, ".lzma");
53 cachedLZMAAvailability = CachedAvailability.DONT_CACHE;
54 setCacheLZMAAvailablity(!OsgiUtils.isRunningInOsgiEnvironment());
55 }
56
57 // only exists to support unit tests
58 static CachedAvailability getCachedLZMAAvailability() {
59 return cachedLZMAAvailability;
60 }
61
62 /**
63 * Maps the given file name to the name that the file should have after compression with LZMA.
64 *
65 * @param fileName name of a file
66 * @return name of the corresponding compressed file
67 * @deprecated Use {@link #getCompressedFileName(String)}.
68 */
69 @Deprecated
70 public static String getCompressedFilename(final String fileName) {
71 return fileNameUtil.getCompressedFileName(fileName);
72 }
73
74 /**
75 * Maps the given file name to the name that the file should have after compression with LZMA.
76 *
77 * @param fileName name of a file
78 * @return name of the corresponding compressed file
79 * @since 1.25.0
80 */
81 public static String getCompressedFileName(final String fileName) {
82 return fileNameUtil.getCompressedFileName(fileName);
83 }
84
85 /**
86 * 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
87 * (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.
88 *
89 * @param fileName name of a file
90 * @return name of the corresponding uncompressed file
91 * @deprecated Use {@link #getUncompressedFileName(String)}.
92 */
93 @Deprecated
94 public static String getUncompressedFilename(final String fileName) {
95 return fileNameUtil.getUncompressedFileName(fileName);
96 }
97
98 /**
99 * 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 * Sets 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 }