001package com.hfg.util; 002 003import java.io.*; 004import java.util.ArrayList; 005import java.util.List; 006import java.util.Stack; 007import java.util.regex.Pattern; 008import java.util.regex.Matcher; 009import java.util.zip.GZIPOutputStream; 010 011import com.hfg.util.io.StreamUtil; 012 013//------------------------------------------------------------------------------ 014/** 015 * General File utility functions. 016 * 017 * @author J. Alex Taylor, hairyfatguy.com 018 */ 019//------------------------------------------------------------------------------ 020// com.hfg XML/HTML Coding Library 021// 022// This library is free software; you can redistribute it and/or 023// modify it under the terms of the GNU Lesser General Public 024// License as published by the Free Software Foundation; either 025// version 2.1 of the License, or (at your option) any later version. 026// 027// This library is distributed in the hope that it will be useful, 028// but WITHOUT ANY WARRANTY; without even the implied warranty of 029// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 030// Lesser General Public License for more details. 031// 032// You should have received a copy of the GNU Lesser General Public 033// License along with this library; if not, write to the Free Software 034// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 035// 036// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com 037// jataylor@hairyfatguy.com 038//------------------------------------------------------------------------------ 039 040public class FileUtil 041{ 042 private static final String UNIX_SEPARATOR = "/"; 043 private static final String WINDOZE_SEPARATOR = "\\\\"; 044 045 private static final Pattern sExtensionPattern = Pattern.compile("\\.[^\\.]+$"); 046 047 //########################################################################## 048 // PUBLIC FUNCTIONS 049 //########################################################################## 050 051 //--------------------------------------------------------------------------- 052 public static String sanitizeFilename(String inFilename) 053 { 054 return inFilename.replaceAll("[\\_\\u0001-\\u001f\"<>\\s\\'\\\"\\|\\?&\\:\\*,;/\\\\\\u007f]+", "_"); 055 } 056 057 //--------------------------------------------------------------------------- 058 public static boolean rmdir(File inDir) 059 { 060 if (inDir.isDirectory()) 061 { 062 File[] files = inDir.listFiles(); 063 if (files != null) 064 { 065 for (int i = 0; i < files.length; i++) 066 { 067 File file = files[i]; 068 if (file.isDirectory()) 069 { 070 rmdir(file); 071 } 072 073 file.delete(); 074 } 075 } 076 } 077 078 return inDir.delete(); 079 } 080 081 //--------------------------------------------------------------------------- 082 /** 083 Returns the relative file path to inFile1 with respect to inFile2. 084 <pre> 085 Ex: If testFile1 is "dir1/file1.xml" and testFile2 is "dir1/dir2/file2.xml", 086 this method will return "../file1.xml" 087 </pre> 088 */ 089 public static String getRelativePath(File inFile1, File inFile2) 090 { 091 Stack<File> file1DirStack = getDirStack(inFile1); 092 Stack<File> file2DirStack = getDirStack(inFile2); 093 094 while (file1DirStack.size() > 0 095 && file2DirStack.size() > 0 096 && file1DirStack.peek().equals(file2DirStack.peek())) 097 { 098 file1DirStack.pop(); 099 file2DirStack.pop(); 100 } 101 102 StringBuilder relativePath = new StringBuilder(StringUtil.polyString("../", file2DirStack.size())); 103 if (0 == relativePath.length()) 104 { 105 relativePath.append("./"); 106 } 107 108 while (file1DirStack.size() > 0) 109 { 110 File dir = file1DirStack.pop(); 111 if (StringUtil.isSet(dir.getName())) 112 { 113 relativePath.append(dir.getName()); 114 relativePath.append("/"); 115 } 116 } 117 118 relativePath.append(inFile1.getName()); 119 120 121 return relativePath.toString(); 122 } 123 124 //--------------------------------------------------------------------------- 125 /** 126 Converts file path directory separators to the Unix separator '/'. 127 128 @param inFilePath the path to be changed, null ignored 129 @return the modified path 130 */ 131 public static String convertSeparatorsToUnix(String inFilePath) 132 { 133 String result = inFilePath; 134 if (inFilePath != null 135 && inFilePath.contains(WINDOZE_SEPARATOR)) 136 { 137 result = inFilePath.replaceAll(WINDOZE_SEPARATOR, UNIX_SEPARATOR); 138 } 139 140 return result; 141 } 142 143 //--------------------------------------------------------------------------- 144 public static String getNameMinusExtension(File inFile) 145 { 146 return getNameMinusExtension(inFile.getName()); 147 } 148 149 //--------------------------------------------------------------------------- 150 /** 151 Returns the specified file's name without its extension. If no extension is 152 present, the original file name is returned. 153 (ex: 'foo.txt' would return 'foo') 154 */ 155 public static String getNameMinusExtension(String inFilename) 156 { 157 String result; 158 Matcher m = sExtensionPattern.matcher(inFilename); 159 if (m.find()) 160 { 161 result = inFilename.substring(0, m.start()); 162 } 163 else 164 { 165 result = inFilename; 166 } 167 168 return result; 169 } 170 171 //--------------------------------------------------------------------------- 172 /** 173 Returns the specified file's extension or null if no extension was found 174 (ex: 'foo.txt' would return 'txt'). 175 */ 176 public static String getExtension(File inFile) 177 { 178 String result = null; 179 Matcher m = sExtensionPattern.matcher(inFile.getName()); 180 if (m.find()) 181 { 182 result = m.group().substring(1); 183 } 184 185 return result; 186 } 187 188 //--------------------------------------------------------------------------- 189 /** 190 Writes the specified content to the specified file overwriting any previous content. 191 */ 192 public static long write(File inDest, String inContent) 193 throws IOException 194 { 195 return write(inDest, new ByteArrayInputStream(inContent.getBytes())); 196 } 197 198 //--------------------------------------------------------------------------- 199 /** 200 Writes the specified content to the specified file overwriting any previous content. 201 */ 202 public static long write(File inDest, InputStream inContent) 203 throws IOException 204 { 205 long bytesWritten = 0; 206 if (inContent != null) 207 { 208 InputStream inStream = null; 209 OutputStream outStream = null; 210 try 211 { 212 inStream = new BufferedInputStream(inContent); 213 outStream = new BufferedOutputStream(new FileOutputStream(inDest)); 214 215 byte[] buffer = new byte[4 * 1024]; 216 int bytesRead; 217 while ((bytesRead = inStream.read(buffer)) != -1) 218 { 219 outStream.write(buffer, 0, bytesRead); 220 bytesWritten += bytesRead; 221 } 222 } 223 finally 224 { 225 if (inStream != null) inStream.close(); 226 if (outStream != null) outStream.close(); 227 } 228 } 229 230 return bytesWritten; 231 } 232 233 //--------------------------------------------------------------------------- 234 /** 235 GZIP compresses and writes the specified content to the specified file overwriting any previous content. 236 */ 237 public static long writeGzipped(File inDest, InputStream inContent) 238 throws IOException 239 { 240 long bytesWritten = 0; 241 if (inContent != null) 242 { 243 InputStream inStream = null; 244 OutputStream outStream = null; 245 try 246 { 247 inStream = new BufferedInputStream(inContent); 248 outStream = new BufferedOutputStream(new GZIPOutputStream(new FileOutputStream(inDest))); 249 250 byte[] buffer = new byte[4 * 1024]; 251 int bytesRead; 252 while ((bytesRead = inStream.read(buffer)) != -1) 253 { 254 outStream.write(buffer, 0, bytesRead); 255 bytesWritten += bytesRead; 256 } 257 } 258 finally 259 { 260 if (inStream != null) inStream.close(); 261 if (outStream != null) outStream.close(); 262 } 263 } 264 265 return bytesWritten; 266 } 267 268 //--------------------------------------------------------------------------- 269 /** 270 Copies the specified file to the specified target. 271 */ 272 public static void copy(File inSrc, File inTarget) 273 throws IOException 274 { 275 if (null == inSrc) 276 { 277 throw new IOException("No source file specified!"); 278 } 279 else if (!inSrc.exists()) 280 { 281 throw new IOException("Specified source file '" + inSrc + "' doesn't exist!"); 282 } 283 284 if (null == inTarget) 285 { 286 throw new IOException("No target file specified!"); 287 } 288 289 BufferedInputStream readStream = null; 290 BufferedOutputStream writeStream = null; 291 try 292 { 293 readStream = new BufferedInputStream(new FileInputStream(inSrc)); 294 writeStream = new BufferedOutputStream(new FileOutputStream(inTarget)); 295 296 byte[] buffer = new byte[8 * 1024]; 297 int bytesRead = 0; 298 while ((bytesRead = readStream.read(buffer, 0, buffer.length)) != -1) 299 { 300 writeStream.write(buffer, 0, bytesRead); 301 } 302 } 303 finally 304 { 305 if (readStream != null) readStream.close(); 306 if (writeStream != null) writeStream.close(); 307 } 308 } 309 310 //--------------------------------------------------------------------------- 311 /** 312 Recursively copies all files and directories within the src directory into the target dir. 313 */ 314 public static void copyDirContents(File inSrcDir, File inTargetDir) 315 throws IOException 316 { 317 if (null == inSrcDir) 318 { 319 throw new IOException("No source directory specified!"); 320 } 321 else if (!inSrcDir.exists()) 322 { 323 throw new IOException("Specified source directory '" + inSrcDir + "' doesn't exist!"); 324 } 325 else if (!inSrcDir.isDirectory()) 326 { 327 throw new IOException("Specified source directory '" + inSrcDir + "' isn't a directory!"); 328 } 329 330 if (null == inTargetDir) 331 { 332 throw new IOException("No target file specified!"); 333 } 334 else if (!inTargetDir.exists()) 335 { 336 if (!inTargetDir.mkdirs()) 337 { 338 throw new IOException("Specified target directory '" + inTargetDir + "' couldn't be created!"); 339 } 340 } 341 else if (!inTargetDir.isDirectory()) 342 { 343 throw new IOException("Specified target directory '" + inTargetDir + "' isn't a directory!"); 344 } 345 346 for (File file : inSrcDir.listFiles()) 347 { 348 if (file.isFile()) 349 { 350 copy(file, new File(inTargetDir, file.getName())); 351 } 352 else if (file.isDirectory()) 353 { 354 copyDirContents(file, new File(inTargetDir, file.getName())); 355 } 356 } 357 } 358 359 //--------------------------------------------------------------------------- 360 public static CharSequence read(File inFile) 361 throws IOException 362 { 363 checkFileReadability(inFile); 364 365 StringBuilderPlus buffer = new StringBuilderPlus(); 366 367 BufferedReader reader = null; 368 try 369 { 370 reader = new BufferedReader(new FileReader(inFile)); 371 String line; 372 while ((line = reader.readLine()) != null) 373 { 374 buffer.appendln(line); 375 } 376 } 377 finally 378 { 379 StreamUtil.close(reader); 380 } 381 382 return buffer; 383 } 384 385 //--------------------------------------------------------------------------- 386 public static List<String> readLines(File inFile) 387 throws IOException 388 { 389 checkFileReadability(inFile); 390 391 List<String> lines = new ArrayList<>(); 392 393 BufferedReader reader = null; 394 try 395 { 396 reader = new BufferedReader(new FileReader(inFile)); 397 String line; 398 while ((line = reader.readLine()) != null) 399 { 400 lines.add(line); 401 } 402 } 403 finally 404 { 405 StreamUtil.close(reader); 406 } 407 408 return lines; 409 } 410 411 //--------------------------------------------------------------------------- 412 private static void checkFileReadability(File inFile) 413 throws IOException 414 { 415 if (null == inFile) 416 { 417 throw new IOException("No file specified!"); 418 } 419 else if (! inFile.exists()) 420 { 421 throw new IOException("The file " + StringUtil.singleQuote(inFile.getPath()) + " doesn't exist!"); 422 } 423 else if (!inFile.canRead()) 424 { 425 throw new IOException("The file " + StringUtil.singleQuote(inFile.getPath()) + " cannot be read!"); 426 } 427 } 428 429 //--------------------------------------------------------------------------- 430 private static Stack<File> getDirStack(File inFile) 431 { 432 Stack<File> dirStack = new Stack<>(); 433 File file = inFile; 434 while ((file = file.getParentFile()) != null) 435 { 436 dirStack.push(file); 437 } 438 439 return dirStack; 440 } 441}