001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.io;
018
019 import java.io.File;
020 import java.io.FileFilter;
021 import java.io.FileInputStream;
022 import java.io.FileNotFoundException;
023 import java.io.FileOutputStream;
024 import java.io.IOException;
025 import java.io.InputStream;
026 import java.io.InputStreamReader;
027 import java.io.OutputStream;
028 import java.io.Reader;
029 import java.math.BigInteger;
030 import java.net.URL;
031 import java.net.URLConnection;
032 import java.nio.ByteBuffer;
033 import java.nio.channels.FileChannel;
034 import java.nio.charset.Charset;
035 import java.util.ArrayList;
036 import java.util.Collection;
037 import java.util.Date;
038 import java.util.Iterator;
039 import java.util.List;
040 import java.util.zip.CRC32;
041 import java.util.zip.CheckedInputStream;
042 import java.util.zip.Checksum;
043
044 import org.apache.commons.io.filefilter.DirectoryFileFilter;
045 import org.apache.commons.io.filefilter.FalseFileFilter;
046 import org.apache.commons.io.filefilter.FileFilterUtils;
047 import org.apache.commons.io.filefilter.IOFileFilter;
048 import org.apache.commons.io.filefilter.SuffixFileFilter;
049 import org.apache.commons.io.filefilter.TrueFileFilter;
050 import org.apache.commons.io.output.NullOutputStream;
051
052 /**
053 * General file manipulation utilities.
054 * <p>
055 * Facilities are provided in the following areas:
056 * <ul>
057 * <li>writing to a file
058 * <li>reading from a file
059 * <li>make a directory including parent directories
060 * <li>copying files and directories
061 * <li>deleting files and directories
062 * <li>converting to and from a URL
063 * <li>listing files and directories by filter and extension
064 * <li>comparing file content
065 * <li>file last changed date
066 * <li>calculating a checksum
067 * </ul>
068 * <p>
069 * Origin of code: Excalibur, Alexandria, Commons-Utils
070 *
071 * @version $Id: FileUtils.java 1304052 2012-03-22 20:55:29Z ggregory $
072 */
073 public class FileUtils {
074
075 /**
076 * Instances should NOT be constructed in standard programming.
077 */
078 public FileUtils() {
079 super();
080 }
081
082 /**
083 * The number of bytes in a kilobyte.
084 */
085 public static final long ONE_KB = 1024;
086
087 /**
088 * The number of bytes in a megabyte.
089 */
090 public static final long ONE_MB = ONE_KB * ONE_KB;
091
092 /**
093 * The file copy buffer size (30 MB)
094 */
095 private static final long FILE_COPY_BUFFER_SIZE = ONE_MB * 30;
096
097 /**
098 * The number of bytes in a gigabyte.
099 */
100 public static final long ONE_GB = ONE_KB * ONE_MB;
101
102 /**
103 * The number of bytes in a terabyte.
104 */
105 public static final long ONE_TB = ONE_KB * ONE_GB;
106
107 /**
108 * The number of bytes in a petabyte.
109 */
110 public static final long ONE_PB = ONE_KB * ONE_TB;
111
112 /**
113 * The number of bytes in an exabyte.
114 */
115 public static final long ONE_EB = ONE_KB * ONE_PB;
116
117 /**
118 * The number of bytes in a zettabyte.
119 */
120 public static final BigInteger ONE_ZB = BigInteger.valueOf(ONE_KB).multiply(BigInteger.valueOf(ONE_EB));
121
122 /**
123 * The number of bytes in a yottabyte.
124 */
125 public static final BigInteger ONE_YB = ONE_ZB.multiply(BigInteger.valueOf(ONE_EB));
126
127 /**
128 * An empty array of type <code>File</code>.
129 */
130 public static final File[] EMPTY_FILE_ARRAY = new File[0];
131
132 /**
133 * The UTF-8 character set, used to decode octets in URLs.
134 */
135 private static final Charset UTF8 = Charset.forName("UTF-8");
136
137 //-----------------------------------------------------------------------
138 /**
139 * Construct a file from the set of name elements.
140 *
141 * @param directory the parent directory
142 * @param names the name elements
143 * @return the file
144 * @since 2.1
145 */
146 public static File getFile(File directory, String... names) {
147 if (directory == null) {
148 throw new NullPointerException("directorydirectory must not be null");
149 }
150 if (names == null) {
151 throw new NullPointerException("names must not be null");
152 }
153 File file = directory;
154 for (String name : names) {
155 file = new File(file, name);
156 }
157 return file;
158 }
159
160 /**
161 * Construct a file from the set of name elements.
162 *
163 * @param names the name elements
164 * @return the file
165 * @since 2.1
166 */
167 public static File getFile(String... names) {
168 if (names == null) {
169 throw new NullPointerException("names must not be null");
170 }
171 File file = null;
172 for (String name : names) {
173 if (file == null) {
174 file = new File(name);
175 } else {
176 file = new File(file, name);
177 }
178 }
179 return file;
180 }
181
182 /**
183 * Returns the path to the system temporary directory.
184 *
185 * @return the path to the system temporary directory.
186 *
187 * @since 2.0
188 */
189 public static String getTempDirectoryPath() {
190 return System.getProperty("java.io.tmpdir");
191 }
192
193 /**
194 * Returns a {@link File} representing the system temporary directory.
195 *
196 * @return the system temporary directory.
197 *
198 * @since 2.0
199 */
200 public static File getTempDirectory() {
201 return new File(getTempDirectoryPath());
202 }
203
204 /**
205 * Returns the path to the user's home directory.
206 *
207 * @return the path to the user's home directory.
208 *
209 * @since 2.0
210 */
211 public static String getUserDirectoryPath() {
212 return System.getProperty("user.home");
213 }
214
215 /**
216 * Returns a {@link File} representing the user's home directory.
217 *
218 * @return the user's home directory.
219 *
220 * @since 2.0
221 */
222 public static File getUserDirectory() {
223 return new File(getUserDirectoryPath());
224 }
225
226 //-----------------------------------------------------------------------
227 /**
228 * Opens a {@link FileInputStream} for the specified file, providing better
229 * error messages than simply calling <code>new FileInputStream(file)</code>.
230 * <p>
231 * At the end of the method either the stream will be successfully opened,
232 * or an exception will have been thrown.
233 * <p>
234 * An exception is thrown if the file does not exist.
235 * An exception is thrown if the file object exists but is a directory.
236 * An exception is thrown if the file exists but cannot be read.
237 *
238 * @param file the file to open for input, must not be <code>null</code>
239 * @return a new {@link FileInputStream} for the specified file
240 * @throws FileNotFoundException if the file does not exist
241 * @throws IOException if the file object is a directory
242 * @throws IOException if the file cannot be read
243 * @since 1.3
244 */
245 public static FileInputStream openInputStream(File file) throws IOException {
246 if (file.exists()) {
247 if (file.isDirectory()) {
248 throw new IOException("File '" + file + "' exists but is a directory");
249 }
250 if (file.canRead() == false) {
251 throw new IOException("File '" + file + "' cannot be read");
252 }
253 } else {
254 throw new FileNotFoundException("File '" + file + "' does not exist");
255 }
256 return new FileInputStream(file);
257 }
258
259 //-----------------------------------------------------------------------
260 /**
261 * Opens a {@link FileOutputStream} for the specified file, checking and
262 * creating the parent directory if it does not exist.
263 * <p>
264 * At the end of the method either the stream will be successfully opened,
265 * or an exception will have been thrown.
266 * <p>
267 * The parent directory will be created if it does not exist.
268 * The file will be created if it does not exist.
269 * An exception is thrown if the file object exists but is a directory.
270 * An exception is thrown if the file exists but cannot be written to.
271 * An exception is thrown if the parent directory cannot be created.
272 *
273 * @param file the file to open for output, must not be <code>null</code>
274 * @return a new {@link FileOutputStream} for the specified file
275 * @throws IOException if the file object is a directory
276 * @throws IOException if the file cannot be written to
277 * @throws IOException if a parent directory needs creating but that fails
278 * @since 1.3
279 */
280 public static FileOutputStream openOutputStream(File file) throws IOException {
281 return openOutputStream(file, false);
282 }
283
284 /**
285 * Opens a {@link FileOutputStream} for the specified file, checking and
286 * creating the parent directory if it does not exist.
287 * <p>
288 * At the end of the method either the stream will be successfully opened,
289 * or an exception will have been thrown.
290 * <p>
291 * The parent directory will be created if it does not exist.
292 * The file will be created if it does not exist.
293 * An exception is thrown if the file object exists but is a directory.
294 * An exception is thrown if the file exists but cannot be written to.
295 * An exception is thrown if the parent directory cannot be created.
296 *
297 * @param file the file to open for output, must not be <code>null</code>
298 * @param append if <code>true</code>, then bytes will be added to the
299 * end of the file rather than overwriting
300 * @return a new {@link FileOutputStream} for the specified file
301 * @throws IOException if the file object is a directory
302 * @throws IOException if the file cannot be written to
303 * @throws IOException if a parent directory needs creating but that fails
304 * @since 2.1
305 */
306 public static FileOutputStream openOutputStream(File file, boolean append) throws IOException {
307 if (file.exists()) {
308 if (file.isDirectory()) {
309 throw new IOException("File '" + file + "' exists but is a directory");
310 }
311 if (file.canWrite() == false) {
312 throw new IOException("File '" + file + "' cannot be written to");
313 }
314 } else {
315 File parent = file.getParentFile();
316 if (parent != null) {
317 if (!parent.mkdirs() && !parent.isDirectory()) {
318 throw new IOException("Directory '" + parent + "' could not be created");
319 }
320 }
321 }
322 return new FileOutputStream(file, append);
323 }
324
325 //-----------------------------------------------------------------------
326 /**
327 * Returns a human-readable version of the file size, where the input
328 * represents a specific number of bytes.
329 *
330 * If the size is over 1GB, the size is returned as the number of whole GB,
331 * i.e. the size is rounded down to the nearest GB boundary.
332 *
333 * Similarly for the 1MB and 1KB boundaries.
334 *
335 * @param size the number of bytes
336 * @return a human-readable display value (includes units - GB, MB, KB or bytes)
337 */
338 // See https://issues.apache.org/jira/browse/IO-226 - should the rounding be changed?
339 public static String byteCountToDisplaySize(long size) {
340 String displaySize;
341
342 if (size / ONE_EB > 0) {
343 displaySize = String.valueOf(size / ONE_EB) + " EB";
344 } else if (size / ONE_PB > 0) {
345 displaySize = String.valueOf(size / ONE_PB) + " PB";
346 } else if (size / ONE_TB > 0) {
347 displaySize = String.valueOf(size / ONE_TB) + " TB";
348 } else if (size / ONE_GB > 0) {
349 displaySize = String.valueOf(size / ONE_GB) + " GB";
350 } else if (size / ONE_MB > 0) {
351 displaySize = String.valueOf(size / ONE_MB) + " MB";
352 } else if (size / ONE_KB > 0) {
353 displaySize = String.valueOf(size / ONE_KB) + " KB";
354 } else {
355 displaySize = String.valueOf(size) + " bytes";
356 }
357 return displaySize;
358 }
359
360 //-----------------------------------------------------------------------
361 /**
362 * Implements the same behaviour as the "touch" utility on Unix. It creates
363 * a new file with size 0 or, if the file exists already, it is opened and
364 * closed without modifying it, but updating the file date and time.
365 * <p>
366 * NOTE: As from v1.3, this method throws an IOException if the last
367 * modified date of the file cannot be set. Also, as from v1.3 this method
368 * creates parent directories if they do not exist.
369 *
370 * @param file the File to touch
371 * @throws IOException If an I/O problem occurs
372 */
373 public static void touch(File file) throws IOException {
374 if (!file.exists()) {
375 OutputStream out = openOutputStream(file);
376 IOUtils.closeQuietly(out);
377 }
378 boolean success = file.setLastModified(System.currentTimeMillis());
379 if (!success) {
380 throw new IOException("Unable to set the last modification time for " + file);
381 }
382 }
383
384 //-----------------------------------------------------------------------
385 /**
386 * Converts a Collection containing java.io.File instanced into array
387 * representation. This is to account for the difference between
388 * File.listFiles() and FileUtils.listFiles().
389 *
390 * @param files a Collection containing java.io.File instances
391 * @return an array of java.io.File
392 */
393 public static File[] convertFileCollectionToFileArray(Collection<File> files) {
394 return files.toArray(new File[files.size()]);
395 }
396
397 //-----------------------------------------------------------------------
398 /**
399 * Finds files within a given directory (and optionally its
400 * subdirectories). All files found are filtered by an IOFileFilter.
401 *
402 * @param files the collection of files found.
403 * @param directory the directory to search in.
404 * @param filter the filter to apply to files and directories.
405 * @param includeSubDirectories indicates if will include the subdirectories themselves
406 */
407 private static void innerListFiles(Collection<File> files, File directory,
408 IOFileFilter filter, boolean includeSubDirectories) {
409 File[] found = directory.listFiles((FileFilter) filter);
410
411 if (found != null) {
412 for (File file : found) {
413 if (file.isDirectory()) {
414 if (includeSubDirectories) {
415 files.add(file);
416 }
417 innerListFiles(files, file, filter, includeSubDirectories);
418 } else {
419 files.add(file);
420 }
421 }
422 }
423 }
424
425 /**
426 * Finds files within a given directory (and optionally its
427 * subdirectories). All files found are filtered by an IOFileFilter.
428 * <p>
429 * If your search should recurse into subdirectories you can pass in
430 * an IOFileFilter for directories. You don't need to bind a
431 * DirectoryFileFilter (via logical AND) to this filter. This method does
432 * that for you.
433 * <p>
434 * An example: If you want to search through all directories called
435 * "temp" you pass in <code>FileFilterUtils.NameFileFilter("temp")</code>
436 * <p>
437 * Another common usage of this method is find files in a directory
438 * tree but ignoring the directories generated CVS. You can simply pass
439 * in <code>FileFilterUtils.makeCVSAware(null)</code>.
440 *
441 * @param directory the directory to search in
442 * @param fileFilter filter to apply when finding files.
443 * @param dirFilter optional filter to apply when finding subdirectories.
444 * If this parameter is <code>null</code>, subdirectories will not be included in the
445 * search. Use TrueFileFilter.INSTANCE to match all directories.
446 * @return an collection of java.io.File with the matching files
447 * @see org.apache.commons.io.filefilter.FileFilterUtils
448 * @see org.apache.commons.io.filefilter.NameFileFilter
449 */
450 public static Collection<File> listFiles(
451 File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
452 validateListFilesParameters(directory, fileFilter);
453
454 IOFileFilter effFileFilter = setUpEffectiveFileFilter(fileFilter);
455 IOFileFilter effDirFilter = setUpEffectiveDirFilter(dirFilter);
456
457 //Find files
458 Collection<File> files = new java.util.LinkedList<File>();
459 innerListFiles(files, directory,
460 FileFilterUtils.or(effFileFilter, effDirFilter), false);
461 return files;
462 }
463
464 /**
465 * Validates the given arguments.
466 * <ul>
467 * <li>Throws {@link IllegalArgumentException} if {@code directory} is not a directory</li>
468 * <li>Throws {@link NullPointerException} if {@code fileFilter} is null</li>
469 * </ul>
470 *
471 * @param directory The File to test
472 * @param fileFilter The IOFileFilter to test
473 */
474 private static void validateListFilesParameters(File directory, IOFileFilter fileFilter) {
475 if (!directory.isDirectory()) {
476 throw new IllegalArgumentException("Parameter 'directory' is not a directory");
477 }
478 if (fileFilter == null) {
479 throw new NullPointerException("Parameter 'fileFilter' is null");
480 }
481 }
482
483 /**
484 * Returns a filter that accepts files in addition to the {@link File} objects accepted by the given filter.
485 *
486 * @param fileFilter a base filter to add to
487 * @return a filter that accepts files
488 */
489 private static IOFileFilter setUpEffectiveFileFilter(IOFileFilter fileFilter) {
490 return FileFilterUtils.and(fileFilter, FileFilterUtils.notFileFilter(DirectoryFileFilter.INSTANCE));
491 }
492
493 /**
494 * Returns a filter that accepts directories in addition to the {@link File} objects accepted by the given filter.
495 *
496 * @param dirFilter a base filter to add to
497 * @return a filter that accepts directories
498 */
499 private static IOFileFilter setUpEffectiveDirFilter(IOFileFilter dirFilter) {
500 return dirFilter == null ? FalseFileFilter.INSTANCE : FileFilterUtils.and(dirFilter,
501 DirectoryFileFilter.INSTANCE);
502 }
503
504 /**
505 * Finds files within a given directory (and optionally its
506 * subdirectories). All files found are filtered by an IOFileFilter.
507 * <p>
508 * The resulting collection includes the subdirectories themselves.
509 * <p>
510 * @see org.apache.commons.io.FileUtils#listFiles
511 *
512 * @param directory the directory to search in
513 * @param fileFilter filter to apply when finding files.
514 * @param dirFilter optional filter to apply when finding subdirectories.
515 * If this parameter is <code>null</code>, subdirectories will not be included in the
516 * search. Use TrueFileFilter.INSTANCE to match all directories.
517 * @return an collection of java.io.File with the matching files
518 * @see org.apache.commons.io.filefilter.FileFilterUtils
519 * @see org.apache.commons.io.filefilter.NameFileFilter
520 * @since 2.2
521 */
522 public static Collection<File> listFilesAndDirs(
523 File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
524 validateListFilesParameters(directory, fileFilter);
525
526 IOFileFilter effFileFilter = setUpEffectiveFileFilter(fileFilter);
527 IOFileFilter effDirFilter = setUpEffectiveDirFilter(dirFilter);
528
529 //Find files
530 Collection<File> files = new java.util.LinkedList<File>();
531 if (directory.isDirectory()) {
532 files.add(directory);
533 }
534 innerListFiles(files, directory,
535 FileFilterUtils.or(effFileFilter, effDirFilter), true);
536 return files;
537 }
538
539 /**
540 * Allows iteration over the files in given directory (and optionally
541 * its subdirectories).
542 * <p>
543 * All files found are filtered by an IOFileFilter. This method is
544 * based on {@link #listFiles(File, IOFileFilter, IOFileFilter)},
545 * which supports Iterable ('foreach' loop).
546 * <p>
547 * @param directory the directory to search in
548 * @param fileFilter filter to apply when finding files.
549 * @param dirFilter optional filter to apply when finding subdirectories.
550 * If this parameter is <code>null</code>, subdirectories will not be included in the
551 * search. Use TrueFileFilter.INSTANCE to match all directories.
552 * @return an iterator of java.io.File for the matching files
553 * @see org.apache.commons.io.filefilter.FileFilterUtils
554 * @see org.apache.commons.io.filefilter.NameFileFilter
555 * @since 1.2
556 */
557 public static Iterator<File> iterateFiles(
558 File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
559 return listFiles(directory, fileFilter, dirFilter).iterator();
560 }
561
562 /**
563 * Allows iteration over the files in given directory (and optionally
564 * its subdirectories).
565 * <p>
566 * All files found are filtered by an IOFileFilter. This method is
567 * based on {@link #listFilesAndDirs(File, IOFileFilter, IOFileFilter)},
568 * which supports Iterable ('foreach' loop).
569 * <p>
570 * The resulting iterator includes the subdirectories themselves.
571 *
572 * @param directory the directory to search in
573 * @param fileFilter filter to apply when finding files.
574 * @param dirFilter optional filter to apply when finding subdirectories.
575 * If this parameter is <code>null</code>, subdirectories will not be included in the
576 * search. Use TrueFileFilter.INSTANCE to match all directories.
577 * @return an iterator of java.io.File for the matching files
578 * @see org.apache.commons.io.filefilter.FileFilterUtils
579 * @see org.apache.commons.io.filefilter.NameFileFilter
580 * @since 2.2
581 */
582 public static Iterator<File> iterateFilesAndDirs(File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
583 return listFilesAndDirs(directory, fileFilter, dirFilter).iterator();
584 }
585
586 //-----------------------------------------------------------------------
587 /**
588 * Converts an array of file extensions to suffixes for use
589 * with IOFileFilters.
590 *
591 * @param extensions an array of extensions. Format: {"java", "xml"}
592 * @return an array of suffixes. Format: {".java", ".xml"}
593 */
594 private static String[] toSuffixes(String[] extensions) {
595 String[] suffixes = new String[extensions.length];
596 for (int i = 0; i < extensions.length; i++) {
597 suffixes[i] = "." + extensions[i];
598 }
599 return suffixes;
600 }
601
602
603 /**
604 * Finds files within a given directory (and optionally its subdirectories)
605 * which match an array of extensions.
606 *
607 * @param directory the directory to search in
608 * @param extensions an array of extensions, ex. {"java","xml"}. If this
609 * parameter is <code>null</code>, all files are returned.
610 * @param recursive if true all subdirectories are searched as well
611 * @return an collection of java.io.File with the matching files
612 */
613 public static Collection<File> listFiles(
614 File directory, String[] extensions, boolean recursive) {
615 IOFileFilter filter;
616 if (extensions == null) {
617 filter = TrueFileFilter.INSTANCE;
618 } else {
619 String[] suffixes = toSuffixes(extensions);
620 filter = new SuffixFileFilter(suffixes);
621 }
622 return listFiles(directory, filter,
623 recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE);
624 }
625
626 /**
627 * Allows iteration over the files in a given directory (and optionally
628 * its subdirectories) which match an array of extensions. This method
629 * is based on {@link #listFiles(File, String[], boolean)},
630 * which supports Iterable ('foreach' loop).
631 *
632 * @param directory the directory to search in
633 * @param extensions an array of extensions, ex. {"java","xml"}. If this
634 * parameter is <code>null</code>, all files are returned.
635 * @param recursive if true all subdirectories are searched as well
636 * @return an iterator of java.io.File with the matching files
637 * @since 1.2
638 */
639 public static Iterator<File> iterateFiles(
640 File directory, String[] extensions, boolean recursive) {
641 return listFiles(directory, extensions, recursive).iterator();
642 }
643
644 //-----------------------------------------------------------------------
645 /**
646 * Compares the contents of two files to determine if they are equal or not.
647 * <p>
648 * This method checks to see if the two files are different lengths
649 * or if they point to the same file, before resorting to byte-by-byte
650 * comparison of the contents.
651 * <p>
652 * Code origin: Avalon
653 *
654 * @param file1 the first file
655 * @param file2 the second file
656 * @return true if the content of the files are equal or they both don't
657 * exist, false otherwise
658 * @throws IOException in case of an I/O error
659 */
660 public static boolean contentEquals(File file1, File file2) throws IOException {
661 boolean file1Exists = file1.exists();
662 if (file1Exists != file2.exists()) {
663 return false;
664 }
665
666 if (!file1Exists) {
667 // two not existing files are equal
668 return true;
669 }
670
671 if (file1.isDirectory() || file2.isDirectory()) {
672 // don't want to compare directory contents
673 throw new IOException("Can't compare directories, only files");
674 }
675
676 if (file1.length() != file2.length()) {
677 // lengths differ, cannot be equal
678 return false;
679 }
680
681 if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) {
682 // same file
683 return true;
684 }
685
686 InputStream input1 = null;
687 InputStream input2 = null;
688 try {
689 input1 = new FileInputStream(file1);
690 input2 = new FileInputStream(file2);
691 return IOUtils.contentEquals(input1, input2);
692
693 } finally {
694 IOUtils.closeQuietly(input1);
695 IOUtils.closeQuietly(input2);
696 }
697 }
698
699 //-----------------------------------------------------------------------
700 /**
701 * Compares the contents of two files to determine if they are equal or not.
702 * <p>
703 * This method checks to see if the two files point to the same file,
704 * before resorting to line-by-line comparison of the contents.
705 * <p>
706 *
707 * @param file1 the first file
708 * @param file2 the second file
709 * @param charsetName the character encoding to be used.
710 * May be null, in which case the platform default is used
711 * @return true if the content of the files are equal or neither exists,
712 * false otherwise
713 * @throws IOException in case of an I/O error
714 * @since 2.2
715 * @see IOUtils#contentEqualsIgnoreEOL(Reader, Reader)
716 */
717 public static boolean contentEqualsIgnoreEOL(File file1, File file2, String charsetName) throws IOException {
718 boolean file1Exists = file1.exists();
719 if (file1Exists != file2.exists()) {
720 return false;
721 }
722
723 if (!file1Exists) {
724 // two not existing files are equal
725 return true;
726 }
727
728 if (file1.isDirectory() || file2.isDirectory()) {
729 // don't want to compare directory contents
730 throw new IOException("Can't compare directories, only files");
731 }
732
733 if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) {
734 // same file
735 return true;
736 }
737
738 Reader input1 = null;
739 Reader input2 = null;
740 try {
741 if (charsetName == null) {
742 input1 = new InputStreamReader(new FileInputStream(file1));
743 input2 = new InputStreamReader(new FileInputStream(file2));
744 } else {
745 input1 = new InputStreamReader(new FileInputStream(file1), charsetName);
746 input2 = new InputStreamReader(new FileInputStream(file2), charsetName);
747 }
748 return IOUtils.contentEqualsIgnoreEOL(input1, input2);
749
750 } finally {
751 IOUtils.closeQuietly(input1);
752 IOUtils.closeQuietly(input2);
753 }
754 }
755
756 //-----------------------------------------------------------------------
757 /**
758 * Convert from a <code>URL</code> to a <code>File</code>.
759 * <p>
760 * From version 1.1 this method will decode the URL.
761 * Syntax such as <code>file:///my%20docs/file.txt</code> will be
762 * correctly decoded to <code>/my docs/file.txt</code>. Starting with version
763 * 1.5, this method uses UTF-8 to decode percent-encoded octets to characters.
764 * Additionally, malformed percent-encoded octets are handled leniently by
765 * passing them through literally.
766 *
767 * @param url the file URL to convert, <code>null</code> returns <code>null</code>
768 * @return the equivalent <code>File</code> object, or <code>null</code>
769 * if the URL's protocol is not <code>file</code>
770 */
771 public static File toFile(URL url) {
772 if (url == null || !"file".equalsIgnoreCase(url.getProtocol())) {
773 return null;
774 } else {
775 String filename = url.getFile().replace('/', File.separatorChar);
776 filename = decodeUrl(filename);
777 return new File(filename);
778 }
779 }
780
781 /**
782 * Decodes the specified URL as per RFC 3986, i.e. transforms
783 * percent-encoded octets to characters by decoding with the UTF-8 character
784 * set. This function is primarily intended for usage with
785 * {@link java.net.URL} which unfortunately does not enforce proper URLs. As
786 * such, this method will leniently accept invalid characters or malformed
787 * percent-encoded octets and simply pass them literally through to the
788 * result string. Except for rare edge cases, this will make unencoded URLs
789 * pass through unaltered.
790 *
791 * @param url The URL to decode, may be <code>null</code>.
792 * @return The decoded URL or <code>null</code> if the input was
793 * <code>null</code>.
794 */
795 static String decodeUrl(String url) {
796 String decoded = url;
797 if (url != null && url.indexOf('%') >= 0) {
798 int n = url.length();
799 StringBuffer buffer = new StringBuffer();
800 ByteBuffer bytes = ByteBuffer.allocate(n);
801 for (int i = 0; i < n;) {
802 if (url.charAt(i) == '%') {
803 try {
804 do {
805 byte octet = (byte) Integer.parseInt(url.substring(i + 1, i + 3), 16);
806 bytes.put(octet);
807 i += 3;
808 } while (i < n && url.charAt(i) == '%');
809 continue;
810 } catch (RuntimeException e) {
811 // malformed percent-encoded octet, fall through and
812 // append characters literally
813 } finally {
814 if (bytes.position() > 0) {
815 bytes.flip();
816 buffer.append(UTF8.decode(bytes).toString());
817 bytes.clear();
818 }
819 }
820 }
821 buffer.append(url.charAt(i++));
822 }
823 decoded = buffer.toString();
824 }
825 return decoded;
826 }
827
828 /**
829 * Converts each of an array of <code>URL</code> to a <code>File</code>.
830 * <p>
831 * Returns an array of the same size as the input.
832 * If the input is <code>null</code>, an empty array is returned.
833 * If the input contains <code>null</code>, the output array contains <code>null</code> at the same
834 * index.
835 * <p>
836 * This method will decode the URL.
837 * Syntax such as <code>file:///my%20docs/file.txt</code> will be
838 * correctly decoded to <code>/my docs/file.txt</code>.
839 *
840 * @param urls the file URLs to convert, <code>null</code> returns empty array
841 * @return a non-<code>null</code> array of Files matching the input, with a <code>null</code> item
842 * if there was a <code>null</code> at that index in the input array
843 * @throws IllegalArgumentException if any file is not a URL file
844 * @throws IllegalArgumentException if any file is incorrectly encoded
845 * @since 1.1
846 */
847 public static File[] toFiles(URL[] urls) {
848 if (urls == null || urls.length == 0) {
849 return EMPTY_FILE_ARRAY;
850 }
851 File[] files = new File[urls.length];
852 for (int i = 0; i < urls.length; i++) {
853 URL url = urls[i];
854 if (url != null) {
855 if (url.getProtocol().equals("file") == false) {
856 throw new IllegalArgumentException(
857 "URL could not be converted to a File: " + url);
858 }
859 files[i] = toFile(url);
860 }
861 }
862 return files;
863 }
864
865 /**
866 * Converts each of an array of <code>File</code> to a <code>URL</code>.
867 * <p>
868 * Returns an array of the same size as the input.
869 *
870 * @param files the files to convert
871 * @return an array of URLs matching the input
872 * @throws IOException if a file cannot be converted
873 */
874 public static URL[] toURLs(File[] files) throws IOException {
875 URL[] urls = new URL[files.length];
876
877 for (int i = 0; i < urls.length; i++) {
878 urls[i] = files[i].toURI().toURL();
879 }
880
881 return urls;
882 }
883
884 //-----------------------------------------------------------------------
885 /**
886 * Copies a file to a directory preserving the file date.
887 * <p>
888 * This method copies the contents of the specified source file
889 * to a file of the same name in the specified destination directory.
890 * The destination directory is created if it does not exist.
891 * If the destination file exists, then this method will overwrite it.
892 * <p>
893 * <strong>Note:</strong> This method tries to preserve the file's last
894 * modified date/times using {@link File#setLastModified(long)}, however
895 * it is not guaranteed that the operation will succeed.
896 * If the modification operation fails, no indication is provided.
897 *
898 * @param srcFile an existing file to copy, must not be <code>null</code>
899 * @param destDir the directory to place the copy in, must not be <code>null</code>
900 *
901 * @throws NullPointerException if source or destination is null
902 * @throws IOException if source or destination is invalid
903 * @throws IOException if an IO error occurs during copying
904 * @see #copyFile(File, File, boolean)
905 */
906 public static void copyFileToDirectory(File srcFile, File destDir) throws IOException {
907 copyFileToDirectory(srcFile, destDir, true);
908 }
909
910 /**
911 * Copies a file to a directory optionally preserving the file date.
912 * <p>
913 * This method copies the contents of the specified source file
914 * to a file of the same name in the specified destination directory.
915 * The destination directory is created if it does not exist.
916 * If the destination file exists, then this method will overwrite it.
917 * <p>
918 * <strong>Note:</strong> Setting <code>preserveFileDate</code> to
919 * <code>true</code> tries to preserve the file's last modified
920 * date/times using {@link File#setLastModified(long)}, however it is
921 * not guaranteed that the operation will succeed.
922 * If the modification operation fails, no indication is provided.
923 *
924 * @param srcFile an existing file to copy, must not be <code>null</code>
925 * @param destDir the directory to place the copy in, must not be <code>null</code>
926 * @param preserveFileDate true if the file date of the copy
927 * should be the same as the original
928 *
929 * @throws NullPointerException if source or destination is <code>null</code>
930 * @throws IOException if source or destination is invalid
931 * @throws IOException if an IO error occurs during copying
932 * @see #copyFile(File, File, boolean)
933 * @since 1.3
934 */
935 public static void copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate) throws IOException {
936 if (destDir == null) {
937 throw new NullPointerException("Destination must not be null");
938 }
939 if (destDir.exists() && destDir.isDirectory() == false) {
940 throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
941 }
942 File destFile = new File(destDir, srcFile.getName());
943 copyFile(srcFile, destFile, preserveFileDate);
944 }
945
946 /**
947 * Copies a file to a new location preserving the file date.
948 * <p>
949 * This method copies the contents of the specified source file to the
950 * specified destination file. The directory holding the destination file is
951 * created if it does not exist. If the destination file exists, then this
952 * method will overwrite it.
953 * <p>
954 * <strong>Note:</strong> This method tries to preserve the file's last
955 * modified date/times using {@link File#setLastModified(long)}, however
956 * it is not guaranteed that the operation will succeed.
957 * If the modification operation fails, no indication is provided.
958 *
959 * @param srcFile an existing file to copy, must not be <code>null</code>
960 * @param destFile the new file, must not be <code>null</code>
961 *
962 * @throws NullPointerException if source or destination is <code>null</code>
963 * @throws IOException if source or destination is invalid
964 * @throws IOException if an IO error occurs during copying
965 * @see #copyFileToDirectory(File, File)
966 */
967 public static void copyFile(File srcFile, File destFile) throws IOException {
968 copyFile(srcFile, destFile, true);
969 }
970
971 /**
972 * Copies a file to a new location.
973 * <p>
974 * This method copies the contents of the specified source file
975 * to the specified destination file.
976 * The directory holding the destination file is created if it does not exist.
977 * If the destination file exists, then this method will overwrite it.
978 * <p>
979 * <strong>Note:</strong> Setting <code>preserveFileDate</code> to
980 * <code>true</code> tries to preserve the file's last modified
981 * date/times using {@link File#setLastModified(long)}, however it is
982 * not guaranteed that the operation will succeed.
983 * If the modification operation fails, no indication is provided.
984 *
985 * @param srcFile an existing file to copy, must not be <code>null</code>
986 * @param destFile the new file, must not be <code>null</code>
987 * @param preserveFileDate true if the file date of the copy
988 * should be the same as the original
989 *
990 * @throws NullPointerException if source or destination is <code>null</code>
991 * @throws IOException if source or destination is invalid
992 * @throws IOException if an IO error occurs during copying
993 * @see #copyFileToDirectory(File, File, boolean)
994 */
995 public static void copyFile(File srcFile, File destFile,
996 boolean preserveFileDate) throws IOException {
997 if (srcFile == null) {
998 throw new NullPointerException("Source must not be null");
999 }
1000 if (destFile == null) {
1001 throw new NullPointerException("Destination must not be null");
1002 }
1003 if (srcFile.exists() == false) {
1004 throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
1005 }
1006 if (srcFile.isDirectory()) {
1007 throw new IOException("Source '" + srcFile + "' exists but is a directory");
1008 }
1009 if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) {
1010 throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same");
1011 }
1012 File parentFile = destFile.getParentFile();
1013 if (parentFile != null) {
1014 if (!parentFile.mkdirs() && !parentFile.isDirectory()) {
1015 throw new IOException("Destination '" + parentFile + "' directory cannot be created");
1016 }
1017 }
1018 if (destFile.exists() && destFile.canWrite() == false) {
1019 throw new IOException("Destination '" + destFile + "' exists but is read-only");
1020 }
1021 doCopyFile(srcFile, destFile, preserveFileDate);
1022 }
1023
1024 /**
1025 * Copy bytes from a <code>File</code> to an <code>OutputStream</code>.
1026 * <p>
1027 * This method buffers the input internally, so there is no need to use a <code>BufferedInputStream</code>.
1028 * </p>
1029 *
1030 * @param input
1031 * the <code>File</code> to read from
1032 * @param output
1033 * the <code>OutputStream</code> to write to
1034 * @return the number of bytes copied
1035 * @throws NullPointerException
1036 * if the input or output is null
1037 * @throws IOException
1038 * if an I/O error occurs
1039 * @since 2.1
1040 */
1041 public static long copyFile(File input, OutputStream output) throws IOException {
1042 final FileInputStream fis = new FileInputStream(input);
1043 try {
1044 return IOUtils.copyLarge(fis, output);
1045 } finally {
1046 fis.close();
1047 }
1048 }
1049
1050 /**
1051 * Internal copy file method.
1052 *
1053 * @param srcFile the validated source file, must not be <code>null</code>
1054 * @param destFile the validated destination file, must not be <code>null</code>
1055 * @param preserveFileDate whether to preserve the file date
1056 * @throws IOException if an error occurs
1057 */
1058 private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException {
1059 if (destFile.exists() && destFile.isDirectory()) {
1060 throw new IOException("Destination '" + destFile + "' exists but is a directory");
1061 }
1062
1063 FileInputStream fis = null;
1064 FileOutputStream fos = null;
1065 FileChannel input = null;
1066 FileChannel output = null;
1067 try {
1068 fis = new FileInputStream(srcFile);
1069 fos = new FileOutputStream(destFile);
1070 input = fis.getChannel();
1071 output = fos.getChannel();
1072 long size = input.size();
1073 long pos = 0;
1074 long count = 0;
1075 while (pos < size) {
1076 count = size - pos > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE : size - pos;
1077 pos += output.transferFrom(input, pos, count);
1078 }
1079 } finally {
1080 IOUtils.closeQuietly(output);
1081 IOUtils.closeQuietly(fos);
1082 IOUtils.closeQuietly(input);
1083 IOUtils.closeQuietly(fis);
1084 }
1085
1086 if (srcFile.length() != destFile.length()) {
1087 throw new IOException("Failed to copy full contents from '" +
1088 srcFile + "' to '" + destFile + "'");
1089 }
1090 if (preserveFileDate) {
1091 destFile.setLastModified(srcFile.lastModified());
1092 }
1093 }
1094
1095 //-----------------------------------------------------------------------
1096 /**
1097 * Copies a directory to within another directory preserving the file dates.
1098 * <p>
1099 * This method copies the source directory and all its contents to a
1100 * directory of the same name in the specified destination directory.
1101 * <p>
1102 * The destination directory is created if it does not exist.
1103 * If the destination directory did exist, then this method merges
1104 * the source with the destination, with the source taking precedence.
1105 * <p>
1106 * <strong>Note:</strong> This method tries to preserve the files' last
1107 * modified date/times using {@link File#setLastModified(long)}, however
1108 * it is not guaranteed that those operations will succeed.
1109 * If the modification operation fails, no indication is provided.
1110 *
1111 * @param srcDir an existing directory to copy, must not be <code>null</code>
1112 * @param destDir the directory to place the copy in, must not be <code>null</code>
1113 *
1114 * @throws NullPointerException if source or destination is <code>null</code>
1115 * @throws IOException if source or destination is invalid
1116 * @throws IOException if an IO error occurs during copying
1117 * @since 1.2
1118 */
1119 public static void copyDirectoryToDirectory(File srcDir, File destDir) throws IOException {
1120 if (srcDir == null) {
1121 throw new NullPointerException("Source must not be null");
1122 }
1123 if (srcDir.exists() && srcDir.isDirectory() == false) {
1124 throw new IllegalArgumentException("Source '" + destDir + "' is not a directory");
1125 }
1126 if (destDir == null) {
1127 throw new NullPointerException("Destination must not be null");
1128 }
1129 if (destDir.exists() && destDir.isDirectory() == false) {
1130 throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
1131 }
1132 copyDirectory(srcDir, new File(destDir, srcDir.getName()), true);
1133 }
1134
1135 /**
1136 * Copies a whole directory to a new location preserving the file dates.
1137 * <p>
1138 * This method copies the specified directory and all its child
1139 * directories and files to the specified destination.
1140 * The destination is the new location and name of the directory.
1141 * <p>
1142 * The destination directory is created if it does not exist.
1143 * If the destination directory did exist, then this method merges
1144 * the source with the destination, with the source taking precedence.
1145 * <p>
1146 * <strong>Note:</strong> This method tries to preserve the files' last
1147 * modified date/times using {@link File#setLastModified(long)}, however
1148 * it is not guaranteed that those operations will succeed.
1149 * If the modification operation fails, no indication is provided.
1150 *
1151 * @param srcDir an existing directory to copy, must not be <code>null</code>
1152 * @param destDir the new directory, must not be <code>null</code>
1153 *
1154 * @throws NullPointerException if source or destination is <code>null</code>
1155 * @throws IOException if source or destination is invalid
1156 * @throws IOException if an IO error occurs during copying
1157 * @since 1.1
1158 */
1159 public static void copyDirectory(File srcDir, File destDir) throws IOException {
1160 copyDirectory(srcDir, destDir, true);
1161 }
1162
1163 /**
1164 * Copies a whole directory to a new location.
1165 * <p>
1166 * This method copies the contents of the specified source directory
1167 * to within the specified destination directory.
1168 * <p>
1169 * The destination directory is created if it does not exist.
1170 * If the destination directory did exist, then this method merges
1171 * the source with the destination, with the source taking precedence.
1172 * <p>
1173 * <strong>Note:</strong> Setting <code>preserveFileDate</code> to
1174 * <code>true</code> tries to preserve the files' last modified
1175 * date/times using {@link File#setLastModified(long)}, however it is
1176 * not guaranteed that those operations will succeed.
1177 * If the modification operation fails, no indication is provided.
1178 *
1179 * @param srcDir an existing directory to copy, must not be <code>null</code>
1180 * @param destDir the new directory, must not be <code>null</code>
1181 * @param preserveFileDate true if the file date of the copy
1182 * should be the same as the original
1183 *
1184 * @throws NullPointerException if source or destination is <code>null</code>
1185 * @throws IOException if source or destination is invalid
1186 * @throws IOException if an IO error occurs during copying
1187 * @since 1.1
1188 */
1189 public static void copyDirectory(File srcDir, File destDir,
1190 boolean preserveFileDate) throws IOException {
1191 copyDirectory(srcDir, destDir, null, preserveFileDate);
1192 }
1193
1194 /**
1195 * Copies a filtered directory to a new location preserving the file dates.
1196 * <p>
1197 * This method copies the contents of the specified source directory
1198 * to within the specified destination directory.
1199 * <p>
1200 * The destination directory is created if it does not exist.
1201 * If the destination directory did exist, then this method merges
1202 * the source with the destination, with the source taking precedence.
1203 * <p>
1204 * <strong>Note:</strong> This method tries to preserve the files' last
1205 * modified date/times using {@link File#setLastModified(long)}, however
1206 * it is not guaranteed that those operations will succeed.
1207 * If the modification operation fails, no indication is provided.
1208 *
1209 * <h4>Example: Copy directories only</h4>
1210 * <pre>
1211 * // only copy the directory structure
1212 * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY);
1213 * </pre>
1214 *
1215 * <h4>Example: Copy directories and txt files</h4>
1216 * <pre>
1217 * // Create a filter for ".txt" files
1218 * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
1219 * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
1220 *
1221 * // Create a filter for either directories or ".txt" files
1222 * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
1223 *
1224 * // Copy using the filter
1225 * FileUtils.copyDirectory(srcDir, destDir, filter);
1226 * </pre>
1227 *
1228 * @param srcDir an existing directory to copy, must not be <code>null</code>
1229 * @param destDir the new directory, must not be <code>null</code>
1230 * @param filter the filter to apply, null means copy all directories and files
1231 * should be the same as the original
1232 *
1233 * @throws NullPointerException if source or destination is <code>null</code>
1234 * @throws IOException if source or destination is invalid
1235 * @throws IOException if an IO error occurs during copying
1236 * @since 1.4
1237 */
1238 public static void copyDirectory(File srcDir, File destDir,
1239 FileFilter filter) throws IOException {
1240 copyDirectory(srcDir, destDir, filter, true);
1241 }
1242
1243 /**
1244 * Copies a filtered directory to a new location.
1245 * <p>
1246 * This method copies the contents of the specified source directory
1247 * to within the specified destination directory.
1248 * <p>
1249 * The destination directory is created if it does not exist.
1250 * If the destination directory did exist, then this method merges
1251 * the source with the destination, with the source taking precedence.
1252 * <p>
1253 * <strong>Note:</strong> Setting <code>preserveFileDate</code> to
1254 * <code>true</code> tries to preserve the files' last modified
1255 * date/times using {@link File#setLastModified(long)}, however it is
1256 * not guaranteed that those operations will succeed.
1257 * If the modification operation fails, no indication is provided.
1258 *
1259 * <h4>Example: Copy directories only</h4>
1260 * <pre>
1261 * // only copy the directory structure
1262 * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false);
1263 * </pre>
1264 *
1265 * <h4>Example: Copy directories and txt files</h4>
1266 * <pre>
1267 * // Create a filter for ".txt" files
1268 * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
1269 * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
1270 *
1271 * // Create a filter for either directories or ".txt" files
1272 * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
1273 *
1274 * // Copy using the filter
1275 * FileUtils.copyDirectory(srcDir, destDir, filter, false);
1276 * </pre>
1277 *
1278 * @param srcDir an existing directory to copy, must not be <code>null</code>
1279 * @param destDir the new directory, must not be <code>null</code>
1280 * @param filter the filter to apply, null means copy all directories and files
1281 * @param preserveFileDate true if the file date of the copy
1282 * should be the same as the original
1283 *
1284 * @throws NullPointerException if source or destination is <code>null</code>
1285 * @throws IOException if source or destination is invalid
1286 * @throws IOException if an IO error occurs during copying
1287 * @since 1.4
1288 */
1289 public static void copyDirectory(File srcDir, File destDir,
1290 FileFilter filter, boolean preserveFileDate) throws IOException {
1291 if (srcDir == null) {
1292 throw new NullPointerException("Source must not be null");
1293 }
1294 if (destDir == null) {
1295 throw new NullPointerException("Destination must not be null");
1296 }
1297 if (srcDir.exists() == false) {
1298 throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
1299 }
1300 if (srcDir.isDirectory() == false) {
1301 throw new IOException("Source '" + srcDir + "' exists but is not a directory");
1302 }
1303 if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) {
1304 throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same");
1305 }
1306
1307 // Cater for destination being directory within the source directory (see IO-141)
1308 List<String> exclusionList = null;
1309 if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) {
1310 File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);
1311 if (srcFiles != null && srcFiles.length > 0) {
1312 exclusionList = new ArrayList<String>(srcFiles.length);
1313 for (File srcFile : srcFiles) {
1314 File copiedFile = new File(destDir, srcFile.getName());
1315 exclusionList.add(copiedFile.getCanonicalPath());
1316 }
1317 }
1318 }
1319 doCopyDirectory(srcDir, destDir, filter, preserveFileDate, exclusionList);
1320 }
1321
1322 /**
1323 * Internal copy directory method.
1324 *
1325 * @param srcDir the validated source directory, must not be <code>null</code>
1326 * @param destDir the validated destination directory, must not be <code>null</code>
1327 * @param filter the filter to apply, null means copy all directories and files
1328 * @param preserveFileDate whether to preserve the file date
1329 * @param exclusionList List of files and directories to exclude from the copy, may be null
1330 * @throws IOException if an error occurs
1331 * @since 1.1
1332 */
1333 private static void doCopyDirectory(File srcDir, File destDir, FileFilter filter,
1334 boolean preserveFileDate, List<String> exclusionList) throws IOException {
1335 // recurse
1336 File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);
1337 if (srcFiles == null) { // null if abstract pathname does not denote a directory, or if an I/O error occurs
1338 throw new IOException("Failed to list contents of " + srcDir);
1339 }
1340 if (destDir.exists()) {
1341 if (destDir.isDirectory() == false) {
1342 throw new IOException("Destination '" + destDir + "' exists but is not a directory");
1343 }
1344 } else {
1345 if (!destDir.mkdirs() && !destDir.isDirectory()) {
1346 throw new IOException("Destination '" + destDir + "' directory cannot be created");
1347 }
1348 }
1349 if (destDir.canWrite() == false) {
1350 throw new IOException("Destination '" + destDir + "' cannot be written to");
1351 }
1352 for (File srcFile : srcFiles) {
1353 File dstFile = new File(destDir, srcFile.getName());
1354 if (exclusionList == null || !exclusionList.contains(srcFile.getCanonicalPath())) {
1355 if (srcFile.isDirectory()) {
1356 doCopyDirectory(srcFile, dstFile, filter, preserveFileDate, exclusionList);
1357 } else {
1358 doCopyFile(srcFile, dstFile, preserveFileDate);
1359 }
1360 }
1361 }
1362
1363 // Do this last, as the above has probably affected directory metadata
1364 if (preserveFileDate) {
1365 destDir.setLastModified(srcDir.lastModified());
1366 }
1367 }
1368
1369 //-----------------------------------------------------------------------
1370 /**
1371 * Copies bytes from the URL <code>source</code> to a file
1372 * <code>destination</code>. The directories up to <code>destination</code>
1373 * will be created if they don't already exist. <code>destination</code>
1374 * will be overwritten if it already exists.
1375 * <p>
1376 * Warning: this method does not set a connection or read timeout and thus
1377 * might block forever. Use {@link #copyURLToFile(URL, File, int, int)}
1378 * with reasonable timeouts to prevent this.
1379 *
1380 * @param source the <code>URL</code> to copy bytes from, must not be <code>null</code>
1381 * @param destination the non-directory <code>File</code> to write bytes to
1382 * (possibly overwriting), must not be <code>null</code>
1383 * @throws IOException if <code>source</code> URL cannot be opened
1384 * @throws IOException if <code>destination</code> is a directory
1385 * @throws IOException if <code>destination</code> cannot be written
1386 * @throws IOException if <code>destination</code> needs creating but can't be
1387 * @throws IOException if an IO error occurs during copying
1388 */
1389 public static void copyURLToFile(URL source, File destination) throws IOException {
1390 InputStream input = source.openStream();
1391 copyInputStreamToFile(input, destination);
1392 }
1393
1394 /**
1395 * Copies bytes from the URL <code>source</code> to a file
1396 * <code>destination</code>. The directories up to <code>destination</code>
1397 * will be created if they don't already exist. <code>destination</code>
1398 * will be overwritten if it already exists.
1399 *
1400 * @param source the <code>URL</code> to copy bytes from, must not be <code>null</code>
1401 * @param destination the non-directory <code>File</code> to write bytes to
1402 * (possibly overwriting), must not be <code>null</code>
1403 * @param connectionTimeout the number of milliseconds until this method
1404 * will timeout if no connection could be established to the <code>source</code>
1405 * @param readTimeout the number of milliseconds until this method will
1406 * timeout if no data could be read from the <code>source</code>
1407 * @throws IOException if <code>source</code> URL cannot be opened
1408 * @throws IOException if <code>destination</code> is a directory
1409 * @throws IOException if <code>destination</code> cannot be written
1410 * @throws IOException if <code>destination</code> needs creating but can't be
1411 * @throws IOException if an IO error occurs during copying
1412 * @since 2.0
1413 */
1414 public static void copyURLToFile(URL source, File destination,
1415 int connectionTimeout, int readTimeout) throws IOException {
1416 URLConnection connection = source.openConnection();
1417 connection.setConnectTimeout(connectionTimeout);
1418 connection.setReadTimeout(readTimeout);
1419 InputStream input = connection.getInputStream();
1420 copyInputStreamToFile(input, destination);
1421 }
1422
1423 /**
1424 * Copies bytes from an {@link InputStream} <code>source</code> to a file
1425 * <code>destination</code>. The directories up to <code>destination</code>
1426 * will be created if they don't already exist. <code>destination</code>
1427 * will be overwritten if it already exists.
1428 *
1429 * @param source the <code>InputStream</code> to copy bytes from, must not be <code>null</code>
1430 * @param destination the non-directory <code>File</code> to write bytes to
1431 * (possibly overwriting), must not be <code>null</code>
1432 * @throws IOException if <code>destination</code> is a directory
1433 * @throws IOException if <code>destination</code> cannot be written
1434 * @throws IOException if <code>destination</code> needs creating but can't be
1435 * @throws IOException if an IO error occurs during copying
1436 * @since 2.0
1437 */
1438 public static void copyInputStreamToFile(InputStream source, File destination) throws IOException {
1439 try {
1440 FileOutputStream output = openOutputStream(destination);
1441 try {
1442 IOUtils.copy(source, output);
1443 output.close(); // don't swallow close Exception if copy completes normally
1444 } finally {
1445 IOUtils.closeQuietly(output);
1446 }
1447 } finally {
1448 IOUtils.closeQuietly(source);
1449 }
1450 }
1451
1452 //-----------------------------------------------------------------------
1453 /**
1454 * Deletes a directory recursively.
1455 *
1456 * @param directory directory to delete
1457 * @throws IOException in case deletion is unsuccessful
1458 */
1459 public static void deleteDirectory(File directory) throws IOException {
1460 if (!directory.exists()) {
1461 return;
1462 }
1463
1464 if (!isSymlink(directory)) {
1465 cleanDirectory(directory);
1466 }
1467
1468 if (!directory.delete()) {
1469 String message =
1470 "Unable to delete directory " + directory + ".";
1471 throw new IOException(message);
1472 }
1473 }
1474
1475 /**
1476 * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories.
1477 * <p>
1478 * The difference between File.delete() and this method are:
1479 * <ul>
1480 * <li>A directory to be deleted does not have to be empty.</li>
1481 * <li>No exceptions are thrown when a file or directory cannot be deleted.</li>
1482 * </ul>
1483 *
1484 * @param file file or directory to delete, can be <code>null</code>
1485 * @return <code>true</code> if the file or directory was deleted, otherwise
1486 * <code>false</code>
1487 *
1488 * @since 1.4
1489 */
1490 public static boolean deleteQuietly(File file) {
1491 if (file == null) {
1492 return false;
1493 }
1494 try {
1495 if (file.isDirectory()) {
1496 cleanDirectory(file);
1497 }
1498 } catch (Exception ignored) {
1499 }
1500
1501 try {
1502 return file.delete();
1503 } catch (Exception ignored) {
1504 return false;
1505 }
1506 }
1507
1508 /**
1509 * Determines whether the {@code parent} directory contains the {@code child} element (a file or directory).
1510 * <p>
1511 * Files are normalized before comparison.
1512 * </p>
1513 *
1514 * Edge cases:
1515 * <ul>
1516 * <li>A {@code directory} must not be null: if null, throw IllegalArgumentException</li>
1517 * <li>A {@code directory} must be a directory: if not a directory, throw IllegalArgumentException</li>
1518 * <li>A directory does not contain itself: return false</li>
1519 * <li>A null child file is not contained in any parent: return false</li>
1520 * </ul>
1521 *
1522 * @param directory
1523 * the file to consider as the parent.
1524 * @param child
1525 * the file to consider as the child.
1526 * @return true is the candidate leaf is under by the specified composite. False otherwise.
1527 * @throws IOException
1528 * if an IO error occurs while checking the files.
1529 * @since 2.2
1530 * @see FilenameUtils#directoryContains(String, String)
1531 */
1532 public static boolean directoryContains(final File directory, final File child) throws IOException {
1533
1534 // Fail fast against NullPointerException
1535 if (directory == null) {
1536 throw new IllegalArgumentException("Directory must not be null");
1537 }
1538
1539 if (!directory.isDirectory()) {
1540 throw new IllegalArgumentException("Not a directory: " + directory);
1541 }
1542
1543 if (child == null) {
1544 return false;
1545 }
1546
1547 if (!directory.exists() || !child.exists()) {
1548 return false;
1549 }
1550
1551 // Canonicalize paths (normalizes relative paths)
1552 String canonicalParent = directory.getCanonicalPath();
1553 String canonicalChild = child.getCanonicalPath();
1554
1555 return FilenameUtils.directoryContains(canonicalParent, canonicalChild);
1556 }
1557
1558 /**
1559 * Cleans a directory without deleting it.
1560 *
1561 * @param directory directory to clean
1562 * @throws IOException in case cleaning is unsuccessful
1563 */
1564 public static void cleanDirectory(File directory) throws IOException {
1565 if (!directory.exists()) {
1566 String message = directory + " does not exist";
1567 throw new IllegalArgumentException(message);
1568 }
1569
1570 if (!directory.isDirectory()) {
1571 String message = directory + " is not a directory";
1572 throw new IllegalArgumentException(message);
1573 }
1574
1575 File[] files = directory.listFiles();
1576 if (files == null) { // null if security restricted
1577 throw new IOException("Failed to list contents of " + directory);
1578 }
1579
1580 IOException exception = null;
1581 for (File file : files) {
1582 try {
1583 forceDelete(file);
1584 } catch (IOException ioe) {
1585 exception = ioe;
1586 }
1587 }
1588
1589 if (null != exception) {
1590 throw exception;
1591 }
1592 }
1593
1594 //-----------------------------------------------------------------------
1595 /**
1596 * Waits for NFS to propagate a file creation, imposing a timeout.
1597 * <p>
1598 * This method repeatedly tests {@link File#exists()} until it returns
1599 * true up to the maximum time specified in seconds.
1600 *
1601 * @param file the file to check, must not be <code>null</code>
1602 * @param seconds the maximum time in seconds to wait
1603 * @return true if file exists
1604 * @throws NullPointerException if the file is <code>null</code>
1605 */
1606 public static boolean waitFor(File file, int seconds) {
1607 int timeout = 0;
1608 int tick = 0;
1609 while (!file.exists()) {
1610 if (tick++ >= 10) {
1611 tick = 0;
1612 if (timeout++ > seconds) {
1613 return false;
1614 }
1615 }
1616 try {
1617 Thread.sleep(100);
1618 } catch (InterruptedException ignore) {
1619 // ignore exception
1620 } catch (Exception ex) {
1621 break;
1622 }
1623 }
1624 return true;
1625 }
1626
1627 //-----------------------------------------------------------------------
1628 /**
1629 * Reads the contents of a file into a String.
1630 * The file is always closed.
1631 *
1632 * @param file the file to read, must not be <code>null</code>
1633 * @param encoding the encoding to use, <code>null</code> means platform default
1634 * @return the file contents, never <code>null</code>
1635 * @throws IOException in case of an I/O error
1636 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1637 */
1638 public static String readFileToString(File file, String encoding) throws IOException {
1639 InputStream in = null;
1640 try {
1641 in = openInputStream(file);
1642 return IOUtils.toString(in, encoding);
1643 } finally {
1644 IOUtils.closeQuietly(in);
1645 }
1646 }
1647
1648
1649 /**
1650 * Reads the contents of a file into a String using the default encoding for the VM.
1651 * The file is always closed.
1652 *
1653 * @param file the file to read, must not be <code>null</code>
1654 * @return the file contents, never <code>null</code>
1655 * @throws IOException in case of an I/O error
1656 * @since 1.3.1
1657 */
1658 public static String readFileToString(File file) throws IOException {
1659 return readFileToString(file, null);
1660 }
1661
1662 /**
1663 * Reads the contents of a file into a byte array.
1664 * The file is always closed.
1665 *
1666 * @param file the file to read, must not be <code>null</code>
1667 * @return the file contents, never <code>null</code>
1668 * @throws IOException in case of an I/O error
1669 * @since 1.1
1670 */
1671 public static byte[] readFileToByteArray(File file) throws IOException {
1672 InputStream in = null;
1673 try {
1674 in = openInputStream(file);
1675 return IOUtils.toByteArray(in, file.length());
1676 } finally {
1677 IOUtils.closeQuietly(in);
1678 }
1679 }
1680
1681 /**
1682 * Reads the contents of a file line by line to a List of Strings.
1683 * The file is always closed.
1684 *
1685 * @param file the file to read, must not be <code>null</code>
1686 * @param encoding the encoding to use, <code>null</code> means platform default
1687 * @return the list of Strings representing each line in the file, never <code>null</code>
1688 * @throws IOException in case of an I/O error
1689 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1690 * @since 1.1
1691 */
1692 public static List<String> readLines(File file, String encoding) throws IOException {
1693 InputStream in = null;
1694 try {
1695 in = openInputStream(file);
1696 return IOUtils.readLines(in, encoding);
1697 } finally {
1698 IOUtils.closeQuietly(in);
1699 }
1700 }
1701
1702 /**
1703 * Reads the contents of a file line by line to a List of Strings using the default encoding for the VM.
1704 * The file is always closed.
1705 *
1706 * @param file the file to read, must not be <code>null</code>
1707 * @return the list of Strings representing each line in the file, never <code>null</code>
1708 * @throws IOException in case of an I/O error
1709 * @since 1.3
1710 */
1711 public static List<String> readLines(File file) throws IOException {
1712 return readLines(file, null);
1713 }
1714
1715 /**
1716 * Returns an Iterator for the lines in a <code>File</code>.
1717 * <p>
1718 * This method opens an <code>InputStream</code> for the file.
1719 * When you have finished with the iterator you should close the stream
1720 * to free internal resources. This can be done by calling the
1721 * {@link LineIterator#close()} or
1722 * {@link LineIterator#closeQuietly(LineIterator)} method.
1723 * <p>
1724 * The recommended usage pattern is:
1725 * <pre>
1726 * LineIterator it = FileUtils.lineIterator(file, "UTF-8");
1727 * try {
1728 * while (it.hasNext()) {
1729 * String line = it.nextLine();
1730 * /// do something with line
1731 * }
1732 * } finally {
1733 * LineIterator.closeQuietly(iterator);
1734 * }
1735 * </pre>
1736 * <p>
1737 * If an exception occurs during the creation of the iterator, the
1738 * underlying stream is closed.
1739 *
1740 * @param file the file to open for input, must not be <code>null</code>
1741 * @param encoding the encoding to use, <code>null</code> means platform default
1742 * @return an Iterator of the lines in the file, never <code>null</code>
1743 * @throws IOException in case of an I/O error (file closed)
1744 * @since 1.2
1745 */
1746 public static LineIterator lineIterator(File file, String encoding) throws IOException {
1747 InputStream in = null;
1748 try {
1749 in = openInputStream(file);
1750 return IOUtils.lineIterator(in, encoding);
1751 } catch (IOException ex) {
1752 IOUtils.closeQuietly(in);
1753 throw ex;
1754 } catch (RuntimeException ex) {
1755 IOUtils.closeQuietly(in);
1756 throw ex;
1757 }
1758 }
1759
1760 /**
1761 * Returns an Iterator for the lines in a <code>File</code> using the default encoding for the VM.
1762 *
1763 * @param file the file to open for input, must not be <code>null</code>
1764 * @return an Iterator of the lines in the file, never <code>null</code>
1765 * @throws IOException in case of an I/O error (file closed)
1766 * @since 1.3
1767 * @see #lineIterator(File, String)
1768 */
1769 public static LineIterator lineIterator(File file) throws IOException {
1770 return lineIterator(file, null);
1771 }
1772
1773 //-----------------------------------------------------------------------
1774 /**
1775 * Writes a String to a file creating the file if it does not exist.
1776 *
1777 * NOTE: As from v1.3, the parent directories of the file will be created
1778 * if they do not exist.
1779 *
1780 * @param file the file to write
1781 * @param data the content to write to the file
1782 * @param encoding the encoding to use, <code>null</code> means platform default
1783 * @throws IOException in case of an I/O error
1784 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1785 */
1786 public static void writeStringToFile(File file, String data, String encoding) throws IOException {
1787 writeStringToFile(file, data, encoding, false);
1788 }
1789
1790 /**
1791 * Writes a String to a file creating the file if it does not exist.
1792 *
1793 * @param file the file to write
1794 * @param data the content to write to the file
1795 * @param encoding the encoding to use, <code>null</code> means platform default
1796 * @param append if <code>true</code>, then the String will be added to the
1797 * end of the file rather than overwriting
1798 * @throws IOException in case of an I/O error
1799 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1800 * @since 2.1
1801 */
1802 public static void writeStringToFile(File file, String data, String encoding, boolean append) throws IOException {
1803 OutputStream out = null;
1804 try {
1805 out = openOutputStream(file, append);
1806 IOUtils.write(data, out, encoding);
1807 out.close(); // don't swallow close Exception if copy completes normally
1808 } finally {
1809 IOUtils.closeQuietly(out);
1810 }
1811 }
1812
1813 /**
1814 * Writes a String to a file creating the file if it does not exist using the default encoding for the VM.
1815 *
1816 * @param file the file to write
1817 * @param data the content to write to the file
1818 * @throws IOException in case of an I/O error
1819 */
1820 public static void writeStringToFile(File file, String data) throws IOException {
1821 writeStringToFile(file, data, null, false);
1822 }
1823
1824 /**
1825 * Writes a String to a file creating the file if it does not exist using the default encoding for the VM.
1826 *
1827 * @param file the file to write
1828 * @param data the content to write to the file
1829 * @param append if <code>true</code>, then the String will be added to the
1830 * end of the file rather than overwriting
1831 * @throws IOException in case of an I/O error
1832 * @since 2.1
1833 */
1834 public static void writeStringToFile(File file, String data, boolean append) throws IOException {
1835 writeStringToFile(file, data, null, append);
1836 }
1837
1838 /**
1839 * Writes a CharSequence to a file creating the file if it does not exist using the default encoding for the VM.
1840 *
1841 * @param file the file to write
1842 * @param data the content to write to the file
1843 * @throws IOException in case of an I/O error
1844 * @since 2.0
1845 */
1846 public static void write(File file, CharSequence data) throws IOException {
1847 write(file, data, null, false);
1848 }
1849
1850 /**
1851 * Writes a CharSequence to a file creating the file if it does not exist using the default encoding for the VM.
1852 *
1853 * @param file the file to write
1854 * @param data the content to write to the file
1855 * @param append if <code>true</code>, then the data will be added to the
1856 * end of the file rather than overwriting
1857 * @throws IOException in case of an I/O error
1858 * @since 2.1
1859 */
1860 public static void write(File file, CharSequence data, boolean append) throws IOException {
1861 write(file, data, null, append);
1862 }
1863
1864 /**
1865 * Writes a CharSequence to a file creating the file if it does not exist.
1866 *
1867 * @param file the file to write
1868 * @param data the content to write to the file
1869 * @param encoding the encoding to use, <code>null</code> means platform default
1870 * @throws IOException in case of an I/O error
1871 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1872 * @since 2.0
1873 */
1874 public static void write(File file, CharSequence data, String encoding) throws IOException {
1875 write(file, data, encoding, false);
1876 }
1877
1878 /**
1879 * Writes a CharSequence to a file creating the file if it does not exist.
1880 *
1881 * @param file the file to write
1882 * @param data the content to write to the file
1883 * @param encoding the encoding to use, <code>null</code> means platform default
1884 * @param append if <code>true</code>, then the data will be added to the
1885 * end of the file rather than overwriting
1886 * @throws IOException in case of an I/O error
1887 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1888 * @since IO 2.1
1889 */
1890 public static void write(File file, CharSequence data, String encoding, boolean append) throws IOException {
1891 String str = data == null ? null : data.toString();
1892 writeStringToFile(file, str, encoding, append);
1893 }
1894
1895 /**
1896 * Writes a byte array to a file creating the file if it does not exist.
1897 * <p>
1898 * NOTE: As from v1.3, the parent directories of the file will be created
1899 * if they do not exist.
1900 *
1901 * @param file the file to write to
1902 * @param data the content to write to the file
1903 * @throws IOException in case of an I/O error
1904 * @since 1.1
1905 */
1906 public static void writeByteArrayToFile(File file, byte[] data) throws IOException {
1907 writeByteArrayToFile(file, data, false);
1908 }
1909
1910 /**
1911 * Writes a byte array to a file creating the file if it does not exist.
1912 *
1913 * @param file the file to write to
1914 * @param data the content to write to the file
1915 * @param append if <code>true</code>, then bytes will be added to the
1916 * end of the file rather than overwriting
1917 * @throws IOException in case of an I/O error
1918 * @since IO 2.1
1919 */
1920 public static void writeByteArrayToFile(File file, byte[] data, boolean append) throws IOException {
1921 OutputStream out = null;
1922 try {
1923 out = openOutputStream(file, append);
1924 out.write(data);
1925 out.close(); // don't swallow close Exception if copy completes normally
1926 } finally {
1927 IOUtils.closeQuietly(out);
1928 }
1929 }
1930
1931 /**
1932 * Writes the <code>toString()</code> value of each item in a collection to
1933 * the specified <code>File</code> line by line.
1934 * The specified character encoding and the default line ending will be used.
1935 * <p>
1936 * NOTE: As from v1.3, the parent directories of the file will be created
1937 * if they do not exist.
1938 *
1939 * @param file the file to write to
1940 * @param encoding the encoding to use, <code>null</code> means platform default
1941 * @param lines the lines to write, <code>null</code> entries produce blank lines
1942 * @throws IOException in case of an I/O error
1943 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1944 * @since 1.1
1945 */
1946 public static void writeLines(File file, String encoding, Collection<?> lines) throws IOException {
1947 writeLines(file, encoding, lines, null, false);
1948 }
1949
1950 /**
1951 * Writes the <code>toString()</code> value of each item in a collection to
1952 * the specified <code>File</code> line by line, optionally appending.
1953 * The specified character encoding and the default line ending will be used.
1954 *
1955 * @param file the file to write to
1956 * @param encoding the encoding to use, <code>null</code> means platform default
1957 * @param lines the lines to write, <code>null</code> entries produce blank lines
1958 * @param append if <code>true</code>, then the lines will be added to the
1959 * end of the file rather than overwriting
1960 * @throws IOException in case of an I/O error
1961 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1962 * @since 2.1
1963 */
1964 public static void writeLines(File file, String encoding, Collection<?> lines, boolean append) throws IOException {
1965 writeLines(file, encoding, lines, null, append);
1966 }
1967
1968 /**
1969 * Writes the <code>toString()</code> value of each item in a collection to
1970 * the specified <code>File</code> line by line.
1971 * The default VM encoding and the default line ending will be used.
1972 *
1973 * @param file the file to write to
1974 * @param lines the lines to write, <code>null</code> entries produce blank lines
1975 * @throws IOException in case of an I/O error
1976 * @since 1.3
1977 */
1978 public static void writeLines(File file, Collection<?> lines) throws IOException {
1979 writeLines(file, null, lines, null, false);
1980 }
1981
1982 /**
1983 * Writes the <code>toString()</code> value of each item in a collection to
1984 * the specified <code>File</code> line by line.
1985 * The default VM encoding and the default line ending will be used.
1986 *
1987 * @param file the file to write to
1988 * @param lines the lines to write, <code>null</code> entries produce blank lines
1989 * @param append if <code>true</code>, then the lines will be added to the
1990 * end of the file rather than overwriting
1991 * @throws IOException in case of an I/O error
1992 * @since 2.1
1993 */
1994 public static void writeLines(File file, Collection<?> lines, boolean append) throws IOException {
1995 writeLines(file, null, lines, null, append);
1996 }
1997
1998 /**
1999 * Writes the <code>toString()</code> value of each item in a collection to
2000 * the specified <code>File</code> line by line.
2001 * The specified character encoding and the line ending will be used.
2002 * <p>
2003 * NOTE: As from v1.3, the parent directories of the file will be created
2004 * if they do not exist.
2005 *
2006 * @param file the file to write to
2007 * @param encoding the encoding to use, <code>null</code> means platform default
2008 * @param lines the lines to write, <code>null</code> entries produce blank lines
2009 * @param lineEnding the line separator to use, <code>null</code> is system default
2010 * @throws IOException in case of an I/O error
2011 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
2012 * @since 1.1
2013 */
2014 public static void writeLines(File file, String encoding, Collection<?> lines, String lineEnding)
2015 throws IOException {
2016 writeLines(file, encoding, lines, lineEnding, false);
2017 }
2018
2019 /**
2020 * Writes the <code>toString()</code> value of each item in a collection to
2021 * the specified <code>File</code> line by line.
2022 * The specified character encoding and the line ending will be used.
2023 *
2024 * @param file the file to write to
2025 * @param encoding the encoding to use, <code>null</code> means platform default
2026 * @param lines the lines to write, <code>null</code> entries produce blank lines
2027 * @param lineEnding the line separator to use, <code>null</code> is system default
2028 * @param append if <code>true</code>, then the lines will be added to the
2029 * end of the file rather than overwriting
2030 * @throws IOException in case of an I/O error
2031 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
2032 * @since 2.1
2033 */
2034 public static void writeLines(File file, String encoding, Collection<?> lines, String lineEnding, boolean append)
2035 throws IOException {
2036 OutputStream out = null;
2037 try {
2038 out = openOutputStream(file, append);
2039 IOUtils.writeLines(lines, lineEnding, out, encoding);
2040 out.close(); // don't swallow close Exception if copy completes normally
2041 } finally {
2042 IOUtils.closeQuietly(out);
2043 }
2044 }
2045
2046 /**
2047 * Writes the <code>toString()</code> value of each item in a collection to
2048 * the specified <code>File</code> line by line.
2049 * The default VM encoding and the specified line ending will be used.
2050 *
2051 * @param file the file to write to
2052 * @param lines the lines to write, <code>null</code> entries produce blank lines
2053 * @param lineEnding the line separator to use, <code>null</code> is system default
2054 * @throws IOException in case of an I/O error
2055 * @since 1.3
2056 */
2057 public static void writeLines(File file, Collection<?> lines, String lineEnding) throws IOException {
2058 writeLines(file, null, lines, lineEnding, false);
2059 }
2060
2061 /**
2062 * Writes the <code>toString()</code> value of each item in a collection to
2063 * the specified <code>File</code> line by line.
2064 * The default VM encoding and the specified line ending will be used.
2065 *
2066 * @param file the file to write to
2067 * @param lines the lines to write, <code>null</code> entries produce blank lines
2068 * @param lineEnding the line separator to use, <code>null</code> is system default
2069 * @param append if <code>true</code>, then the lines will be added to the
2070 * end of the file rather than overwriting
2071 * @throws IOException in case of an I/O error
2072 * @since 2.1
2073 */
2074 public static void writeLines(File file, Collection<?> lines, String lineEnding, boolean append)
2075 throws IOException {
2076 writeLines(file, null, lines, lineEnding, append);
2077 }
2078
2079 //-----------------------------------------------------------------------
2080 /**
2081 * Deletes a file. If file is a directory, delete it and all sub-directories.
2082 * <p>
2083 * The difference between File.delete() and this method are:
2084 * <ul>
2085 * <li>A directory to be deleted does not have to be empty.</li>
2086 * <li>You get exceptions when a file or directory cannot be deleted.
2087 * (java.io.File methods returns a boolean)</li>
2088 * </ul>
2089 *
2090 * @param file file or directory to delete, must not be <code>null</code>
2091 * @throws NullPointerException if the directory is <code>null</code>
2092 * @throws FileNotFoundException if the file was not found
2093 * @throws IOException in case deletion is unsuccessful
2094 */
2095 public static void forceDelete(File file) throws IOException {
2096 if (file.isDirectory()) {
2097 deleteDirectory(file);
2098 } else {
2099 boolean filePresent = file.exists();
2100 if (!file.delete()) {
2101 if (!filePresent){
2102 throw new FileNotFoundException("File does not exist: " + file);
2103 }
2104 String message =
2105 "Unable to delete file: " + file;
2106 throw new IOException(message);
2107 }
2108 }
2109 }
2110
2111 /**
2112 * Schedules a file to be deleted when JVM exits.
2113 * If file is directory delete it and all sub-directories.
2114 *
2115 * @param file file or directory to delete, must not be <code>null</code>
2116 * @throws NullPointerException if the file is <code>null</code>
2117 * @throws IOException in case deletion is unsuccessful
2118 */
2119 public static void forceDeleteOnExit(File file) throws IOException {
2120 if (file.isDirectory()) {
2121 deleteDirectoryOnExit(file);
2122 } else {
2123 file.deleteOnExit();
2124 }
2125 }
2126
2127 /**
2128 * Schedules a directory recursively for deletion on JVM exit.
2129 *
2130 * @param directory directory to delete, must not be <code>null</code>
2131 * @throws NullPointerException if the directory is <code>null</code>
2132 * @throws IOException in case deletion is unsuccessful
2133 */
2134 private static void deleteDirectoryOnExit(File directory) throws IOException {
2135 if (!directory.exists()) {
2136 return;
2137 }
2138
2139 directory.deleteOnExit();
2140 if (!isSymlink(directory)) {
2141 cleanDirectoryOnExit(directory);
2142 }
2143 }
2144
2145 /**
2146 * Cleans a directory without deleting it.
2147 *
2148 * @param directory directory to clean, must not be <code>null</code>
2149 * @throws NullPointerException if the directory is <code>null</code>
2150 * @throws IOException in case cleaning is unsuccessful
2151 */
2152 private static void cleanDirectoryOnExit(File directory) throws IOException {
2153 if (!directory.exists()) {
2154 String message = directory + " does not exist";
2155 throw new IllegalArgumentException(message);
2156 }
2157
2158 if (!directory.isDirectory()) {
2159 String message = directory + " is not a directory";
2160 throw new IllegalArgumentException(message);
2161 }
2162
2163 File[] files = directory.listFiles();
2164 if (files == null) { // null if security restricted
2165 throw new IOException("Failed to list contents of " + directory);
2166 }
2167
2168 IOException exception = null;
2169 for (File file : files) {
2170 try {
2171 forceDeleteOnExit(file);
2172 } catch (IOException ioe) {
2173 exception = ioe;
2174 }
2175 }
2176
2177 if (null != exception) {
2178 throw exception;
2179 }
2180 }
2181
2182 /**
2183 * Makes a directory, including any necessary but nonexistent parent
2184 * directories. If a file already exists with specified name but it is
2185 * not a directory then an IOException is thrown.
2186 * If the directory cannot be created (or does not already exist)
2187 * then an IOException is thrown.
2188 *
2189 * @param directory directory to create, must not be <code>null</code>
2190 * @throws NullPointerException if the directory is <code>null</code>
2191 * @throws IOException if the directory cannot be created or the file already exists but is not a directory
2192 */
2193 public static void forceMkdir(File directory) throws IOException {
2194 if (directory.exists()) {
2195 if (!directory.isDirectory()) {
2196 String message =
2197 "File "
2198 + directory
2199 + " exists and is "
2200 + "not a directory. Unable to create directory.";
2201 throw new IOException(message);
2202 }
2203 } else {
2204 if (!directory.mkdirs()) {
2205 // Double-check that some other thread or process hasn't made
2206 // the directory in the background
2207 if (!directory.isDirectory())
2208 {
2209 String message =
2210 "Unable to create directory " + directory;
2211 throw new IOException(message);
2212 }
2213 }
2214 }
2215 }
2216
2217 //-----------------------------------------------------------------------
2218 /**
2219 * Returns the size of the specified file or directory. If the provided
2220 * {@link File} is a regular file, then the file's length is returned.
2221 * If the argument is a directory, then the size of the directory is
2222 * calculated recursively. If a directory or subdirectory is security
2223 * restricted, its size will not be included.
2224 *
2225 * @param file the regular file or directory to return the size
2226 * of (must not be <code>null</code>).
2227 *
2228 * @return the length of the file, or recursive size of the directory,
2229 * provided (in bytes).
2230 *
2231 * @throws NullPointerException if the file is <code>null</code>
2232 * @throws IllegalArgumentException if the file does not exist.
2233 *
2234 * @since 2.0
2235 */
2236 public static long sizeOf(File file) {
2237
2238 if (!file.exists()) {
2239 String message = file + " does not exist";
2240 throw new IllegalArgumentException(message);
2241 }
2242
2243 if (file.isDirectory()) {
2244 return sizeOfDirectory(file);
2245 } else {
2246 return file.length();
2247 }
2248
2249 }
2250
2251 /**
2252 * Counts the size of a directory recursively (sum of the length of all files).
2253 *
2254 * @param directory directory to inspect, must not be <code>null</code>
2255 * @return size of directory in bytes, 0 if directory is security restricted
2256 * @throws NullPointerException if the directory is <code>null</code>
2257 */
2258 public static long sizeOfDirectory(File directory) {
2259 if (!directory.exists()) {
2260 String message = directory + " does not exist";
2261 throw new IllegalArgumentException(message);
2262 }
2263
2264 if (!directory.isDirectory()) {
2265 String message = directory + " is not a directory";
2266 throw new IllegalArgumentException(message);
2267 }
2268
2269 long size = 0;
2270
2271 File[] files = directory.listFiles();
2272 if (files == null) { // null if security restricted
2273 return 0L;
2274 }
2275 for (File file : files) {
2276 size += sizeOf(file);
2277 }
2278
2279 return size;
2280 }
2281
2282 //-----------------------------------------------------------------------
2283 /**
2284 * Tests if the specified <code>File</code> is newer than the reference
2285 * <code>File</code>.
2286 *
2287 * @param file the <code>File</code> of which the modification date must
2288 * be compared, must not be <code>null</code>
2289 * @param reference the <code>File</code> of which the modification date
2290 * is used, must not be <code>null</code>
2291 * @return true if the <code>File</code> exists and has been modified more
2292 * recently than the reference <code>File</code>
2293 * @throws IllegalArgumentException if the file is <code>null</code>
2294 * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist
2295 */
2296 public static boolean isFileNewer(File file, File reference) {
2297 if (reference == null) {
2298 throw new IllegalArgumentException("No specified reference file");
2299 }
2300 if (!reference.exists()) {
2301 throw new IllegalArgumentException("The reference file '"
2302 + reference + "' doesn't exist");
2303 }
2304 return isFileNewer(file, reference.lastModified());
2305 }
2306
2307 /**
2308 * Tests if the specified <code>File</code> is newer than the specified
2309 * <code>Date</code>.
2310 *
2311 * @param file the <code>File</code> of which the modification date
2312 * must be compared, must not be <code>null</code>
2313 * @param date the date reference, must not be <code>null</code>
2314 * @return true if the <code>File</code> exists and has been modified
2315 * after the given <code>Date</code>.
2316 * @throws IllegalArgumentException if the file is <code>null</code>
2317 * @throws IllegalArgumentException if the date is <code>null</code>
2318 */
2319 public static boolean isFileNewer(File file, Date date) {
2320 if (date == null) {
2321 throw new IllegalArgumentException("No specified date");
2322 }
2323 return isFileNewer(file, date.getTime());
2324 }
2325
2326 /**
2327 * Tests if the specified <code>File</code> is newer than the specified
2328 * time reference.
2329 *
2330 * @param file the <code>File</code> of which the modification date must
2331 * be compared, must not be <code>null</code>
2332 * @param timeMillis the time reference measured in milliseconds since the
2333 * epoch (00:00:00 GMT, January 1, 1970)
2334 * @return true if the <code>File</code> exists and has been modified after
2335 * the given time reference.
2336 * @throws IllegalArgumentException if the file is <code>null</code>
2337 */
2338 public static boolean isFileNewer(File file, long timeMillis) {
2339 if (file == null) {
2340 throw new IllegalArgumentException("No specified file");
2341 }
2342 if (!file.exists()) {
2343 return false;
2344 }
2345 return file.lastModified() > timeMillis;
2346 }
2347
2348
2349 //-----------------------------------------------------------------------
2350 /**
2351 * Tests if the specified <code>File</code> is older than the reference
2352 * <code>File</code>.
2353 *
2354 * @param file the <code>File</code> of which the modification date must
2355 * be compared, must not be <code>null</code>
2356 * @param reference the <code>File</code> of which the modification date
2357 * is used, must not be <code>null</code>
2358 * @return true if the <code>File</code> exists and has been modified before
2359 * the reference <code>File</code>
2360 * @throws IllegalArgumentException if the file is <code>null</code>
2361 * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist
2362 */
2363 public static boolean isFileOlder(File file, File reference) {
2364 if (reference == null) {
2365 throw new IllegalArgumentException("No specified reference file");
2366 }
2367 if (!reference.exists()) {
2368 throw new IllegalArgumentException("The reference file '"
2369 + reference + "' doesn't exist");
2370 }
2371 return isFileOlder(file, reference.lastModified());
2372 }
2373
2374 /**
2375 * Tests if the specified <code>File</code> is older than the specified
2376 * <code>Date</code>.
2377 *
2378 * @param file the <code>File</code> of which the modification date
2379 * must be compared, must not be <code>null</code>
2380 * @param date the date reference, must not be <code>null</code>
2381 * @return true if the <code>File</code> exists and has been modified
2382 * before the given <code>Date</code>.
2383 * @throws IllegalArgumentException if the file is <code>null</code>
2384 * @throws IllegalArgumentException if the date is <code>null</code>
2385 */
2386 public static boolean isFileOlder(File file, Date date) {
2387 if (date == null) {
2388 throw new IllegalArgumentException("No specified date");
2389 }
2390 return isFileOlder(file, date.getTime());
2391 }
2392
2393 /**
2394 * Tests if the specified <code>File</code> is older than the specified
2395 * time reference.
2396 *
2397 * @param file the <code>File</code> of which the modification date must
2398 * be compared, must not be <code>null</code>
2399 * @param timeMillis the time reference measured in milliseconds since the
2400 * epoch (00:00:00 GMT, January 1, 1970)
2401 * @return true if the <code>File</code> exists and has been modified before
2402 * the given time reference.
2403 * @throws IllegalArgumentException if the file is <code>null</code>
2404 */
2405 public static boolean isFileOlder(File file, long timeMillis) {
2406 if (file == null) {
2407 throw new IllegalArgumentException("No specified file");
2408 }
2409 if (!file.exists()) {
2410 return false;
2411 }
2412 return file.lastModified() < timeMillis;
2413 }
2414
2415 //-----------------------------------------------------------------------
2416 /**
2417 * Computes the checksum of a file using the CRC32 checksum routine.
2418 * The value of the checksum is returned.
2419 *
2420 * @param file the file to checksum, must not be <code>null</code>
2421 * @return the checksum value
2422 * @throws NullPointerException if the file or checksum is <code>null</code>
2423 * @throws IllegalArgumentException if the file is a directory
2424 * @throws IOException if an IO error occurs reading the file
2425 * @since 1.3
2426 */
2427 public static long checksumCRC32(File file) throws IOException {
2428 CRC32 crc = new CRC32();
2429 checksum(file, crc);
2430 return crc.getValue();
2431 }
2432
2433 /**
2434 * Computes the checksum of a file using the specified checksum object.
2435 * Multiple files may be checked using one <code>Checksum</code> instance
2436 * if desired simply by reusing the same checksum object.
2437 * For example:
2438 * <pre>
2439 * long csum = FileUtils.checksum(file, new CRC32()).getValue();
2440 * </pre>
2441 *
2442 * @param file the file to checksum, must not be <code>null</code>
2443 * @param checksum the checksum object to be used, must not be <code>null</code>
2444 * @return the checksum specified, updated with the content of the file
2445 * @throws NullPointerException if the file or checksum is <code>null</code>
2446 * @throws IllegalArgumentException if the file is a directory
2447 * @throws IOException if an IO error occurs reading the file
2448 * @since 1.3
2449 */
2450 public static Checksum checksum(File file, Checksum checksum) throws IOException {
2451 if (file.isDirectory()) {
2452 throw new IllegalArgumentException("Checksums can't be computed on directories");
2453 }
2454 InputStream in = null;
2455 try {
2456 in = new CheckedInputStream(new FileInputStream(file), checksum);
2457 IOUtils.copy(in, new NullOutputStream());
2458 } finally {
2459 IOUtils.closeQuietly(in);
2460 }
2461 return checksum;
2462 }
2463
2464 /**
2465 * Moves a directory.
2466 * <p>
2467 * When the destination directory is on another file system, do a "copy and delete".
2468 *
2469 * @param srcDir the directory to be moved
2470 * @param destDir the destination directory
2471 * @throws NullPointerException if source or destination is <code>null</code>
2472 * @throws FileExistsException if the destination directory exists
2473 * @throws IOException if source or destination is invalid
2474 * @throws IOException if an IO error occurs moving the file
2475 * @since 1.4
2476 */
2477 public static void moveDirectory(File srcDir, File destDir) throws IOException {
2478 if (srcDir == null) {
2479 throw new NullPointerException("Source must not be null");
2480 }
2481 if (destDir == null) {
2482 throw new NullPointerException("Destination must not be null");
2483 }
2484 if (!srcDir.exists()) {
2485 throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
2486 }
2487 if (!srcDir.isDirectory()) {
2488 throw new IOException("Source '" + srcDir + "' is not a directory");
2489 }
2490 if (destDir.exists()) {
2491 throw new FileExistsException("Destination '" + destDir + "' already exists");
2492 }
2493 boolean rename = srcDir.renameTo(destDir);
2494 if (!rename) {
2495 if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) {
2496 throw new IOException("Cannot move directory: "+srcDir+" to a subdirectory of itself: "+destDir);
2497 }
2498 copyDirectory( srcDir, destDir );
2499 deleteDirectory( srcDir );
2500 if (srcDir.exists()) {
2501 throw new IOException("Failed to delete original directory '" + srcDir +
2502 "' after copy to '" + destDir + "'");
2503 }
2504 }
2505 }
2506
2507 /**
2508 * Moves a directory to another directory.
2509 *
2510 * @param src the file to be moved
2511 * @param destDir the destination file
2512 * @param createDestDir If <code>true</code> create the destination directory,
2513 * otherwise if <code>false</code> throw an IOException
2514 * @throws NullPointerException if source or destination is <code>null</code>
2515 * @throws FileExistsException if the directory exists in the destination directory
2516 * @throws IOException if source or destination is invalid
2517 * @throws IOException if an IO error occurs moving the file
2518 * @since 1.4
2519 */
2520 public static void moveDirectoryToDirectory(File src, File destDir, boolean createDestDir) throws IOException {
2521 if (src == null) {
2522 throw new NullPointerException("Source must not be null");
2523 }
2524 if (destDir == null) {
2525 throw new NullPointerException("Destination directory must not be null");
2526 }
2527 if (!destDir.exists() && createDestDir) {
2528 destDir.mkdirs();
2529 }
2530 if (!destDir.exists()) {
2531 throw new FileNotFoundException("Destination directory '" + destDir +
2532 "' does not exist [createDestDir=" + createDestDir +"]");
2533 }
2534 if (!destDir.isDirectory()) {
2535 throw new IOException("Destination '" + destDir + "' is not a directory");
2536 }
2537 moveDirectory(src, new File(destDir, src.getName()));
2538
2539 }
2540
2541 /**
2542 * Moves a file.
2543 * <p>
2544 * When the destination file is on another file system, do a "copy and delete".
2545 *
2546 * @param srcFile the file to be moved
2547 * @param destFile the destination file
2548 * @throws NullPointerException if source or destination is <code>null</code>
2549 * @throws FileExistsException if the destination file exists
2550 * @throws IOException if source or destination is invalid
2551 * @throws IOException if an IO error occurs moving the file
2552 * @since 1.4
2553 */
2554 public static void moveFile(File srcFile, File destFile) throws IOException {
2555 if (srcFile == null) {
2556 throw new NullPointerException("Source must not be null");
2557 }
2558 if (destFile == null) {
2559 throw new NullPointerException("Destination must not be null");
2560 }
2561 if (!srcFile.exists()) {
2562 throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
2563 }
2564 if (srcFile.isDirectory()) {
2565 throw new IOException("Source '" + srcFile + "' is a directory");
2566 }
2567 if (destFile.exists()) {
2568 throw new FileExistsException("Destination '" + destFile + "' already exists");
2569 }
2570 if (destFile.isDirectory()) {
2571 throw new IOException("Destination '" + destFile + "' is a directory");
2572 }
2573 boolean rename = srcFile.renameTo(destFile);
2574 if (!rename) {
2575 copyFile( srcFile, destFile );
2576 if (!srcFile.delete()) {
2577 FileUtils.deleteQuietly(destFile);
2578 throw new IOException("Failed to delete original file '" + srcFile +
2579 "' after copy to '" + destFile + "'");
2580 }
2581 }
2582 }
2583
2584 /**
2585 * Moves a file to a directory.
2586 *
2587 * @param srcFile the file to be moved
2588 * @param destDir the destination file
2589 * @param createDestDir If <code>true</code> create the destination directory,
2590 * otherwise if <code>false</code> throw an IOException
2591 * @throws NullPointerException if source or destination is <code>null</code>
2592 * @throws FileExistsException if the destination file exists
2593 * @throws IOException if source or destination is invalid
2594 * @throws IOException if an IO error occurs moving the file
2595 * @since 1.4
2596 */
2597 public static void moveFileToDirectory(File srcFile, File destDir, boolean createDestDir) throws IOException {
2598 if (srcFile == null) {
2599 throw new NullPointerException("Source must not be null");
2600 }
2601 if (destDir == null) {
2602 throw new NullPointerException("Destination directory must not be null");
2603 }
2604 if (!destDir.exists() && createDestDir) {
2605 destDir.mkdirs();
2606 }
2607 if (!destDir.exists()) {
2608 throw new FileNotFoundException("Destination directory '" + destDir +
2609 "' does not exist [createDestDir=" + createDestDir +"]");
2610 }
2611 if (!destDir.isDirectory()) {
2612 throw new IOException("Destination '" + destDir + "' is not a directory");
2613 }
2614 moveFile(srcFile, new File(destDir, srcFile.getName()));
2615 }
2616
2617 /**
2618 * Moves a file or directory to the destination directory.
2619 * <p>
2620 * When the destination is on another file system, do a "copy and delete".
2621 *
2622 * @param src the file or directory to be moved
2623 * @param destDir the destination directory
2624 * @param createDestDir If <code>true</code> create the destination directory,
2625 * otherwise if <code>false</code> throw an IOException
2626 * @throws NullPointerException if source or destination is <code>null</code>
2627 * @throws FileExistsException if the directory or file exists in the destination directory
2628 * @throws IOException if source or destination is invalid
2629 * @throws IOException if an IO error occurs moving the file
2630 * @since 1.4
2631 */
2632 public static void moveToDirectory(File src, File destDir, boolean createDestDir) throws IOException {
2633 if (src == null) {
2634 throw new NullPointerException("Source must not be null");
2635 }
2636 if (destDir == null) {
2637 throw new NullPointerException("Destination must not be null");
2638 }
2639 if (!src.exists()) {
2640 throw new FileNotFoundException("Source '" + src + "' does not exist");
2641 }
2642 if (src.isDirectory()) {
2643 moveDirectoryToDirectory(src, destDir, createDestDir);
2644 } else {
2645 moveFileToDirectory(src, destDir, createDestDir);
2646 }
2647 }
2648
2649 /**
2650 * Determines whether the specified file is a Symbolic Link rather than an actual file.
2651 * <p>
2652 * Will not return true if there is a Symbolic Link anywhere in the path,
2653 * only if the specific file is.
2654 * <p>
2655 * <b>Note:</b> the current implementation always returns {@code false} if the system
2656 * is detected as Windows using {@link FilenameUtils#isSystemWindows()}
2657 *
2658 * @param file the file to check
2659 * @return true if the file is a Symbolic Link
2660 * @throws IOException if an IO error occurs while checking the file
2661 * @since 2.0
2662 */
2663 public static boolean isSymlink(File file) throws IOException {
2664 if (file == null) {
2665 throw new NullPointerException("File must not be null");
2666 }
2667 if (FilenameUtils.isSystemWindows()) {
2668 return false;
2669 }
2670 File fileInCanonicalDir = null;
2671 if (file.getParent() == null) {
2672 fileInCanonicalDir = file;
2673 } else {
2674 File canonicalDir = file.getParentFile().getCanonicalFile();
2675 fileInCanonicalDir = new File(canonicalDir, file.getName());
2676 }
2677
2678 if (fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile())) {
2679 return false;
2680 } else {
2681 return true;
2682 }
2683 }
2684
2685 }