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