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 source 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 <code>true</code> if the directory was copied 121successfully, <code>false</code> otherwise. (for example, <code>false</code> 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 source 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 <code>true</code> if the directory was copied 196successfully, <code>false</code> otherwise. (for example, <code>false</code> 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 289public static void saveStreamToFile(InputStream in, File f) 290 throws IOException 291 { 292 copyStream(in, new FileOutputStream(f)); 293 } 294 295 296/** 297Alphabetizes the specified list by the filename. Only the 298filename is considered and not the path name (if any). The 299collection should contain one of: 300<ul> 301 <li> <code>java.io.File</code> 302 <li> <code>String[]</code> 303 <li> <code>Object</code> 304</ul> 305Any/all of these can be contained in the specified list at the 306same time. If a <code>String[]</code> is found, the <b>0</b>th element 307(i.e., (String[] foo)[0]) is used for comparison purposes. The 308list is sorted by the default {@link String#compareTo(String)} implementation 309of <code>String</code>. 310 311@param c the list to be sorted 312**/ 313public static void sortByFileName(List c) { 314 Collections.sort(c, new Comparator() { 315 public int compare(Object o1, Object o2) { 316 return getStr(o1).compareTo(getStr(o2)); 317 } 318 private String getStr(Object o) { 319 String str = null; 320 if ( o instanceof File ) 321 str = ((File)o).getName(); 322 else if (o instanceof String) 323 str = (String) o; 324 else 325 str = (o!=null)? o.toString() : null; 326 return str; 327 } 328 }); 329 } 330 331/** 332This method recursively removes a specified file or recursively 333removes a specified directory. It is needed (as of JDK 1.4) because 334{@link File#delete} lacks the ability to delete a directory if the 335directory is not empty. 336<p> 337Internally, this method delegates to {@link File#delete}, so if 338{@link File#delete} follows sym links, then so will this method. 339Be careful ! 340 341@param f the file or directory to be removed 342@return <code>true</code> on success, <code>false</code> otherwise. Also returns false if 343 the specified file or directory does not exist. 344**/ 345public static boolean deepDelete(File f) 346 { 347 Argcheck.notnull(f, "File was null"); 348 if (dbg) System.out.println("deepDelete(): deleting: " + f); 349 350 boolean ok = true; 351 352 if (! f.exists()) 353 return false; 354 355 if (f.isFile()) { 356 ok = f.delete(); 357 return ok; 358 } 359 360 //f is a directory 361 File[] files = f.listFiles(); //does not return "." or ".." 362 boolean subok = false; 363 364 //1. delete sub directories 365 for (int n = 0; n < files.length; n++) { 366 subok = deepDelete(files[n]); 367 if (! subok) ok = false; 368 } 369 370 //2. delete current directory 371 subok = f.delete(); 372 if (! subok) ok = false; 373 374 return ok; 375 } 376 377/** 378Gets the total size for a directory and all of it's contents. If 379the specified argument is a regular file, returns the size of that 380file itself. 381 382@param dir the target dir 383@return the directory or file size in bytes 384**/ 385public static long dirSize(File dir) 386 { 387 Argcheck.notnull(dir, "File was null"); 388 long size = 0; 389 390 if (! dir.exists()) 391 return 0; 392 393 if (dir.isFile()) { 394 return dir.length(); 395 } 396 397 File[] files = dir.listFiles(); //does not return "." or ".." 398 399 for (int n = 0; n < files.length; n++) 400 size += dirSize(files[n]); 401 402 return size; 403 } 404 405/** 406Buffers and returns the specified InputStream, if it is not already buffered. 407Does not buffer an already buffered stream but returns it as is. 408 409@param in the input stream to be buffered 410@return the buffered stream 411*/ 412public static BufferedInputStream bufferStream(InputStream in) 413 { 414 Argcheck.notnull(in, "InputStream was null"); 415 BufferedInputStream bin; 416 if (! (in instanceof BufferedInputStream)) { 417 bin = new BufferedInputStream(in); 418 } 419 else { 420 bin = (BufferedInputStream) in; 421 } 422 return bin; 423 } 424 425/** 426Buffers and returns the specified OutputStream, if it is not already buffered. 427Does not buffer an already buffered stream but returns it as is. 428 429@param out the output stream to be buffered 430@return the buffered stream 431 432**/ 433public static BufferedOutputStream bufferStream(OutputStream out) 434 { 435 Argcheck.notnull(out, "OutputStream was null"); 436 BufferedOutputStream bout; 437 if (! (out instanceof BufferedOutputStream)) { 438 bout = new BufferedOutputStream(out); 439 } 440 else { 441 bout = (BufferedOutputStream) out; 442 } 443 return bout; 444 } 445 446public static BufferedReader bufferReader(Reader in) 447 { 448 Argcheck.notnull(in, "Reader was null"); 449 BufferedReader bin; 450 if ( ! (in instanceof BufferedReader)) { 451 bin = new BufferedReader(in); 452 } 453 else { 454 bin = (BufferedReader) in; 455 } 456 return bin; 457 } 458 459public static PrintStream toPrintStream(OutputStream out) 460 { 461 Argcheck.notnull(out, "OutputStream was null"); 462 if ( ! (out instanceof PrintStream)) { 463 out = new PrintStream(out); 464 } 465 return (PrintStream) out; 466 } 467 468public static PrintWriter toPrintWriter(Writer out) 469 { 470 Argcheck.notnull(out, "Writer was null"); 471 if ( ! (out instanceof PrintWriter)) { 472 out = new PrintWriter(out); 473 } 474 return (PrintWriter) out; 475 } 476 477public static BufferedWriter bufferWriter(Writer out) 478 { 479 Argcheck.notnull(out, "Writer was null"); 480 BufferedWriter bout; 481 if ( ! (out instanceof BufferedWriter)) { 482 bout = new BufferedWriter(out); 483 } 484 else { 485 bout = (BufferedWriter) out; 486 } 487 return bout; 488 } 489 490/** 491Convenience method to print the contents of a java.util.Property object 492to a String (using the default platform encoding). 493 494**/ 495public static String propertiesToString(Properties props) 496 { 497 String temp = null; 498 final ByteArrayOutputStream bout = new ByteArrayOutputStream(); 499 final PrintStream pout = new PrintStream(bout); 500 props.list(pout); 501 pout.flush(); 502 temp = bout.toString(); 503 pout.close(); 504 return temp; 505 } 506 507private static String defaultEncoding = null; 508 509/** 510Returns the default encoding used by the current platform. Returns 511<code>null</code> is the default encoding cannot be determined. 512**/ 513public static String getDefaultEncoding() 514 { 515 if (defaultEncoding != null) 516 return defaultEncoding; 517 String de = null; 518 try { 519 final InputStream in = ClassLoader.getSystemResourceAsStream("fc/io/IOUtil.class"); 520 final InputStreamReader defaultReader = new InputStreamReader(in); 521 de = defaultReader.getEncoding(); 522 defaultEncoding = de; 523 if (dbg) System.out.println("IOUtil.getDefaultEncoding() = " + de); 524 } 525 catch (Exception e) { 526 e.printStackTrace(); 527 } 528 return de; 529 } 530 531 532/** 533Returns the contents of an entire file as a List of individual lines. If the specified 534file does not exist or have no content return an empty List. Uses UTF-8 encoding to read the file. 535 536@param instream the stream to be read 537@param trim if <code>true</code>, any leading or trailing blank lines are trimmed are ignored. 538@param comment_chars Regex of comment chars. Any lines that start with this (or have leading spaces 539 and then start with this) are ignored. (example: <code>#</code> or <code>#|//</code>) 540*/ 541public static List fileToLines(InputStream instream, boolean trim, String comment_chars) throws IOException 542 { 543 BufferedReader in = new BufferedReader(new InputStreamReader(instream, "UTF-8")); 544 545 List list = new ArrayList(); 546 String line = null; 547 Pattern pat = null; 548 Matcher m = null; 549 if (comment_chars != null) { 550 pat = Pattern.compile("^[ \\t]*(" + comment_chars + ")+"); 551 } 552 553 while ( (line = in.readLine()) != null) 554 { 555 if (trim) { 556 line = line.trim(); //this gets rid of spaces, empty newlines, etc 557 if (line.length() == 0) { 558 continue; 559 } 560 } 561 562 if (pat != null) 563 { 564 m = pat.matcher(line); 565 if (m.find()) { 566 continue; 567 } 568 } 569 list.add(line); 570 } 571 return list; 572 } 573 574/** 575Returns the contents of an entire file as a List of individual lines. If the specified 576file does not exist or have no content return an empty List. 577 578@param file the file to be read ("UTF-8" encoding is used) 579@param trim if <code>true</code>, any leading or trailing blank lines are trimmed are ignored. 580@param comment_chars Regex of comment chars. Any lines that start with this (or have leading spaces 581 and then start with this) are ignored. (example: <code>#</code> or <code>#|//</code>) 582*/ 583public static List fileToLines(File file, boolean trim, String comment_chars) throws IOException 584 { 585 return fileToLines(new FileInputStream(file), trim, comment_chars); 586 } 587 588/** 589Returns the contents of an entire file as a List of individual lines. Empty lines are trimmed 590and lines beginning with the following characters are ignored: <code>#</code> and <code>//</code> 591 592@param file the file to be read ("UTF-8" encoding is used) 593*/ 594public static List fileToLines(File file) throws IOException 595 { 596 return fileToLines(file, true, "#|//"); 597 } 598 599/** 600Returns the contents of an entire file as a List of individual lines. Empty lines are trimmed 601and lines beginning with the following characters are ignored: <code>#</code> and <code>//</code> 602 603@param filename the file to be read ("UTF-8" encoding is used) 604*/ 605public static List fileToLines(String filename) throws IOException 606 { 607 return fileToLines(new File(filename)); 608 } 609 610/** 611Returns the contents of an entire file as a List of individual lines. Empty lines are trimmed 612and lines beginning with the following characters are ignored: <code>#</code> and <code>//</code> 613 614@param in the input stream to be read 615*/ 616public static List fileToLines(InputStream in) throws IOException 617 { 618 return fileToLines(in, true, "#|//"); 619 } 620 621/** 622Returns the contents of an entire file as a String. If the specified 623file does not exist returns <code>null</code>. Files that exist but have no 624content return an empty String. 625<p> 626<b>Note 1:</b> Due to jdk1.4 brain damage, this method is limited to files 627less than 2^32 bytes. If the specified file is greater than 2^32 bytes, 628an <code>IOException</code> will be thrown. 629<br> 630<b>Note 2:</b> The files is converted into a String using an encoding 631that is determined programmatically (from the filesystem). This may 632not be totally reliable but there is no way around this because JDK 1.4 633provides <b>no</b> way to easily get the default platform encoding. 634Uses the <code>ISO_8859_1</code> encoding as a fallback measure, if the 635default encoding cannot be determined. 636 637@param file the file to be read 638@param trim if <code>true</code>, any trailing whitespace is trimmed from the file's end. 639**/ 640public static String fileToString(File file, boolean trim) throws IOException 641 { 642 byte[] buf = fileToByteArray(file); 643 if (buf == null) 644 return null; 645 646 //there is no way to convert a byte buffer to a String 647 648 //because we cannot get a Charset that uses the default platform 649 //encoding in JDK 1.4.0. So we are going to IOUtil.getDefaultEncoding 650 //method (which is a workaround) to get the default platform encoding. 651 652 //update: instead of default encoding, arrayToCharBuffer will UTF-8 if 653 //encoding is not specified. 654 655 // resultstr will be "" if buf contains 0 chars (it can't be null 656 // if we have reached this point) 657 658 String resultstr = arrayToCharBuffer(buf).toString(); 659 if (trim) { 660 //might trigger g.c if string size is large 661 resultstr = resultstr.trim(); 662 } 663 return resultstr; 664 } 665 666 667/** 668Returns the contents of an entire file as a String. If the specified 669file does not exist returns <code>null</code>. Files that exist but have no 670content return an empty String. 671<p> 672<b>Note 1:</b> Due to jdk1.4 brain damage, this method is limited to files 673less than 2^32 bytes. If the specified file is greater than 2^32 bytes, 674an <code>IOException</code> will be thrown. 675<br> 676<b>Note 2:</b> The files is converted into a String using an encoding 677that is determined programmatically (from the filesystem). This may 678not be totally reliable but there is no way around this because JDK 1.4 679provides <b>no</b> way to easily get the default platform encoding. 680Uses the <code>ISO_8859_1</code> encoding as a fallback measure, if the 681default encoding cannot be determined. 682 683@param filename the absolute path to the file name to be read 684@param trim if <code>true</code>, any trailing whitespace is trimmed from the file's end. 685**/ 686public static String fileToString(String filename, boolean trim) throws IOException 687 { 688 return fileToString(new File(filename), trim); 689 } 690 691/** 692Calls {@link #fileToString(String, boolean)} with trim being 693<code>false</code> (that is, files are not trimmed at their trailing end). 694 695@param filename the absolute path to the file name to be read 696**/ 697public static String fileToString(String filename) throws IOException 698 { 699 return fileToString(filename, false); 700 } 701 702/** 703Calls {@link #fileToString(String, boolean)} with trim being 704<code>false</code> (that is, files are not trimmed at their trailing end). 705 706@param file the file to be read 707**/ 708public static String fileToString(File file) throws IOException 709 { 710 return fileToString(file, false); 711 } 712 713 714/** 715Returns the contents of an entire file as a <code>byte[]</code>. If the specified 716file does not exist returns <code>null</code>. 717<p> 718<b>Note 1:</b> Since java arrays cannot be greater than 2^32 elements, 719this method is limited to files less than or equal to 2^32 bytes. If 720the specified file is greater than 2^32 bytes, an <code>IOException</code> 721will be thrown. 722 723@param filename the absolute path to the file name to be read 724@return ByteBuffer contains the bytes for that file 725**/ 726public static byte[] fileToByteArray(String filename) throws IOException 727 { 728 return fileToByteArray(new File(filename)); 729 } 730 731/** 732Returns the contents of an entire file as a <code>byte[]</code>. If the specified 733file does not exist returns <code>null</code>. 734<p> 735<b>Note 1:</b> Since java arrays cannot be greater than 2^32 elements, 736this method is limited to files less than or equal to 2^32 bytes. If 737the specified file is greater than 2^32 bytes, an <code>IOException</code> 738will be thrown. 739 740@param file the file to be read 741@return byte[] contains the bytes for that file 742**/ 743public static byte[] fileToByteArray(File file) throws IOException 744 { 745 if (dbg) System.out.println("ENTER fileToByteBuffer(" + file + ")"); 746 747 Argcheck.notnull(file); 748 749 if ( ! file.exists() ) 750 return null; 751 752 if (dbg) System.out.println("file '" + file + "' exists"); 753 754 long longfsize = file.length(); 755 if (dbg) System.out.println("'" + file + "' size = " + longfsize); 756 757 if ( longfsize > FOUR_GB ) 758 throw new IOException("File size of " + longfsize + " too large for this method"); 759 760 FileInputStream fin = new FileInputStream(file); 761 int fsize = (int) longfsize; 762 byte[] buf = new byte[fsize]; 763 764 try { 765 int read, pos = 0; 766 while (pos < fsize) 767 { 768 /* Usually, this will read everything the first time */ 769 read = fin.read(buf, pos, fsize - pos); 770 pos += read; 771 if (read < 0) 772 break; 773 } 774 775 if (dbg) System.out.println("Read file byte[] = " + buf.length + " bytes"); 776 777 if (pos != fsize) 778 throw new IOException( "Can't read entire file, filesize = " + fsize + ", read = " + pos); 779 780 if (dbg) System.out.println("EXIT fileToByteBuffer(" + file + ")"); 781 } 782 finally { 783 fin.close(); 784 } 785 786 return buf; 787 } 788 789/** 790Returns the contents of an entire file as a ByteBuffer backed by mapping the 791file to memory. If the specified file does not exist returns <code>null</code>. 792Mapped files do <b>not</b> have have a accesible backing array and the 793<code>ByteBuffer.hasArray()</code> will be <code>false</code>. See the {@link 794java.nio.MappedByteBuffer} documentation about concurrent modification or 795deletion of files that are mapped into memory. 796<p> 797The ByteBuffer returned by this method will have <code>{@link 798ByteBuffer#rewind()} </code> called on it before it is returned. 799<p> 800<b>Note 1:</b> This method is limited to files less than 2^32 bytes, since 801ByteBuffers cannot be greater than this size. If the specified file is greater 802than 2^32 bytes, an <code>IOException</code> will be thrown. 803 804@param file the file to be read 805@return ByteBuffer contains the bytes for that file 806**/ 807public static ByteBuffer fileToByteBuffer(File file) throws IOException 808 { 809 if (dbg) System.out.println("ENTER fileAsByteBuffer(" + file + ")"); 810 Argcheck.notnull(file); 811 long fsize = 0; 812 813 if ( ! file.exists() ) 814 return null; 815 816 if (dbg) System.out.println("file '" + file + "' exists"); 817 818 fsize = file.length(); 819 820 if (dbg) System.out.println("'" + file + "' size = " + fsize); 821 822 if ( fsize > FOUR_GB) 823 throw new IOException("File size of " + file.length() + " too large for this method"); 824 825 FileChannel fcin = new FileInputStream(file).getChannel(); 826 827 BufferedReader reader = null; 828 final ByteBuffer bufin = fcin.map(FileChannel.MapMode.READ_ONLY, 0, fsize); 829 830 if (dbg) System.out.println("File ByteBuffer = " + bufin); 831 832 //This is very important and easy to forget -- rewind the buffer !!! 833 834 bufin.rewind(); 835 836 if (dbg) System.out.println("EXIT fileAsByteBuffer(" + file + ")"); 837 return bufin; 838 } 839 840/** 841Returns the contents of an entire file as a <code>char[]</code>. If the specified 842file does not exist returns <code>null</code>. 843<p> 844<b>Note 1:</b> Since java arrays cannot be greater than 2^32 elements, 845this method is limited to files less than or equal to 2^32 bytes. If 846the specified file is greater than 2^32 bytes, an <code>IOException</code> 847will be thrown. 848 849@param file the file to be read 850@param encoding the name of the character encoding to use. 851 Specify <code>null</code> to use UTF-8 encoding. 852@return char[] contains the chars for that file 853**/ 854public static char[] fileToCharArray(File file, String encoding) 855throws IOException 856 { 857 Argcheck.notnull(file); 858 859 if ( ! file.exists() ) 860 return null; 861 862 if (dbg) System.out.println("file '" + file + "' exists"); 863 864 long longfsize = file.length(); 865 if (dbg) System.out.println("'" + file + "' size = " + longfsize); 866 867 if ( longfsize > FOUR_GB ) 868 throw new IOException("File size of " + longfsize + " too large for this method"); 869 870 FileInputStream fin = new FileInputStream(file); 871 872 if (encoding == null) { 873 encoding = "UTF-8"; 874 } 875 876 final Reader reader = new InputStreamReader(fin, encoding); 877 878 int fsize = (int) longfsize; 879 char[] buf = readerToCharArray(reader, (int)fsize); //buf_fize (fsize) can be greater than chars read 880 881 /* 882 Can't do it this way - we create a buf of file size bytes but the number of characters 883 read can be less than file size (because of various encodings). This way returns a buf 884 with NULL characters at the end, so either have to trim the buf or just use a char array 885 writer 886 */ 887 /* 888 buf = new char[fsize]; 889 try { 890 int read, pos = 0; 891 while (pos < fsize) 892 { 893 //Usually, this will read everything the first time 894 read = reader.read(buf, pos, fsize - pos); 895 pos += read; 896 if (read < 0) { //EOF 897 break; 898 } 899 } 900 901 if (dbg && pos != fsize) { 902 System.out.println("File: " + file.getAbsolutePath() 903 + " has bytes [" + fsize + "], read [" + pos + "] chars\n" 904 + "This is expected since byte->char will loose characters for non-ascii files. Currently using [" 905 + encoding + "] to read this file" 906 ); 907 } 908 } 909 910 //at this point buf will have pos characters, not fsize characters. 911 912 finally { 913 fin.close(); 914 } 915 */ 916 917 return buf; 918 } 919 920/* 921Reads the entire Stream and returns all read data as a 922<tt>char[]</tt>. If no data is available, returns an empty char[]. 923*/ 924public static char[] readerToCharArray(Reader reader) 925throws IOException 926 { 927 Argcheck.notnull(reader); 928 929 return readerToCharArray(reader, 1024); 930 } 931 932 933/* 934Reads the entire Stream and returns all read data as a 935<tt>char[]</tt>. If no data is available, returns an empty char[]. 936*/ 937public static char[] readerToCharArray(Reader reader, int buffer_size) 938throws IOException 939 { 940 Argcheck.notnull(reader); 941 942 char[] buf = new char[buffer_size]; 943 if (dbg) System.out.println("readerToCharArray(), block=yes"); 944 945 final CharArrayWriter cout = new CharArrayWriter(buffer_size); 946 int read = 0; 947 while (true) { 948 read = reader.read(buf, 0, buffer_size); 949 if (read == -1) 950 break; 951 cout.write(buf, 0, read); 952 } 953 954 return cout.toCharArray(); 955 } 956 957 958 959 960/** 961Converts the specified byte array into a CharBuffer using the specified 962encoding. The returned CharBuffer can be directly used in statements such 963as <code>System.out.println</code> to print it's contents, 964<p> 965This method returns <code>null</code> if the specified array is <code>null</code> 966or if the specified encoding is <code>null</code>. 967 968@param array the array to convert 969@param encoding the {@link java.nio.charset.Charset charset} encoding to use to 970 convert bytes into chars 971**/ 972public static CharBuffer arrayToCharBuffer(byte[] array, String encoding) 973 { 974 if ( (array == null) || (encoding == null)) 975 return null; 976 977 Charset cset = Charset.forName(encoding); 978 CharBuffer cbuf = cset.decode(ByteBuffer.wrap(array)); 979 return cbuf; 980 } 981 982/** 983Convenience method that delegates to {@link #arrayToCharBuffer(byte[], 984String)} using UTF-8 encoding by default. 985**/ 986public static CharBuffer arrayToCharBuffer(byte[] array) 987 { 988 //String enc = IOUtil.getDefaultEncoding(); 989 //enc = (enc != null) ? enc : "ISO-8859-1"; 990 //UTF-8 is safer overall 991 return arrayToCharBuffer(array, "UTF-8"); 992 } 993 994/** 995Reads the entire InputStream and returns all read data as a 996<code>byte[]</code>. If no data is available, returns <code>null</code>. 997 998@param in the InputStream to read 999@param block if <code>true</code>, this method will block until all 1000 available data from the specified input stream 1001 has been read. If <code>false</code>, this method will 1002 read and return as much data as currently is available 1003 is read without blocking. The available amount is 1004 that returned by the available() method of the specified 1005 input stream. 1006 1007@throws NegativeArraySizeException if the specified input stream returns 1008 a negative number for available() 1009**/ 1010public static byte[] inputStreamToByteArray(InputStream in, boolean block) 1011throws IOException 1012 { 1013 Argcheck.notnull(in, "InputStream was null"); 1014 1015 final BufferedInputStream bin = bufferStream(in); 1016 1017 if (! block) { 1018 int buffer_size = bin.available(); 1019 if (dbg) System.out.println("inputStreamToByteArray(), block=no, buffersize=" + buffer_size); 1020 byte[] buf = new byte[buffer_size]; 1021 int read = 0; 1022 int pos = 0; 1023 while (read < buffer_size) { 1024 read += bin.read(buf, pos, buffer_size - read); 1025 pos = read + 1; 1026 } 1027 if (dbg) System.out.println("inputStreamToByteArray(), returning buf=" + buf.length + " bytes"); 1028 return buf; 1029 } 1030 1031 //block 1032 int buffer_size = 1024; 1033 byte[] buf = new byte[buffer_size]; 1034 if (dbg) System.out.println("inputStreamToByteArray(), block=yes"); 1035 1036 final ByteArrayOutputStream bout = new ByteArrayOutputStream(buffer_size); 1037 int read = 0; 1038 while (true) { 1039 read = bin.read(buf, 0, buffer_size); 1040 if (read == -1) 1041 break; 1042 bout.write(buf, 0, read); 1043 } 1044 //if size() is 0, toByteArray returns an array of size 0. we 1045 //return null instead. 1046 if (bout.size() == 0) { 1047 return null; 1048 } 1049 return bout.toByteArray(); 1050 } 1051 1052/** 1053Calls inputStreamToByteArray(in, <code>true</code>) 1054*/ 1055public static byte[] inputStreamToByteArray(InputStream in) throws IOException 1056 { 1057 return inputStreamToByteArray(in, true); 1058 } 1059 1060 1061/** 1062Reads the entire InputStream and returns all read data as a <code>String</code>, using UTF-8 1063encoding. If no data is available, returns <code>null</code>. The specified input stream is 1064<u>not</u> closed. 1065 1066@param in the InputStream to read 1067@param block if <code>true</code>, this method will block until all 1068 available data from the specified input stream 1069 has been read. If <code>false</code>, this method will 1070 read and return as much data as currently is available 1071 is read without blocking. The available amount is 1072 that returned by the specified input stream. 1073 1074@throws NegativeArraySizeException if the specified input stream returns 1075 a negative number for available() 1076**/ 1077public static String inputStreamToString(InputStream in, boolean block) 1078throws IOException 1079 { 1080 final byte[] buf = inputStreamToByteArray(in, block); 1081 if (buf == null) { 1082 return null; 1083 } 1084 return new String(buf, "UTF-8"); 1085 } //~inputStreamToString 1086 1087 1088 1089/** 1090Reads the entire InputStream and returns all read data as a 1091<code>String</code> (using the specified platform encoding). If 1092no data is available, returns <code>null</code>. The specified input 1093stream is <u>not</u> closed. 1094<p> 1095This method will block until all available data from the specified 1096input stream has been read. 1097 1098@param in the input stream to read 1099@param encoding the {@link java.nio.charset.Charset} encoding name to use to 1100 convert bytes into chars 1101*/ 1102public static String inputStreamToString(InputStream in, String encoding) 1103throws IOException 1104 { 1105 final byte[] buf = inputStreamToByteArray(in, true); 1106 if (buf == null) { 1107 return null; 1108 } 1109 return new String(buf, encoding); 1110 } 1111 1112 1113/** 1114Calls inputStreamToString(in, <code>true</code>) 1115*/ 1116public static String inputStreamToString(InputStream in) throws IOException 1117 { 1118 return inputStreamToString(in, true); 1119 } 1120 1121 1122/** 1123Convenience method to print the stack trace of an Exception (or Throwable) 1124to a String (using the default platform encoding). (The <code>getMessage()</code> 1125method of a Throwable does not print the entire stack trace). 1126**/ 1127public static String throwableToString(final Throwable e) 1128 { 1129 Argcheck.notnull(e, "The specified exception object was null"); 1130 String temp = null; 1131 final ByteArrayOutputStream bout = new ByteArrayOutputStream(768); 1132 final PrintStream pout = new PrintStream(bout); 1133 e.printStackTrace(pout); 1134 pout.flush(); 1135 temp = bout.toString(); 1136 pout.close(); 1137 return temp; 1138 } 1139 1140/** 1141Convenience method that returns the current execution stack trace as a String. 1142(using the default platform encoding). 1143**/ 1144public static String stackTrace() 1145 { 1146 String temp = null; 1147 final ByteArrayOutputStream bout = new ByteArrayOutputStream(768); 1148 final PrintStream pout = new PrintStream(bout); 1149 pout.println("==================== Debug Stack Trace ======================"); 1150 new Exception().printStackTrace(pout); 1151 pout.println("============================================================="); 1152 pout.flush(); 1153 temp = bout.toString(); 1154 pout.close(); 1155 return temp; 1156 } 1157 1158/** 1159Calls {@link #fileSizeToString(long)} and truncates the 1160size to fit in the specified number of digits. For example, 1161a file size of <code>4.455 KB</code> and a length of 2 will 1162return <code>4.45 KB</code> 1163 1164@param filesize the size of the file in bytes 1165@param length the max number of digits <b>after</b> 1166 the decimal point 1167*/ 1168public static String fileSizeToString(long filesize, int length) 1169 { 1170 NumberFormat nf = NumberFormat.getInstance(); 1171 nf.setMaximumFractionDigits(length); 1172 return fileSizeToStringImpl(nf, filesize); 1173 } 1174 1175 1176/** 1177Converts the specified file size into a human readable description. 1178Similar to the <code>"--human-readable"</code> flag found in various 1179GNU programs. 1180 1181@param filesize the size of the file in bytes 1182*/ 1183public static String fileSizeToString(long filesize) 1184 { 1185 NumberFormat nf = NumberFormat.getInstance(); 1186 return fileSizeToStringImpl(nf, filesize); 1187 } 1188 1189private static final String fileSizeToStringImpl( 1190 NumberFormat nf, long filesize) 1191 { 1192 StringBuffer buf = new StringBuffer(32); 1193 if (filesize > ONE_GB) 1194 buf.append(nf.format(filesize / ONE_GB)).append(" GB"); 1195 else if (filesize > ONE_MB) 1196 buf.append(nf.format( filesize / ONE_MB)).append(" MB"); 1197 else if (filesize > ONE_KB) 1198 buf.append(nf.format(filesize / ONE_KB)).append(" KB"); 1199 else 1200 buf.append(nf.format(filesize)).append(" bytes"); 1201 1202 return buf.toString(); 1203 } 1204 1205/** 1206Converts a string file size into a number denoting the equivalent bytes. 1207For example: 1208<pre> 120934K, 34KB --> 34 bytes 121034M, 34megabytes --> 34 * 1024 bytes 1211</pre> 1212Allows numbers to end with <code>k..., m..., g...., b....</code> or no suffix 1213at all. Suffixes are case insensitive. 1214*/ 1215public static long stringToFileSize(String str) 1216 { 1217 Argcheck.notnull(str, "the specified string was null"); 1218 1219 str = str.replace(" ",""); //remove all leading, trailing, embedded spaces 1220 1221 int pos = 0; //this is slighty easier than str.indexOf 1222 for (int n = 0; n < str.length(); n++) 1223 { 1224 char c = str.charAt(n); 1225 switch (c) { 1226 case 'g': case 'G': 1227 return Long.parseLong(str.substring(0,n)) * ONE_GB; 1228 case 'm': case 'M': 1229 return Long.parseLong(str.substring(0,n)) * ONE_MB; 1230 case 'k': case 'K': 1231 return Long.parseLong(str.substring(0,n)) * ONE_KB; 1232 case 'b': case 'B': 1233 return Long.parseLong(str.substring(0,n)); 1234 default: 1235 //nothing to do 1236 } 1237 } 1238 return Long.parseLong(str); 1239 } 1240 1241/** 1242Returns the SHA-1 hash of the specified byte buffer. 1243<p> 1244This method is <b>not</b> thread safe (as far as I can tell, since it uses 1245<code>MessageDigest.getInstance</code>, but <i>if</i> that is thread safe, then 1246this method is thread safe). The invoker should invoke it in a thread safe way. 1247*/ 1248public static final String sha1hash(byte[] buf) throws NoSuchAlgorithmException 1249 { 1250 MessageDigest md = MessageDigest.getInstance("SHA-1"); 1251 byte[] digest = md.digest(buf); 1252 StringBuilder sb = new StringBuilder(); 1253 for (byte b: digest) 1254 { 1255 String hex = Integer.toHexString((int)0xff & b); 1256 if (hex.length()==1) sb.append("0"); 1257 sb.append(hex); 1258 } 1259 1260 return sb.toString(); 1261 } 1262 1263 1264/** 1265 Get the InputStream for the specified resource (in the same directory as from where the specified class was loaded). 1266 Returns null if not found. 1267 */ 1268public static InputStream getClassResource(Class clazz, String resource_name) throws IOException 1269 { 1270 if (clazz == null) { 1271 return null; 1272 } 1273 1274 if (resource_name.startsWith("/")) { 1275 resource_name = resource_name.substring(1,resource_name.length()); 1276 } 1277 InputStream in = clazz.getResourceAsStream(resource_name); 1278 return in; 1279 } 1280 1281/** 1282Usage: 1283java IOUtil args where args are: 1284 -file full-path-to-file 1285 [for fileToString and other tests] 1286 1287**/ 1288public static void main(String[] args) throws Exception 1289 { 1290 Args myargs = new Args(args); 1291 String fts = myargs.getRequired("file"); 1292 new Test(fts); 1293 } 1294 1295 1296/** 1297Unit Test History 1298<pre> 1299Class Version Tester Status Notes 13001.1 hj still-testing limited testing only, more needs to be done 1301</pre> 1302*/ 1303static private class Test { 1304Test(String fileToString) throws Exception 1305 { 1306 java.io.File sourcefile = new java.io.File("testsourcefile"); 1307 java.io.File sourcedir = new java.io.File("testsourcedir"); 1308 java.io.File destfile = new java.io.File("testdestfile"); 1309 java.io.File destdir = new java.io.File("testdestdir"); 1310 1311 String dir1 = "foo"; 1312 String dir2 = "foo" + FILE_SEP; 1313 String f = "f1"; 1314 //String r1 = IOUtil.makeFilePath(dir1,f); 1315 //String r2 = IOUtil.makeFilePath(dir2,f); 1316 for(long n = 2; n < 4000000001L; n *= 1000) { 1317 System.out.println("file size " + n + " = " 1318 + IOUtil.fileSizeToString(n)); 1319 } 1320 1321 System.out.println("Platform encoding, via file.encoding: " + System.getProperty("file.encoding")); 1322 System.out.println("Platform encoding: " + IOUtil.getDefaultEncoding()); 1323 System.out.println("--- fileToString('"+ fileToString + "') -----"); 1324 String filestr = fileToString(fileToString); 1325 System.out.println("file '" + fileToString + "' read into String"); 1326 System.out.println("String length = " + filestr.length()); 1327 System.out.println("String data = "); 1328 System.out.println(filestr); 1329 1330 List linelist = fileToLines(new File(fileToString)); 1331 System.out.println("--- fileToList('"+ fileToString + "') -----"); 1332 for (int n = 0; n < linelist.size(); n++) { 1333 System.out.println("[line:" + n + "]: " + linelist.get(n)); 1334 } 1335 1336 FileInputStream fin = new FileInputStream(fileToString); 1337 System.out.println("--- inputStreamToString('"+ fileToString + "') -----"); 1338 String str = inputStreamToString(fin, false); 1339 System.out.println(str); 1340 System.out.println(fileToString + ", size=" + str.length()); 1341 1342 Socket sock = new Socket("www.yahoo.com", 80); 1343 System.out.println("--- inputStreamToString('"+ sock + "') -----"); 1344 InputStream sin = sock.getInputStream(); 1345 OutputStream sout = sock.getOutputStream(); 1346 sout.write("GET /index.html\n\n".getBytes()); 1347 sout.flush(); 1348 System.out.println(inputStreamToString(sin, true)); 1349 1350 Object obj = new Object(); 1351 System.out.println("--- getClassResource('"+ obj.getClass() + ", 'String.class') -----"); 1352 System.out.println(getClassResource(obj.getClass(), "String")); 1353 1354 System.out.println("--- getClassResource('"+ IOUtil.class + ", 'Args.class') -----"); 1355 System.out.println(inputStreamToString(getClassResource(IOUtil.class, "compile.sh"))); 1356 1357 copyFileTest(); 1358 } //~constructor 1359 1360 void copyFileTest() throws IOException { 1361 File source = new File("/tmp/foo"); 1362 File dest = new File("/tmp/bar"); 1363 System.out.println("Copy test: " + source + " to " + dest); 1364 copyDirectory(source, dest); 1365 } 1366} //~Test 1367 1368} //~ IOUtil 1369 1370 1371 1372 1373