View Javadoc
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.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      * 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 }