You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

FileUtils.java 21 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2001-2002 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Ant", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.tools.ant.util;
  55. import java.io.BufferedInputStream;
  56. import java.io.BufferedReader;
  57. import java.io.BufferedWriter;
  58. import java.io.File;
  59. import java.io.FileInputStream;
  60. import java.io.FileOutputStream;
  61. import java.io.FileReader;
  62. import java.io.FileWriter;
  63. import java.io.IOException;
  64. import java.io.InputStream;
  65. import java.lang.reflect.Method;
  66. import java.text.DecimalFormat;
  67. import java.util.Random;
  68. import java.util.Stack;
  69. import java.util.StringTokenizer;
  70. import org.apache.tools.ant.BuildException;
  71. import org.apache.tools.ant.Project;
  72. import org.apache.tools.ant.types.FilterSetCollection;
  73. /**
  74. * This class also encapsulates methods which allow Files to be
  75. * refered to using abstract path names which are translated to native
  76. * system file paths at runtime as well as copying files or setting
  77. * there last modification time.
  78. *
  79. * @author duncan@x180.com
  80. * @author <a href="mailto:conor@apache.org">Conor MacNeill</a>
  81. * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
  82. *
  83. * @version $Revision$
  84. */
  85. public class FileUtils {
  86. private static Random rand = new Random(System.currentTimeMillis());
  87. private static Object lockReflection = new Object();
  88. private static java.lang.reflect.Method setLastModified = null;
  89. /**
  90. * Factory method.
  91. */
  92. public static FileUtils newFileUtils() {
  93. return new FileUtils();
  94. }
  95. /**
  96. * Empty constructor.
  97. */
  98. protected FileUtils() {}
  99. /**
  100. * Convienence method to copy a file from a source to a destination.
  101. * No filtering is performed.
  102. *
  103. * @throws IOException
  104. */
  105. public void copyFile(String sourceFile, String destFile) throws IOException {
  106. copyFile(new File(sourceFile), new File(destFile), null, false, false);
  107. }
  108. /**
  109. * Convienence method to copy a file from a source to a destination
  110. * specifying if token filtering must be used.
  111. *
  112. * @throws IOException
  113. */
  114. public void copyFile(String sourceFile, String destFile, FilterSetCollection filters)
  115. throws IOException
  116. {
  117. copyFile(new File(sourceFile), new File(destFile), filters, false, false);
  118. }
  119. /**
  120. * Convienence method to copy a file from a source to a
  121. * destination specifying if token filtering must be used and if
  122. * source files may overwrite newer destination files.
  123. *
  124. * @throws IOException
  125. */
  126. public void copyFile(String sourceFile, String destFile, FilterSetCollection filters,
  127. boolean overwrite) throws IOException {
  128. copyFile(new File(sourceFile), new File(destFile), filters,
  129. overwrite, false);
  130. }
  131. /**
  132. * Convienence method to copy a file from a source to a
  133. * destination specifying if token filtering must be used, if
  134. * source files may overwrite newer destination files and the
  135. * last modified time of <code>destFile</code> file should be made equal
  136. * to the last modified time of <code>sourceFile</code>.
  137. *
  138. * @throws IOException
  139. */
  140. public void copyFile(String sourceFile, String destFile, FilterSetCollection filters,
  141. boolean overwrite, boolean preserveLastModified)
  142. throws IOException {
  143. copyFile(new File(sourceFile), new File(destFile), filters,
  144. overwrite, preserveLastModified);
  145. }
  146. /**
  147. * Convienence method to copy a file from a source to a destination.
  148. * No filtering is performed.
  149. *
  150. * @throws IOException
  151. */
  152. public void copyFile(File sourceFile, File destFile) throws IOException {
  153. copyFile(sourceFile, destFile, null, false, false);
  154. }
  155. /**
  156. * Convienence method to copy a file from a source to a destination
  157. * specifying if token filtering must be used.
  158. *
  159. * @throws IOException
  160. */
  161. public void copyFile(File sourceFile, File destFile, FilterSetCollection filters)
  162. throws IOException {
  163. copyFile(sourceFile, destFile, filters, false, false);
  164. }
  165. /**
  166. * Convienence method to copy a file from a source to a
  167. * destination specifying if token filtering must be used and if
  168. * source files may overwrite newer destination files.
  169. *
  170. * @throws IOException
  171. */
  172. public void copyFile(File sourceFile, File destFile, FilterSetCollection filters,
  173. boolean overwrite) throws IOException {
  174. copyFile(sourceFile, destFile, filters, overwrite, false);
  175. }
  176. /**
  177. * Convienence method to copy a file from a source to a
  178. * destination specifying if token filtering must be used, if
  179. * source files may overwrite newer destination files and the
  180. * last modified time of <code>destFile</code> file should be made equal
  181. * to the last modified time of <code>sourceFile</code>.
  182. *
  183. * @throws IOException
  184. */
  185. public void copyFile(File sourceFile, File destFile, FilterSetCollection filters,
  186. boolean overwrite, boolean preserveLastModified)
  187. throws IOException {
  188. if (overwrite || !destFile.exists() ||
  189. destFile.lastModified() < sourceFile.lastModified()) {
  190. if (destFile.exists() && destFile.isFile()) {
  191. destFile.delete();
  192. }
  193. // ensure that parent dir of dest file exists!
  194. // not using getParentFile method to stay 1.1 compat
  195. File parent = getParentFile(destFile);
  196. if (!parent.exists()) {
  197. parent.mkdirs();
  198. }
  199. if (filters != null && filters.hasFilters()) {
  200. BufferedReader in = new BufferedReader(new FileReader(sourceFile));
  201. BufferedWriter out = new BufferedWriter(new FileWriter(destFile));
  202. int length;
  203. String newline = null;
  204. String line = in.readLine();
  205. while (line != null) {
  206. if (line.length() == 0) {
  207. out.newLine();
  208. } else {
  209. newline = filters.replaceTokens(line);
  210. out.write(newline);
  211. out.newLine();
  212. }
  213. line = in.readLine();
  214. }
  215. out.close();
  216. in.close();
  217. } else {
  218. FileInputStream in = new FileInputStream(sourceFile);
  219. FileOutputStream out = new FileOutputStream(destFile);
  220. byte[] buffer = new byte[8 * 1024];
  221. int count = 0;
  222. do {
  223. out.write(buffer, 0, count);
  224. count = in.read(buffer, 0, buffer.length);
  225. } while (count != -1);
  226. in.close();
  227. out.close();
  228. }
  229. if (preserveLastModified) {
  230. setFileLastModified(destFile, sourceFile.lastModified());
  231. }
  232. }
  233. }
  234. /**
  235. * see whether we have a setLastModified method in File and return it.
  236. */
  237. protected final Method getSetLastModified() {
  238. if (Project.getJavaVersion() == Project.JAVA_1_1) {
  239. return null;
  240. }
  241. if (setLastModified == null) {
  242. synchronized (lockReflection) {
  243. if (setLastModified == null) {
  244. try {
  245. setLastModified =
  246. java.io.File.class.getMethod("setLastModified",
  247. new Class[] {Long.TYPE});
  248. } catch (NoSuchMethodException nse) {
  249. throw new BuildException("File.setlastModified not in JDK > 1.1?",
  250. nse);
  251. }
  252. }
  253. }
  254. }
  255. return setLastModified;
  256. }
  257. /**
  258. * Calls File.setLastModified(long time) in a Java 1.1 compatible way.
  259. */
  260. public void setFileLastModified(File file, long time) throws BuildException {
  261. if (Project.getJavaVersion() == Project.JAVA_1_1) {
  262. return;
  263. }
  264. Long[] times = new Long[1];
  265. if (time < 0) {
  266. times[0] = new Long(System.currentTimeMillis());
  267. } else {
  268. times[0] = new Long(time);
  269. }
  270. try {
  271. getSetLastModified().invoke(file, times);
  272. } catch (java.lang.reflect.InvocationTargetException ite) {
  273. Throwable nested = ite.getTargetException();
  274. throw new BuildException("Exception setting the modification time "
  275. + "of " + file, nested);
  276. } catch (Throwable other) {
  277. throw new BuildException("Exception setting the modification time "
  278. + "of " + file, other);
  279. }
  280. }
  281. /**
  282. * Interpret the filename as a file relative to the given file -
  283. * unless the filename already represents an absolute filename.
  284. *
  285. * @param file the "reference" file for relative paths. This
  286. * instance must be an absolute file and must not contain
  287. * &quot;./&quot; or &quot;../&quot; sequences (same for \ instead
  288. * of /). If it is null, this call is equivalent to
  289. * <code>new java.io.File(filename)</code>.
  290. *
  291. * @param filename a file name
  292. *
  293. * @return an absolute file that doesn't contain &quot;./&quot; or
  294. * &quot;../&quot; sequences and uses the correct separator for
  295. * the current platform.
  296. */
  297. public File resolveFile(File file, String filename) {
  298. filename = filename.replace('/', File.separatorChar)
  299. .replace('\\', File.separatorChar);
  300. // deal with absolute files
  301. if (filename.startsWith(File.separator) ||
  302. (filename.length() >= 2 &&
  303. Character.isLetter(filename.charAt(0)) &&
  304. filename.charAt(1) == ':')
  305. ) {
  306. return normalize(filename);
  307. }
  308. if (file == null) {
  309. return new File(filename);
  310. }
  311. File helpFile = new File(file.getAbsolutePath());
  312. StringTokenizer tok = new StringTokenizer(filename, File.separator);
  313. while (tok.hasMoreTokens()) {
  314. String part = tok.nextToken();
  315. if (part.equals("..")) {
  316. helpFile = getParentFile(helpFile);
  317. if (helpFile == null) {
  318. String msg = "The file or path you specified ("
  319. + filename + ") is invalid relative to "
  320. + file.getPath();
  321. throw new BuildException(msg);
  322. }
  323. } else if (part.equals(".")) {
  324. // Do nothing here
  325. } else {
  326. helpFile = new File(helpFile, part);
  327. }
  328. }
  329. return new File(helpFile.getAbsolutePath());
  330. }
  331. /**
  332. * &quot;normalize&quot; the given absolute path.
  333. *
  334. * <p>This includes:
  335. * <ul>
  336. * <li>Uppercase the drive letter if there is one.</li>
  337. * <li>Remove redundant slashes after the drive spec.</li>
  338. * <li>resolve all ./, .\, ../ and ..\ sequences.</li>
  339. * <li>DOS style paths that start with a drive letter will have
  340. * \ as the separator.</li>
  341. * </ul>
  342. *
  343. * @throws java.lang.NullPointerException if the file path is
  344. * equal to null.
  345. */
  346. public File normalize(String path) {
  347. String orig = path;
  348. path = path.replace('/', File.separatorChar)
  349. .replace('\\', File.separatorChar);
  350. // make sure we are dealing with an absolute path
  351. if (!path.startsWith(File.separator) &&
  352. ! (path.length() >= 2 &&
  353. Character.isLetter(path.charAt(0)) &&
  354. path.charAt(1) == ':')
  355. ) {
  356. String msg = path + " is not an absolute path";
  357. throw new BuildException(msg);
  358. }
  359. boolean dosWithDrive = false;
  360. String root = null;
  361. // Eliminate consecutive slashes after the drive spec
  362. if (path.length() >= 2 &&
  363. Character.isLetter(path.charAt(0)) &&
  364. path.charAt(1) == ':') {
  365. dosWithDrive = true;
  366. char[] ca = path.replace('/', '\\').toCharArray();
  367. StringBuffer sb = new StringBuffer();
  368. sb.append(Character.toUpperCase(ca[0])).append(':');
  369. for (int i = 2; i < ca.length; i++) {
  370. if ((ca[i] != '\\') ||
  371. (ca[i] == '\\' && ca[i - 1] != '\\')
  372. ) {
  373. sb.append(ca[i]);
  374. }
  375. }
  376. path = sb.toString().replace('\\', File.separatorChar);
  377. if (path.length() == 2) {
  378. root = path;
  379. path = "";
  380. } else {
  381. root = path.substring(0, 3);
  382. path = path.substring(3);
  383. }
  384. } else {
  385. if (path.length() == 1) {
  386. root = File.separator;
  387. path = "";
  388. } else if (path.charAt(1) == File.separatorChar) {
  389. // UNC drive
  390. root = File.separator+File.separator;
  391. path = path.substring(2);
  392. } else {
  393. root = File.separator;
  394. path = path.substring(1);
  395. }
  396. }
  397. Stack s = new Stack();
  398. s.push(root);
  399. StringTokenizer tok = new StringTokenizer(path, File.separator);
  400. while (tok.hasMoreTokens()) {
  401. String thisToken = tok.nextToken();
  402. if (".".equals(thisToken)) {
  403. continue;
  404. } else if ("..".equals(thisToken)) {
  405. if (s.size() < 2) {
  406. throw new BuildException("Cannot resolve path "+orig);
  407. } else {
  408. s.pop();
  409. }
  410. } else { // plain component
  411. s.push(thisToken);
  412. }
  413. }
  414. StringBuffer sb = new StringBuffer();
  415. for (int i=0; i<s.size(); i++) {
  416. if (i > 1) {
  417. // not before the filesystem root and not after it, since root
  418. // already contains one
  419. sb.append(File.separatorChar);
  420. }
  421. sb.append(s.elementAt(i));
  422. }
  423. path = sb.toString();
  424. if (dosWithDrive) {
  425. path = path.replace('/', '\\');
  426. }
  427. return new File(path);
  428. }
  429. /**
  430. * Create a temporary file in a given directory.
  431. *
  432. * <p>The file denoted by the returned abstract pathname did not
  433. * exist before this method was invoked, any subsequent invocation
  434. * of this method will yield a different file name.</p>
  435. *
  436. * <p>This method is different to File.createTempFile of JDK 1.2
  437. * as it doesn't create the file itself and doesn't use platform
  438. * specific temporary directory when the parentDir attribute is
  439. * null.</p>
  440. *
  441. * @param parentDir Directory to create the temporary file in -
  442. * current working directory will be assumed if this parameter is
  443. * null.
  444. *
  445. * @since 1.8
  446. */
  447. public File createTempFile(String prefix, String suffix, File parentDir) {
  448. File result = null;
  449. String parent = null;
  450. if (parentDir != null) {
  451. parent = parentDir.getPath();
  452. }
  453. DecimalFormat fmt = new DecimalFormat("#####");
  454. synchronized (rand) {
  455. do {
  456. result = new File(parent,
  457. prefix + fmt.format(rand.nextInt())
  458. + suffix);
  459. } while (result.exists());
  460. }
  461. return result;
  462. }
  463. /**
  464. * Compares the contents of two files.
  465. *
  466. * <p>simple but sub-optimal comparision algorithm. written for
  467. * working rather than fast. Better would be a block read into
  468. * buffers followed by long comparisions apart from the final 1-7
  469. * bytes.</p>
  470. *
  471. * @since 1.9
  472. */
  473. public boolean contentEquals(File f1, File f2) throws IOException {
  474. if (f1.exists() != f2.exists()) {
  475. return false;
  476. }
  477. if (!f1.exists()) {
  478. // two not existing files are equal
  479. return true;
  480. }
  481. if (f1.isDirectory() || f2.isDirectory()) {
  482. // don't want to compare directory contents for now
  483. return false;
  484. }
  485. if (f1.equals(f2)) {
  486. // same filename => true
  487. return true;
  488. }
  489. if (f1.length() != f2.length()) {
  490. // different size =>false
  491. return false;
  492. }
  493. InputStream in1 = null;
  494. InputStream in2 = null;
  495. try {
  496. in1 = new BufferedInputStream(new FileInputStream(f1));
  497. in2 = new BufferedInputStream(new FileInputStream(f2));
  498. int expectedByte = in1.read();
  499. while (expectedByte != -1) {
  500. if (expectedByte != in2.read()) {
  501. return false;
  502. }
  503. expectedByte = in1.read();
  504. }
  505. if (in2.read() != -1) {
  506. return false;
  507. }
  508. return true;
  509. } finally {
  510. if (in1 != null) {
  511. try {
  512. in1.close();
  513. } catch (IOException e) {}
  514. }
  515. if (in2 != null) {
  516. try {
  517. in2.close();
  518. } catch (IOException e) {}
  519. }
  520. }
  521. }
  522. /**
  523. * Emulation of File.getParentFile for JDK 1.1
  524. *
  525. * @since 1.10
  526. */
  527. public File getParentFile(File f) {
  528. if (f != null) {
  529. String p = f.getParent();
  530. if (p != null) {
  531. return new File(p);
  532. }
  533. }
  534. return null;
  535. }
  536. }