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. Uses UTF-8 encoding to read the file. 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 871 int fsize = (int) longfsize; 872 char[] buf = readerToCharArray(reader, (int)fsize); //buf_fize (fsize) can be greater than chars read 873 874 /* 875 Can't do it this way - we create a buf of file size bytes but the number of characters 876 read can be less than file size (because of various encodings). This way returns a buf 877 with NULL characters at the end, so either have to trim the buf or just use a char array 878 writer 879 */ 880 /* 881 buf = new char[fsize]; 882 try { 883 int read, pos = 0; 884 while (pos < fsize) 885 { 886 //Usually, this will read everything the first time 887 read = reader.read(buf, pos, fsize - pos); 888 pos += read; 889 if (read < 0) { //EOF 890 break; 891 } 892 } 893 894 if (dbg && pos != fsize) { 895 System.out.println("File: " + file.getAbsolutePath() 896 + " has bytes [" + fsize + "], read [" + pos + "] chars\n" 897 + "This is expected since byte->char will loose characters for non-ascii files. Currently using [" 898 + encoding + "] to read this file" 899 ); 900 } 901 } 902 903 //at this point buf will have pos characters, not fsize characters. 904 905 finally { 906 fin.close(); 907 } 908 */ 909 910 return buf; 911 } 912 913/* 914Reads the entire Stream and returns all read data as a 915<tt>char[]</tt>. If no data is available, returns an empty char[]. 916*/ 917public static char[] readerToCharArray(Reader reader) 918throws IOException 919 { 920 Argcheck.notnull(reader); 921 922 return readerToCharArray(reader, 1024); 923 } 924 925 926/* 927Reads the entire Stream and returns all read data as a 928<tt>char[]</tt>. If no data is available, returns an empty char[]. 929*/ 930public static char[] readerToCharArray(Reader reader, int buffer_size) 931throws IOException 932 { 933 Argcheck.notnull(reader); 934 935 char[] buf = new char[buffer_size]; 936 if (dbg) System.out.println("readerToCharArray(), block=yes"); 937 938 final CharArrayWriter cout = new CharArrayWriter(buffer_size); 939 int read = 0; 940 while (true) { 941 read = reader.read(buf, 0, buffer_size); 942 if (read == -1) 943 break; 944 cout.write(buf, 0, read); 945 } 946 947 return cout.toCharArray(); 948 } 949 950 951 952 953/** 954Converts the specified byte array into a CharBuffer using the specified 955encoding. The returned CharBuffer can be directly used in statements such 956as <tt>System.out.println</tt> to print it's contents, 957<p> 958This method returns <tt>null</tt> if the specified array is <tt>null</tt> 959or if the specified encoding is <tt>null</tt>. 960 961@param array the array to convert 962@param encoding the {@link java.nio.charset.Charset charset} encoding to use to 963 convert bytes into chars 964**/ 965public static CharBuffer arrayToCharBuffer(byte[] array, String encoding) 966 { 967 if ( (array == null) || (encoding == null)) 968 return null; 969 970 Charset cset = Charset.forName(encoding); 971 CharBuffer cbuf = cset.decode(ByteBuffer.wrap(array)); 972 return cbuf; 973 } 974 975/** 976Convenience method that delegates to {@link #arrayToCharBuffer(byte[], 977String)} using UTF-8 encoding by default. 978**/ 979public static CharBuffer arrayToCharBuffer(byte[] array) 980 { 981 //String enc = IOUtil.getDefaultEncoding(); 982 //enc = (enc != null) ? enc : "ISO-8859-1"; 983 //UTF-8 is safer overall 984 return arrayToCharBuffer(array, "UTF-8"); 985 } 986 987/** 988Reads the entire InputStream and returns all read data as a 989<tt>byte[]</tt>. If no data is available, returns <tt>null</tt>. 990 991@param in the InputStream to read 992@param block if <tt>true</tt>, this method will block until all 993 available data from the specified input stream 994 has been read. If <tt>false</tt>, this method will 995 read and return as much data as currently is available 996 is read without blocking. The available amount is 997 that returned by the available() method of the specified 998 input stream. 999 1000@throws NegativeArraySizeException if the specified input stream returns 1001 a negative number for available() 1002**/ 1003public static byte[] inputStreamToByteArray(InputStream in, boolean block) 1004throws IOException 1005 { 1006 Argcheck.notnull(in, "InputStream was null"); 1007 1008 final BufferedInputStream bin = bufferStream(in); 1009 1010 if (! block) { 1011 int buffer_size = bin.available(); 1012 if (dbg) System.out.println("inputStreamToByteArray(), block=no, buffersize=" + buffer_size); 1013 byte[] buf = new byte[buffer_size]; 1014 int read = 0; 1015 int pos = 0; 1016 while (read < buffer_size) { 1017 read += bin.read(buf, pos, buffer_size - read); 1018 pos = read + 1; 1019 } 1020 if (dbg) System.out.println("inputStreamToByteArray(), returning buf=" + buf.length + " bytes"); 1021 return buf; 1022 } 1023 1024 //block 1025 int buffer_size = 1024; 1026 byte[] buf = new byte[buffer_size]; 1027 if (dbg) System.out.println("inputStreamToByteArray(), block=yes"); 1028 1029 final ByteArrayOutputStream bout = new ByteArrayOutputStream(buffer_size); 1030 int read = 0; 1031 while (true) { 1032 read = bin.read(buf, 0, buffer_size); 1033 if (read == -1) 1034 break; 1035 bout.write(buf, 0, read); 1036 } 1037 //if size() is 0, toByteArray returns an array of size 0. we 1038 //return null instead. 1039 if (bout.size() == 0) { 1040 return null; 1041 } 1042 return bout.toByteArray(); 1043 } 1044 1045/** 1046Calls inputStreamToByteArray(in, <tt>true</tt>) 1047*/ 1048public static byte[] inputStreamToByteArray(InputStream in) throws IOException 1049 { 1050 return inputStreamToByteArray(in, true); 1051 } 1052 1053 1054/** 1055Reads the entire InputStream and returns all read data as a <tt>String</tt>, using UTF-8 1056encoding. If no data is available, returns <tt>null</tt>. The specified input stream is 1057<u>not</u> closed. 1058 1059@param in the InputStream to read 1060@param block if <tt>true</tt>, this method will block until all 1061 available data from the specified input stream 1062 has been read. If <tt>false</tt>, this method will 1063 read and return as much data as currently is available 1064 is read without blocking. The available amount is 1065 that returned by the specified input stream. 1066 1067@throws NegativeArraySizeException if the specified input stream returns 1068 a negative number for available() 1069**/ 1070public static String inputStreamToString(InputStream in, boolean block) 1071throws IOException 1072 { 1073 final byte[] buf = inputStreamToByteArray(in, block); 1074 if (buf == null) { 1075 return null; 1076 } 1077 return new String(buf, "UTF-8"); 1078 } //~inputStreamToString 1079 1080 1081 1082/** 1083Reads the entire InputStream and returns all read data as a 1084<tt>String</tt> (using the specified platform encoding). If 1085no data is available, returns <tt>null</tt>. The specified input 1086stream is <u>not</u> closed. 1087<p> 1088This method will block until all available data from the specified 1089input stream has been read. 1090 1091@param in the input stream to read 1092@param encoding the {@link java.nio.charset.Charset} encoding name to use to 1093 convert bytes into chars 1094*/ 1095public static String inputStreamToString(InputStream in, String encoding) 1096throws IOException 1097 { 1098 final byte[] buf = inputStreamToByteArray(in, true); 1099 if (buf == null) { 1100 return null; 1101 } 1102 return new String(buf, encoding); 1103 } 1104 1105 1106/** 1107Calls inputStreamToString(in, <tt>true</tt>) 1108*/ 1109public static String inputStreamToString(InputStream in) throws IOException 1110 { 1111 return inputStreamToString(in, true); 1112 } 1113 1114 1115/** 1116Convenience method to print the stack trace of an Exception (or Throwable) 1117to a String (using the default platform encoding). (The <tt>getMessage()</tt> 1118method of a Throwable does not print the entire stack trace). 1119**/ 1120public static String throwableToString(final Throwable e) 1121 { 1122 Argcheck.notnull(e, "The specified exception object was null"); 1123 String temp = null; 1124 final ByteArrayOutputStream bout = new ByteArrayOutputStream(768); 1125 final PrintStream pout = new PrintStream(bout); 1126 e.printStackTrace(pout); 1127 pout.flush(); 1128 temp = bout.toString(); 1129 pout.close(); 1130 return temp; 1131 } 1132 1133/** 1134Convenience method that returns the current execution stack trace as a String. 1135(using the default platform encoding). 1136**/ 1137public static String stackTrace() 1138 { 1139 String temp = null; 1140 final ByteArrayOutputStream bout = new ByteArrayOutputStream(768); 1141 final PrintStream pout = new PrintStream(bout); 1142 pout.println("==================== Debug Stack Trace ======================"); 1143 new Exception().printStackTrace(pout); 1144 pout.println("============================================================="); 1145 pout.flush(); 1146 temp = bout.toString(); 1147 pout.close(); 1148 return temp; 1149 } 1150 1151/** 1152Calls {@link #fileSizeToString(long)} and truncates the 1153size to fit in the specified number of digits. For example, 1154a file size of <tt>4.455 KB</tt> and a length of 2 will 1155return <tt>4.45 KB</tt> 1156 1157@param filesize the size of the file in bytes 1158@param length the max number of digits <b>after</b> 1159 the decimal point 1160*/ 1161public static String fileSizeToString(long filesize, int length) 1162 { 1163 NumberFormat nf = NumberFormat.getInstance(); 1164 nf.setMaximumFractionDigits(length); 1165 return fileSizeToStringImpl(nf, filesize); 1166 } 1167 1168 1169/** 1170Converts the specified file size into a human readable description. 1171Similar to the <tt>"--human-readable"</tt> flag found in various 1172GNU programs. 1173 1174@param filesize the size of the file in bytes 1175*/ 1176public static String fileSizeToString(long filesize) 1177 { 1178 NumberFormat nf = NumberFormat.getInstance(); 1179 return fileSizeToStringImpl(nf, filesize); 1180 } 1181 1182private static final String fileSizeToStringImpl( 1183 NumberFormat nf, long filesize) 1184 { 1185 StringBuffer buf = new StringBuffer(32); 1186 if (filesize > ONE_GB) 1187 buf.append(nf.format(filesize / ONE_GB)).append(" GB"); 1188 else if (filesize > ONE_MB) 1189 buf.append(nf.format( filesize / ONE_MB)).append(" MB"); 1190 else if (filesize > ONE_KB) 1191 buf.append(nf.format(filesize / ONE_KB)).append(" KB"); 1192 else 1193 buf.append(nf.format(filesize)).append(" bytes"); 1194 1195 return buf.toString(); 1196 } 1197 1198/** 1199Converts a string file size into a number denoting the equivalent bytes. 1200For example: 1201<pre> 120234K, 34KB --> 34 bytes 120334M, 34megabytes --> 34 * 1024 bytes 1204</pre> 1205Allows numbers to end with <tt>k..., m..., g...., b....</tt> or no suffix 1206at all. Suffixes are case insensitive. 1207*/ 1208public static long stringToFileSize(String str) 1209 { 1210 Argcheck.notnull(str, "the specified string was null"); 1211 1212 str = str.replace(" ",""); //remove all leading, trailing, embedded spaces 1213 1214 int pos = 0; //this is slighty easier than str.indexOf 1215 for (int n = 0; n < str.length(); n++) 1216 { 1217 char c = str.charAt(n); 1218 switch (c) { 1219 case 'g': case 'G': 1220 return Long.parseLong(str.substring(0,n)) * ONE_GB; 1221 case 'm': case 'M': 1222 return Long.parseLong(str.substring(0,n)) * ONE_MB; 1223 case 'k': case 'K': 1224 return Long.parseLong(str.substring(0,n)) * ONE_KB; 1225 case 'b': case 'B': 1226 return Long.parseLong(str.substring(0,n)); 1227 default: 1228 //nothing to do 1229 } 1230 } 1231 return Long.parseLong(str); 1232 } 1233 1234/** 1235Returns the SHA-1 hash of the specified byte buffer. 1236<p> 1237This method is <b>not</b> thread safe (as far as I can tell, since it uses 1238<code>MessageDigest.getInstance</code>, but <i>if</i> that is thread safe, then 1239this method is thread safe). The invoker should invoke it in a thread safe way. 1240*/ 1241public static final String sha1hash(byte[] buf) throws NoSuchAlgorithmException 1242 { 1243 MessageDigest md = MessageDigest.getInstance("SHA-1"); 1244 byte[] digest = md.digest(buf); 1245 StringBuilder sb = new StringBuilder(); 1246 for (byte b: digest) 1247 { 1248 String hex = Integer.toHexString((int)0xff & b); 1249 if (hex.length()==1) sb.append("0"); 1250 sb.append(hex); 1251 } 1252 1253 return sb.toString(); 1254 } 1255 1256 1257/** 1258 Get the InputStream for the specified resource (in the same directory as from where the specified class was loaded). 1259 Returns null if not found. 1260 */ 1261public static InputStream getClassResource(Class clazz, String resource_name) throws IOException 1262 { 1263 if (clazz == null) { 1264 return null; 1265 } 1266 1267 if (resource_name.startsWith("/")) { 1268 resource_name = resource_name.substring(1,resource_name.length()); 1269 } 1270 InputStream in = clazz.getResourceAsStream(resource_name); 1271 return in; 1272 } 1273 1274/** 1275Usage: 1276java IOUtil args where args are: 1277 -file full-path-to-file 1278 [for fileToString and other tests] 1279 1280**/ 1281public static void main(String[] args) throws Exception 1282 { 1283 Args myargs = new Args(args); 1284 String fts = myargs.getRequired("file"); 1285 new Test(fts); 1286 } 1287 1288 1289/** 1290Unit Test History 1291<pre> 1292Class Version Tester Status Notes 12931.1 hj still-testing limited testing only, more needs to be done 1294</pre> 1295*/ 1296static private class Test { 1297Test(String fileToString) throws Exception 1298 { 1299 java.io.File sourcefile = new java.io.File("testsourcefile"); 1300 java.io.File sourcedir = new java.io.File("testsourcedir"); 1301 java.io.File destfile = new java.io.File("testdestfile"); 1302 java.io.File destdir = new java.io.File("testdestdir"); 1303 1304 String dir1 = "foo"; 1305 String dir2 = "foo" + FILE_SEP; 1306 String f = "f1"; 1307 //String r1 = IOUtil.makeFilePath(dir1,f); 1308 //String r2 = IOUtil.makeFilePath(dir2,f); 1309 for(long n = 2; n < 4000000001L; n *= 1000) { 1310 System.out.println("file size " + n + " = " 1311 + IOUtil.fileSizeToString(n)); 1312 } 1313 1314 System.out.println("Platform encoding, via file.encoding: " + System.getProperty("file.encoding")); 1315 System.out.println("Platform encoding: " + IOUtil.getDefaultEncoding()); 1316 System.out.println("--- fileToString('"+ fileToString + "') -----"); 1317 String filestr = fileToString(fileToString); 1318 System.out.println("file '" + fileToString + "' read into String"); 1319 System.out.println("String length = " + filestr.length()); 1320 System.out.println("String data = "); 1321 System.out.println(filestr); 1322 1323 List linelist = fileToLines(new File(fileToString)); 1324 System.out.println("--- fileToList('"+ fileToString + "') -----"); 1325 for (int n = 0; n < linelist.size(); n++) { 1326 System.out.println("[line:" + n + "]: " + linelist.get(n)); 1327 } 1328 1329 FileInputStream fin = new FileInputStream(fileToString); 1330 System.out.println("--- inputStreamToString('"+ fileToString + "') -----"); 1331 String str = inputStreamToString(fin, false); 1332 System.out.println(str); 1333 System.out.println(fileToString + ", size=" + str.length()); 1334 1335 Socket sock = new Socket("www.yahoo.com", 80); 1336 System.out.println("--- inputStreamToString('"+ sock + "') -----"); 1337 InputStream sin = sock.getInputStream(); 1338 OutputStream sout = sock.getOutputStream(); 1339 sout.write("GET /index.html\n\n".getBytes()); 1340 sout.flush(); 1341 System.out.println(inputStreamToString(sin, true)); 1342 1343 Object obj = new Object(); 1344 System.out.println("--- getClassResource('"+ obj.getClass() + ", 'String.class') -----"); 1345 System.out.println(getClassResource(obj.getClass(), "String")); 1346 1347 System.out.println("--- getClassResource('"+ IOUtil.class + ", 'Args.class') -----"); 1348 System.out.println(inputStreamToString(getClassResource(IOUtil.class, "compile.sh"))); 1349 1350 copyFileTest(); 1351 } //~constructor 1352 1353 void copyFileTest() throws IOException { 1354 File source = new File("/tmp/foo"); 1355 File dest = new File("/tmp/bar"); 1356 System.out.println("Copy test: " + source + " to " + dest); 1357 copyDirectory(source, dest); 1358 } 1359} //~Test 1360 1361} //~ IOUtil 1362 1363 1364 1365 1366