~ubuntu-branches/ubuntu/karmic/commons-io/karmic

« back to all changes in this revision

Viewing changes to src/java/org/apache/commons/io/FileUtils.java

  • Committer: Bazaar Package Importer
  • Author(s): Wolfgang Baer
  • Date: 2005-10-16 13:44:21 UTC
  • Revision ID: james.westby@ubuntu.com-20051016134421-v2gfddy6iovz449t
Tags: upstream-1.0
ImportĀ upstreamĀ versionĀ 1.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright 2001-2004 The Apache Software Foundation.
 
3
 * 
 
4
 * Licensed under the Apache License, Version 2.0 (the "License");
 
5
 * you may not use this file except in compliance with the License.
 
6
 * You may obtain a copy of the License at
 
7
 * 
 
8
 *      http://www.apache.org/licenses/LICENSE-2.0
 
9
 * 
 
10
 * Unless required by applicable law or agreed to in writing, software
 
11
 * distributed under the License is distributed on an "AS IS" BASIS,
 
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
13
 * See the License for the specific language governing permissions and
 
14
 * limitations under the License.
 
15
 */
 
16
package org.apache.commons.io;
 
17
 
 
18
import java.io.File;
 
19
import java.io.FileInputStream;
 
20
import java.io.FileNotFoundException;
 
21
import java.io.FileOutputStream;
 
22
import java.io.IOException;
 
23
import java.io.InputStream;
 
24
import java.io.FileFilter;
 
25
import java.io.OutputStream;
 
26
import java.net.URL;
 
27
import java.util.Collection;
 
28
import java.util.Date;
 
29
 
 
30
import org.apache.commons.io.filefilter.DirectoryFileFilter;
 
31
import org.apache.commons.io.filefilter.FalseFileFilter;
 
32
import org.apache.commons.io.filefilter.FileFilterUtils;
 
33
import org.apache.commons.io.filefilter.IOFileFilter;
 
34
import org.apache.commons.io.filefilter.SuffixFileFilter;
 
35
import org.apache.commons.io.filefilter.TrueFileFilter;
 
36
 
 
37
/**
 
38
 * This class provides basic facilities for manipulating files and file paths.
 
39
 *
 
40
 * <h3>Path-related methods</h3>
 
41
 *
 
42
 * <p>Methods exist to retrieve the components of a typical file path. For example
 
43
 * <code>/www/hosted/mysite/index.html</code>, can be broken into:
 
44
 * <ul>
 
45
 *   <li><code>/www/hosted/mysite/</code> -- retrievable through {@link #getPath}</li>
 
46
 *   <li><code>index.html</code> -- retrievable through {@link #removePath}</li>
 
47
 *   <li><code>/www/hosted/mysite/index</code> -- retrievable through {@link #removeExtension}</li>
 
48
 *   <li><code>html</code> -- retrievable through {@link #getExtension}</li>
 
49
 * </ul>
 
50
 * There are also methods to {@link #catPath concatenate two paths}, {@link #resolveFile resolve a
 
51
 * path relative to a File} and {@link #normalize} a path.
 
52
 * </p>
 
53
 *
 
54
 * <h3>File-related methods</h3>
 
55
 * <p>
 
56
 * There are methods to  create a {@link #toFile File from a URL}, copy a
 
57
 * {@link #copyFileToDirectory File to a directory},
 
58
 * copy a {@link #copyFile File to another File},
 
59
 * copy a {@link #copyURLToFile URL's contents to a File},
 
60
 * as well as methods to {@link #deleteDirectory(File) delete} and {@link #cleanDirectory(File)
 
61
 * clean} a directory.
 
62
 * </p>
 
63
 *
 
64
 * Common {@link java.io.File} manipulation routines.
 
65
 *
 
66
 * <h3>Origin of code</h3>
 
67
 * <ul>
 
68
 *   <li>commons-utils repo</li>
 
69
 *   <li>Alexandria's FileUtils.</li>
 
70
 *   <li>Avalon Excalibur's IO.</li>
 
71
 * </ul>
 
72
 *
 
73
 * @author <a href="mailto:burton@relativity.yi.org">Kevin A. Burton</A>
 
74
 * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
 
75
 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
 
76
 * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph.Reck</a>
 
77
 * @author <a href="mailto:peter@apache.org">Peter Donald</a>
 
78
 * @author <a href="mailto:jefft@apache.org">Jeff Turner</a>
 
79
 * @author Matthew Hawthorne
 
80
 * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
 
81
 * @version $Id: FileUtils.java,v 1.31 2004/04/24 19:46:16 jeremias Exp $
 
82
 */
 
