001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.compress.compressors; 020 021import java.util.Collections; 022import java.util.HashMap; 023import java.util.Locale; 024import java.util.Map; 025 026/** 027 * File name mapping code for the compression formats. 028 * 029 * @ThreadSafe 030 * @since 1.4 031 */ 032public class FileNameUtil { 033 034 /** 035 * Map from common file name suffixes to the suffixes that identify compressed versions of those file types. For example: from ".tar" to ".tgz". 036 */ 037 private final Map<String, String> compressSuffix = new HashMap<>(); 038 039 /** 040 * Map from common file name suffixes of compressed files to the corresponding suffixes of uncompressed files. For example: from ".tgz" to ".tar". 041 * <p> 042 * 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 043 * the file name when the file is uncompressed. 044 */ 045 private final Map<String, String> uncompressSuffix; 046 047 /** 048 * Length of the longest compressed suffix. 049 */ 050 private final int longestCompressedSuffix; 051 052 /** 053 * Length of the shortest compressed suffix. 054 */ 055 private final int shortestCompressedSuffix; 056 057 /** 058 * Length of the longest uncompressed suffix. 059 */ 060 private final int longestUncompressedSuffix; 061 062 /** 063 * Length of the shortest uncompressed suffix longer than the empty string. 064 */ 065 private final int shortestUncompressedSuffix; 066 067 /** 068 * The format's default extension. 069 */ 070 private final String defaultExtension; 071 072 /** 073 * sets up the utility with a map of known compressed to uncompressed suffix mappings and the default extension of the format. 074 * 075 * @param uncompressSuffix Map from common file name suffixes of compressed files to the corresponding suffixes of uncompressed files. For example: from 076 * ".tgz" to ".tar". This map also contains format-specific suffixes like ".gz" and "-z". These suffixes are mapped to the empty 077 * string, as they should simply be removed from the file name when the file is uncompressed. 078 * 079 * @param defaultExtension the format's default extension like ".gz" 080 */ 081 public FileNameUtil(final Map<String, String> uncompressSuffix, final String defaultExtension) { 082 this.uncompressSuffix = Collections.unmodifiableMap(uncompressSuffix); 083 int lc = Integer.MIN_VALUE, sc = Integer.MAX_VALUE; 084 int lu = Integer.MIN_VALUE, su = Integer.MAX_VALUE; 085 for (final Map.Entry<String, String> ent : uncompressSuffix.entrySet()) { 086 final int cl = ent.getKey().length(); 087 if (cl > lc) { 088 lc = cl; 089 } 090 if (cl < sc) { 091 sc = cl; 092 } 093 094 final String u = ent.getValue(); 095 final int ul = u.length(); 096 if (ul > 0) { 097 compressSuffix.computeIfAbsent(u, k -> ent.getKey()); 098 if (ul > lu) { 099 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.ROOT); 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.ROOT); 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.ROOT); 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}