001 // Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 002 // The Molly framework is freely distributable under the terms of an 003 // MIT-style license. For details, see the molly pages web site at: 004 // http://www.mollypages.org/. Use, modify, have fun ! 005 006 package fc.io; 007 008 import fc.util.*; 009 import java.io.*; 010 import java.net.*; 011 import java.nio.*; 012 import java.nio.channels.*; 013 import java.nio.charset.*; 014 import java.util.*; 015 import java.util.regex.*; 016 import java.text.*; 017 import java.security.*; 018 019 /** Misc. IO utilities 020 021 @author hursh jain 022 @version 1.1 023 **/ 024 public final class IOUtil 025 { 026 //for internal class debugging at development time 027 private static final boolean dbg = false; 028 029 /** System.getProperty("file.separator") for convenience **/ 030 public static final String FILE_SEP = System.getProperty("file.separator"); 031 032 /** System.getProperty("path.separator") for convenience **/ 033 public static final String PATH_SEP = System.getProperty("path.separator"); 034 035 /** System.getProperty("line.separator") for convenience **/ 036 public static final String LINE_SEP = System.getProperty("line.separator"); 037 038 public static final int FILECOPY_OVERWRITE = 0; 039 public static final int FILECOPY_NO_OVERWRITE = 1; 040 041 /** 042 Ignores a directory copy command if the destination directory already 043 exists. 044 **/ 045 public static final int DIRCOPY_NO_OVERWRITE = 2; 046 047 /** 048 Copies the existing directory and overwrites any files with 049 the same name in the destination directory. Files/directories that 050 exist in the destination but not in the source directory are left 051 untouched. 052 **/ 053 public static final int DIRCOPY_ADD_OR_OVERWRITE = 3; 054 055 /** 056 Copies the existing directory. If the destination directory already 057 exists, the entire target directory is first deleted and then 058 the specified directory is copied to the destination 059 **/ 060 public static final int DIRCOPY_DELETE_AND_WRITE = 4; 061 062 063 /** number of bytes contained in a kilobyte */ 064 public static final int ONE_KB = 1024; 065 066 /** number of bytes contained in a megabyte. */ 067 public static final int ONE_MB = ONE_KB * ONE_KB; 068 069 /** number of bytes contained in a gigabyte. */ 070 public static final int ONE_GB = ONE_KB * ONE_MB; 071 072 /** number of bytes equal to 2^32 **/ 073 public static final long FOUR_GB = ONE_GB * 4L; 074 075 /** 076 Beeps by writing the beep control code to System.out 077 */ 078 public static void beep() 079 { 080 System.out.print("\007"); 081 System.out.flush(); 082 } 083 084 /** 085 Copies source file (not directory) to destination. If the destination 086 already exists, then no copy is made (that is, the destination is 087 <b>not</b> overwritten and this method returns silently (an Exception 088 is not thrown). 089 090 @param file the source file to be copied, a java.io.File 091 @param dest the destination file or directory. 092 @see copyFile(File, File, int) 093 */ 094 public static boolean copyFile(File source, File dest) throws FileNotFoundException, IOException 095 { 096 return copyFile(source, dest, IOUtil.FILECOPY_NO_OVERWRITE); 097 } 098 099 /** 100 Copies the source file (not directory) to the specified 101 destination file or directory. If a directory is specified as 102 the destination, then the source file is copied into that 103 directory. To specify the action when a file with the same name 104 already exists in the specified directory, use the appropriate 105 {@link #FILECOPY_OVERWRITE} or {@link #FILECOPY_NO_OVERWRITE} 106 flags. 107 If a file is specified as the destination, then the source file 108 is copied to that file. If the specified file exists already 109 then specify the appropriate overwrite flag. 110 <p> 111 Try to use absolute path names for files and directories. Relative 112 path names can be relative either to user.dir or where the jvm was invoked 113 or some platform/jvm dependent place, so don't rely on relative paths. 114 Copying, moving or working with files is tricky in java. Similar 115 behavior is not defined for all platforms. For example, in WindowsNT/java1.2, 116 aliases cannot be opened/resolved and hence cannot be copied via 117 FileInput/Output streams. This sort of thing is best left to 118 JNI calls native code. 119 <p> 120 This method returns <tt>true</tt> if the directory was copied 121 successfully, <tt>false</tt> otherwise. (for example, <tt>false</tt> 122 will be returned when the copy mode is not to overwrite but the 123 target file already exists). 124 125 @param source the source file (<b>not</b> directory) to be copied, a java.io.File 126 @param dest the destination file or directory. 127 @param copyflag the file copy mode. {@link #FILECOPY_OVERWRITE}, {@link #FILECOPY_NO_OVERWRITE} 128 */ 129 public static boolean copyFile(File source, File dest, int copyflag) throws FileNotFoundException, IOException 130 { 131 Argcheck.notnull(source, "copyFile(): source file argument is null"); 132 Argcheck.notnull(dest, "copyFile(): destination file argument is null"); 133 Argcheck.istrue((copyflag == FILECOPY_OVERWRITE || copyflag == FILECOPY_NO_OVERWRITE),"copyflag not valid"); 134 Argcheck.isfalse(source.isDirectory(), "A directory [" + source + "] was specified for the source. This method cannot only copy normal files"); 135 136 if (dest.isDirectory()) { 137 dest = new File(dest, source.getName()); 138 } 139 140 if (dest.exists()) { 141 if (copyflag == IOUtil.FILECOPY_NO_OVERWRITE) { 142 return false; 143 } 144 } 145 146 final FileInputStream fin = new FileInputStream(source); 147 final FileOutputStream fout = new FileOutputStream(dest); 148 final FileChannel fcin = fin.getChannel(); 149 final FileChannel fcout = fout.getChannel(); 150 151 final MappedByteBuffer mbb = fcin.map( 152 FileChannel.MapMode.READ_ONLY, 0, fcin.size()); 153 154 fcout.write(mbb); 155 156 fcin.close(); 157 fcout.close(); 158 fin.close(); 159 fout.close(); 160 return true; 161 } 162 163 /** 164 Calls {@link #copyFile(File, File, int)} with the 165 {@link #DIRCOPY_ADD_OR_OVERWRITE} flag. 166 167 @param file the source directory to be copied 168 @param dest the destination directory. 169 @see copyFile(File, File, int) 170 */ 171 public static boolean copyDirectory(File source, File dest) throws FileNotFoundException, IOException 172 { 173 return copyDirectory(source, dest, IOUtil.DIRCOPY_ADD_OR_OVERWRITE); 174 } 175 176 /** 177 Copies the source directory and all it's contents to the specified 178 destination directory. A directory must be specified both for the source 179 and the destination. 180 <p> 181 To handle cases where the destination directory already exists 182 use the appropriate {@link #DIRCOPY_NO_OVERWRITE} 183 {@link #DIRCOPY_DELETE_AND_WRITE} and 184 {@link #DIRCOPY_ADD_OR_OVERWRITE} flags. 185 <p> 186 Try to use absolute path names for files and directories. Relative 187 path names can be relative either to user.dir or where the jvm was invoked 188 or some platform/jvm dependent place, so don't rely on relative paths. 189 <u>Copying, moving or working with files is tricky in java</u>. Similar 190 behavior is not defined for all platforms. For example, in WindowsNT/java1.2, 191 aliases cannot be opened/resolved and hence cannot be copied via 192 FileInput/Output streams. This sort of thing is best left to 193 JNI calls to POSIX or to platform specific code. 194 <p> 195 This method returns <tt>true</tt> if the directory was copied 196 successfully, <tt>false</tt> otherwise. (for example, <tt>false</tt> 197 will be returned when the copy mode is not to overwrite but the 198 target directory already exists). 199 200 @param source the source directory (<b>not</b> file) to be copied, a java.io.File 201 @param dest the destination file or directory. 202 @param copyflag the dir copy mode. {@link #DIRCOPY_NO_OVERWRITE}, {@link #DIRCOPY_ADD_OR_OVERWRITE} 203 */ 204 public static boolean copyDirectory(File source, File dest, int copyflag) 205 throws IOException 206 { 207 Argcheck.notnull(source, "copyDirectory(): source file argument is null"); 208 Argcheck.notnull(dest, "copyDirectory(): destination file argument is null"); 209 Argcheck.istrue((copyflag == DIRCOPY_NO_OVERWRITE || copyflag == DIRCOPY_ADD_OR_OVERWRITE || copyflag == DIRCOPY_DELETE_AND_WRITE), "copyflag not valid"); 210 211 if (source.exists()) { 212 Argcheck.istrue(source.isDirectory(), "IOUtil.copyDirectory(): A file was specified for the source, need a directory not a file"); 213 } 214 215 if (dest.exists()) 216 { 217 if (dbg) System.out.println("IOUtil.copyDirectory(): destination '" + dest + "' exists"); 218 219 if ( ! dest.isDirectory() ) { 220 if (dbg) System.out.println("IOUtil.copyDirectory('" + source + "','" + dest + "'): A file was specified for the destination, need a directory not a file"); 221 return false; 222 } 223 224 if (copyflag == IOUtil.DIRCOPY_NO_OVERWRITE) { 225 System.out.println("IOUtil.copyDirectory(): Incompatible flag DIRCOPY_NO_OVERWRITE specified, returning false"); 226 return false; 227 } 228 if (copyflag == IOUtil.DIRCOPY_DELETE_AND_WRITE) 229 { 230 boolean good = deepDelete(dest); 231 if (! good) 232 throw new IOException("IOUtil.copyDirectory, flag=DIRCOPY_DELETE_AND_WRITE, cannot delete the destination directory:" + dest); 233 } 234 } 235 else { //dest dir does not exist 236 if (dbg) System.out.println("IOUtil.copyDirectory(): destination dir '" + dest + "' does not exist. Creating.."); 237 boolean good = dest.mkdirs(); 238 if (! good) { 239 if (dbg) System.out.println("IOUtil.copyDirectory(): could not make directory '" + dest + "' ; returning false"); 240 return false; 241 } 242 } 243 244 String files[] = source.list(); //does not return "." or ".." 245 246 boolean copiedOne = false; 247 boolean copiedAll = true; 248 for(int i = 0; i < files.length; i++) 249 { 250 File source_f = new File(source, files[i]); 251 File dest_f = new File(dest, files[i]); 252 if(source_f.isDirectory()) { 253 if (dbg) System.out.println("IOUtil.copyDirectory(): recursive copy directory call: '" + source_f + "' to '" + dest_f + "'"); 254 copiedOne = copyDirectory(source_f, dest_f, copyflag); 255 } 256 else { 257 if (dbg) System.out.println("IOUtil.copyDirectory(): copying file: '" + source_f + "' to '" + dest_f + "'"); 258 copiedOne = copyFile(source_f, dest_f, IOUtil.FILECOPY_OVERWRITE); 259 } 260 if (! copiedOne) 261 copiedAll = false; 262 } 263 if (dbg) System.out.println("IOUtil.copyDirectory: returning: " + copiedAll); 264 return copiedAll; 265 } //~copyDirectory 266 267 /** 268 Copies all data from the specific input stream to the specified output stream. 269 Closes both streams after it is finished. 270 271 @param in the InputStream 272 @param out the OutputStream 273 **/ 274 public static void copyStream(InputStream in, OutputStream out) 275 throws IOException 276 { 277 final BufferedInputStream bin = bufferStream(in); 278 final BufferedOutputStream bout = bufferStream(out); 279 int i = 0; 280 while ( (i = bin.read()) > -1) { 281 bout.write(i); 282 } 283 //FilterStream (like bufferedinputstream etc) close all internal 284 //streams when they are closed too ! 285 bin.close(); 286 bout.close(); 287 } 288 289 /** 290 Alphabetizes the specified list by the filename. Only the 291 filename is considered and not the path name (if any). The 292 collection should contain one of: 293 <ul> 294 <li> <tt>java.io.File</tt> 295 <li> <tt>String[]</tt> 296 <li> <tt>Object</tt> 297 </ul> 298 Any/all of these can be contained in the specified list at the 299 same time. If a <tt>String[]</tt> is found, the <b>0</b>th element 300 (i.e., (String[] foo)[0]) is used for comparison purposes. The 301 list is sorted by the default {@link String#compareTo(String)} implementation 302 of <tt>String</tt>. 303 304 @param list the list to be sorted 305 **/ 306 public static void sortByFileName(List c) { 307 Collections.sort(c, new Comparator() { 308 public int compare(Object o1, Object o2) { 309 return getStr(o1).compareTo(getStr(o2)); 310 } 311 private String getStr(Object o) { 312 String str = null; 313 if ( o instanceof File ) 314 str = ((File)o).getName(); 315 else if (o instanceof String) 316 str = (String) o; 317 else 318 str = (o!=null)? o.toString() : null; 319 return str; 320 } 321 }); 322 } 323 324 /** 325 This method recursively removes a specified file or recursively 326 removes a specified directory. It is needed (as of JDK 1.4) because 327 {@link File#delete} lacks the ability to delete a directory if the 328 directory is not empty. 329 <p> 330 Internally, this method delegates to {@link File#delete}, so if 331 {@link File#delete} follows sym links, then so will this method. 332 Be careful ! 333 334 @param file the file or directory to be removed 335 @return <tt>true</tt> on success, <tt>false</tt> otherwise. Also returns false if 336 the specified file or directory does not exist. 337 **/ 338 public static boolean deepDelete(File f) 339 { 340 Argcheck.notnull(f, "File was null"); 341 if (dbg) System.out.println("deepDelete(): deleting: " + f); 342 343 boolean ok = true; 344 345 if (! f.exists()) 346 return false; 347 348 if (f.isFile()) { 349 ok = f.delete(); 350 return ok; 351 } 352 353 //f is a directory 354 File[] files = f.listFiles(); //does not return "." or ".." 355 boolean subok = false; 356 357 //1. delete sub directories 358 for (int n = 0; n < files.length; n++) { 359 subok = deepDelete(files[n]); 360 if (! subok) ok = false; 361 } 362 363 //2. delete current directory 364 subok = f.delete(); 365 if (! subok) ok = false; 366 367 return ok; 368 } 369 370 /** 371 Gets the total size for a directory and all of it's contents. If 372 the specified argument is a regular file, returns the size of that 373 file itself. 374 375 @param dir the target dir 376 @return the directory or file size in bytes 377 **/ 378 public static long dirSize(File dir) 379 { 380 Argcheck.notnull(dir, "File was null"); 381 long size = 0; 382 383 if (! dir.exists()) 384 return 0; 385 386 if (dir.isFile()) { 387 return dir.length(); 388 } 389 390 File[] files = dir.listFiles(); //does not return "." or ".." 391 392 for (int n = 0; n < files.length; n++) 393 size += dirSize(files[n]); 394 395 return size; 396 } 397 398 /** 399 Buffers and returns the specified InputStream, if it is not already buffered. 400 Does not buffer an already buffered stream but returns it as is. 401 402 @param in the input stream to be buffered 403 @return the buffered stream 404 */ 405 public static BufferedInputStream bufferStream(InputStream in) 406 { 407 Argcheck.notnull(in, "InputStream was null"); 408 BufferedInputStream bin; 409 if (! (in instanceof BufferedInputStream)) { 410 bin = new BufferedInputStream(in); 411 } 412 else { 413 bin = (BufferedInputStream) in; 414 } 415 return bin; 416 } 417 418 /** 419 Buffers and returns the specified OutputStream, if it is not already buffered. 420 Does not buffer an already buffered stream but returns it as is. 421 422 @param out the output stream to be buffered 423 @return the buffered stream 424 425 **/ 426 public static BufferedOutputStream bufferStream(OutputStream out) 427 { 428 Argcheck.notnull(out, "OutputStream was null"); 429 BufferedOutputStream bout; 430 if (! (out instanceof BufferedOutputStream)) { 431 bout = new BufferedOutputStream(out); 432 } 433 else { 434 bout = (BufferedOutputStream) out; 435 } 436 return bout; 437 } 438 439 public static BufferedReader bufferReader(Reader in) 440 { 441 Argcheck.notnull(in, "Reader was null"); 442 BufferedReader bin; 443 if ( ! (in instanceof BufferedReader)) { 444 bin = new BufferedReader(in); 445 } 446 else { 447 bin = (BufferedReader) in; 448 } 449 return bin; 450 } 451 452 public static PrintStream toPrintStream(OutputStream out) 453 { 454 Argcheck.notnull(out, "OutputStream was null"); 455 if ( ! (out instanceof PrintStream)) { 456 out = new PrintStream(out); 457 } 458 return (PrintStream) out; 459 } 460 461 public static PrintWriter toPrintWriter(Writer out) 462 { 463 Argcheck.notnull(out, "Writer was null"); 464 if ( ! (out instanceof PrintWriter)) { 465 out = new PrintWriter(out); 466 } 467 return (PrintWriter) out; 468 } 469 470 public static BufferedWriter bufferWriter(Writer out) 471 { 472 Argcheck.notnull(out, "Writer was null"); 473 BufferedWriter bout; 474 if ( ! (out instanceof BufferedWriter)) { 475 bout = new BufferedWriter(out); 476 } 477 else { 478 bout = (BufferedWriter) out; 479 } 480 return bout; 481 } 482 483 /** 484 Convenience method to print the contents of a java.util.Property object 485 to a String (using the default platform encoding). 486 487 **/ 488 public static String propertiesToString(Properties props) 489 { 490 String temp = null; 491 final ByteArrayOutputStream bout = new ByteArrayOutputStream(); 492 final PrintStream pout = new PrintStream(bout); 493 props.list(pout); 494 pout.flush(); 495 temp = bout.toString(); 496 pout.close(); 497 return temp; 498 } 499 500 private static String defaultEncoding = null; 501 502 /** 503 Returns the default encoding used by the current platform. Returns 504 <tt>null</tt> is the default encoding cannot be determined. 505 **/ 506 public static String getDefaultEncoding() 507 { 508 if (defaultEncoding != null) 509 return defaultEncoding; 510 String de = null; 511 try { 512 final InputStream in = ClassLoader.getSystemResourceAsStream("fc/io/IOUtil.class"); 513 final InputStreamReader defaultReader = new InputStreamReader(in); 514 de = defaultReader.getEncoding(); 515 defaultEncoding = de; 516 if (dbg) System.out.println("IOUtil.getDefaultEncoding() = " + de); 517 } 518 catch (Exception e) { 519 e.printStackTrace(); 520 } 521 return de; 522 } 523 524 525 /** 526 Returns the contents of an entire file as a List of individual lines. If the specified 527 file does not exist or have no content return an empty List. 528 529 @param instream the stream to be read 530 @param trim if <tt>true</tt>, any leading or trailing blank lines are trimmed are ignored. 531 @param comment_chars Regex of comment chars. Any lines that start with this (or have leading spaces 532 and then start with this) are ignored. (example: <tt>#</tt> or <tt>#|//</tt>) 533 */ 534 public static List fileToLines(InputStream instream, boolean trim, String comment_chars) throws IOException 535 { 536 BufferedReader in = new BufferedReader(new InputStreamReader(instream, "UTF-8")); 537 538 List list = new ArrayList(); 539 String line = null; 540 Pattern pat = null; 541 Matcher m = null; 542 if (comment_chars != null) { 543 pat = Pattern.compile("^[ \\t]*(" + comment_chars + ")+"); 544 } 545 546 while ( (line = in.readLine()) != null) 547 { 548 if (trim) { 549 line = line.trim(); //this gets rid of spaces, empty newlines, etc 550 if (line.length() == 0) { 551 continue; 552 } 553 } 554 555 if (pat != null) 556 { 557 m = pat.matcher(line); 558 if (m.find()) { 559 continue; 560 } 561 } 562 list.add(line); 563 } 564 return list; 565 } 566 567 /** 568 Returns the contents of an entire file as a List of individual lines. If the specified 569 file does not exist or have no content return an empty List. 570 571 @param File the file to be read ("UTF-8" encoding is used) 572 @param trim if <tt>true</tt>, any leading or trailing blank lines are trimmed are ignored. 573 @param comment_chars Regex of comment chars. Any lines that start with this (or have leading spaces 574 and then start with this) are ignored. (example: <tt>#</tt> or <tt>#|//</tt>) 575 */ 576 public static List fileToLines(File file, boolean trim, String comment_chars) throws IOException 577 { 578 return fileToLines(new FileInputStream(file), trim, comment_chars); 579 } 580 581 /** 582 Returns the contents of an entire file as a List of individual lines. Empty lines are trimmed 583 and lines beginning with the following characters are ignored: <tt>#</tt> and <tt>//</tt> 584 585 @param file the file to be read ("UTF-8" encoding is used) 586 */ 587 public static List fileToLines(File file) throws IOException 588 { 589 return fileToLines(file, true, "#|//"); 590 } 591 592 /** 593 Returns the contents of an entire file as a List of individual lines. Empty lines are trimmed 594 and lines beginning with the following characters are ignored: <tt>#</tt> and <tt>//</tt> 595 596 @param filename the file to be read ("UTF-8" encoding is used) 597 */ 598 public static List fileToLines(String filename) throws IOException 599 { 600 return fileToLines(new File(filename)); 601 } 602 603 /** 604 Returns the contents of an entire file as a List of individual lines. Empty lines are trimmed 605 and lines beginning with the following characters are ignored: <tt>#</tt> and <tt>//</tt> 606 607 @param in the input stream to be read 608 */ 609 public static List fileToLines(InputStream in) throws IOException 610 { 611 return fileToLines(in, true, "#|//"); 612 } 613 614 /** 615 Returns the contents of an entire file as a String. If the specified 616 file does not exist returns <tt>null</tt>. Files that exist but have no 617 content return an empty String. 618 <p> 619 <b>Note 1:</b> Due to jdk1.4 brain damage, this method is limited to files 620 less than 2^32 bytes. If the specified file is greater than 2^32 bytes, 621 an <tt>IOException</tt> will be thrown. 622 <br> 623 <b>Note 2:</b> The files is converted into a String using an encoding 624 that is determined programmatically (from the filesystem). This may 625 not be totally reliable but there is no way around this because JDK 1.4 626 provides <b>no</b> way to easily get the default platform encoding. 627 Uses the <tt>ISO_8859_1</tt> encoding as a fallback measure, if the 628 default encoding cannot be determined. 629 630 @param filename the file to be read 631 @param trim if <tt>true</tt>, any trailing whitespace is trimmed from the file's end. 632 **/ 633 public static String fileToString(File file, boolean trim) throws IOException 634 { 635 byte[] buf = fileToByteArray(file); 636 if (buf == null) 637 return null; 638 639 //there is no way to convert a byte buffer to a String 640 641 //because we cannot get a Charset that uses the default platform 642 //encoding in JDK 1.4.0. So we are going to IOUtil.getDefaultEncoding 643 //method (which is a workaround) to get the default platform encoding. 644 645 //update: instead of default encoding, arrayToCharBuffer will UTF-8 if 646 //encoding is not specified. 647 648 // resultstr will be "" if buf contains 0 chars (it can't be null 649 // if we have reached this point) 650 651 String resultstr = arrayToCharBuffer(buf).toString(); 652 if (trim) { 653 //might trigger g.c if string size is large 654 resultstr = resultstr.trim(); 655 } 656 return resultstr; 657 } 658 659 660 /** 661 Returns the contents of an entire file as a String. If the specified 662 file does not exist returns <tt>null</tt>. Files that exist but have no 663 content return an empty String. 664 <p> 665 <b>Note 1:</b> Due to jdk1.4 brain damage, this method is limited to files 666 less than 2^32 bytes. If the specified file is greater than 2^32 bytes, 667 an <tt>IOException</tt> will be thrown. 668 <br> 669 <b>Note 2:</b> The files is converted into a String using an encoding 670 that is determined programmatically (from the filesystem). This may 671 not be totally reliable but there is no way around this because JDK 1.4 672 provides <b>no</b> way to easily get the default platform encoding. 673 Uses the <tt>ISO_8859_1</tt> encoding as a fallback measure, if the 674 default encoding cannot be determined. 675 676 @param file the absolute path to the file name to be read 677 @param trim if <tt>true</tt>, any trailing whitespace is trimmed from the file's end. 678 **/ 679 public static String fileToString(String filename, boolean trim) throws IOException 680 { 681 return fileToString(new File(filename), trim); 682 } 683 684 /** 685 Calls {@link #fileToString(String, boolean)} with trim being 686 <tt>false</tt> (that is, files are not trimmed at their trailing end). 687 688 @param filename the absolute path to the file name to be read 689 **/ 690 public static String fileToString(String filename) throws IOException 691 { 692 return fileToString(filename, false); 693 } 694 695 /** 696 Calls {@link #fileToString(String, boolean)} with trim being 697 <tt>false</tt> (that is, files are not trimmed at their trailing end). 698 699 @param file the file to be read 700 **/ 701 public static String fileToString(File file) throws IOException 702 { 703 return fileToString(file, false); 704 } 705 706 707 /** 708 Returns the contents of an entire file as a <tt>byte[]</tt>. If the specified 709 file does not exist returns <tt>null</tt>. 710 <p> 711 <b>Note 1:</b> Since java arrays cannot be greater than 2^32 elements, 712 this method is limited to files less than or equal to 2^32 bytes. If 713 the specified file is greater than 2^32 bytes, an <tt>IOException</tt> 714 will be thrown. 715 716 @param filename the absolute path to the file name to be read 717 @return ByteBuffer contains the bytes for that file 718 **/ 719 public static byte[] fileToByteArray(String filename) throws IOException 720 { 721 return fileToByteArray(new File(filename)); 722 } 723 724 /** 725 Returns the contents of an entire file as a <tt>byte[]</tt>. If the specified 726 file does not exist returns <tt>null</tt>. 727 <p> 728 <b>Note 1:</b> Since java arrays cannot be greater than 2^32 elements, 729 this method is limited to files less than or equal to 2^32 bytes. If 730 the specified file is greater than 2^32 bytes, an <tt>IOException</tt> 731 will be thrown. 732 733 @param file the file to be read 734 @return byte[] contains the bytes for that file 735 **/ 736 public static byte[] fileToByteArray(File file) throws IOException 737 { 738 if (dbg) System.out.println("ENTER fileToByteBuffer(" + file + ")"); 739 740 Argcheck.notnull(file); 741 742 if ( ! file.exists() ) 743 return null; 744 745 if (dbg) System.out.println("file '" + file + "' exists"); 746 747 long longfsize = file.length(); 748 if (dbg) System.out.println("'" + file + "' size = " + longfsize); 749 750 if ( longfsize > FOUR_GB ) 751 throw new IOException("File size of " + longfsize + " too large for this method"); 752 753 FileInputStream fin = new FileInputStream(file); 754 int fsize = (int) longfsize; 755 byte[] buf = new byte[fsize]; 756 757 try { 758 int read, pos = 0; 759 while (pos < fsize) 760 { 761 /* Usually, this will read everything the first time */ 762 read = fin.read(buf, pos, fsize - pos); 763 pos += read; 764 if (read < 0) 765 break; 766 } 767 768 if (dbg) System.out.println("Read file byte[] = " + buf.length + " bytes"); 769 770 if (pos != fsize) 771 throw new IOException( "Can't read entire file, filesize = " + fsize + ", read = " + pos); 772 773 if (dbg) System.out.println("EXIT fileToByteBuffer(" + file + ")"); 774 } 775 finally { 776 fin.close(); 777 } 778 779 return buf; 780 } 781 782 /** 783 Returns the contents of an entire file as a ByteBuffer backed by mapping the 784 file to memory. If the specified file does not exist returns <tt>null</tt>. 785 Mapped files do <b>not</b> have have a accesible backing array and the 786 <tt>ByteBuffer.hasArray()</tt> will be <tt>false</tt>. See the {@link 787 java.nio.MappedByteBuffer} documentation about concurrent modification or 788 deletion of files that are mapped into memory. 789 <p> 790 The ByteBuffer returned by this method will have <tt>{@link 791 ByteBuffer#rewind()} </tt> called on it before it is returned. 792 <p> 793 <b>Note 1:</b> This method is limited to files less than 2^32 bytes, since 794 ByteBuffers cannot be greater than this size. If the specified file is greater 795 than 2^32 bytes, an <tt>IOException</tt> will be thrown. 796 797 @param file the file to be read 798 @return ByteBuffer contains the bytes for that file 799 **/ 800 public static ByteBuffer fileToByteBuffer(File file) throws IOException 801 { 802 if (dbg) System.out.println("ENTER fileAsByteBuffer(" + file + ")"); 803 Argcheck.notnull(file); 804 long fsize = 0; 805 806 if ( ! file.exists() ) 807 return null; 808 809 if (dbg) System.out.println("file '" + file + "' exists"); 810 811 fsize = file.length(); 812 813 if (dbg) System.out.println("'" + file + "' size = " + fsize); 814 815 if ( fsize > FOUR_GB) 816 throw new IOException("File size of " + file.length() + " too large for this method"); 817 818 FileChannel fcin = new FileInputStream(file).getChannel(); 819 820 BufferedReader reader = null; 821 final ByteBuffer bufin = fcin.map(FileChannel.MapMode.READ_ONLY, 0, fsize); 822 823 if (dbg) System.out.println("File ByteBuffer = " + bufin); 824 825 //This is very important and easy to forget -- rewind the buffer !!! 826 827 bufin.rewind(); 828 829 if (dbg) System.out.println("EXIT fileAsByteBuffer(" + file + ")"); 830 return bufin; 831 } 832 833 /** 834 Returns the contents of an entire file as a <tt>char[]</tt>. If the specified 835 file does not exist returns <tt>null</tt>. 836 <p> 837 <b>Note 1:</b> Since java arrays cannot be greater than 2^32 elements, 838 this method is limited to files less than or equal to 2^32 bytes. If 839 the specified file is greater than 2^32 bytes, an <tt>IOException</tt> 840 will be thrown. 841 842 @param file the file to be read 843 @param encoding the name of the character encoding to use. 844 Specify <tt>null</tt> to use UTF-8 encoding. 845 @return char[] contains the chars for that file 846 **/ 847 public static char[] fileToCharArray(File file, String encoding) 848 throws IOException 849 { 850 Argcheck.notnull(file); 851 852 if ( ! file.exists() ) 853 return null; 854 855 if (dbg) System.out.println("file '" + file + "' exists"); 856 857 long longfsize = file.length(); 858 if (dbg) System.out.println("'" + file + "' size = " + longfsize); 859 860 if ( longfsize > FOUR_GB ) 861 throw new IOException("File size of " + longfsize + " too large for this method"); 862 863 FileInputStream fin = new FileInputStream(file); 864 865 if (encoding == null) { 866 encoding = "UTF-8"; 867 } 868 869 final Reader reader = new InputStreamReader(fin, encoding); 870 int fsize = (int) longfsize; 871 char[] buf = new char[fsize]; 872 873 try { 874 int read, pos = 0; 875 while (pos < fsize) 876 { 877 /* Usually, this will read everything the first time */ 878 read = reader.read(buf, pos, fsize - pos); 879 pos += read; 880 if (read < 0) { //EOF 881 break; 882 } 883 } 884 885 if (dbg) System.out.println("Read file char[] = " + buf.length + " count"); 886 887 if (dbg && pos != fsize) { 888 System.out.println("File: " + file.getAbsolutePath() 889 + " has bytes [" + fsize + "], read [" + pos + "] chars\n" 890 + "This is expected since byte->char will loose characters for non-ascii files. Currently using [" 891 + encoding + "] to read this file" 892 ); 893 } 894 } 895 finally { 896 fin.close(); 897 } 898 899 return buf; 900 } 901 902 /* 903 Reads the entire Stream and returns all read data as a 904 <tt>char[]</tt>. If no data is available, returns an empty char[]. 905 */ 906 public static char[] readerToCharArray(Reader reader) 907 throws IOException 908 { 909 Argcheck.notnull(reader); 910 911 int buffer_size = 1024; 912 char[] buf = new char[buffer_size]; 913 if (dbg) System.out.println("readerToCharArray(), block=yes"); 914 915 final CharArrayWriter cout = new CharArrayWriter(buffer_size); 916 int read = 0; 917 while (true) { 918 read = reader.read(buf, 0, buffer_size); 919 if (read == -1) 920 break; 921 cout.write(buf, 0, read); 922 } 923 924 return cout.toCharArray(); 925 } 926 927 928 /** 929 Converts the specified byte array into a CharBuffer using the specified 930 encoding. The returned CharBuffer can be directly used in statements such 931 as <tt>System.out.println</tt> to print it's contents, 932 <p> 933 This method returns <tt>null</tt> if the specified array is <tt>null</tt> 934 or if the specified encoding is <tt>null</tt>. 935 936 @param array the array to convert 937 @param encoding the {@link java.nio.charset.Charset charset} encoding to use to 938 convert bytes into chars 939 **/ 940 public static CharBuffer arrayToCharBuffer(byte[] array, String encoding) 941 { 942 if ( (array == null) || (encoding == null)) 943 return null; 944 945 Charset cset = Charset.forName(encoding); 946 CharBuffer cbuf = cset.decode(ByteBuffer.wrap(array)); 947 return cbuf; 948 } 949 950 /** 951 Convenience method that delegates to {@link #arrayToCharBuffer(byte[], 952 String)} using UTF-8 encoding by default. 953 **/ 954 public static CharBuffer arrayToCharBuffer(byte[] array) 955 { 956 //String enc = IOUtil.getDefaultEncoding(); 957 //enc = (enc != null) ? enc : "ISO-8859-1"; 958 //UTF-8 is safer overall 959 return arrayToCharBuffer(array, "UTF-8"); 960 } 961 962 /** 963 Reads the entire InputStream and returns all read data as a 964 <tt>byte[]</tt>. If no data is available, returns <tt>null</tt>. 965 966 @param in the InputStream to read 967 @param block if <tt>true</tt>, this method will block until all 968 available data from the specified input stream 969 has been read. If <tt>false</tt>, this method will 970 read and return as much data as currently is available 971 is read without blocking. The available amount is 972 that returned by the available() method of the specified 973 input stream. 974 975 @throws NegativeArraySizeException if the specified input stream returns 976 a negative number for available() 977 **/ 978 public static byte[] inputStreamToByteArray(InputStream in, boolean block) 979 throws IOException 980 { 981 Argcheck.notnull(in, "InputStream was null"); 982 983 final BufferedInputStream bin = bufferStream(in); 984 985 if (! block) { 986 int buffer_size = bin.available(); 987 if (dbg) System.out.println("inputStreamToByteArray(), block=no, buffersize=" + buffer_size); 988 byte[] buf = new byte[buffer_size]; 989 int read = 0; 990 int pos = 0; 991 while (read < buffer_size) { 992 read += bin.read(buf, pos, buffer_size - read); 993 pos = read + 1; 994 } 995 if (dbg) System.out.println("inputStreamToByteArray(), returning buf=" + buf.length + " bytes"); 996 return buf; 997 } 998 999 //block 1000 int buffer_size = 1024; 1001 byte[] buf = new byte[buffer_size]; 1002 if (dbg) System.out.println("inputStreamToByteArray(), block=yes"); 1003 1004 final ByteArrayOutputStream bout = new ByteArrayOutputStream(buffer_size); 1005 int read = 0; 1006 while (true) { 1007 read = bin.read(buf, 0, buffer_size); 1008 if (read == -1) 1009 break; 1010 bout.write(buf, 0, read); 1011 } 1012 //if size() is 0, toByteArray returns an array of size 0. we 1013 //return null instead. 1014 if (bout.size() == 0) { 1015 return null; 1016 } 1017 return bout.toByteArray(); 1018 } 1019 1020 /** 1021 Calls inputStreamToByteArray(in, <tt>true</tt>) 1022 */ 1023 public static byte[] inputStreamToByteArray(InputStream in) throws IOException 1024 { 1025 return inputStreamToByteArray(in, true); 1026 } 1027 1028 1029 /** 1030 Reads the entire InputStream and returns all read data as a 1031 <tt>String</tt> (using the default platform encoding). If 1032 no data is available, returns <tt>null</tt>. The specified input 1033 stream is <u>not</u> closed. 1034 1035 @param in the InputStream to read 1036 @param block if <tt>true</tt>, this method will block until all 1037 available data from the specified input stream 1038 has been read. If <tt>false</tt>, this method will 1039 read and return as much data as currently is available 1040 is read without blocking. The available amount is 1041 that returned by the specified input stream. 1042 1043 @throws NegativeArraySizeException if the specified input stream returns 1044 a negative number for available() 1045 **/ 1046 public static String inputStreamToString(InputStream in, boolean block) 1047 throws IOException 1048 { 1049 final byte[] buf = inputStreamToByteArray(in, block); 1050 if (buf == null) 1051 return null; 1052 return new String(buf); 1053 } //~inputStreamToString 1054 1055 1056 1057 /** 1058 Reads the entire InputStream and returns all read data as a 1059 <tt>String</tt> (using the specified platform encoding). If 1060 no data is available, returns <tt>null</tt>. The specified input 1061 stream is <u>not</u> closed. 1062 <p> 1063 This method will block until all available data from the specified 1064 input stream has been read. 1065 1066 @param in the input stream to read 1067 @param encoding the {@link java.nio.charset.Charset} encoding name to use to 1068 convert bytes into chars 1069 */ 1070 public static String inputStreamToString(InputStream in, String encoding) 1071 throws IOException 1072 { 1073 final byte[] buf = inputStreamToByteArray(in, true); 1074 if (buf == null) 1075 return null; 1076 return new String(buf, encoding); 1077 } 1078 1079 1080 /** 1081 Calls inputStreamToString(in, <tt>true</tt>) 1082 */ 1083 public static String inputStreamToString(InputStream in) throws IOException 1084 { 1085 return inputStreamToString(in, true); 1086 } 1087 1088 1089 /** 1090 Convenience method to print the stack trace of an Exception (or Throwable) 1091 to a String (using the default platform encoding). (The <tt>getMessage()</tt> 1092 method of a Throwable does not print the entire stack trace). 1093 **/ 1094 public static String throwableToString(final Throwable e) 1095 { 1096 Argcheck.notnull(e, "The specified exception object was null"); 1097 String temp = null; 1098 final ByteArrayOutputStream bout = new ByteArrayOutputStream(768); 1099 final PrintStream pout = new PrintStream(bout); 1100 e.printStackTrace(pout); 1101 pout.flush(); 1102 temp = bout.toString(); 1103 pout.close(); 1104 return temp; 1105 } 1106 1107 /** 1108 Convenience method that returns the current execution stack trace as a String. 1109 (using the default platform encoding). 1110 **/ 1111 public static String stackTrace() 1112 { 1113 String temp = null; 1114 final ByteArrayOutputStream bout = new ByteArrayOutputStream(768); 1115 final PrintStream pout = new PrintStream(bout); 1116 pout.println("==================== Debug Stack Trace ======================"); 1117 new Exception().printStackTrace(pout); 1118 pout.println("============================================================="); 1119 pout.flush(); 1120 temp = bout.toString(); 1121 pout.close(); 1122 return temp; 1123 } 1124 1125 /** 1126 Calls {@link #fileSizeToString(long)} and truncates the 1127 size to fit in the specified number of digits. For example, 1128 a file size of <tt>4.455 KB</tt> and a length of 2 will 1129 return <tt>4.45 KB</tt> 1130 1131 @param filesize the size of the file in bytes 1132 @param length the max number of digits <b>after</b> 1133 the decimal point 1134 */ 1135 public static String fileSizeToString(long filesize, int length) 1136 { 1137 NumberFormat nf = NumberFormat.getInstance(); 1138 nf.setMaximumFractionDigits(length); 1139 return fileSizeToStringImpl(nf, filesize); 1140 } 1141 1142 1143 /** 1144 Converts the specified file size into a human readable description. 1145 Similar to the <tt>"--human-readable"</tt> flag found in various 1146 GNU programs. 1147 1148 @param filesize the size of the file in bytes 1149 */ 1150 public static String fileSizeToString(long filesize) 1151 { 1152 NumberFormat nf = NumberFormat.getInstance(); 1153 return fileSizeToStringImpl(nf, filesize); 1154 } 1155 1156 private static final String fileSizeToStringImpl( 1157 NumberFormat nf, long filesize) 1158 { 1159 StringBuffer buf = new StringBuffer(32); 1160 if (filesize > ONE_GB) 1161 buf.append(nf.format(filesize / ONE_GB)).append(" GB"); 1162 else if (filesize > ONE_MB) 1163 buf.append(nf.format( filesize / ONE_MB)).append(" MB"); 1164 else if (filesize > ONE_KB) 1165 buf.append(nf.format(filesize / ONE_KB)).append(" KB"); 1166 else 1167 buf.append(nf.format(filesize)).append(" bytes"); 1168 1169 return buf.toString(); 1170 } 1171 1172 /** 1173 Converts a string file size into a number denoting the equivalent bytes. 1174 For example: 1175 <pre> 1176 34K, 34KB --> 34 bytes 1177 34M, 34megabytes --> 34 * 1024 bytes 1178 </pre> 1179 Allows numbers to end with <tt>k..., m..., g...., b....</tt> or no suffix 1180 at all. Suffixes are case insensitive. 1181 */ 1182 public static long stringToFileSize(String str) 1183 { 1184 Argcheck.notnull(str, "the specified string was null"); 1185 1186 str = str.replace(" ",""); //remove all leading, trailing, embedded spaces 1187 1188 int pos = 0; //this is slighty easier than str.indexOf 1189 for (int n = 0; n < str.length(); n++) 1190 { 1191 char c = str.charAt(n); 1192 switch (c) { 1193 case 'g': case 'G': 1194 return Long.parseLong(str.substring(0,n)) * ONE_GB; 1195 case 'm': case 'M': 1196 return Long.parseLong(str.substring(0,n)) * ONE_MB; 1197 case 'k': case 'K': 1198 return Long.parseLong(str.substring(0,n)) * ONE_KB; 1199 case 'b': case 'B': 1200 return Long.parseLong(str.substring(0,n)); 1201 default: 1202 //nothing to do 1203 } 1204 } 1205 return Long.parseLong(str); 1206 } 1207 1208 /** 1209 Returns the SHA-1 hash of the specified byte buffer. 1210 <p> 1211 This method is <b>not</b> thread safe (as far as I can tell, since it uses 1212 <code>MessageDigest.getInstance</code>, but <i>if</i> that is thread safe, then 1213 this method is thread safe). The invoker should invoke it in a thread safe way. 1214 */ 1215 public static final String sha1hash(byte[] buf) throws NoSuchAlgorithmException 1216 { 1217 MessageDigest md = MessageDigest.getInstance("SHA-1"); 1218 byte[] digest = md.digest(buf); 1219 StringBuilder sb = new StringBuilder(); 1220 for (byte b: digest) 1221 { 1222 String hex = Integer.toHexString((int)0xff & b); 1223 if (hex.length()==1) sb.append("0"); 1224 sb.append(hex); 1225 } 1226 1227 return sb.toString(); 1228 } 1229 1230 1231 /** 1232 Get the InputStream for the specified resource (in the same directory as from where the specified class was loaded). 1233 Returns null if not found. 1234 */ 1235 public static InputStream getClassResource(Class clazz, String resource_name) throws IOException 1236 { 1237 if (clazz == null) { 1238 return null; 1239 } 1240 1241 if (resource_name.startsWith("/")) { 1242 resource_name = resource_name.substring(1,resource_name.length()); 1243 } 1244 InputStream in = clazz.getResourceAsStream(resource_name); 1245 return in; 1246 } 1247 1248 /** 1249 Usage: 1250 java IOUtil args where args are: 1251 -file full-path-to-file 1252 [for fileToString and other tests] 1253 1254 **/ 1255 public static void main(String[] args) throws Exception 1256 { 1257 Args myargs = new Args(args); 1258 String fts = myargs.getRequired("file"); 1259 new Test(fts); 1260 } 1261 1262 1263 /** 1264 Unit Test History 1265 <pre> 1266 Class Version Tester Status Notes 1267 1.1 hj still-testing limited testing only, more needs to be done 1268 </pre> 1269 */ 1270 static private class Test { 1271 Test(String fileToString) throws Exception 1272 { 1273 java.io.File sourcefile = new java.io.File("testsourcefile"); 1274 java.io.File sourcedir = new java.io.File("testsourcedir"); 1275 java.io.File destfile = new java.io.File("testdestfile"); 1276 java.io.File destdir = new java.io.File("testdestdir"); 1277 1278 String dir1 = "foo"; 1279 String dir2 = "foo" + FILE_SEP; 1280 String f = "f1"; 1281 //String r1 = IOUtil.makeFilePath(dir1,f); 1282 //String r2 = IOUtil.makeFilePath(dir2,f); 1283 for(long n = 2; n < 4000000001L; n *= 1000) { 1284 System.out.println("file size " + n + " = " 1285 + IOUtil.fileSizeToString(n)); 1286 } 1287 1288 System.out.println("Platform encoding, via file.encoding: " + System.getProperty("file.encoding")); 1289 System.out.println("Platform encoding: " + IOUtil.getDefaultEncoding()); 1290 System.out.println("--- fileToString('"+ fileToString + "') -----"); 1291 String filestr = fileToString(fileToString); 1292 System.out.println("file '" + fileToString + "' read into String"); 1293 System.out.println("String length = " + filestr.length()); 1294 System.out.println("String data = "); 1295 System.out.println(filestr); 1296 1297 List linelist = fileToLines(new File(fileToString)); 1298 System.out.println("--- fileToList('"+ fileToString + "') -----"); 1299 for (int n = 0; n < linelist.size(); n++) { 1300 System.out.println("[line:" + n + "]: " + linelist.get(n)); 1301 } 1302 1303 FileInputStream fin = new FileInputStream(fileToString); 1304 System.out.println("--- inputStreamToString('"+ fileToString + "') -----"); 1305 String str = inputStreamToString(fin, false); 1306 System.out.println(str); 1307 System.out.println(fileToString + ", size=" + str.length()); 1308 1309 Socket sock = new Socket("www.yahoo.com", 80); 1310 System.out.println("--- inputStreamToString('"+ sock + "') -----"); 1311 InputStream sin = sock.getInputStream(); 1312 OutputStream sout = sock.getOutputStream(); 1313 sout.write("GET /index.html\n\n".getBytes()); 1314 sout.flush(); 1315 System.out.println(inputStreamToString(sin, true)); 1316 1317 Object obj = new Object(); 1318 System.out.println("--- getClassResource('"+ obj.getClass() + ", 'String.class') -----"); 1319 System.out.println(getClassResource(obj.getClass(), "String")); 1320 1321 System.out.println("--- getClassResource('"+ IOUtil.class + ", 'Args.class') -----"); 1322 System.out.println(inputStreamToString(getClassResource(IOUtil.class, "compile.sh"))); 1323 1324 copyFileTest(); 1325 } //~constructor 1326 1327 void copyFileTest() throws IOException { 1328 File source = new File("/tmp/foo"); 1329 File dest = new File("/tmp/bar"); 1330 System.out.println("Copy test: " + source + " to " + dest); 1331 copyDirectory(source, dest); 1332 } 1333 } //~Test 1334 1335 } //~ IOUtil 1336 1337 1338 1339 1340