83
public class FileUtils {
 
84
 
 
85
    /**
 
86
     * Instances should NOT be constructed in standard programming.
 
87
     */
 
88
    public FileUtils() { }
 
89
 
 
90
    /**
 
91
     * The number of bytes in a kilobyte.
 
92
     */
 
93
    public static final long ONE_KB = 1024;
 
94
 
 
95
    /**
 
96
     * The number of bytes in a megabyte.
 
97
     */
 
98
    public static final long ONE_MB = ONE_KB * ONE_KB;
 
99
 
 
100
    /**
 
101
     * The number of bytes in a gigabyte.
 
102
     */
 
103
    public static final long ONE_GB = ONE_KB * ONE_MB;
 
104
 
 
105
    /**
 
106
     * Returns a human-readable version of the file size (original is in
 
107
     * bytes).
 
108
     *
 
109
     * @param size The number of bytes.
 
110
     * @return     A human-readable display value (includes units).
 
111
     * @todo need for I18N?
 
112
     */
 
113
    public static String byteCountToDisplaySize(long size) {
 
114
        String displaySize;
 
115
 
 
116
        if (size / ONE_GB > 0) {
 
117
            displaySize = String.valueOf(size / ONE_GB) + " GB";
 
118
        } else if (size / ONE_MB > 0) {
 
119
            displaySize = String.valueOf(size / ONE_MB) + " MB";
 
120
        } else if (size / ONE_KB > 0) {
 
121
            displaySize = String.valueOf(size / ONE_KB) + " KB";
 
122
        } else {
 
123
            displaySize = String.valueOf(size) + " bytes";
 
124
        }
 
125
 
 
126
        return displaySize;
 
127
    }
 
128
 
 
129
 
 
130
    /**
 
131
     * Implements the same behaviour as the "touch" utility on Unix. It creates
 
132
     * a new file with size 0 or, if the file exists already, it is opened and
 
133
     * closed without modifying it, but updating the file date and time.
 
134
     * @param file the File to touch
 
135
     * @throws IOException If an I/O problem occurs
 
136
     */
 
137
    public static void touch(File file) throws IOException {
 
138
        OutputStream out = new java.io.FileOutputStream(file);
 
139
        IOUtils.closeQuietly(out);
 
140
    }
 
141
 
 
142
 
 
143
    private static void innerListFiles(Collection files, File directory, IOFileFilter filter) {
 
144
        File[] found = directory.listFiles((FileFilter)filter);
 
145
        for (int i = 0; i < found.length; i++) {
 
146
            if (found[i].isDirectory()) {
 
147
                innerListFiles(files, found[i], filter);
 
148
            } else {
 
149
                files.add(found[i]);
 
150
            }
 
151
        }
 
152
    }
 
153
 
 
154
 
 
155
    /**
 
156
     * Converts a Collection containing java.io.File instanced into array
 
157
     * representation. This is to account for the difference between
 
158
     * File.listFiles() and FileUtils.listFiles().
 
159
     * @param files a Collection containing java.io.File instances
 
160
     * @return an array of java.io.File
 
161
     */
 
162
    public static File[] convertFileCollectionToFileArray(Collection files) {
 
163
         return (File[])files.toArray(new File[files.size()]);
 
164
    }
 
165
 
 
166
 
 
167
    /**
 
168
     * <p>Finds files within a given directory (and optionally its 
 
169
     * subdirectories). All files found are filtered by an IOFileFilter.
 
170
     * </p>
 
171
     * <p>If your search should recurse into subdirectories you can pass in 
 
172
     * an IOFileFilter for directories. You don't need to bind a 
 
173
     * DirectoryFileFilter (via logical AND) to this filter. This method does 
 
174
     * that for you.
 
175
     * </p>
 
176
     * <p>An example: If you want to search through all directories called
 
177
     * "temp" you pass in <code>FileFilterUtils.NameFileFilter("temp")</code>
 
178
     * </p>
 
179
     * <p>Another common usage of this method is find files in a directory
 
180
     * tree but ignoring the directories generated CVS. You can simply pass
 
181
     * in <code>FileFilterUtils.makeCVSAware(null)</code>.
 
182
     * </p>  
 
183
     * @param directory the directory to search in
 
184
     * @param fileFilter filter to apply when finding files.
 
185
     * @param dirFilter optional filter to apply when finding subdirectories. 
 
186
     * If this parameter is null, subdirectories will not be included in the
 
187
     * search. Use TrueFileFilter.INSTANCE to match all directories.
 
188
     * @return an collection of java.io.File with the matching files
 
189
     * @see org.apache.commons.io.filefilter.FileFilterUtils
 
190
     * @see org.apache.commons.io.filefilter.NameFileFilter
 
191
     */
 
192
    public static Collection listFiles(File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
 
193
        if (!directory.isDirectory()) {
 
194
            throw new IllegalArgumentException("Parameter 'directory' is not a directory");
 
195
        }
 
196
        if (fileFilter == null) {
 
197
            throw new NullPointerException("Parameter 'fileFilter' is null");
 
198
        }
 
199
        
 
200
        //Setup effective file filter
 
201
        IOFileFilter effFileFilter = FileFilterUtils.andFileFilter(fileFilter, 
 
202
            FileFilterUtils.notFileFilter(DirectoryFileFilter.INSTANCE));
 
203
            
 
204
        //Setup effective directory filter
 
205
        IOFileFilter effDirFilter;
 
206
        if (dirFilter == null) {
 
207
            effDirFilter = FalseFileFilter.INSTANCE;
 
208
        } else {
 
209
            effDirFilter = FileFilterUtils.andFileFilter(dirFilter,
 
210
                DirectoryFileFilter.INSTANCE);
 
211
        }
 
212
        
 
213
        //Find files
 
214
        Collection files = new java.util.LinkedList();
 
215
        innerListFiles(files, directory, 
 
216
            FileFilterUtils.orFileFilter(effFileFilter, effDirFilter));
 
217
        return files;
 
218
    }
 
219
    
 
220
 
 
221
    /**
 
222
     * Converts an array of file extensions to suffixes for use
 
223
     * with IOFileFilters.
 
224
     * @param extensions an array of extensions. Format: {"java", "xml"}
 
225
     * @return an array of suffixes. Format: {".java", ".xml"}
 
226
     */
 
227
    private static String[] toSuffixes(String[] extensions) {
 
228
        String[] suffixes = new String[extensions.length];
 
229
        for (int i = 0; i < extensions.length; i++) {
 
230
            suffixes[i] = "." + extensions[i];
 
231
        }
 
232
        return suffixes;
 
233
    }
 
234
 
 
235
 
 
236
    /**
 
237
     * Finds files within a given directory (and optionally its subdirectories)
 
238
     * which match an array of extensions. 
 
239
     * @param directory the directory to search in
 
240
     * @param extensions an array of extensions, ex. {"java","xml"}. If this
 
241
     * parameter is null, all files are returned.
 
242
     * @param recursive If true all subdirectories are searched, too.
 
243
     * @return an collection of java.io.File with the matching files
 
244
     */
 
245
    public static Collection listFiles(File directory, String[] extensions, boolean recursive) {
 
246
        IOFileFilter filter;
 
247
        if (extensions == null) {
 
248
            filter = TrueFileFilter.INSTANCE;
 
249
        } else {
 
250
            String[] suffixes = toSuffixes(extensions);
 
251
            filter = new SuffixFileFilter(suffixes);
 
252
        }
 
253
        return listFiles(directory, filter, 
 
254
            (recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE));
 
255
    }
 
256
 
 
257
 
 
258
    /**
 
259
     * <p>Compare the contents of two files to determine if they are equal or not.</p>
 
260
     * <p>Code origin: Avalon</p>
 
261
     *
 
262
     * @param file1 the first file
 
263
     * @param file2 the second file
 
264
     * @return true if the content of the files are equal or they both don't exist, false otherwise
 
265
     * @throws IOException in case of an I/O error
 
266
     */
 
267
    public static boolean contentEquals(File file1, File file2)
 
268
            throws IOException {
 
269
        boolean file1Exists = file1.exists();
 
270
        if (file1Exists != file2.exists()) {
 
271
            return false;
 
272
        }
 
273
 
 
274
        if (!file1Exists) {
 
275
            // two not existing files are equal
 
276
            return true;
 
277
        }
 
278
 
 
279
        if (file1.isDirectory() || file2.isDirectory()) {
 
280
            // don't want to compare directory contents
 
281
            throw new IOException("Can't compare directories, only files");
 
282
        }
 
283
 
 
284
        InputStream input1 = null;
 
285
        InputStream input2 = null;
 
286
        try {
 
287
            input1 = new java.io.FileInputStream(file1);
 
288
            input2 = new java.io.FileInputStream(file2);
 
289
            return IOUtils.contentEquals(input1, input2);
 
290
 
 
291
        } finally {
 
292
            IOUtils.closeQuietly(input1);
 
293
            IOUtils.closeQuietly(input2);
 
294
        }
 
295
    }
 
296
 
 
297
    /**
 
298
     * Convert from a <code>URL</code> to a <code>File</code>.
 
299
     * @param url File URL.
 
300
     * @return The equivalent <code>File</code> object, or <code>null</code> if the URL's protocol
 
301
     * is not <code>file</code>
 
302
     */
 
303
    public static File toFile(URL url) {
 
304
        if (url.getProtocol().equals("file") == false) {
 
305
            return null;
 
306
        } else {
 
307
            String filename =
 
308
                url.getFile().replace('/', File.separatorChar);
 
309
            return new File(filename);
 
310
        }
 
311
    }
 
312
 
 
313
    /**
 
314
     * Convert the array of Files into a list of URLs.
 
315
     *
 
316
     * @param files the array of files
 
317
     * @return the array of URLs
 
318
     * @throws IOException if an error occurs
 
319
     */
 
320
    public static URL[] toURLs(File[] files) throws IOException {
 
321
        URL[] urls = new URL[files.length];
 
322
 
 
323
        for (int i = 0; i < urls.length; i++) {
 
324
            urls[i] = files[i].toURL();
 
325
        }
 
326
 
 
327
        return urls;
 
328
    }
 
329
 
 
330
 
 
331
    /**
 
332
     * Copy file from source to destination. If <code>destinationDirectory</code> does not exist, it
 
333
     * (and any parent directories) will be created. If a file <code>source</code> in
 
334
     * <code>destinationDirectory</code> exists, it will be overwritten.
 
335
     * The copy will have the same file date as the original.
 
336
     *
 
337
     * @param source An existing <code>File</code> to copy.
 
338
     * @param destinationDirectory A directory to copy <code>source</code> into.
 
339
     *
 
340
     * @throws FileNotFoundException if <code>source</code> isn't a normal file.
 
341
     * @throws IllegalArgumentException if <code>destinationDirectory</code> isn't a directory.
 
342
     * @throws IOException if <code>source</code> does not exist, the file in
 
343
     * <code>destinationDirectory</code> cannot be written to, or an IO error occurs during copying.
 
344
     */
 
345
    public static void copyFileToDirectory(
 
346
        File source,
 
347
        File destinationDirectory)
 
348
        throws IOException {
 
349
        if (destinationDirectory.exists()
 
350
            && !destinationDirectory.isDirectory()) {
 
351
            throw new IllegalArgumentException("Destination is not a directory");
 
352
        }
 
353
 
 
354
        copyFile(source, new File(destinationDirectory, source.getName()), true);
 
355
    }
 
356
 
 
357
    /**
 
358
     * Copy file from source to destination. The directories up to 
 
359
     * <code>destination</code> will be created if they don't already exist. 
 
360
     * <code>destination</code> will be overwritten if it already exists.
 
361
     * The copy will have the same file date as the original.
 
362
     *
 
363
     * @param source An existing non-directory <code>File</code> to copy 
 
364
     * bytes from.
 
365
     * @param destination A non-directory <code>File</code> to write bytes to 
 
366
     * (possibly overwriting).
 
367
     *
 
368
     * @throws IOException if <code>source</code> does not exist, <code>destination</code> cannot be
 
369
     * written to, or an IO error occurs during copying.
 
370
     *
 
371
     * @throws FileNotFoundException if <code>destination</code> is a directory
 
372
     * (use {@link #copyFileToDirectory}).
 
373
     */
 
374
    public static void copyFile(File source, File destination)
 
375
                throws IOException {
 
376
        copyFile(source, destination, true);
 
377
    }
 
378
                
 
379
                
 
380
    /**
 
381
     * Copy file from source to destination. The directories up to 
 
382
     * <code>destination</code> will be created if they don't already exist. 
 
383
     * <code>destination</code> will be overwritten if it already exists.
 
384
     *
 
385
     * @param source An existing non-directory <code>File</code> to copy 
 
386
     * bytes from.
 
387
     * @param destination A non-directory <code>File</code> to write bytes to 
 
388
     * (possibly overwriting).
 
389
     * @param preserveFileDate True if the file date of the copy should be the
 
390
     * same as the original.
 
391
     *
 
392
     * @throws IOException if <code>source</code> does not exist, <code>destination</code> cannot be
 
393
     * written to, or an IO error occurs during copying.
 
394
     *
 
395
     * @throws FileNotFoundException if <code>destination</code> is a directory
 
396
     * (use {@link #copyFileToDirectory}).
 
397
     */
 
398
    public static void copyFile(File source, File destination, boolean preserveFileDate)
 
399
                throws IOException {
 
400
        //check source exists
 
401
        if (!source.exists()) {
 
402
            String message = "File " + source + " does not exist";
 
403
            throw new FileNotFoundException(message);
 
404
        }
 
405
 
 
406
        //does destinations directory exist ?
 
407
        if (destination.getParentFile() != null
 
408
            && !destination.getParentFile().exists()) {
 
409
            destination.getParentFile().mkdirs();
 
410
        }
 
411
 
 
412
        //make sure we can write to destination
 
413
        if (destination.exists() && !destination.canWrite()) {
 
414
            String message =
 
415
                "Unable to open file " + destination + " for writing.";
 
416
            throw new IOException(message);
 
417
        }
 
418
 
 
419
        //makes sure it is not the same file        
 
420
        if (source.getCanonicalPath().equals(destination.getCanonicalPath())) {
 
421
            String message =
 
422
                "Unable to write file " + source + " on itself.";
 
423
            throw new IOException(message);
 
424
        }
 
425
 
 
426
        FileInputStream input = new FileInputStream(source);
 
427
        try {
 
428
            FileOutputStream output = new FileOutputStream(destination);
 
429
            try {
 
430
                CopyUtils.copy(input, output);
 
431
            } finally {
 
432
                IOUtils.closeQuietly(output);
 
433
            }
 
434
        } finally {
 
435
            IOUtils.closeQuietly(input);
 
436
        }
 
437
 
 
438
        if (source.length() != destination.length()) {
 
439
            String message =
 
440
                "Failed to copy full contents from "
 
441
                    + source
 
442
                    + " to "
 
443
                    + destination;
 
444
            throw new IOException(message);
 
445
        }
 
446
        
 
447
        if (preserveFileDate) {
 
448
            //file copy should preserve file date
 
449
            destination.setLastModified(source.lastModified());        
 
450
        }
 
451
    }
 
452
 
 
453
    /**
 
454
     * Copies bytes from the URL <code>source</code> to a file <code>destination</code>.
 
455
     * The directories up to <code>destination</code> will be created if they don't already exist.
 
456
     * <code>destination</code> will be overwritten if it already exists.
 
457
     *
 
458
     * @param source A <code>URL</code> to copy bytes from.
 
459
     * @param destination A non-directory <code>File</code> to write bytes to (possibly
 
460
     * overwriting).
 
461
     *
 
462
     * @throws IOException if
 
463
     * <ul>
 
464
     *  <li><code>source</code> URL cannot be opened</li>
 
465
     *  <li><code>destination</code> cannot be written to</li>
 
466
     *  <li>an IO error occurs during copying</li>
 
467
     * </ul>
 
468
     */
 
469
    public static void copyURLToFile(URL source, File destination)
 
470
                throws IOException {
 
471
        //does destination directory exist ?
 
472
        if (destination.getParentFile() != null
 
473
            && !destination.getParentFile().exists()) {
 
474
            destination.getParentFile().mkdirs();
 
475
        }
 
476
 
 
477
        //make sure we can write to destination
 
478
        if (destination.exists() && !destination.canWrite()) {
 
479
            String message =
 
480
                "Unable to open file " + destination + " for writing.";
 
481
            throw new IOException(message);
 
482
        }
 
483
 
 
484
        InputStream input = source.openStream();
 
485
        try {
 
486
            FileOutputStream output = new FileOutputStream(destination);
 
487
            try {
 
488
                CopyUtils.copy(input, output);
 
489
            } finally {
 
490
                IOUtils.closeQuietly(output);
 
491
            }
 
492
        } finally {
 
493
            IOUtils.closeQuietly(input);
 
494
        }
 
495
    }
 
496
 
 
497
 
 
498
    /**
 
499
     * Recursively delete a directory.
 
500
     * @param directory directory to delete
 
501
     * @throws IOException in case deletion is unsuccessful
 
502
     */
 
503
    public static void deleteDirectory(File directory)
 
504
        throws IOException {
 
505
        if (!directory.exists()) {
 
506
            return;
 
507
        }
 
508
 
 
509
        cleanDirectory(directory);
 
510
        if (!directory.delete()) {
 
511
            String message =
 
512
                "Unable to delete directory " + directory + ".";
 
513
            throw new IOException(message);
 
514
        }
 
515
    }
 
516
 
 
517
    /**
 
518
     * Clean a directory without deleting it.
 
519
     * @param directory directory to clean
 
520
     * @throws IOException in case cleaning is unsuccessful
 
521
     */
 
522
    public static void cleanDirectory(File directory)
 
523
        throws IOException {
 
524
        if (!directory.exists()) {
 
525
            String message = directory + " does not exist";
 
526
            throw new IllegalArgumentException(message);
 
527
        }
 
528
 
 
529
        if (!directory.isDirectory()) {
 
530
            String message = directory + " is not a directory";
 
531
            throw new IllegalArgumentException(message);
 
532
        }
 
533
 
 
534
        IOException exception = null;
 
535
 
 
536
        File[] files = directory.listFiles();
 
537
        for (int i = 0; i < files.length; i++) {
 
538
            File file = files[i];
 
539
            try {
 
540
                forceDelete(file);
 
541
            } catch (IOException ioe) {
 
542
                exception = ioe;
 
543
            }
 
544
        }
 
545
 
 
546
        if (null != exception) {
 
547
            throw exception;
 
548
        }
 
549
    }
 
550
 
 
551
    /**
 
552
     * Waits for NFS to propagate a file creation, imposing a timeout.
 
553
     *
 
554
     * @param file The file
 
555
     * @param seconds The maximum time in seconds to wait.
 
556
     * @return True if file exists.
 
557
     * TODO Needs a clearer javadoc to see its real purpose for someone without
 
558
     *       NFS-knowledge.
 
559
     */
 
560
    public static boolean waitFor(File file, int seconds) {
 
561
        int timeout = 0;
 
562
        int tick = 0;
 
563
        while (!file.exists()) {
 
564
            if (tick++ >= 10) {
 
565
                tick = 0;
 
566
                if (timeout++ > seconds) {
 
567
                    return false;
 
568
                }
 
569
            }
 
570
            try {
 
571
                Thread.sleep(100);
 
572
            } catch (InterruptedException ignore) {} catch (Exception ex) {
 
573
                break;
 
574
            }
 
575
        }
 
576
        return true;
 
577
    }
 
578
 
 
579
 
 
580
    /**
 
581
     * <p>
 
582
     * Reads the contents of a file into a String.
 
583
     * </p>
 
584
     * <p>
 
585
     * There is no readFileToString method without encoding parameter because
 
586
     * the default encoding can differ between platforms and therefore results
 
587
     * in inconsistent results.
 
588
     * </p>
 
589
     *
 
590
     * @param file the file to read.
 
591
     * @param encoding the encoding to use
 
592
     * @return The file contents or null if read failed.
 
593
     * @throws IOException in case of an I/O error
 
594
     * @throws UnsupportedEncodingException if the encoding is not supported
 
595
     *   by the VM
 
596
     */
 
597
    public static String readFileToString(
 
598
            File file, String encoding) throws IOException {
 
599
        InputStream in = new java.io.FileInputStream(file);
 
600
        try {
 
601
            return IOUtils.toString(in, encoding);
 
602
        } finally {
 
603
            IOUtils.closeQuietly(in);
 
604
        }
 
605
    }
 
606
 
 
607
    /**
 
608
     * <p>
 
609
     * Writes data to a file. The file will be created if it does not exist.
 
610
     * </p>
 
611
     * <p>
 
612
     * There is no readFileToString method without encoding parameter because
 
613
     * the default encoding can differ between platforms and therefore results
 
614
     * in inconsistent results.
 
615
     * </p>
 
616
     *
 
617
     * @param file the file to write.
 
618
     * @param data The content to write to the file.
 
619
     * @param encoding encoding to use
 
620
     * @throws IOException in case of an I/O error
 
621
     * @throws UnsupportedEncodingException if the encoding is not supported
 
622
     *   by the VM
 
623
     */
 
624
    public static void writeStringToFile(File file, 
 
625
            String data, String encoding) throws IOException {
 
626
        OutputStream out = new java.io.FileOutputStream(file);
 
627
        try {
 
628
            out.write(data.getBytes(encoding));
 
629
        } finally {
 
630
            IOUtils.closeQuietly(out);
 
631
        }
 
632
    }
 
633
 
 
634
    /**
 
635
     * <p>
 
636
     * Delete a file. If file is a directory, delete it and all sub-directories.
 
637
     * </p>
 
638
     * <p>
 
639
     * The difference between File.delete() and this method are:
 
640
     * </p>
 
641
     * <ul>
 
642
     * <li>A directory to be deleted does not have to be empty.</li>
 
643
     * <li>You get exceptions when a file or directory cannot be deleted. 
 
644
     *      (java.io.File methods returns a boolean)</li>
 
645
     * </ul>
 
646
     * @param file file or directory to delete.
 
647
     * @throws IOException in case deletion is unsuccessful
 
648
     */
 
649
    public static void forceDelete(File file) throws IOException {
 
650
        if (file.isDirectory()) {
 
651
            deleteDirectory(file);
 
652
        } else {
 
653
            if (!file.exists()) {
 
654
                throw new FileNotFoundException("File does not exist: " + file);
 
655
            }
 
656
            if (!file.delete()) {
 
657
                String message =
 
658
                    "Unable to delete file: " + file;
 
659
                throw new IOException(message);
 
660
            }
 
661
        }
 
662
    }
 
663
 
 
664
    /**
 
665
     * Schedule a file to be deleted when JVM exits.
 
666
     * If file is directory delete it and all sub-directories.
 
667
     * @param file file or directory to delete.
 
668
     * @throws IOException in case deletion is unsuccessful
 
669
     */
 
670
    public static void forceDeleteOnExit(File file) throws IOException {
 
671
        if (file.isDirectory()) {
 
672
            deleteDirectoryOnExit(file);
 
673
        } else {
 
674
            file.deleteOnExit();
 
675
        }
 
676
    }
 
677
 
 
678
    /**
 
679
     * Recursively schedule directory for deletion on JVM exit.
 
680
     * @param directory directory to delete.
 
681
     * @throws IOException in case deletion is unsuccessful
 
682
     */
 
683
    private static void deleteDirectoryOnExit(File directory)
 
684
            throws IOException {
 
685
        if (!directory.exists()) {
 
686
            return;
 
687
        }
 
688
 
 
689
        cleanDirectoryOnExit(directory);
 
690
        directory.deleteOnExit();
 
691
    }
 
692
 
 
693
    /**
 
694
     * Clean a directory without deleting it.
 
695
     * @param directory directory to clean.
 
696
     * @throws IOException in case cleaning is unsuccessful
 
697
     */
 
698
    private static void cleanDirectoryOnExit(File directory)
 
699
            throws IOException {
 
700
        if (!directory.exists()) {
 
701
            String message = directory + " does not exist";
 
702
            throw new IllegalArgumentException(message);
 
703
        }
 
704
 
 
705
        if (!directory.isDirectory()) {
 
706
            String message = directory + " is not a directory";
 
707
            throw new IllegalArgumentException(message);
 
708
        }
 
709
 
 
710
        IOException exception = null;
 
711
 
 
712
        File[] files = directory.listFiles();
 
713
        for (int i = 0; i < files.length; i++) {
 
714
            File file = files[i];
 
715
            try {
 
716
                forceDeleteOnExit(file);
 
717
            } catch (IOException ioe) {
 
718
                exception = ioe;
 
719
            }
 
720
        }
 
721
 
 
722
        if (null != exception) {
 
723
            throw exception;
 
724
        }
 
725
    }
 
726
 
 
727
 
 
728
    /**
 
729
     * Make a directory. If there already exists a file with specified name or
 
730
     * the directory cannot be created then an exception is thrown.
 
731
     * @param directory directory to create
 
732
     * @throws IOException if the directory cannot be created.
 
733
     */
 
734
    public static void forceMkdir(File directory) throws IOException {
 
735
        if (directory.exists()) {
 
736
            if (directory.isFile()) {
 
737
                String message =
 
738
                    "File "
 
739
                        + directory
 
740
                        + " exists and is "
 
741
                        + "not a directory. Unable to create directory.";
 
742
                throw new IOException(message);
 
743
            }
 
744
        } else {
 
745
            if (false == directory.mkdirs()) {
 
746
                String message =
 
747
                    "Unable to create directory " + directory;
 
748
                throw new IOException(message);
 
749
            }
 
750
        }
 
751
    }
 
752
 
 
753
    /**
 
754
     * Recursively count size of a directory (sum of the length of all files).
 
755
     *
 
756
     * @param directory directory to inspect
 
757
     * @return size of directory in bytes.
 
758
     */
 
759
    public static long sizeOfDirectory(File directory) {
 
760
        if (!directory.exists()) {
 
761
            String message = directory + " does not exist";
 
762
            throw new IllegalArgumentException(message);
 
763
        }
 
764
 
 
765
        if (!directory.isDirectory()) {
 
766
            String message = directory + " is not a directory";
 
767
            throw new IllegalArgumentException(message);
 
768
        }
 
769
 
 
770
        long size = 0;
 
771
 
 
772
        File[] files = directory.listFiles();
 
773
        for (int i = 0; i < files.length; i++) {
 
774
            File file = files[i];
 
775
 
 
776
            if (file.isDirectory()) {
 
777
                size += sizeOfDirectory(file);
 
778
            } else {
 
779
                size += file.length();
 
780
            }
 
781
        }
 
782
 
 
783
        return size;
 
784
    }
 
785
   
 
786
     /**
 
787
      * Tests if the specified <code>File</code> is newer than the reference 
 
788
      * <code>File</code>.
 
789
      *
 
790
      * @param file the <code>File</code> of which the modification date must be compared
 
791
      * @param reference the <code>File</code> of which the modification date is used 
 
792
      * like reference
 
793
      * @return true if the <code>File</code> exists and has been modified more recently
 
794
      * than the reference <code>File</code>.
 
795
      */
 
796
     public static boolean isFileNewer(File file, File reference) {
 
797
         if (reference == null) {
 
798
             throw new IllegalArgumentException("No specified reference file");
 
799
         }
 
800
         if (!reference.exists()) {
 
801
             throw new IllegalArgumentException("The reference file '" + file + "' doesn't exist");
 
802
         }
 
803
 
 
804
         return isFileNewer(file, reference.lastModified());
 
805
     }
 
806
 
 
807
     /**
 
808
      * Tests if the specified <code>File</code> is newer than the specified 
 
809
      * <code>Date</code>
 
810
      *
 
811
      * @param file the <code>File</code> of which the modification date must be compared
 
812
      * @param date the date reference
 
813
      * @return true if the <code>File</code> exists and has been modified after
 
814
      * the given <code>Date</code>.
 
815
      */
 
816
     public static boolean isFileNewer(File file, Date date) {
 
817
         if (date == null) {
 
818
             throw new IllegalArgumentException("No specified date");
 
819
         }
 
820
         return isFileNewer(file, date.getTime());
 
821
     }
 
822
 
 
823
     /**
 
824
      * Tests if the specified <code>File</code> is newer than the specified 
 
825
      * time reference.
 
826
      *
 
827
      * @param file the <code>File</code> of which the modification date must be compared.
 
828
      * @param timeMillis the time reference measured in milliseconds since the epoch 
 
829
      * (00:00:00 GMT, January 1, 1970)
 
830
      * @return true if the <code>File</code> exists and has been modified after
 
831
      * the given time reference.
 
832
      */
 
833
     public static boolean isFileNewer(File file, long timeMillis) {
 
834
         if (file == null) {
 
835
             throw new IllegalArgumentException("No specified file");
 
836
         }
 
837
         if (!file.exists()) {
 
838
             return false;
 
839
         }
 
840
 
 
841
         return file.lastModified() > timeMillis;
 
842
    }
 
843
 
 
844
}