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;
20
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.Map;
24
25 import org.apache.commons.lang3.StringUtils;
26
27 /**
28 * File name mapping code for the compression formats.
29 *
30 * @ThreadSafe
31 * @since 1.4
32 */
33 public class FileNameUtil {
34
35 /**
36 * Map from common file name suffixes to the suffixes that identify compressed versions of those file types. For example: from ".tar" to ".tgz".
37 */
38 private final Map<String, String> compressSuffix = new HashMap<>();
39
40 /**
41 * Map from common file name suffixes of compressed files to the corresponding suffixes of uncompressed files. For example: from ".tgz" to ".tar".
42 * <p>
43 * This map also contains format-specific suffixes like ".gz" and "-z". These suffixes are mapped to the empty string, as they should simply be removed from
44 * the file name when the file is uncompressed.
45 */
46 private final Map<String, String> uncompressSuffix;
47
48 /**
49 * Length of the longest compressed suffix.
50 */
51 private final int longestCompressedSuffix;
52
53 /**
54 * Length of the shortest compressed suffix.
55 */
56 private final int shortestCompressedSuffix;
57
58 /**
59 * Length of the longest uncompressed suffix.
60 */
61 private final int longestUncompressedSuffix;
62
63 /**
64 * Length of the shortest uncompressed suffix longer than the empty string.
65 */
66 private final int shortestUncompressedSuffix;
67
68 /**
69 * The format's default extension.
70 */
71 private final String defaultExtension;
72
73 /**
74 * sets up the utility with a map of known compressed to uncompressed suffix mappings and the default extension of the format.
75 *
76 * @param uncompressSuffix Map from common file name suffixes of compressed files to the corresponding suffixes of uncompressed files. For example: from
77 * ".tgz" to ".tar". This map also contains format-specific suffixes like ".gz" and "-z". These suffixes are mapped to the empty
78 * string, as they should simply be removed from the file name when the file is uncompressed.
79 *
80 * @param defaultExtension the format's default extension like ".gz"
81 */
82 public FileNameUtil(final Map<String, String> uncompressSuffix, final String defaultExtension) {
83 this.uncompressSuffix = Collections.unmodifiableMap(uncompressSuffix);
84 int lc = Integer.MIN_VALUE;
85 int sc = Integer.MAX_VALUE;
86 int lu = Integer.MIN_VALUE;
87 int su = Integer.MAX_VALUE;
88 for (final Map.Entry<String, String> ent : uncompressSuffix.entrySet()) {
89 final int cl = ent.getKey().length();
90 if (cl > lc) {
91 lc = cl;
92 }
93 if (cl < sc) {
94 sc = cl;
95 }
96
97 final String u = ent.getValue();
98 final int ul = u.length();
99 if (ul > 0) {
100 compressSuffix.computeIfAbsent(u, k -> ent.getKey());
101 if (ul > lu) {
102 lu = ul;
103 }
104 if (ul < su) {
105 su = ul;
106 }
107 }
108 }
109 longestCompressedSuffix = lc;
110 longestUncompressedSuffix = lu;
111 shortestCompressedSuffix = sc;
112 shortestUncompressedSuffix = su;
113 this.defaultExtension = defaultExtension;
114 }
115
116 /**
117 * Maps the given file name to the name that the file should have after compression. Common file types with custom suffixes for compressed versions are
118 * automatically detected and correctly mapped. For example the name "package.tar" is mapped to "package.tgz". If no custom mapping is applicable, then the
119 * default ".gz" suffix is appended to the file name.
120 *
121 * @param fileName name of a file
122 * @return name of the corresponding compressed file
123 * @deprecated Use {@link #getCompressedFileName(String)}.
124 */
125 @Deprecated
126 public String getCompressedFilename(final String fileName) {
127 return getCompressedFileName(fileName);
128 }
129
130 /**
131 * Maps the given file name to the name that the file should have after compression. Common file types with custom suffixes for compressed versions are
132 * automatically detected and correctly mapped. For example the name "package.tar" is mapped to "package.tgz". If no custom mapping is applicable, then the
133 * default ".gz" suffix is appended to the file name.
134 *
135 * @param fileName name of a file
136 * @return name of the corresponding compressed file
137 * @since 1.25.0
138 */
139 public String getCompressedFileName(final String fileName) {
140 final String lower = StringUtils.toRootLowerCase(fileName);
141 final int n = lower.length();
142 for (int i = shortestUncompressedSuffix; i <= longestUncompressedSuffix && i < n; i++) {
143 final String suffix = compressSuffix.get(lower.substring(n - i));
144 if (suffix != null) {
145 return fileName.substring(0, n - i) + suffix;
146 }
147 }
148 // No custom suffix found, just append the default
149 return fileName + defaultExtension;
150 }
151
152 /**
153 * Maps the given name of a compressed file to the name that the file should have after uncompression. Commonly used file type specific suffixes like ".tgz"
154 * or ".svgz" are automatically detected and correctly mapped. For example the name "package.tgz" is mapped to "package.tar". And any file names with the
155 * generic ".gz" suffix (or any other generic gzip suffix) is mapped to a name without that suffix. If no format suffix is detected, then the file name is
156 * returned unmapped.
157 *
158 * @param fileName name of a file
159 * @return name of the corresponding uncompressed file
160 * @deprecated Use {@link #getUncompressedFileName(String)}.
161 */
162 @Deprecated
163 public String getUncompressedFilename(final String fileName) {
164 return getUncompressedFileName(fileName);
165 }
166
167 /**
168 * Maps the given name of a compressed file to the name that the file should have after uncompression. Commonly used file type specific suffixes like ".tgz"
169 * or ".svgz" are automatically detected and correctly mapped. For example the name "package.tgz" is mapped to "package.tar". And any file names with the
170 * generic ".gz" suffix (or any other generic gzip suffix) is mapped to a name without that suffix. If no format suffix is detected, then the file name is
171 * returned unmapped.
172 *
173 * @param fileName name of a file
174 * @return name of the corresponding uncompressed file
175 * @since 1.25.0
176 */
177 public String getUncompressedFileName(final String fileName) {
178 final String lower = StringUtils.toRootLowerCase(fileName);
179 final int n = lower.length();
180 for (int i = shortestCompressedSuffix; i <= longestCompressedSuffix && i < n; i++) {
181 final String suffix = uncompressSuffix.get(lower.substring(n - i));
182 if (suffix != null) {
183 return fileName.substring(0, n - i) + suffix;
184 }
185 }
186 return fileName;
187 }
188
189 /**
190 * Detects common format suffixes in the given file name.
191 *
192 * @param fileName name of a file
193 * @return {@code true} if the file name has a common format suffix, {@code false} otherwise
194 * @deprecated Use {@link #isCompressedFileName(String)}.
195 */
196 @Deprecated
197 public boolean isCompressedFilename(final String fileName) {
198 return isCompressedFileName(fileName);
199 }
200
201 /**
202 * Detects common format suffixes in the given file name.
203 *
204 * @param fileName name of a file
205 * @return {@code true} if the file name has a common format suffix, {@code false} otherwise
206 * @since 1.25.0
207 */
208 public boolean isCompressedFileName(final String fileName) {
209 final String lower = StringUtils.toRootLowerCase(fileName);
210 final int n = lower.length();
211 for (int i = shortestCompressedSuffix; i <= longestCompressedSuffix && i < n; i++) {
212 if (uncompressSuffix.containsKey(lower.substring(n - i))) {
213 return true;
214 }
215 }
216 return false;
217 }
218 }