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 }