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 006package fc.io; 007 008import fc.util.*; 009import java.io.*; 010import java.net.*; 011import java.nio.*; 012import java.nio.channels.*; 013import java.nio.charset.*; 014import java.util.*; 015import java.util.regex.*; 016import java.text.*; 017import java.security.*; 018 019/** Misc. IO utilities 020 021@author hursh jain 022@version 1.1 023**/ 024public final class IOUtil 025{ 026//for internal class debugging at development time 027private static final boolean dbg = false; 028 029/** System.getProperty("file.separator") for convenience **/ 030public static final String FILE_SEP = System.getProperty("file.separator"); 031 032/** System.getProperty("path.separator") for convenience **/ 033public static final String PATH_SEP = System.getProperty("path.separator"); 034 035/** System.getProperty("line.separator") for convenience **/ 036public static final String LINE_SEP = System.getProperty("line.separator"); 037 038public static final int FILECOPY_OVERWRITE = 0; 039public static final int FILECOPY_NO_OVERWRITE = 1; 040 041/** 042Ignores a directory copy command if the destination directory already 043exists. 044**/ 045public static final int DIRCOPY_NO_OVERWRITE = 2; 046 047/** 048Copies the existing directory and overwrites any files with 049the same name in the destination directory. Files/directories that 050exist in the destination but not in the source directory are left 051untouched. 052**/ 053public static final int DIRCOPY_ADD_OR_OVERWRITE = 3; 054 055/** 056Copies the existing directory. If the destination directory already 057exists, the entire target directory is first deleted and then 058the specified directory is copied to the destination 059**/ 060public static final int DIRCOPY_DELETE_AND_WRITE = 4; 061 062 063/** number of bytes contained in a kilobyte */ 064public static final int ONE_KB = 1024; 065 066/** number of bytes contained in a megabyte. */ 067public static final int ONE_MB = ONE_KB * ONE_KB; 068 069/** number of bytes contained in a gigabyte. */ 070public static final int ONE_GB = ONE_KB * ONE_MB; 071 072/** number of bytes equal to 2^32 **/ 073public static final long FOUR_GB = ONE_GB * 4L; 074 075/** 076Beeps by writing the beep control code to System.out 077*/ 078public static void beep() 079 { 080 System.out.print("\007"); 081 System.out.flush(); 082 } 083 084/** 085Copies source file (not directory) to destination. If the destination 086already exists, then no copy is made (that is, the destination is 087<b>not</b> overwritten and this method returns silently (an Exception 088is 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*/ 094public static boolean copyFile(File source, File dest) throws FileNotFoundException, IOException 095 { 096 return copyFile(source, dest, IOUtil.FILECOPY_NO_OVERWRITE); 097 } 098 099/** 100Copies the source file (not directory) to the specified 101destination file or directory. If a directory is specified as 102the destination, then the source file is copied into that 103directory. To specify the action when a file with the same name 104already exists in the specified directory, use the appropriate 105{@link #FILECOPY_OVERWRITE} or {@link #FILECOPY_NO_OVERWRITE} 106flags. 107If a file is specified as the destination, then the source file 108is copied to that file. If the specified file exists already 109then specify the appropriate overwrite flag. 110<p> 111Try to use absolute path names for files and directories. Relative 112path names can be relative either to user.dir or where the jvm was invoked 113or some platform/jvm dependent place, so don't rely on relative paths. 114Copying, moving or working with files is tricky in java. Similar 115behavior is not defined for all platforms. For example, in WindowsNT/java1.2, 116aliases cannot be opened/resolved and hence cannot be copied via 117FileInput/Output streams. This sort of thing is best left to 118JNI calls native code. 119<p> 120This method returns <tt>true</tt> if the directory was copied 121successfully, <tt>false</tt> otherwise. (for example, <tt>false</tt> 122will be returned when the copy mode is not to overwrite but the 123target 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*/ 129public 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/** 164Calls {@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*/ 171public static boolean copyDirectory(File source, File dest) throws FileNotFoundException, IOException 172 { 173 return copyDirectory(source, dest, IOUtil.DIRCOPY_ADD_OR_OVERWRITE); 174 } 175 176/** 177Copies the source directory and all it's contents to the specified 178destination directory. A directory must be specified both for the source 179and the destination. 180<p> 181To handle cases where the destination directory already exists 182use the appropriate {@link #DIRCOPY_NO_OVERWRITE} 183{@link #DIRCOPY_DELETE_AND_WRITE} and 184{@link #DIRCOPY_ADD_OR_OVERWRITE} flags. 185<p> 186Try to use absolute path names for files and directories. Relative 187path names can be relative either to user.dir or where the jvm was invoked 188or 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 190behavior is not defined for all platforms. For example, in WindowsNT/java1.2, 191aliases cannot be opened/resolved and hence cannot be copied via 192FileInput/Output streams. This sort of thing is best left to 193JNI calls to POSIX or to platform specific code. 194<p> 195This method returns <tt>true</tt> if the directory was copied 196successfully, <tt>false</tt> otherwise. (for example, <tt>false</tt> 197will be returned when the copy mode is not to overwrite but the 198target 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*/ 204public static boolean copyDirectory(File source, File dest, int copyflag) 205throws 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/** 268Copies all data from the specific input stream to the specified output stream. 269Closes both streams after it is finished. 270 271@param in the InputStream 272@param out the OutputStream 273**/ 274public 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/** 290Alphabetizes the specified list by the filename. Only the 291filename is considered and not the path name (if any). The 292collection 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> 298Any/all of these can be contained in the specified list at the 299same 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 301list is sorted by the default {@link String#compareTo(String)} implementation 302of <tt>String</tt>. 303 304@param list the list to be sorted 305**/ 306public 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/** 325This method recursively removes a specified file or recursively 326removes 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 328directory is not empty. 329<p> 330Internally, this method delegates to {@link File#delete}, so if 331{@link File#delete} follows sym links, then so will this method. 332Be 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**/ 338public 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/** 371Gets the total size for a directory and all of it's contents. If 372the specified argument is a regular file, returns the size of that 373file itself. 374 375@param dir the target dir 376@return the directory or file size in bytes 377**/ 378public 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/** 399Buffers and returns the specified InputStream, if it is not already buffered. 400Does 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*/ 405public 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/** 419Buffers and returns the specified OutputStream, if it is not already buffered. 420Does 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**/ 426public 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 439public 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 452public 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 461public 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 470public 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/** 484Convenience method to print the contents of a java.util.Property object 485to a String (using the default platform encoding). 486 487**/ 488public 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 500private static String defaultEncoding = null; 501 502/** 503Returns the default encoding used by the current platform. Returns 504<tt>null</tt> is the default encoding cannot be determined. 505**/ 506public 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/** 526Returns the contents of an entire file as a List of individual lines. If the specified 527file 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*/ 534public 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/** 568Returns the contents of an entire file as a List of individual lines. If the specified 569file 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*/ 576public 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/** 582Returns the contents of an entire file as a List of individual lines. Empty lines are trimmed 583and 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*/ 587public static List fileToLines(File file) throws IOException 588 { 589 return fileToLines(file, true, "#|//"); 590 } 591 592/** 593Returns the contents of an entire file as a List of individual lines. Empty lines are trimmed 594and 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*/ 598public static List fileToLines(String filename) throws IOException 599 { 600 return fileToLines(new File(filename)); 601 } 602 603/** 604Returns the contents of an entire file as a List of individual lines. Empty lines are trimmed 605and lines beginning with the following characters are ignored: <tt>#</tt> and <tt>//</tt> 606 607@param in the input stream to be read 608*/ 609public static List fileToLines(InputStream in) throws IOException 610 { 611 return fileToLines(in, true, "#|//"); 612 } 613 614/** 615Returns the contents of an entire file as a String. If the specified 616file does not exist returns <tt>null</tt>. Files that exist but have no 617content return an empty String. 618<p> 619<b>Note 1:</b> Due to jdk1.4 brain damage, this method is limited to files 620less than 2^32 bytes. If the specified file is greater than 2^32 bytes, 621an <tt>IOException</tt> will be thrown. 622<br> 623<b>Note 2:</b> The files is converted into a String using an encoding 624that is determined programmatically (from the filesystem). This may 625not be totally reliable but there is no way around this because JDK 1.4 626provides <b>no</b> way to easily get the default platform encoding. 627Uses the <tt>ISO_8859_1</tt> encoding as a fallback measure, if the 628default 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**/ 633public 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/** 661Returns the contents of an entire file as a String. If the specified 662file does not exist returns <tt>null</tt>. Files that exist but have no 663content return an empty String. 664<p> 665<b>Note 1:</b> Due to jdk1.4 brain damage, this method is limited to files 666less than 2^32 bytes. If the specified file is greater than 2^32 bytes, 667an <tt>IOException</tt> will be thrown. 668<br> 669<b>Note 2:</b> The files is converted into a String using an encoding 670that is determined programmatically (from the filesystem). This may 671not be totally reliable but there is no way around this because JDK 1.4 672provides <b>no</b> way to easily get the default platform encoding. 673Uses the <tt>ISO_8859_1</tt> encoding as a fallback measure, if the 674default 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**/ 679public static String fileToString(String filename, boolean trim) throws IOException 680 { 681 return fileToString(new File(filename), trim); 682 } 683 684/** 685Calls {@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**/ 690public static String fileToString(String filename) throws IOException 691 { 692 return fileToString(filename, false); 693 } 694 695/** 696Calls {@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**/ 701public static String fileToString(File file) throws IOException 702 { 703 return fileToString(file, false); 704 } 705 706 707/** 708Returns the contents of an entire file as a <tt>byte[]</tt>. If the specified 709file does not exist returns <tt>null</tt>. 710<p> 711<b>Note 1:</b> Since java arrays cannot be greater than 2^32 elements, 712this method is limited to files less than or equal to 2^32 bytes. If 713the specified file is greater than 2^32 bytes, an <tt>IOException</tt> 714will 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**/ 719public static byte[] fileToByteArray(String filename) throws IOException 720 { 721 return fileToByteArray(new File(filename)); 722 } 723 724/** 725Returns the contents of an entire file as a <tt>byte[]</tt>. If the specified 726file does not exist returns <tt>null</tt>. 727<p> 728<b>Note 1:</b> Since java arrays cannot be greater than 2^32 elements, 729this method is limited to files less than or equal to 2^32 bytes. If 730the specified file is greater than 2^32 bytes, an <tt>IOException</tt> 731will be thrown. 732 733@param file the file to be read 734@return byte[] contains the bytes for that file 735**/ 736public 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/** 783Returns the contents of an entire file as a ByteBuffer backed by mapping the 784file to memory. If the specified file does not exist returns <tt>null</tt>. 785Mapped 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 787java.nio.MappedByteBuffer} documentation about concurrent modification or 788deletion of files that are mapped into memory. 789<p> 790The ByteBuffer returned by this method will have <tt>{@link 791ByteBuffer#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 794ByteBuffers cannot be greater than this size. If the specified file is greater 795than 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**/ 800public 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/** 834Returns the contents of an entire file as a <tt>char[]</tt>. If the specified 835file does not exist returns <tt>null</tt>. 836<p> 837<b>Note 1:</b> Since java arrays cannot be greater than 2^32 elements, 838this method is limited to files less than or equal to 2^32 bytes. If 839the specified file is greater than 2^32 bytes, an <tt>IOException</tt> 840will 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**/ 847public static char[] fileToCharArray(File file, String encoding) 848throws 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/* 903Reads the entire Stream and returns all read data as a 904<tt>char[]</tt>. If no data is available, returns an empty char[]. 905*/ 906public static char[] readerToCharArray(Reader reader) 907throws 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/** 929Converts the specified byte array into a CharBuffer using the specified 930encoding. The returned CharBuffer can be directly used in statements such 931as <tt>System.out.println</tt> to print it's contents, 932<p> 933This method returns <tt>null</tt> if the specified array is <tt>null</tt> 934or 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**/ 940public 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/** 951Convenience method that delegates to {@link #arrayToCharBuffer(byte[], 952String)} using UTF-8 encoding by default. 953**/ 954public 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/** 963Reads 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**/ 978public static byte[] inputStreamToByteArray(InputStream in, boolean block) 979throws 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/** 1021Calls inputStreamToByteArray(in, <tt>true</tt>) 1022*/ 1023public static byte[] inputStreamToByteArray(InputStream in) throws IOException 1024 { 1025 return inputStreamToByteArray(in, true); 1026 } 1027 1028 1029/** 1030Reads the entire InputStream and returns all read data as a 1031<tt>String</tt> (using the default platform encoding). If 1032no data is available, returns <tt>null</tt>. The specified input 1033stream 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**/ 1046public static String inputStreamToString(InputStream in, boolean block) 1047throws 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/** 1058Reads the entire InputStream and returns all read data as a 1059<tt>String</tt> (using the specified platform encoding). If 1060no data is available, returns <tt>null</tt>. The specified input 1061stream is <u>not</u> closed. 1062<p> 1063This method will block until all available data from the specified 1064input 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*/ 1070public static String inputStreamToString(InputStream in, String encoding) 1071throws 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/** 1081Calls inputStreamToString(in, <tt>true</tt>) 1082*/ 1083public static String inputStreamToString(InputStream in) throws IOException 1084 { 1085 return inputStreamToString(in, true); 1086 } 1087 1088 1089/** 1090Convenience method to print the stack trace of an Exception (or Throwable) 1091to a String (using the default platform encoding). (The <tt>getMessage()</tt> 1092method of a Throwable does not print the entire stack trace). 1093**/ 1094public 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/** 1108Convenience method that returns the current execution stack trace as a String. 1109(using the default platform encoding). 1110**/ 1111public 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/** 1126Calls {@link #fileSizeToString(long)} and truncates the 1127size to fit in the specified number of digits. For example, 1128a file size of <tt>4.455 KB</tt> and a length of 2 will 1129return <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*/ 1135public 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/** 1144Converts the specified file size into a human readable description. 1145Similar to the <tt>"--human-readable"</tt> flag found in various 1146GNU programs. 1147 1148@param filesize the size of the file in bytes 1149*/ 1150public static String fileSizeToString(long filesize) 1151 { 1152 NumberFormat nf = NumberFormat.getInstance(); 1153 return fileSizeToStringImpl(nf, filesize); 1154 } 1155 1156private 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/** 1173Converts a string file size into a number denoting the equivalent bytes. 1174For example: 1175<pre> 117634K, 34KB --> 34 bytes 117734M, 34megabytes --> 34 * 1024 bytes 1178</pre> 1179Allows numbers to end with <tt>k..., m..., g...., b....</tt> or no suffix 1180at all. Suffixes are case insensitive. 1181*/ 1182public 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/** 1209Returns the SHA-1 hash of the specified byte buffer. 1210<p> 1211This 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 1213this method is thread safe). The invoker should invoke it in a thread safe way. 1214*/ 1215public 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 */ 1235public 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/** 1249Usage: 1250java IOUtil args where args are: 1251 -file full-path-to-file 1252 [for fileToString and other tests] 1253 1254**/ 1255public 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/** 1264Unit Test History 1265<pre> 1266Class Version Tester Status Notes 12671.1 hj still-testing limited testing only, more needs to be done 1268</pre> 1269*/ 1270static private class Test { 1271Test(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