001/* 002 * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Nuxeo - initial API and implementation 018 * Bogdan Stefanescu <[email protected]> 019 * Estelle Giuly <[email protected]> 020 */ 021package org.nuxeo.common.utils; 022 023import java.io.File; 024import java.io.IOException; 025import java.io.UnsupportedEncodingException; 026import java.net.MalformedURLException; 027import java.net.URISyntaxException; 028import java.net.URL; 029import java.net.URLDecoder; 030import java.util.ArrayList; 031import java.util.List; 032 033import org.apache.commons.logging.Log; 034import org.apache.commons.logging.LogFactory; 035 036public final class FileUtils { 037 038 private static final Log log = LogFactory.getLog(FileUtils.class); 039 040 // This is an utility class 041 private FileUtils() { 042 } 043 044 /** 045 * Copies source to destination. If source and destination are the same, does nothing. Both single files and 046 * directories are handled. 047 * 048 * @param src the source file or directory 049 * @param dst the destination file or directory 050 */ 051 public static void copy(File src, File dst) throws IOException { 052 if (src.equals(dst)) { 053 return; 054 } 055 if (src.isFile()) { 056 copyFile(src, dst); 057 } else { 058 copyTree(src, dst); 059 } 060 } 061 062 public static void copy(File[] src, File dst) throws IOException { 063 for (File file : src) { 064 copy(file, dst); 065 } 066 } 067 068 /** 069 * @deprecated since 10.1 - use {@link org.apache.commons.io.FileUtils#copyFile(File, File)} or 070 * {@link org.apache.commons.io.FileUtils#copyFileToDirectory(File, File)} instead. 071 */ 072 @Deprecated 073 public static void copyFile(File src, File dst) throws IOException { 074 if (dst.isDirectory()) { 075 dst = new File(dst, src.getName()); 076 } 077 org.apache.commons.io.FileUtils.copyFile(src, dst, false); 078 } 079 080 /** 081 * Copies recursively source to destination. 082 * <p> 083 * The source file is assumed to be a directory. 084 * 085 * @param src the source directory 086 * @param dst the destination directory 087 * @deprecated since 10.1 - waiting ReloadComponent to be cleaned 088 */ 089 @Deprecated 090 public static void copyTree(File src, File dst) throws IOException { 091 if (src.isFile()) { 092 copyFile(src, dst); 093 } else if (src.isDirectory()) { 094 if (dst.exists()) { 095 dst = new File(dst, src.getName()); 096 dst.mkdir(); 097 } else { // allows renaming dest dir 098 dst.mkdirs(); 099 } 100 File[] files = src.listFiles(); 101 for (File file : files) { 102 copyTree(file, dst); 103 } 104 } 105 } 106 107 /** 108 * @deprecated since 10.1 - seems unused 109 */ 110 @Deprecated 111 public static void copyTree(File src, File dst, PathFilter filter) throws IOException { 112 copyTree(src, dst, new Path("/"), filter); 113 } 114 115 public static void copyTree(File src, File dst, Path prefix, PathFilter filter) throws IOException { 116 if (!prefix.isAbsolute()) { 117 prefix = prefix.makeAbsolute(); 118 } 119 int rootIndex = src.getPath().length() + 1; 120 for (File file : src.listFiles()) { 121 copyTree(rootIndex, file, new File(dst, file.getName()), prefix, filter); 122 } 123 } 124 125 protected static void copyTree(int rootIndex, File src, File dst, Path prefix, PathFilter filter) 126 throws IOException { 127 if (src.isFile()) { 128 String relPath = src.getPath().substring(rootIndex); 129 if (!filter.accept(new Path(relPath))) { 130 return; 131 } 132 if (!prefix.isRoot()) { // remove prefix from path 133 String path = dst.getPath(); 134 String pff = prefix.toString(); 135 int prefixIndex = path.lastIndexOf(pff); 136 if (prefixIndex > 0) { 137 path = path.substring(0, prefixIndex) + path.substring(prefixIndex + pff.length()); 138 dst = new File(path.toString()); 139 } 140 } 141 dst.getParentFile().mkdirs(); 142 copyFile(src, dst); 143 } else if (src.isDirectory()) { 144 File[] files = src.listFiles(); 145 for (File file : files) { 146 copyTree(rootIndex, file, new File(dst, file.getName()), prefix, filter); 147 } 148 } 149 } 150 151 /** 152 * Decodes an URL path so that is can be processed as a filename later. 153 * 154 * @param url the Url to be processed. 155 * @return the decoded path. 156 */ 157 public static String getFilePathFromUrl(URL url) { 158 String path = ""; 159 if (url.getProtocol().equals("file")) { 160 try { 161 path = URLDecoder.decode(url.getPath(), "UTF-8"); 162 } catch (UnsupportedEncodingException e) { 163 log.error(e); 164 } 165 } 166 return path; 167 } 168 169 public static File getFileFromURL(URL url) { 170 File file; 171 String filename = getFilePathFromUrl(url); 172 if (filename.equals("")) { 173 file = null; 174 } else { 175 file = new File(filename); 176 } 177 return file; 178 } 179 180 public static String getParentPath(String path) { 181 int p = path.lastIndexOf(File.separator); 182 if (p == -1) { 183 return null; 184 } 185 return path.substring(0, p); 186 } 187 188 public static String getFileName(String path) { 189 int p = path.lastIndexOf(File.separator); 190 if (p == -1) { 191 return path; 192 } 193 return path.substring(p + 1); 194 } 195 196 public static String getFileExtension(String path) { 197 int p = path.lastIndexOf('.'); 198 if (p == -1) { 199 return null; 200 } 201 return path.substring(p + 1); 202 } 203 204 public static String getFileNameNoExt(String path) { 205 String name = getFileName(path); 206 int p = name.lastIndexOf('.'); 207 if (p == -1) { 208 return name; 209 } 210 return name.substring(0, p); 211 } 212 213 /** 214 * Retrieves the total path of a resource from the Thread Context. 215 * 216 * @param resource the resource name to be retrieved. 217 * @return the decoded path. 218 */ 219 public static String getResourcePathFromContext(String resource) { 220 URL url = Thread.currentThread().getContextClassLoader().getResource(resource); 221 return getFilePathFromUrl(url); 222 } 223 224 public static File getResourceFileFromContext(String resource) { 225 File file; 226 String filename = getResourcePathFromContext(resource); 227 if (filename.equals("")) { 228 file = null; 229 } else { 230 file = new File(filename); 231 } 232 return file; 233 } 234 235 public static File[] findFiles(File root, String pattern, boolean recurse) { 236 List<File> result = new ArrayList<>(); 237 if (pattern == null) { 238 if (recurse) { 239 collectFiles(root, result); 240 } else { 241 return root.listFiles(); 242 } 243 } else { 244 FileNamePattern pat = new FileNamePattern(pattern); 245 if (recurse) { 246 collectFiles(root, pat, result); 247 } else { 248 File[] files = root.listFiles(); 249 for (File file : files) { 250 if (pat.match(file.getName())) { 251 result.add(file); 252 } 253 } 254 } 255 } 256 return result.toArray(new File[result.size()]); 257 } 258 259 public static void collectFiles(File root, FileNamePattern pattern, List<File> result) { 260 File[] files = root.listFiles(); 261 for (File file : files) { 262 if (pattern.match(file.getName())) { 263 result.add(file); 264 if (file.isDirectory()) { 265 collectFiles(file, pattern, result); 266 } 267 } 268 } 269 } 270 271 public static void collectFiles(File root, List<File> result) { 272 File[] files = root.listFiles(); 273 for (File file : files) { 274 result.add(file); 275 if (file.isDirectory()) { 276 collectFiles(file, result); 277 } 278 } 279 } 280 281 /** 282 * Create a file handler (this doesn't create a real file) given a file URI. This method can be used to create files 283 * from invalid URL strings (e.g. containing spaces ..) 284 * 285 * @return a file object 286 */ 287 public static File urlToFile(String url) throws MalformedURLException { 288 return urlToFile(new URL(url)); 289 } 290 291 public static File urlToFile(URL url) { 292 try { 293 return new File(url.toURI()); 294 } catch (URISyntaxException e) { 295 return new File(url.getPath()); 296 } 297 } 298 299 /** 300 * Compares two files content as String even if their EOL are different 301 * 302 * @param expected a file content with Windows or Unix like EOL 303 * @param source another file content with Windows or Unix like EOL 304 * @return the result of equals after replacing their EOL 305 */ 306 public static boolean areFilesContentEquals(String expected, String source) { 307 if (expected == source) { 308 return true; 309 } 310 311 if (expected == null || source == null) { 312 return false; 313 } 314 315 if (expected.length() != source.length()) { 316 // Prevent from comparing files with Windows EOL 317 return expected.replace("\r\n", "\n").equals(source.replace("\r\n", "\n")); 318 } else { 319 return expected.equals(source); 320 } 321 } 322 323 /** 324 * Returns a safe filename, replacing unsafe characters (: \ / * ..) with "_". For instance, it turns 325 * "tmp/../2349:876398/foo.png" into "tmp___2349_876398_foo.png" 326 * 327 * @param filename the filename 328 * @return the safe filename with underscores instead of unsafe characters 329 * @since 9.1 330 */ 331 public static String getSafeFilename(String filename) { 332 return filename.replaceAll("(\\\\)|(\\/)|(\\:)|(\\*)|(\\.\\.)", "_"); 333 } 334 335}