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 62 kB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package org.apache.tools.ant.util;
  19. import java.io.File;
  20. import java.io.FilenameFilter;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.io.InputStreamReader;
  24. import java.io.OutputStream;
  25. import java.io.Reader;
  26. import java.io.UnsupportedEncodingException;
  27. import java.io.Writer;
  28. import java.net.MalformedURLException;
  29. import java.net.URL;
  30. import java.text.DecimalFormat;
  31. import java.util.ArrayList;
  32. import java.util.Arrays;
  33. import java.util.Iterator;
  34. import java.util.List;
  35. import java.util.Random;
  36. import java.util.Stack;
  37. import java.util.StringTokenizer;
  38. import java.util.Vector;
  39. import org.apache.tools.ant.BuildException;
  40. import org.apache.tools.ant.PathTokenizer;
  41. import org.apache.tools.ant.Project;
  42. import org.apache.tools.ant.launch.Locator;
  43. import org.apache.tools.ant.taskdefs.condition.Os;
  44. import org.apache.tools.ant.types.FilterSetCollection;
  45. import org.apache.tools.ant.types.resources.FileResource;
  46. /**
  47. * This class also encapsulates methods which allow Files to be
  48. * referred to using abstract path names which are translated to native
  49. * system file paths at runtime as well as copying files or setting
  50. * their last modification time.
  51. *
  52. */
  53. public class FileUtils {
  54. private static final int DELETE_RETRY_SLEEP_MILLIS = 10;
  55. private static final int EXPAND_SPACE = 50;
  56. private static final FileUtils PRIMARY_INSTANCE = new FileUtils();
  57. //get some non-crypto-grade randomness from various places.
  58. private static Random rand = new Random(System.currentTimeMillis()
  59. + Runtime.getRuntime().freeMemory());
  60. private static final boolean ON_NETWARE = Os.isFamily("netware");
  61. private static final boolean ON_DOS = Os.isFamily("dos");
  62. private static final boolean ON_WIN9X = Os.isFamily("win9x");
  63. private static final boolean ON_WINDOWS = Os.isFamily("windows");
  64. static final int BUF_SIZE = 8192;
  65. /**
  66. * The granularity of timestamps under FAT.
  67. */
  68. public static final long FAT_FILE_TIMESTAMP_GRANULARITY = 2000;
  69. /**
  70. * The granularity of timestamps under Unix.
  71. */
  72. public static final long UNIX_FILE_TIMESTAMP_GRANULARITY = 1000;
  73. /**
  74. * The granularity of timestamps under the NT File System.
  75. * NTFS has a granularity of 100 nanoseconds, which is less
  76. * than 1 millisecond, so we round this up to 1 millisecond.
  77. */
  78. public static final long NTFS_FILE_TIMESTAMP_GRANULARITY = 1;
  79. /**
  80. * A one item cache for fromUri.
  81. * fromUri is called for each element when parseing ant build
  82. * files. It is a costly operation. This just caches the result
  83. * of the last call.
  84. */
  85. private Object cacheFromUriLock = new Object();
  86. private String cacheFromUriRequest = null;
  87. private String cacheFromUriResponse = null;
  88. /**
  89. * Factory method.
  90. *
  91. * @return a new instance of FileUtils.
  92. * @deprecated since 1.7.
  93. * Use getFileUtils instead,
  94. * FileUtils do not have state.
  95. */
  96. public static FileUtils newFileUtils() {
  97. return new FileUtils();
  98. }
  99. /**
  100. * Method to retrieve The FileUtils, which is shared by all users of this
  101. * method.
  102. * @return an instance of FileUtils.
  103. * @since Ant 1.6.3
  104. */
  105. public static FileUtils getFileUtils() {
  106. return PRIMARY_INSTANCE;
  107. }
  108. /**
  109. * Empty constructor.
  110. */
  111. protected FileUtils() {
  112. }
  113. /**
  114. * Get the URL for a file taking into account # characters.
  115. *
  116. * @param file the file whose URL representation is required.
  117. * @return The FileURL value.
  118. * @throws MalformedURLException if the URL representation cannot be
  119. * formed.
  120. */
  121. public URL getFileURL(File file) throws MalformedURLException {
  122. return new URL(toURI(file.getAbsolutePath()));
  123. }
  124. /**
  125. * Convenience method to copy a file from a source to a destination.
  126. * No filtering is performed.
  127. *
  128. * @param sourceFile Name of file to copy from.
  129. * Must not be <code>null</code>.
  130. * @param destFile Name of file to copy to.
  131. * Must not be <code>null</code>.
  132. *
  133. * @throws IOException if the copying fails.
  134. */
  135. public void copyFile(String sourceFile, String destFile) throws IOException {
  136. copyFile(new File(sourceFile), new File(destFile), null, false, false);
  137. }
  138. /**
  139. * Convenience method to copy a file from a source to a destination
  140. * specifying if token filtering must be used.
  141. *
  142. * @param sourceFile Name of file to copy from.
  143. * Must not be <code>null</code>.
  144. * @param destFile Name of file to copy to.
  145. * Must not be <code>null</code>.
  146. * @param filters the collection of filters to apply to this copy.
  147. *
  148. * @throws IOException if the copying fails.
  149. */
  150. public void copyFile(String sourceFile, String destFile, FilterSetCollection filters)
  151. throws IOException {
  152. copyFile(new File(sourceFile), new File(destFile), filters, false, false);
  153. }
  154. /**
  155. * Convenience method to copy a file from a source to a destination specifying if token
  156. * filtering must be used and if source files may overwrite newer destination files.
  157. *
  158. * @param sourceFile Name of file to copy from. Must not be <code>null</code>.
  159. * @param destFile Name of file to copy to. Must not be <code>null</code>.
  160. * @param filters the collection of filters to apply to this copy.
  161. * @param overwrite Whether or not the destination file should be overwritten if it already
  162. * exists.
  163. *
  164. * @throws IOException if the copying fails.
  165. */
  166. public void copyFile(String sourceFile, String destFile, FilterSetCollection filters,
  167. boolean overwrite) throws IOException {
  168. copyFile(new File(sourceFile), new File(destFile), filters, overwrite, false);
  169. }
  170. /**
  171. * Convenience method to copy a file from a source to a destination
  172. * specifying if token
  173. * filtering must be used, if source files may overwrite newer destination
  174. * files and the last
  175. * modified time of <code>destFile</code> file should be made equal to
  176. * the last modified time
  177. * of <code>sourceFile</code>.
  178. *
  179. * @param sourceFile Name of file to copy from. Must not be <code>null</code>.
  180. * @param destFile Name of file to copy to. Must not be <code>null</code>.
  181. * @param filters the collection of filters to apply to this copy.
  182. * @param overwrite Whether or not the destination file should be
  183. * overwritten if it already exists.
  184. * @param preserveLastModified Whether or not the last modified time of
  185. * the resulting file
  186. * should be set to that of the source file.
  187. *
  188. * @throws IOException if the copying fails.
  189. */
  190. public void copyFile(String sourceFile, String destFile,
  191. FilterSetCollection filters,
  192. boolean overwrite, boolean preserveLastModified)
  193. throws IOException {
  194. copyFile(new File(sourceFile), new File(destFile), filters, overwrite,
  195. preserveLastModified);
  196. }
  197. /**
  198. * Convenience method to copy a file from a source to a destination specifying if token
  199. * filtering must be used, if source files may overwrite newer destination files and the last
  200. * modified time of <code>destFile</code> file should be made equal to the last modified time
  201. * of <code>sourceFile</code>.
  202. *
  203. * @param sourceFile Name of file to copy from. Must not be <code>null</code>.
  204. * @param destFile Name of file to copy to. Must not be <code>null</code>.
  205. * @param filters the collection of filters to apply to this copy.
  206. * @param overwrite Whether or not the destination file should be overwritten if it already
  207. * exists.
  208. * @param preserveLastModified Whether or not the last modified time of the resulting file
  209. * should be set to that of the source file.
  210. * @param encoding the encoding used to read and write the files.
  211. *
  212. * @throws IOException if the copying fails.
  213. *
  214. * @since Ant 1.5
  215. */
  216. public void copyFile(String sourceFile, String destFile,
  217. FilterSetCollection filters, boolean overwrite,
  218. boolean preserveLastModified, String encoding) throws IOException {
  219. copyFile(new File(sourceFile), new File(destFile), filters,
  220. overwrite, preserveLastModified, encoding);
  221. }
  222. // CheckStyle:ParameterNumberCheck OFF - bc
  223. /**
  224. * Convenience method to copy a file from a source to a
  225. * destination specifying if token filtering must be used, if
  226. * filter chains must be used, if source files may overwrite
  227. * newer destination files and the last modified time of
  228. * <code>destFile</code> file should be made equal
  229. * to the last modified time of <code>sourceFile</code>.
  230. *
  231. * @param sourceFile Name of file to copy from.
  232. * Must not be <code>null</code>.
  233. * @param destFile Name of file to copy to.
  234. * Must not be <code>null</code>.
  235. * @param filters the collection of filters to apply to this copy.
  236. * @param filterChains filterChains to apply during the copy.
  237. * @param overwrite Whether or not the destination file should be
  238. * overwritten if it already exists.
  239. * @param preserveLastModified Whether or not the last modified time of
  240. * the resulting file should be set to that
  241. * of the source file.
  242. * @param encoding the encoding used to read and write the files.
  243. * @param project the project instance.
  244. *
  245. * @throws IOException if the copying fails.
  246. *
  247. * @since Ant 1.5
  248. */
  249. public void copyFile(String sourceFile, String destFile,
  250. FilterSetCollection filters, Vector filterChains,
  251. boolean overwrite, boolean preserveLastModified,
  252. String encoding, Project project) throws IOException {
  253. copyFile(new File(sourceFile), new File(destFile), filters, filterChains, overwrite,
  254. preserveLastModified, encoding, project);
  255. }
  256. /**
  257. * Convenience method to copy a file from a source to a destination specifying if token
  258. * filtering must be used, if filter chains must be used, if source files may overwrite newer
  259. * destination files and the last modified time of <code>destFile</code> file should be made
  260. * equal to the last modified time of <code>sourceFile</code>.
  261. *
  262. * @param sourceFile Name of file to copy from. Must not be <code>null</code>.
  263. * @param destFile Name of file to copy to. Must not be <code>null</code>.
  264. * @param filters the collection of filters to apply to this copy.
  265. * @param filterChains filterChains to apply during the copy.
  266. * @param overwrite Whether or not the destination file should be overwritten if it already
  267. * exists.
  268. * @param preserveLastModified Whether or not the last modified time of the resulting file
  269. * should be set to that of the source file.
  270. * @param inputEncoding the encoding used to read the files.
  271. * @param outputEncoding the encoding used to write the files.
  272. * @param project the project instance.
  273. *
  274. * @throws IOException if the copying fails.
  275. *
  276. * @since Ant 1.6
  277. */
  278. public void copyFile(String sourceFile, String destFile,
  279. FilterSetCollection filters, Vector filterChains,
  280. boolean overwrite, boolean preserveLastModified,
  281. String inputEncoding, String outputEncoding,
  282. Project project) throws IOException {
  283. copyFile(new File(sourceFile), new File(destFile), filters, filterChains, overwrite,
  284. preserveLastModified, inputEncoding, outputEncoding, project);
  285. }
  286. /**
  287. * Convenience method to copy a file from a source to a destination. No filtering is performed.
  288. *
  289. * @param sourceFile the file to copy from. Must not be <code>null</code>.
  290. * @param destFile the file to copy to. Must not be <code>null</code>.
  291. *
  292. * @throws IOException if the copying fails.
  293. */
  294. public void copyFile(File sourceFile, File destFile) throws IOException {
  295. copyFile(sourceFile, destFile, null, false, false);
  296. }
  297. /**
  298. * Convenience method to copy a file from a source to a destination
  299. * specifying if token filtering must be used.
  300. *
  301. * @param sourceFile the file to copy from.
  302. * Must not be <code>null</code>.
  303. * @param destFile the file to copy to.
  304. * Must not be <code>null</code>.
  305. * @param filters the collection of filters to apply to this copy.
  306. *
  307. * @throws IOException if the copying fails.
  308. */
  309. public void copyFile(File sourceFile, File destFile, FilterSetCollection filters)
  310. throws IOException {
  311. copyFile(sourceFile, destFile, filters, false, false);
  312. }
  313. /**
  314. * Convenience method to copy a file from a source to a
  315. * destination specifying if token filtering must be used and if
  316. * source files may overwrite newer destination files.
  317. *
  318. * @param sourceFile the file to copy from.
  319. * Must not be <code>null</code>.
  320. * @param destFile the file to copy to.
  321. * Must not be <code>null</code>.
  322. * @param filters the collection of filters to apply to this copy.
  323. * @param overwrite Whether or not the destination file should be
  324. * overwritten if it already exists.
  325. *
  326. * @throws IOException if the copying fails.
  327. */
  328. public void copyFile(File sourceFile, File destFile, FilterSetCollection filters,
  329. boolean overwrite) throws IOException {
  330. copyFile(sourceFile, destFile, filters, overwrite, false);
  331. }
  332. /**
  333. * Convenience method to copy a file from a source to a
  334. * destination specifying if token filtering must be used, if
  335. * source files may overwrite newer destination files and the
  336. * last modified time of <code>destFile</code> file should be made equal
  337. * to the last modified time of <code>sourceFile</code>.
  338. *
  339. * @param sourceFile the file to copy from.
  340. * Must not be <code>null</code>.
  341. * @param destFile the file to copy to.
  342. * Must not be <code>null</code>.
  343. * @param filters the collection of filters to apply to this copy.
  344. * @param overwrite Whether or not the destination file should be
  345. * overwritten if it already exists.
  346. * @param preserveLastModified Whether or not the last modified time of
  347. * the resulting file should be set to that
  348. * of the source file.
  349. *
  350. * @throws IOException if the copying fails.
  351. */
  352. public void copyFile(File sourceFile, File destFile, FilterSetCollection filters,
  353. boolean overwrite, boolean preserveLastModified) throws IOException {
  354. copyFile(sourceFile, destFile, filters, overwrite, preserveLastModified, null);
  355. }
  356. /**
  357. * Convenience method to copy a file from a source to a destination specifying if token
  358. * filtering must be used, if source files may overwrite newer destination files, the last
  359. * modified time of <code>destFile</code> file should be made equal to the last modified time
  360. * of <code>sourceFile</code> and which character encoding to assume.
  361. *
  362. * @param sourceFile the file to copy from. Must not be <code>null</code>.
  363. * @param destFile the file to copy to. Must not be <code>null</code>.
  364. * @param filters the collection of filters to apply to this copy.
  365. * @param overwrite Whether or not the destination file should be overwritten if it already
  366. * exists.
  367. * @param preserveLastModified Whether or not the last modified time of the resulting file
  368. * should be set to that of the source file.
  369. * @param encoding the encoding used to read and write the files.
  370. *
  371. * @throws IOException if the copying fails.
  372. *
  373. * @since Ant 1.5
  374. */
  375. public void copyFile(File sourceFile, File destFile,
  376. FilterSetCollection filters, boolean overwrite,
  377. boolean preserveLastModified, String encoding) throws IOException {
  378. copyFile(sourceFile, destFile, filters, null, overwrite,
  379. preserveLastModified, encoding, null);
  380. }
  381. /**
  382. * Convenience method to copy a file from a source to a
  383. * destination specifying if token filtering must be used, if
  384. * filter chains must be used, if source files may overwrite
  385. * newer destination files and the last modified time of
  386. * <code>destFile</code> file should be made equal
  387. * to the last modified time of <code>sourceFile</code>.
  388. *
  389. * @param sourceFile the file to copy from.
  390. * Must not be <code>null</code>.
  391. * @param destFile the file to copy to.
  392. * Must not be <code>null</code>.
  393. * @param filters the collection of filters to apply to this copy.
  394. * @param filterChains filterChains to apply during the copy.
  395. * @param overwrite Whether or not the destination file should be
  396. * overwritten if it already exists.
  397. * @param preserveLastModified Whether or not the last modified time of
  398. * the resulting file should be set to that
  399. * of the source file.
  400. * @param encoding the encoding used to read and write the files.
  401. * @param project the project instance.
  402. *
  403. * @throws IOException if the copying fails.
  404. *
  405. * @since Ant 1.5
  406. */
  407. public void copyFile(File sourceFile, File destFile,
  408. FilterSetCollection filters, Vector filterChains,
  409. boolean overwrite, boolean preserveLastModified,
  410. String encoding, Project project) throws IOException {
  411. copyFile(sourceFile, destFile, filters, filterChains,
  412. overwrite, preserveLastModified, encoding, encoding, project);
  413. }
  414. /**
  415. * Convenience method to copy a file from a source to a
  416. * destination specifying if token filtering must be used, if
  417. * filter chains must be used, if source files may overwrite
  418. * newer destination files and the last modified time of
  419. * <code>destFile</code> file should be made equal
  420. * to the last modified time of <code>sourceFile</code>.
  421. *
  422. * @param sourceFile the file to copy from.
  423. * Must not be <code>null</code>.
  424. * @param destFile the file to copy to.
  425. * Must not be <code>null</code>.
  426. * @param filters the collection of filters to apply to this copy.
  427. * @param filterChains filterChains to apply during the copy.
  428. * @param overwrite Whether or not the destination file should be
  429. * overwritten if it already exists.
  430. * @param preserveLastModified Whether or not the last modified time of
  431. * the resulting file should be set to that
  432. * of the source file.
  433. * @param inputEncoding the encoding used to read the files.
  434. * @param outputEncoding the encoding used to write the files.
  435. * @param project the project instance.
  436. *
  437. *
  438. * @throws IOException if the copying fails.
  439. *
  440. * @since Ant 1.6
  441. */
  442. public void copyFile(File sourceFile, File destFile,
  443. FilterSetCollection filters, Vector filterChains,
  444. boolean overwrite, boolean preserveLastModified,
  445. String inputEncoding, String outputEncoding,
  446. Project project) throws IOException {
  447. ResourceUtils.copyResource(
  448. new FileResource(sourceFile), new FileResource(destFile),
  449. filters, filterChains, overwrite, preserveLastModified,
  450. inputEncoding, outputEncoding, project);
  451. }
  452. // CheckStyle:ParameterNumberCheck ON
  453. /**
  454. * Calls File.setLastModified(long time). Originally written to
  455. * to dynamically bind to that call on Java1.2+.
  456. *
  457. * @param file the file whose modified time is to be set
  458. * @param time the time to which the last modified time is to be set.
  459. * if this is -1, the current time is used.
  460. */
  461. public void setFileLastModified(File file, long time) {
  462. ResourceUtils.setLastModified(new FileResource(file), time);
  463. }
  464. /**
  465. * Interpret the filename as a file relative to the given file
  466. * unless the filename already represents an absolute filename.
  467. * Differs from <code>new File(file, filename)</code> in that
  468. * the resulting File's path will always be a normalized,
  469. * absolute pathname. Also, if it is determined that
  470. * <code>filename</code> is context-relative, <code>file</code>
  471. * will be discarded and the reference will be resolved using
  472. * available context/state information about the filesystem.
  473. *
  474. * @param file the "reference" file for relative paths. This
  475. * instance must be an absolute file and must not contain
  476. * &quot;./&quot; or &quot;../&quot; sequences (same for \ instead
  477. * of /). If it is null, this call is equivalent to
  478. * <code>new java.io.File(filename).getAbsoluteFile()</code>.
  479. *
  480. * @param filename a file name.
  481. *
  482. * @return an absolute file.
  483. * @throws java.lang.NullPointerException if filename is null.
  484. */
  485. public File resolveFile(File file, String filename) {
  486. if (!isAbsolutePath(filename)) {
  487. char sep = File.separatorChar;
  488. filename = filename.replace('/', sep).replace('\\', sep);
  489. if (isContextRelativePath(filename)) {
  490. file = null;
  491. // on cygwin, our current directory can be a UNC;
  492. // assume user.dir is absolute or all hell breaks loose...
  493. String udir = System.getProperty("user.dir");
  494. if (filename.charAt(0) == sep && udir.charAt(0) == sep) {
  495. filename = dissect(udir)[0] + filename.substring(1);
  496. }
  497. }
  498. filename = new File(file, filename).getAbsolutePath();
  499. }
  500. return normalize(filename);
  501. }
  502. /**
  503. * On DOS and NetWare, the evaluation of certain file
  504. * specifications is context-dependent. These are filenames
  505. * beginning with a single separator (relative to current root directory)
  506. * and filenames with a drive specification and no intervening separator
  507. * (relative to current directory of the specified root).
  508. * @param filename the filename to evaluate.
  509. * @return true if the filename is relative to system context.
  510. * @throws java.lang.NullPointerException if filename is null.
  511. * @since Ant 1.7
  512. */
  513. public static boolean isContextRelativePath(String filename) {
  514. if (!(ON_DOS || ON_NETWARE) || filename.length() == 0) {
  515. return false;
  516. }
  517. char sep = File.separatorChar;
  518. filename = filename.replace('/', sep).replace('\\', sep);
  519. char c = filename.charAt(0);
  520. int len = filename.length();
  521. return (c == sep && (len == 1 || filename.charAt(1) != sep))
  522. || (Character.isLetter(c) && len > 1
  523. && filename.indexOf(':') == 1
  524. && (len == 2 || filename.charAt(2) != sep));
  525. }
  526. /**
  527. * Verifies that the specified filename represents an absolute path.
  528. * Differs from new java.io.File("filename").isAbsolute() in that a path
  529. * beginning with a double file separator--signifying a Windows UNC--must
  530. * at minimum match "\\a\b" to be considered an absolute path.
  531. * @param filename the filename to be checked.
  532. * @return true if the filename represents an absolute path.
  533. * @throws java.lang.NullPointerException if filename is null.
  534. * @since Ant 1.6.3
  535. */
  536. public static boolean isAbsolutePath(String filename) {
  537. int len = filename.length();
  538. if (len == 0) {
  539. return false;
  540. }
  541. char sep = File.separatorChar;
  542. filename = filename.replace('/', sep).replace('\\', sep);
  543. char c = filename.charAt(0);
  544. if (!(ON_DOS || ON_NETWARE)) {
  545. return (c == sep);
  546. }
  547. if (c == sep) {
  548. // CheckStyle:MagicNumber OFF
  549. if (!(ON_DOS && len > 4 && filename.charAt(1) == sep)) {
  550. return false;
  551. }
  552. // CheckStyle:MagicNumber ON
  553. int nextsep = filename.indexOf(sep, 2);
  554. return nextsep > 2 && nextsep + 1 < len;
  555. }
  556. int colon = filename.indexOf(':');
  557. return (Character.isLetter(c) && colon == 1
  558. && filename.length() > 2 && filename.charAt(2) == sep)
  559. || (ON_NETWARE && colon > 0);
  560. }
  561. /**
  562. * Translate a path into its native (platform specific) format.
  563. * <p>
  564. * This method uses PathTokenizer to separate the input path
  565. * into its components. This handles DOS style paths in a relatively
  566. * sensible way. The file separators are then converted to their platform
  567. * specific versions.
  568. *
  569. * @param toProcess The path to be translated.
  570. * May be <code>null</code>.
  571. *
  572. * @return the native version of the specified path or
  573. * an empty string if the path is <code>null</code> or empty.
  574. *
  575. * @since ant 1.7
  576. * @see PathTokenizer
  577. */
  578. public static String translatePath(String toProcess) {
  579. if (toProcess == null || toProcess.length() == 0) {
  580. return "";
  581. }
  582. StringBuffer path = new StringBuffer(toProcess.length() + EXPAND_SPACE);
  583. PathTokenizer tokenizer = new PathTokenizer(toProcess);
  584. while (tokenizer.hasMoreTokens()) {
  585. String pathComponent = tokenizer.nextToken();
  586. pathComponent = pathComponent.replace('/', File.separatorChar);
  587. pathComponent = pathComponent.replace('\\', File.separatorChar);
  588. if (path.length() != 0) {
  589. path.append(File.pathSeparatorChar);
  590. }
  591. path.append(pathComponent);
  592. }
  593. return path.toString();
  594. }
  595. /**
  596. * &quot;Normalize&quot; the given absolute path.
  597. *
  598. * <p>This includes:
  599. * <ul>
  600. * <li>Uppercase the drive letter if there is one.</li>
  601. * <li>Remove redundant slashes after the drive spec.</li>
  602. * <li>Resolve all ./, .\, ../ and ..\ sequences.</li>
  603. * <li>DOS style paths that start with a drive letter will have
  604. * \ as the separator.</li>
  605. * </ul>
  606. * Unlike {@link File#getCanonicalPath()} this method
  607. * specifically does not resolve symbolic links.
  608. *
  609. * @param path the path to be normalized.
  610. * @return the normalized version of the path.
  611. *
  612. * @throws java.lang.NullPointerException if path is null.
  613. */
  614. public File normalize(final String path) {
  615. Stack s = new Stack();
  616. String[] dissect = dissect(path);
  617. s.push(dissect[0]);
  618. StringTokenizer tok = new StringTokenizer(dissect[1], File.separator);
  619. while (tok.hasMoreTokens()) {
  620. String thisToken = tok.nextToken();
  621. if (".".equals(thisToken)) {
  622. continue;
  623. }
  624. if ("..".equals(thisToken)) {
  625. if (s.size() < 2) {
  626. // Cannot resolve it, so skip it.
  627. return new File(path);
  628. }
  629. s.pop();
  630. } else { // plain component
  631. s.push(thisToken);
  632. }
  633. }
  634. StringBuffer sb = new StringBuffer();
  635. for (int i = 0; i < s.size(); i++) {
  636. if (i > 1) {
  637. // not before the filesystem root and not after it, since root
  638. // already contains one
  639. sb.append(File.separatorChar);
  640. }
  641. sb.append(s.elementAt(i));
  642. }
  643. return new File(sb.toString());
  644. }
  645. /**
  646. * Dissect the specified absolute path.
  647. * @param path the path to dissect.
  648. * @return String[] {root, remaining path}.
  649. * @throws java.lang.NullPointerException if path is null.
  650. * @since Ant 1.7
  651. */
  652. public String[] dissect(String path) {
  653. char sep = File.separatorChar;
  654. path = path.replace('/', sep).replace('\\', sep);
  655. // make sure we are dealing with an absolute path
  656. if (!isAbsolutePath(path)) {
  657. throw new BuildException(path + " is not an absolute path");
  658. }
  659. String root = null;
  660. int colon = path.indexOf(':');
  661. if (colon > 0 && (ON_DOS || ON_NETWARE)) {
  662. int next = colon + 1;
  663. root = path.substring(0, next);
  664. char[] ca = path.toCharArray();
  665. root += sep;
  666. //remove the initial separator; the root has it.
  667. next = (ca[next] == sep) ? next + 1 : next;
  668. StringBuffer sbPath = new StringBuffer();
  669. // Eliminate consecutive slashes after the drive spec:
  670. for (int i = next; i < ca.length; i++) {
  671. if (ca[i] != sep || ca[i - 1] != sep) {
  672. sbPath.append(ca[i]);
  673. }
  674. }
  675. path = sbPath.toString();
  676. } else if (path.length() > 1 && path.charAt(1) == sep) {
  677. // UNC drive
  678. int nextsep = path.indexOf(sep, 2);
  679. nextsep = path.indexOf(sep, nextsep + 1);
  680. root = (nextsep > 2) ? path.substring(0, nextsep + 1) : path;
  681. path = path.substring(root.length());
  682. } else {
  683. root = File.separator;
  684. path = path.substring(1);
  685. }
  686. return new String[] {root, path};
  687. }
  688. /**
  689. * Returns a VMS String representation of a <code>File</code> object.
  690. * This is useful since the JVM by default internally converts VMS paths
  691. * to Unix style.
  692. * The returned String is always an absolute path.
  693. *
  694. * @param f The <code>File</code> to get the VMS path for.
  695. * @return The absolute VMS path to <code>f</code>.
  696. */
  697. public String toVMSPath(File f) {
  698. // format: "DEVICE:[DIR.SUBDIR]FILE"
  699. String osPath;
  700. String path = normalize(f.getAbsolutePath()).getPath();
  701. String name = f.getName();
  702. boolean isAbsolute = path.charAt(0) == File.separatorChar;
  703. // treat directories specified using .DIR syntax as files
  704. // CheckStyle:MagicNumber OFF
  705. boolean isDirectory = f.isDirectory()
  706. && !name.regionMatches(true, name.length() - 4, ".DIR", 0, 4);
  707. // CheckStyle:MagicNumber ON
  708. String device = null;
  709. StringBuffer directory = null;
  710. String file = null;
  711. int index = 0;
  712. if (isAbsolute) {
  713. index = path.indexOf(File.separatorChar, 1);
  714. if (index == -1) {
  715. return path.substring(1) + ":[000000]";
  716. }
  717. device = path.substring(1, index++);
  718. }
  719. if (isDirectory) {
  720. directory = new StringBuffer(path.substring(index).replace(File.separatorChar, '.'));
  721. } else {
  722. int dirEnd = path.lastIndexOf(File.separatorChar, path.length());
  723. if (dirEnd == -1 || dirEnd < index) {
  724. file = path.substring(index);
  725. } else {
  726. directory = new StringBuffer(path.substring(index, dirEnd).
  727. replace(File.separatorChar, '.'));
  728. index = dirEnd + 1;
  729. if (path.length() > index) {
  730. file = path.substring(index);
  731. }
  732. }
  733. }
  734. if (!isAbsolute && directory != null) {
  735. directory.insert(0, '.');
  736. }
  737. osPath = ((device != null) ? device + ":" : "")
  738. + ((directory != null) ? "[" + directory + "]" : "")
  739. + ((file != null) ? file : "");
  740. return osPath;
  741. }
  742. /**
  743. * Create a File object for a temporary file in a given directory. Without
  744. * actually creating the file.
  745. *
  746. * <p>
  747. * The file denoted by the returned abstract pathname did not exist before
  748. * this method was invoked, any subsequent invocation of this method will
  749. * yield a different file name.
  750. * </p>
  751. * <p>
  752. * The filename is prefixNNNNNsuffix where NNNN is a random number.
  753. * </p>
  754. *
  755. * @param prefix
  756. * prefix before the random number.
  757. * @param suffix
  758. * file extension; include the '.'.
  759. * @param parentDir
  760. * Directory to create the temporary file in; java.io.tmpdir used
  761. * if not specified.
  762. *
  763. * @deprecated since ant 1.7.1 use createTempFile(String, String, File,
  764. * boolean, boolean) instead.
  765. * @return a File reference to the new, nonexistent temporary file.
  766. */
  767. public File createTempFile(String prefix, String suffix, File parentDir) {
  768. return createTempFile(prefix, suffix, parentDir, false, false);
  769. }
  770. /**
  771. * Create a temporary file in a given directory.
  772. *
  773. * <p>The file denoted by the returned abstract pathname did not
  774. * exist before this method was invoked, any subsequent invocation
  775. * of this method will yield a different file name.</p>
  776. *
  777. * @param prefix prefix before the random number.
  778. * @param suffix file extension; include the '.'.
  779. * @param parentDir Directory to create the temporary file in;
  780. * java.io.tmpdir used if not specified.
  781. * @param deleteOnExit whether to set the tempfile for deletion on
  782. * normal VM exit.
  783. * @param createFile true if the file must actually be created. If false
  784. * chances exist that a file with the same name is created in the time
  785. * between invoking this method and the moment the file is actually created.
  786. * If possible set to true.
  787. *
  788. * @return a File reference to the new temporary file.
  789. * @since Ant 1.7.1
  790. */
  791. public File createTempFile(String prefix, String suffix, File parentDir,
  792. boolean deleteOnExit, boolean createFile) {
  793. File result = null;
  794. String parent = (parentDir == null)
  795. ? System.getProperty("java.io.tmpdir")
  796. : parentDir.getPath();
  797. if (createFile) {
  798. try {
  799. result = File.createTempFile(prefix, suffix, new File(parent));
  800. } catch (IOException e) {
  801. throw new BuildException("Could not create tempfile in "
  802. + parent, e);
  803. }
  804. } else {
  805. DecimalFormat fmt = new DecimalFormat("#####");
  806. synchronized (rand) {
  807. do {
  808. result = new File(parent, prefix
  809. + fmt.format(Math.abs(rand.nextInt())) + suffix);
  810. } while (result.exists());
  811. }
  812. }
  813. if (deleteOnExit) {
  814. result.deleteOnExit();
  815. }
  816. return result;
  817. }
  818. /**
  819. * Create a File object for a temporary file in a given directory. Without
  820. * actually creating the file.
  821. *
  822. * <p>
  823. * The file denoted by the returned abstract pathname did not exist before
  824. * this method was invoked, any subsequent invocation of this method will
  825. * yield a different file name.
  826. * </p>
  827. * <p>
  828. * The filename is prefixNNNNNsuffix where NNNN is a random number.
  829. * </p>
  830. *
  831. * @param prefix
  832. * prefix before the random number.
  833. * @param suffix
  834. * file extension; include the '.'.
  835. * @param parentDir
  836. * Directory to create the temporary file in; java.io.tmpdir used
  837. * if not specified.
  838. * @param deleteOnExit
  839. * whether to set the tempfile for deletion on normal VM exit.
  840. *
  841. * @deprecated since ant 1.7.1 use createTempFile(String, String, File,
  842. * boolean, boolean) instead.
  843. * @return a File reference to the new, nonexistent temporary file.
  844. */
  845. public File createTempFile(String prefix, String suffix,
  846. File parentDir, boolean deleteOnExit) {
  847. return createTempFile(prefix, suffix, parentDir, deleteOnExit, false);
  848. }
  849. /**
  850. * Compares the contents of two files.
  851. *
  852. * @param f1 the file whose content is to be compared.
  853. * @param f2 the other file whose content is to be compared.
  854. *
  855. * @return true if the content of the files is the same.
  856. *
  857. * @throws IOException if the files cannot be read.
  858. */
  859. public boolean contentEquals(File f1, File f2) throws IOException {
  860. return contentEquals(f1, f2, false);
  861. }
  862. /**
  863. * Compares the contents of two files.
  864. *
  865. * @param f1 the file whose content is to be compared.
  866. * @param f2 the other file whose content is to be compared.
  867. * @param textfile true if the file is to be treated as a text file and
  868. * differences in kind of line break are to be ignored.
  869. *
  870. * @return true if the content of the files is the same.
  871. *
  872. * @throws IOException if the files cannot be read.
  873. * @since Ant 1.6.3
  874. */
  875. public boolean contentEquals(File f1, File f2, boolean textfile) throws IOException {
  876. return ResourceUtils.contentEquals(new FileResource(f1), new FileResource(f2), textfile);
  877. }
  878. /**
  879. * This was originally an emulation of {@link File#getParentFile} for JDK 1.1, but it is now
  880. * implemented using that method (Ant 1.6.3 onwards).
  881. *
  882. * @param f the file whose parent is required.
  883. * @return the given file's parent, or null if the file does not have a parent.
  884. * @since 1.10
  885. * @deprecated since 1.7. Just use {@link File#getParentFile} directly.
  886. */
  887. public File getParentFile(File f) {
  888. return (f == null) ? null : f.getParentFile();
  889. }
  890. /**
  891. * Read from reader till EOF.
  892. * @param rdr the reader from which to read.
  893. * @return the contents read out of the given reader.
  894. *
  895. * @throws IOException if the contents could not be read out from the
  896. * reader.
  897. */
  898. public static String readFully(Reader rdr) throws IOException {
  899. return readFully(rdr, BUF_SIZE);
  900. }
  901. /**
  902. * Read from reader till EOF.
  903. *
  904. * @param rdr the reader from which to read.
  905. * @param bufferSize the buffer size to use when reading.
  906. *
  907. * @return the contents read out of the given reader.
  908. *
  909. * @throws IOException if the contents could not be read out from the
  910. * reader.
  911. */
  912. public static String readFully(Reader rdr, int bufferSize)
  913. throws IOException {
  914. if (bufferSize <= 0) {
  915. throw new IllegalArgumentException("Buffer size must be greater "
  916. + "than 0");
  917. }
  918. final char[] buffer = new char[bufferSize];
  919. int bufferLength = 0;
  920. StringBuffer textBuffer = null;
  921. while (bufferLength != -1) {
  922. bufferLength = rdr.read(buffer);
  923. if (bufferLength > 0) {
  924. textBuffer = (textBuffer == null) ? new StringBuffer() : textBuffer;
  925. textBuffer.append(new String(buffer, 0, bufferLength));
  926. }
  927. }
  928. return (textBuffer == null) ? null : textBuffer.toString();
  929. }
  930. /**
  931. * Safe read fully - do not return a null for an empty reader.
  932. * @param reader the input to read from.
  933. * @return the string.
  934. * @throws IOException if unable to read from reader.
  935. * @since Ant 1.7.1
  936. */
  937. public static String safeReadFully(Reader reader) throws IOException {
  938. String ret = readFully(reader);
  939. return ret == null ? "" : ret;
  940. }
  941. /**
  942. * This was originally an emulation of File.createNewFile for JDK 1.1,
  943. * but it is now implemented using that method (Ant 1.6.3 onwards).
  944. *
  945. * <p>This method has historically <strong>not</strong> guaranteed that the
  946. * operation was atomic. In its current implementation it is.
  947. *
  948. * @param f the file to be created.
  949. * @return true if the file did not exist already.
  950. * @throws IOException on error.
  951. * @since Ant 1.5
  952. */
  953. public boolean createNewFile(File f) throws IOException {
  954. return f.createNewFile();
  955. }
  956. /**
  957. * Create a new file, optionally creating parent directories.
  958. *
  959. * @param f the file to be created.
  960. * @param mkdirs <code>boolean</code> whether to create parent directories.
  961. * @return true if the file did not exist already.
  962. * @throws IOException on error.
  963. * @since Ant 1.6.3
  964. */
  965. public boolean createNewFile(File f, boolean mkdirs) throws IOException {
  966. File parent = f.getParentFile();
  967. if (mkdirs && !(parent.exists())) {
  968. parent.mkdirs();
  969. }
  970. return f.createNewFile();
  971. }
  972. /**
  973. * Checks whether a given file is a symbolic link.
  974. *
  975. * <p>It doesn't really test for symbolic links but whether the
  976. * canonical and absolute paths of the file are identical--this
  977. * may lead to false positives on some platforms.</p>
  978. *
  979. * @param parent the parent directory of the file to test
  980. * @param name the name of the file to test.
  981. *
  982. * @return true if the file is a symbolic link.
  983. * @throws IOException on error.
  984. * @since Ant 1.5
  985. * @deprecated use SymbolicLinkUtils instead
  986. */
  987. public boolean isSymbolicLink(File parent, String name)
  988. throws IOException {
  989. SymbolicLinkUtils u = SymbolicLinkUtils.getSymbolicLinkUtils();
  990. if (parent == null) {
  991. return u.isSymbolicLink(name);
  992. }
  993. return u.isSymbolicLink(parent, name);
  994. }
  995. /**
  996. * Removes a leading path from a second path.
  997. *
  998. * @param leading The leading path, must not be null, must be absolute.
  999. * @param path The path to remove from, must not be null, must be absolute.
  1000. *
  1001. * @return path's normalized absolute if it doesn't start with
  1002. * leading; path's path with leading's path removed otherwise.
  1003. *
  1004. * @since Ant 1.5
  1005. */
  1006. public String removeLeadingPath(File leading, File path) {
  1007. String l = normalize(leading.getAbsolutePath()).getAbsolutePath();
  1008. String p = normalize(path.getAbsolutePath()).getAbsolutePath();
  1009. if (l.equals(p)) {
  1010. return "";
  1011. }
  1012. // ensure that l ends with a /
  1013. // so we never think /foo was a parent directory of /foobar
  1014. if (!l.endsWith(File.separator)) {
  1015. l += File.separator;
  1016. }
  1017. return (p.startsWith(l)) ? p.substring(l.length()) : p;
  1018. }
  1019. /**
  1020. * Learn whether one path "leads" another.
  1021. * @param leading The leading path, must not be null, must be absolute.
  1022. * @param path The path to remove from, must not be null, must be absolute.
  1023. * @return true if path starts with leading; false otherwise.
  1024. * @since Ant 1.7
  1025. */
  1026. public boolean isLeadingPath(File leading, File path) {
  1027. String l = normalize(leading.getAbsolutePath()).getAbsolutePath();
  1028. String p = normalize(path.getAbsolutePath()).getAbsolutePath();
  1029. if (l.equals(p)) {
  1030. return true;
  1031. }
  1032. // ensure that l ends with a /
  1033. // so we never think /foo was a parent directory of /foobar
  1034. if (!l.endsWith(File.separator)) {
  1035. l += File.separator;
  1036. }
  1037. return p.startsWith(l);
  1038. }
  1039. /**
  1040. * Constructs a <code>file:</code> URI that represents the
  1041. * external form of the given pathname.
  1042. *
  1043. * <p>Will be an absolute URI if the given path is absolute.</p>
  1044. *
  1045. * <p>This code encodes non ASCII characters too.</p>
  1046. *
  1047. * <p>The coding of the output is the same as what File.toURI().toASCIIString() produces</p>
  1048. *
  1049. * See <a href="http://www.w3.org/TR/xml11/#dt-sysid">dt-sysid</a>
  1050. * which makes some mention of how
  1051. * characters not supported by URI Reference syntax should be escaped.
  1052. *
  1053. * @param path the path in the local file system.
  1054. * @return the URI version of the local path.
  1055. * @since Ant 1.6
  1056. */
  1057. public String toURI(String path) {
  1058. // #8031: first try Java 1.4.
  1059. Class uriClazz = null;
  1060. try {
  1061. uriClazz = Class.forName("java.net.URI");
  1062. } catch (ClassNotFoundException e) {
  1063. // OK, Java 1.3.
  1064. }
  1065. if (uriClazz != null) {
  1066. try {
  1067. File f = new File(path).getAbsoluteFile();
  1068. java.lang.reflect.Method toURIMethod = File.class.getMethod("toURI", new Class[0]);
  1069. Object uriObj = toURIMethod.invoke(f, new Object[] {});
  1070. java.lang.reflect.Method toASCIIStringMethod
  1071. = uriClazz.getMethod("toASCIIString", new Class[0]);
  1072. return (String) toASCIIStringMethod.invoke(uriObj, new Object[] {});
  1073. } catch (Exception e) {
  1074. // Reflection problems? Should not happen, debug.
  1075. e.printStackTrace();
  1076. }
  1077. }
  1078. boolean isDir = new File(path).isDirectory();
  1079. StringBuffer sb = new StringBuffer("file:");
  1080. path = resolveFile(null, path).getPath();
  1081. sb.append("//");
  1082. // add an extra slash for filesystems with drive-specifiers
  1083. if (!path.startsWith(File.separator)) {
  1084. sb.append("/");
  1085. }
  1086. path = path.replace('\\', '/');
  1087. try {
  1088. sb.append(Locator.encodeURI(path));
  1089. } catch (UnsupportedEncodingException exc) {
  1090. throw new BuildException(exc);
  1091. }
  1092. if (isDir && !path.endsWith("/")) {
  1093. sb.append('/');
  1094. }
  1095. return sb.toString();
  1096. }
  1097. /**
  1098. * Constructs a file path from a <code>file:</code> URI.
  1099. *
  1100. * <p>Will be an absolute path if the given URI is absolute.</p>
  1101. *
  1102. * <p>Swallows '%' that are not followed by two characters,
  1103. * doesn't deal with non-ASCII characters.</p>
  1104. *
  1105. * @param uri the URI designating a file in the local filesystem.
  1106. * @return the local file system path for the file.
  1107. * @since Ant 1.6
  1108. */
  1109. public String fromURI(String uri) {
  1110. synchronized (cacheFromUriLock) {
  1111. if (uri.equals(cacheFromUriRequest)) {
  1112. return cacheFromUriResponse;
  1113. }
  1114. String path = Locator.fromURI(uri);
  1115. String ret = isAbsolutePath(path) ? normalize(path).getAbsolutePath() : path;
  1116. cacheFromUriRequest = uri;
  1117. cacheFromUriResponse = ret;
  1118. return ret;
  1119. }
  1120. }
  1121. /**
  1122. * Compares two filenames.
  1123. *
  1124. * <p>Unlike java.io.File#equals this method will try to compare
  1125. * the absolute paths and &quot;normalize&quot; the filenames
  1126. * before comparing them.</p>
  1127. *
  1128. * @param f1 the file whose name is to be compared.
  1129. * @param f2 the other file whose name is to be compared.
  1130. *
  1131. * @return true if the file are for the same file.
  1132. *
  1133. * @since Ant 1.5.3
  1134. */
  1135. public boolean fileNameEquals(File f1, File f2) {
  1136. return normalize(f1.getAbsolutePath()).getAbsolutePath().equals(
  1137. normalize(f2.getAbsolutePath()).getAbsolutePath());
  1138. }
  1139. /**
  1140. * Renames a file, even if that involves crossing file system boundaries.
  1141. *
  1142. * <p>This will remove <code>to</code> (if it exists), ensure that
  1143. * <code>to</code>'s parent directory exists and move
  1144. * <code>from</code>, which involves deleting <code>from</code> as
  1145. * well.</p>
  1146. *
  1147. * @param from the file to move.
  1148. * @param to the new file name.
  1149. *
  1150. * @throws IOException if anything bad happens during this
  1151. * process. Note that <code>to</code> may have been deleted
  1152. * already when this happens.
  1153. *
  1154. * @since Ant 1.6
  1155. */
  1156. public void rename(File from, File to) throws IOException {
  1157. from = normalize(from.getAbsolutePath()).getCanonicalFile();
  1158. to = normalize(to.getAbsolutePath());
  1159. if (!from.exists()) {
  1160. System.err.println("Cannot rename nonexistent file " + from);
  1161. return;
  1162. }
  1163. if (from.equals(to)) {
  1164. System.err.println("Rename of " + from + " to " + to + " is a no-op.");
  1165. return;
  1166. }
  1167. if (to.exists() && !(from.equals(to.getCanonicalFile()) || tryHardToDelete(to))) {
  1168. throw new IOException("Failed to delete " + to + " while trying to rename " + from);
  1169. }
  1170. File parent = to.getParentFile();
  1171. if (parent != null && !parent.exists() && !parent.mkdirs()) {
  1172. throw new IOException("Failed to create directory " + parent
  1173. + " while trying to rename " + from);
  1174. }
  1175. if (!from.renameTo(to)) {
  1176. copyFile(from, to);
  1177. if (!tryHardToDelete(from)) {
  1178. throw new IOException("Failed to delete " + from + " while trying to rename it.");
  1179. }
  1180. }
  1181. }
  1182. /**
  1183. * Get the granularity of file timestamps. The choice is made based on OS, which is
  1184. * incorrect--it should really be by filesystem. We do not have an easy way to probe for file
  1185. * systems, however, so this heuristic gives us a decent default.
  1186. *
  1187. * @return the difference, in milliseconds, which two file timestamps must have in order for the
  1188. * two files to be considered to have different timestamps.
  1189. */
  1190. public long getFileTimestampGranularity() {
  1191. if (ON_WIN9X) {
  1192. return FAT_FILE_TIMESTAMP_GRANULARITY;
  1193. }
  1194. if (ON_WINDOWS) {
  1195. return NTFS_FILE_TIMESTAMP_GRANULARITY;
  1196. }
  1197. if (ON_DOS) {
  1198. return FAT_FILE_TIMESTAMP_GRANULARITY;
  1199. }
  1200. return UNIX_FILE_TIMESTAMP_GRANULARITY;
  1201. }
  1202. /**
  1203. * test whether a file or directory exists, with an error in the
  1204. * upper/lower case spelling of the name.
  1205. * Using this method is only interesting on case insensitive file systems
  1206. * (Windows).<br/>
  1207. * It will return true only if 3 conditions are met :
  1208. * <br/>
  1209. * <ul>
  1210. * <li>operating system is case insensitive</li>
  1211. * <li>file exists</li>
  1212. * <li>actual name from directory reading is different from the
  1213. * supplied argument</li>
  1214. * </ul>
  1215. * <br/>
  1216. * the purpose is to identify files or directories on case-insensitive
  1217. * filesystems whose case is not what is expected.<br/>
  1218. * Possibly to rename them afterwards to the desired upper/lowercase
  1219. * combination.
  1220. * <br/>
  1221. * @param localFile file to test
  1222. * @return true if the file exists and the case of the actual file
  1223. * is not the case of the parameter
  1224. * @since Ant 1.7.1
  1225. */
  1226. public boolean hasErrorInCase(File localFile) {
  1227. localFile = normalize(localFile.getAbsolutePath());
  1228. if (!localFile.exists()) {
  1229. return false;
  1230. }
  1231. final String localFileName = localFile.getName();
  1232. FilenameFilter ff = new FilenameFilter () {
  1233. public boolean accept(File dir, String name) {
  1234. return name.equalsIgnoreCase(localFileName) && (!name.equals(localFileName));
  1235. }
  1236. };
  1237. String[] names = localFile.getParentFile().list(ff);
  1238. return names != null && names.length == 1;
  1239. }
  1240. /**
  1241. * Returns true if the source is older than the dest.
  1242. * If the dest file does not exist, then the test returns false; it is
  1243. * implicitly not up do date.
  1244. * @param source source file (should be the older).
  1245. * @param dest dest file (should be the newer).
  1246. * @param granularity an offset added to the source time.
  1247. * @return true if the source is older than the dest after accounting
  1248. * for granularity.
  1249. * @since Ant 1.6.3
  1250. */
  1251. public boolean isUpToDate(File source, File dest, long granularity) {
  1252. //do a check for the destination file existing
  1253. if (!dest.exists()) {
  1254. //if it does not, then the file is not up to date.
  1255. return false;
  1256. }
  1257. long sourceTime = source.lastModified();
  1258. long destTime = dest.lastModified();
  1259. return isUpToDate(sourceTime, destTime, granularity);
  1260. }
  1261. /**
  1262. * Returns true if the source is older than the dest.
  1263. * @param source source file (should be the older).
  1264. * @param dest dest file (should be the newer).
  1265. * @return true if the source is older than the dest, taking the granularity into account.
  1266. * @since Ant 1.6.3
  1267. */
  1268. public boolean isUpToDate(File source, File dest) {
  1269. return isUpToDate(source, dest, getFileTimestampGranularity());
  1270. }
  1271. /**
  1272. * Compare two timestamps for being up to date using
  1273. * the specified granularity.
  1274. *
  1275. * @param sourceTime timestamp of source file.
  1276. * @param destTime timestamp of dest file.
  1277. * @param granularity os/filesys granularity.
  1278. * @return true if the dest file is considered up to date.
  1279. */
  1280. public boolean isUpToDate(long sourceTime, long destTime, long granularity) {
  1281. return destTime != -1 && destTime >= sourceTime + granularity;
  1282. }
  1283. /**
  1284. * Compare two timestamps for being up to date using the
  1285. * current granularity.
  1286. *
  1287. * @param sourceTime timestamp of source file.
  1288. * @param destTime timestamp of dest file.
  1289. * @return true if the dest file is considered up to date.
  1290. */
  1291. public boolean isUpToDate(long sourceTime, long destTime) {
  1292. return isUpToDate(sourceTime, destTime, getFileTimestampGranularity());
  1293. }
  1294. /**
  1295. * Close a Writer without throwing any exception if something went wrong.
  1296. * Do not attempt to close it if the argument is null.
  1297. * @param device output writer, can be null.
  1298. */
  1299. public static void close(Writer device) {
  1300. if (null != device) {
  1301. try {
  1302. device.close();
  1303. } catch (IOException e) {
  1304. //ignore
  1305. }
  1306. }
  1307. }
  1308. /**
  1309. * Close a Reader without throwing any exception if something went wrong.
  1310. * Do not attempt to close it if the argument is null.
  1311. *
  1312. * @param device Reader, can be null.
  1313. */
  1314. public static void close(Reader device) {
  1315. if (null != device) {
  1316. try {
  1317. device.close();
  1318. } catch (IOException e) {
  1319. //ignore
  1320. }
  1321. }
  1322. }
  1323. /**
  1324. * Close a stream without throwing any exception if something went wrong.
  1325. * Do not attempt to close it if the argument is null.
  1326. *
  1327. * @param device stream, can be null.
  1328. */
  1329. public static void close(OutputStream device) {
  1330. if (null != device) {
  1331. try {
  1332. device.close();
  1333. } catch (IOException e) {
  1334. //ignore
  1335. }
  1336. }
  1337. }
  1338. /**
  1339. * Close a stream without throwing any exception if something went wrong.
  1340. * Do not attempt to close it if the argument is null.
  1341. *
  1342. * @param device stream, can be null.
  1343. */
  1344. public static void close(InputStream device) {
  1345. if (null != device) {
  1346. try {
  1347. device.close();
  1348. } catch (IOException e) {
  1349. //ignore
  1350. }
  1351. }
  1352. }
  1353. /**
  1354. * Delete the file with {@link File#delete()} if the argument is not null.
  1355. * Do nothing on a null argument.
  1356. * @param file file to delete.
  1357. */
  1358. public static void delete(File file) {
  1359. if (file != null) {
  1360. file.delete();
  1361. }
  1362. }
  1363. /**
  1364. * Accommodate Windows bug encountered in both Sun and IBM JDKs.
  1365. * Others possible. If the delete does not work, call System.gc(),
  1366. * wait a little and try again.
  1367. *
  1368. * @return whether deletion was successful
  1369. * @since Ant 1.8.0
  1370. */
  1371. public boolean tryHardToDelete(File f) {
  1372. if (!f.delete()) {
  1373. if (ON_WINDOWS) {
  1374. System.gc();
  1375. }
  1376. try {
  1377. Thread.sleep(DELETE_RETRY_SLEEP_MILLIS);
  1378. } catch (InterruptedException ex) {
  1379. // Ignore Exception
  1380. }
  1381. return f.delete();
  1382. }
  1383. return true;
  1384. }
  1385. /**
  1386. * Calculates the relative path between two files.
  1387. * <p>
  1388. * Implementation note:<br/> This function may throw an IOException if an I/O error occurs
  1389. * because its use of the canonical pathname may require filesystem queries.
  1390. * </p>
  1391. *
  1392. * @param fromFile the <code>File</code> to calculate the path from
  1393. * @param toFile the <code>File</code> to calculate the path to
  1394. * @return the relative path between the files
  1395. * @throws Exception for undocumented reasons
  1396. * @see File#getCanonicalPath()
  1397. *
  1398. * @since Ant 1.7
  1399. */
  1400. public static String getRelativePath(File fromFile, File toFile) throws Exception {
  1401. String fromPath = fromFile.getCanonicalPath();
  1402. String toPath = toFile.getCanonicalPath();
  1403. // build the path stack info to compare
  1404. String[] fromPathStack = getPathStack(fromPath);
  1405. String[] toPathStack = getPathStack(toPath);
  1406. if (0 < toPathStack.length && 0 < fromPathStack.length) {
  1407. if (!fromPathStack[0].equals(toPathStack[0])) {
  1408. // not the same device (would be "" on Linux/Unix)
  1409. return getPath(Arrays.asList(toPathStack));
  1410. }
  1411. } else {
  1412. // no comparison possible
  1413. return getPath(Arrays.asList(toPathStack));
  1414. }
  1415. int minLength = Math.min(fromPathStack.length, toPathStack.length);
  1416. int same = 1; // Used outside the for loop
  1417. // get index of parts which are equal
  1418. for (;
  1419. same < minLength && fromPathStack[same].equals(toPathStack[same]);
  1420. same++) {
  1421. // Do nothing
  1422. }
  1423. List relativePathStack = new ArrayList();
  1424. // if "from" part is longer, fill it up with ".."
  1425. // to reach path which is equal to both paths
  1426. for (int i = same; i < fromPathStack.length; i++) {
  1427. relativePathStack.add("..");
  1428. }
  1429. // fill it up path with parts which were not equal
  1430. for (int i = same; i < toPathStack.length; i++) {
  1431. relativePathStack.add(toPathStack[i]);
  1432. }
  1433. return getPath(relativePathStack);
  1434. }
  1435. /**
  1436. * Gets all names of the path as an array of <code>String</code>s.
  1437. *
  1438. * @param path to get names from
  1439. * @return <code>String</code>s, never <code>null</code>
  1440. *
  1441. * @since Ant 1.7
  1442. */
  1443. public static String[] getPathStack(String path) {
  1444. String normalizedPath = path.replace(File.separatorChar, '/');
  1445. // since Java 1.4
  1446. //return normalizedPath.split("/");
  1447. // workaround for Java 1.2-1.3
  1448. Object[] tokens = StringUtils.split(normalizedPath, '/').toArray();
  1449. String[] rv = new String[tokens.length];
  1450. System.arraycopy(tokens, 0, rv, 0, tokens.length);
  1451. return rv;
  1452. }
  1453. /**
  1454. * Gets path from a <code>List</code> of <code>String</code>s.
  1455. *
  1456. * @param pathStack <code>List</code> of <code>String</code>s to be concatenated as a path.
  1457. * @return <code>String</code>, never <code>null</code>
  1458. *
  1459. * @since Ant 1.7
  1460. */
  1461. public static String getPath(List pathStack) {
  1462. // can safely use '/' because Windows understands '/' as separator
  1463. return getPath(pathStack, '/');
  1464. }
  1465. /**
  1466. * Gets path from a <code>List</code> of <code>String</code>s.
  1467. *
  1468. * @param pathStack <code>List</code> of <code>String</code>s to be concated as a path.
  1469. * @param separatorChar <code>char</code> to be used as separator between names in path
  1470. * @return <code>String</code>, never <code>null</code>
  1471. *
  1472. * @since Ant 1.7
  1473. */
  1474. public static String getPath(final List pathStack, final char separatorChar) {
  1475. final StringBuffer buffer = new StringBuffer();
  1476. final Iterator iter = pathStack.iterator();
  1477. if (iter.hasNext()) {
  1478. buffer.append(iter.next());
  1479. }
  1480. while (iter.hasNext()) {
  1481. buffer.append(separatorChar);
  1482. buffer.append(iter.next());
  1483. }
  1484. return buffer.toString();
  1485. }
  1486. /**
  1487. * Get the default encoding.
  1488. * This is done by opening an InputStreamReader on
  1489. * a dummy InputStream and getting the encoding.
  1490. * Could use System.getProperty("file.encoding"), but cannot
  1491. * see where this is documented.
  1492. * @return the default file encoding.
  1493. */
  1494. public String getDefaultEncoding() {
  1495. InputStreamReader is = new InputStreamReader(
  1496. new InputStream() {
  1497. public int read() {
  1498. return -1;
  1499. }
  1500. });
  1501. try {
  1502. return is.getEncoding();
  1503. } finally {
  1504. close(is);
  1505. }
  1506. }
  1507. }