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