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