KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > util > FileUtils


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
19 package org.apache.tools.ant.util;
20
21 import java.io.File JavaDoc;
22 import java.io.IOException JavaDoc;
23 import java.io.InputStream JavaDoc;
24 import java.io.InputStreamReader JavaDoc;
25 import java.io.Reader JavaDoc;
26 import java.io.UnsupportedEncodingException JavaDoc;
27 import java.io.Writer JavaDoc;
28 import java.io.OutputStream JavaDoc;
29 import java.net.MalformedURLException JavaDoc;
30 import java.net.URL JavaDoc;
31 import java.text.DecimalFormat JavaDoc;
32 import java.util.ArrayList JavaDoc;
33 import java.util.Arrays JavaDoc;
34 import java.util.Iterator JavaDoc;
35 import java.util.List JavaDoc;
36 import java.util.Random JavaDoc;
37 import java.util.Stack JavaDoc;
38 import java.util.StringTokenizer JavaDoc;
39 import java.util.Vector JavaDoc;
40 import org.apache.tools.ant.BuildException;
41 import org.apache.tools.ant.PathTokenizer;
42 import org.apache.tools.ant.Project;
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 import org.apache.tools.ant.launch.Locator;
47
48 /**
49  * This class also encapsulates methods which allow Files to be
50  * referred to using abstract path names which are translated to native
51  * system file paths at runtime as well as copying files or setting
52  * their last modification time.
53  *
54  */

55 public class FileUtils {
56
57     private static final FileUtils PRIMARY_INSTANCE = new FileUtils();
58
59     //get some non-crypto-grade randomness from various places.
60
private static Random JavaDoc rand = new Random JavaDoc(System.currentTimeMillis()
61             + Runtime.getRuntime().freeMemory());
62
63     private static boolean onNetWare = Os.isFamily("netware");
64     private static boolean onDos = Os.isFamily("dos");
65     private static boolean onWin9x = Os.isFamily("win9x");
66     private static boolean onWindows = Os.isFamily("windows");
67
68     static final int BUF_SIZE = 8192;
69
70
71     /**
72      * The granularity of timestamps under FAT.
73      */

74     public static final long FAT_FILE_TIMESTAMP_GRANULARITY = 2000;
75
76     /**
77      * The granularity of timestamps under Unix.
78      */

79     public static final long UNIX_FILE_TIMESTAMP_GRANULARITY = 1000;
80
81     /**
82      * The granularity of timestamps under the NT File System.
83      * NTFS has a granularity of 100 nanoseconds, which is less
84      * than 1 millisecond, so we round this up to 1 millisecond.
85      */

86     public static final long NTFS_FILE_TIMESTAMP_GRANULARITY = 1;
87
88
89     /**
90      * A one item cache for fromUri.
91      * fromUri is called for each element when parseing ant build
92      * files. It is a costly operation. This just caches the result
93      * of the last call.
94      */

95     private Object JavaDoc cacheFromUriLock = new Object JavaDoc();
96     private String JavaDoc cacheFromUriRequest = null;
97     private String JavaDoc cacheFromUriResponse = null;
98
99     /**
100      * Factory method.
101      *
102      * @return a new instance of FileUtils.
103      * @deprecated since 1.7.
104      * Use getFileUtils instead,
105      * FileUtils do not have state.
106      */

107     public static FileUtils newFileUtils() {
108         return new FileUtils();
109     }
110
111     /**
112      * Method to retrieve The FileUtils, which is shared by all users of this
113      * method.
114      * @return an instance of FileUtils.
115      * @since Ant 1.6.3
116      */

117     public static FileUtils getFileUtils() {
118         return PRIMARY_INSTANCE;
119     }
120
121     /**
122      * Empty constructor.
123      */

124     protected FileUtils() {
125     }
126
127     /**
128      * Get the URL for a file taking into account # characters.
129      *
130      * @param file the file whose URL representation is required.
131      * @return The FileURL value.
132      * @throws MalformedURLException if the URL representation cannot be
133      * formed.
134      */

135     public URL JavaDoc getFileURL(File JavaDoc file) throws MalformedURLException JavaDoc {
136         return new URL JavaDoc(toURI(file.getAbsolutePath()));
137     }
138
139     /**
140      * Convenience method to copy a file from a source to a destination.
141      * No filtering is performed.
142      *
143      * @param sourceFile Name of file to copy from.
144      * Must not be <code>null</code>.
145      * @param destFile Name of file to copy to.
146      * Must not be <code>null</code>.
147      *
148      * @throws IOException if the copying fails.
149      */

150     public void copyFile(String JavaDoc sourceFile, String JavaDoc destFile)
151         throws IOException JavaDoc {
152         copyFile(new File JavaDoc(sourceFile), new File JavaDoc(destFile), null, false, false);
153     }
154
155     /**
156      * Convenience method to copy a file from a source to a destination
157      * specifying if token filtering must be used.
158      *
159      * @param sourceFile Name of file to copy from.
160      * Must not be <code>null</code>.
161      * @param destFile Name of file to copy to.
162      * Must not be <code>null</code>.
163      * @param filters the collection of filters to apply to this copy.
164      *
165      * @throws IOException if the copying fails.
166      */

167     public void copyFile(String JavaDoc sourceFile, String JavaDoc destFile,
168                          FilterSetCollection filters)
169         throws IOException JavaDoc {
170         copyFile(new File JavaDoc(sourceFile), new File JavaDoc(destFile), filters,
171                  false, false);
172     }
173
174     /**
175      * Convenience method to copy a file from a source to a
176      * destination specifying if token filtering must be used and if
177      * source files may overwrite newer destination files.
178      *
179      * @param sourceFile Name of file to copy from.
180      * Must not be <code>null</code>.
181      * @param destFile Name of file to copy to.
182      * Must not be <code>null</code>.
183      * @param filters the collection of filters to apply to this copy.
184      * @param overwrite Whether or not the destination file should be
185      * overwritten if it already exists.
186      *
187      * @throws IOException if the copying fails.
188      */

189     public void copyFile(String JavaDoc sourceFile, String JavaDoc destFile, FilterSetCollection filters,
190                          boolean overwrite) throws IOException JavaDoc {
191         copyFile(new File JavaDoc(sourceFile), new File JavaDoc(destFile), filters,
192                  overwrite, false);
193     }
194
195     /**
196      * Convenience method to copy a file from a source to a
197      * destination specifying if token filtering must be used, if
198      * source files may overwrite newer destination files and the
199      * last modified time of <code>destFile</code> file should be made equal
200      * to the last modified time of <code>sourceFile</code>.
201      *
202      * @param sourceFile Name of file to copy from.
203      * Must not be <code>null</code>.
204      * @param destFile Name of file to copy to.
205      * Must not be <code>null</code>.
206      * @param filters the collection of filters to apply to this copy.
207      * @param overwrite Whether or not the destination file should be
208      * overwritten if it already exists.
209      * @param preserveLastModified Whether or not the last modified time of
210      * the resulting file should be set to that
211      * of the source file.
212      *
213      * @throws IOException if the copying fails.
214      */

215     public void copyFile(String JavaDoc sourceFile, String JavaDoc destFile, FilterSetCollection filters,
216                          boolean overwrite, boolean preserveLastModified)
217         throws IOException JavaDoc {
218         copyFile(new File JavaDoc(sourceFile), new File JavaDoc(destFile), filters,
219                  overwrite, preserveLastModified);
220     }
221
222     /**
223      * Convenience method to copy a file from a source to a
224      * destination specifying if token filtering must be used, if
225      * source files may overwrite newer destination files and the
226      * last modified time of <code>destFile</code> file should be made equal
227      * to the last modified time of <code>sourceFile</code>.
228      *
229      * @param sourceFile Name of file to copy from.
230      * Must not be <code>null</code>.
231      * @param destFile Name of file to copy to.
232      * Must not be <code>null</code>.
233      * @param filters the collection of filters to apply to this copy.
234      * @param overwrite Whether or not the destination file should be
235      * overwritten if it already exists.
236      * @param preserveLastModified Whether or not the last modified time of
237      * the resulting file should be set to that
238      * of the source file.
239      * @param encoding the encoding used to read and write the files.
240      *
241      * @throws IOException if the copying fails.
242      *
243      * @since Ant 1.5
244      */

245     public void copyFile(String JavaDoc sourceFile, String JavaDoc destFile,
246                          FilterSetCollection filters, boolean overwrite,
247                          boolean preserveLastModified, String JavaDoc encoding)
248         throws IOException JavaDoc {
249         copyFile(new File JavaDoc(sourceFile), new File JavaDoc(destFile), filters,
250                  overwrite, preserveLastModified, encoding);
251     }
252
253     // CheckStyle:ParameterNumberCheck OFF - bc
254
/**
255      * Convenience method to copy a file from a source to a
256      * destination specifying if token filtering must be used, if
257      * filter chains must be used, if source files may overwrite
258      * newer destination files and the last modified time of
259      * <code>destFile</code> file should be made equal
260      * to the last modified time of <code>sourceFile</code>.
261      *
262      * @param sourceFile Name of file to copy from.
263      * Must not be <code>null</code>.
264      * @param destFile Name of file to copy to.
265      * Must not be <code>null</code>.
266      * @param filters the collection of filters to apply to this copy.
267      * @param filterChains filterChains to apply during the copy.
268      * @param overwrite Whether or not the destination file should be
269      * overwritten if it already exists.
270      * @param preserveLastModified Whether or not the last modified time of
271      * the resulting file should be set to that
272      * of the source file.
273      * @param encoding the encoding used to read and write the files.
274      * @param project the project instance.
275      *
276      * @throws IOException if the copying fails.
277      *
278      * @since Ant 1.5
279      */

280     public void copyFile(String JavaDoc sourceFile, String JavaDoc destFile,
281                          FilterSetCollection filters, Vector JavaDoc filterChains,
282                          boolean overwrite, boolean preserveLastModified,
283                          String JavaDoc encoding, Project project)
284         throws IOException JavaDoc {
285         copyFile(new File JavaDoc(sourceFile), new File JavaDoc(destFile), filters,
286                  filterChains, overwrite, preserveLastModified,
287                  encoding, project);
288     }
289
290     /**
291      * Convenience method to copy a file from a source to a
292      * destination specifying if token filtering must be used, if
293      * filter chains must be used, if source files may overwrite
294      * newer destination files and the last modified time of
295      * <code>destFile</code> file should be made equal
296      * to the last modified time of <code>sourceFile</code>.
297      *
298      * @param sourceFile Name of file to copy from.
299      * Must not be <code>null</code>.
300      * @param destFile Name of file to copy to.
301      * Must not be <code>null</code>.
302      * @param filters the collection of filters to apply to this copy.
303      * @param filterChains filterChains to apply during the copy.
304      * @param overwrite Whether or not the destination file should be
305      * overwritten if it already exists.
306      * @param preserveLastModified Whether or not the last modified time of
307      * the resulting file should be set to that
308      * of the source file.
309      * @param inputEncoding the encoding used to read the files.
310      * @param outputEncoding the encoding used to write the files.
311      * @param project the project instance.
312      *
313      * @throws IOException if the copying fails.
314      *
315      * @since Ant 1.6
316      */

317     public void copyFile(String JavaDoc sourceFile, String JavaDoc destFile,
318                          FilterSetCollection filters, Vector JavaDoc filterChains,
319                          boolean overwrite, boolean preserveLastModified,
320                          String JavaDoc inputEncoding, String JavaDoc outputEncoding,
321                          Project project)
322         throws IOException JavaDoc {
323         copyFile(new File JavaDoc(sourceFile), new File JavaDoc(destFile), filters,
324                  filterChains, overwrite, preserveLastModified,
325                  inputEncoding, outputEncoding, project);
326     }
327
328     /**
329      * Convenience method to copy a file from a source to a destination.
330      * No filtering is performed.
331      *
332      * @param sourceFile the file to copy from.
333      * Must not be <code>null</code>.
334      * @param destFile the file to copy to.
335      * Must not be <code>null</code>.
336      *
337      * @throws IOException if the copying fails.
338      */

339     public void copyFile(File JavaDoc sourceFile, File JavaDoc destFile) throws IOException JavaDoc {
340         copyFile(sourceFile, destFile, null, false, false);
341     }
342
343     /**
344      * Convenience method to copy a file from a source to a destination
345      * specifying if token filtering must be used.
346      *
347      * @param sourceFile the file to copy from.
348      * Must not be <code>null</code>.
349      * @param destFile the file to copy to.
350      * Must not be <code>null</code>.
351      * @param filters the collection of filters to apply to this copy.
352      *
353      * @throws IOException if the copying fails.
354      */

355     public void copyFile(File JavaDoc sourceFile, File JavaDoc destFile, FilterSetCollection filters)
356         throws IOException JavaDoc {
357         copyFile(sourceFile, destFile, filters, false, false);
358     }
359
360     /**
361      * Convenience method to copy a file from a source to a
362      * destination specifying if token filtering must be used and if
363      * source files may overwrite newer destination files.
364      *
365      * @param sourceFile the file to copy from.
366      * Must not be <code>null</code>.
367      * @param destFile the file to copy to.
368      * Must not be <code>null</code>.
369      * @param filters the collection of filters to apply to this copy.
370      * @param overwrite Whether or not the destination file should be
371      * overwritten if it already exists.
372      *
373      * @throws IOException if the copying fails.
374      */

375     public void copyFile(File JavaDoc sourceFile, File JavaDoc destFile, FilterSetCollection filters,
376                          boolean overwrite) throws IOException JavaDoc {
377         copyFile(sourceFile, destFile, filters, overwrite, false);
378     }
379
380     /**
381      * Convenience method to copy a file from a source to a
382      * destination specifying if token filtering must be used, if
383      * source files may overwrite newer destination files and the
384      * last modified time of <code>destFile</code> file should be made equal
385      * to the last modified time of <code>sourceFile</code>.
386      *
387      * @param sourceFile the file to copy from.
388      * Must not be <code>null</code>.
389      * @param destFile the file to copy to.
390      * Must not be <code>null</code>.
391      * @param filters the collection of filters to apply to this copy.
392      * @param overwrite Whether or not the destination file should be
393      * overwritten if it already exists.
394      * @param preserveLastModified Whether or not the last modified time of
395      * the resulting file should be set to that
396      * of the source file.
397      *
398      * @throws IOException if the copying fails.
399      */

400     public void copyFile(File JavaDoc sourceFile, File JavaDoc destFile, FilterSetCollection filters,
401                          boolean overwrite, boolean preserveLastModified)
402         throws IOException JavaDoc {
403         copyFile(sourceFile, destFile, filters, overwrite,
404                  preserveLastModified, null);
405     }
406
407     /**
408      * Convenience method to copy a file from a source to a
409      * destination specifying if token filtering must be used, if
410      * source files may overwrite newer destination files, the last
411      * modified time of <code>destFile</code> file should be made
412      * equal to the last modified time of <code>sourceFile</code> and
413      * which character encoding to assume.
414      *
415      * @param sourceFile the file to copy from.
416      * Must not be <code>null</code>.
417      * @param destFile the file to copy to.
418      * Must not be <code>null</code>.
419      * @param filters the collection of filters to apply to this copy.
420      * @param overwrite Whether or not the destination file should be
421      * overwritten if it already exists.
422      * @param preserveLastModified Whether or not the last modified time of
423      * the resulting file should be set to that
424      * of the source file.
425      * @param encoding the encoding used to read and write the files.
426      *
427      * @throws IOException if the copying fails.
428      *
429      * @since Ant 1.5
430      */

431     public void copyFile(File JavaDoc sourceFile, File JavaDoc destFile,
432                          FilterSetCollection filters, boolean overwrite,
433                          boolean preserveLastModified, String JavaDoc encoding)
434         throws IOException JavaDoc {
435         copyFile(sourceFile, destFile, filters, null, overwrite,
436                  preserveLastModified, encoding, null);
437     }
438
439     /**
440      * Convenience method to copy a file from a source to a
441      * destination specifying if token filtering must be used, if
442      * filter chains must be used, if source files may overwrite
443      * newer destination files and the last modified time of
444      * <code>destFile</code> file should be made equal
445      * to the last modified time of <code>sourceFile</code>.
446      *
447      * @param sourceFile the file to copy from.
448      * Must not be <code>null</code>.
449      * @param destFile the file to copy to.
450      * Must not be <code>null</code>.
451      * @param filters the collection of filters to apply to this copy.
452      * @param filterChains filterChains to apply during the copy.
453      * @param overwrite Whether or not the destination file should be
454      * overwritten if it already exists.
455      * @param preserveLastModified Whether or not the last modified time of
456      * the resulting file should be set to that
457      * of the source file.
458      * @param encoding the encoding used to read and write the files.
459      * @param project the project instance.
460      *
461      * @throws IOException if the copying fails.
462      *
463      * @since Ant 1.5
464      */

465     public void copyFile(File JavaDoc sourceFile, File JavaDoc destFile,
466                          FilterSetCollection filters, Vector JavaDoc filterChains,
467                          boolean overwrite, boolean preserveLastModified,
468                          String JavaDoc encoding, Project project)
469         throws IOException JavaDoc {
470         copyFile(sourceFile, destFile, filters, filterChains,
471                  overwrite, preserveLastModified, encoding, encoding, project);
472     }
473
474     /**
475      * Convenience method to copy a file from a source to a
476      * destination specifying if token filtering must be used, if
477      * filter chains must be used, if source files may overwrite
478      * newer destination files and the last modified time of
479      * <code>destFile</code> file should be made equal
480      * to the last modified time of <code>sourceFile</code>.
481      *
482      * @param sourceFile the file to copy from.
483      * Must not be <code>null</code>.
484      * @param destFile the file to copy to.
485      * Must not be <code>null</code>.
486      * @param filters the collection of filters to apply to this copy.
487      * @param filterChains filterChains to apply during the copy.
488      * @param overwrite Whether or not the destination file should be
489      * overwritten if it already exists.
490      * @param preserveLastModified Whether or not the last modified time of
491      * the resulting file should be set to that
492      * of the source file.
493      * @param inputEncoding the encoding used to read the files.
494      * @param outputEncoding the encoding used to write the files.
495      * @param project the project instance.
496      *
497      *
498      * @throws IOException if the copying fails.
499      *
500      * @since Ant 1.6
501      */

502     public void copyFile(File JavaDoc sourceFile, File JavaDoc destFile,
503                          FilterSetCollection filters, Vector JavaDoc filterChains,
504                          boolean overwrite, boolean preserveLastModified,
505                          String JavaDoc inputEncoding, String JavaDoc outputEncoding,
506                          Project project)
507         throws IOException JavaDoc {
508         ResourceUtils.copyResource(
509             new FileResource(sourceFile), new FileResource(destFile),
510             filters, filterChains, overwrite, preserveLastModified,
511             inputEncoding, outputEncoding, project);
512     }
513
514     // CheckStyle:ParameterNumberCheck ON
515

516     /**
517      * Calls File.setLastModified(long time). Originally written to
518      * to dynamically bind to that call on Java1.2+.
519      *
520      * @param file the file whose modified time is to be set
521      * @param time the time to which the last modified time is to be set.
522      * if this is -1, the current time is used.
523      */

524     public void setFileLastModified(File JavaDoc file, long time) {
525         ResourceUtils.setLastModified(new FileResource(file), time);
526     }
527
528     /**
529      * Interpret the filename as a file relative to the given file
530      * unless the filename already represents an absolute filename.
531      * Differs from <code>new File(file, filename)</code> in that
532      * the resulting File's path will always be a normalized,
533      * absolute pathname. Also, if it is determined that
534      * <code>filename</code> is context-relative, <code>file</code>
535      * will be discarded and the reference will be resolved using
536      * available context/state information about the filesystem.
537      *
538      * @param file the "reference" file for relative paths. This
539      * instance must be an absolute file and must not contain
540      * &quot;./&quot; or &quot;../&quot; sequences (same for \ instead
541      * of /). If it is null, this call is equivalent to
542      * <code>new java.io.File(filename).getAbsoluteFile()</code>.
543      *
544      * @param filename a file name.
545      *
546      * @return an absolute file.
547      * @throws java.lang.NullPointerException if filename is null.
548      */

549     public File JavaDoc resolveFile(File JavaDoc file, String JavaDoc filename) {
550         if (!isAbsolutePath(filename)) {
551             char sep = File.separatorChar;
552             filename = filename.replace('/', sep).replace('\\', sep);
553             if (isContextRelativePath(filename)) {
554                 file = null;
555                 // on cygwin, our current directory can be a UNC;
556
// assume user.dir is absolute or all hell breaks loose...
557
String JavaDoc udir = System.getProperty("user.dir");
558                 if (filename.charAt(0) == sep && udir.charAt(0) == sep) {
559                     filename = dissect(udir)[0] + filename.substring(1);
560                 }
561             }
562             filename = new File JavaDoc(file, filename).getAbsolutePath();
563         }
564         return normalize(filename);
565     }
566
567     /**
568      * On DOS and NetWare, the evaluation of certain file
569      * specifications is context-dependent. These are filenames
570      * beginning with a single separator (relative to current root directory)
571      * and filenames with a drive specification and no intervening separator
572      * (relative to current directory of the specified root).
573      * @param filename the filename to evaluate.
574      * @return true if the filename is relative to system context.
575      * @throws java.lang.NullPointerException if filename is null.
576      * @since Ant 1.7
577      */

578     public static boolean isContextRelativePath(String JavaDoc filename) {
579         if (!(onDos || onNetWare) || filename.length() == 0) {
580             return false;
581         }
582         char sep = File.separatorChar;
583         filename = filename.replace('/', sep).replace('\\', sep);
584         char c = filename.charAt(0);
585         int len = filename.length();
586         return (c == sep && (len == 1 || filename.charAt(1) != sep))
587             || (Character.isLetter(c) && len > 1
588             && filename.indexOf(':') == 1
589             && (len == 2 || filename.charAt(2) != sep));
590     }
591
592     /**
593      * Verifies that the specified filename represents an absolute path.
594      * Differs from new java.io.File("filename").isAbsolute() in that a path
595      * beginning with a double file separator--signifying a Windows UNC--must
596      * at minimum match "\\a\b" to be considered an absolute path.
597      * @param filename the filename to be checked.
598      * @return true if the filename represents an absolute path.
599      * @throws java.lang.NullPointerException if filename is null.
600      * @since Ant 1.6.3
601      */

602     public static boolean isAbsolutePath(String JavaDoc filename) {
603         int len = filename.length();
604         if (len == 0) {
605             return false;
606         }
607         char sep = File.separatorChar;
608         filename = filename.replace('/', sep).replace('\\', sep);
609         char c = filename.charAt(0);
610         if (!(onDos || onNetWare)) {
611             return (c == sep);
612         }
613         if (c == sep) {
614             if (!(onDos && len > 4 && filename.charAt(1) == sep)) {
615                 return false;
616             }
617             int nextsep = filename.indexOf(sep, 2);
618             return nextsep > 2 && nextsep + 1 < len;
619         }
620         int colon = filename.indexOf(':');
621         return (Character.isLetter(c) && colon == 1
622             && filename.length() > 2 && filename.charAt(2) == sep)
623             || (onNetWare && colon > 0);
624     }
625
626     /**
627      * Translate a path into its native (platform specific) format.
628      * <p>
629      * This method uses PathTokenizer to separate the input path
630      * into its components. This handles DOS style paths in a relatively
631      * sensible way. The file separators are then converted to their platform
632      * specific versions.
633      *
634      * @param toProcess The path to be translated.
635      * May be <code>null</code>.
636      *
637      * @return the native version of the specified path or
638      * an empty string if the path is <code>null</code> or empty.
639      *
640      * @since ant 1.7
641      * @see PathTokenizer
642      */

643     public static String JavaDoc translatePath(String JavaDoc toProcess) {
644         if (toProcess == null || toProcess.length() == 0) {
645             return "";
646         }
647         StringBuffer JavaDoc path = new StringBuffer JavaDoc(toProcess.length() + 50);
648         PathTokenizer tokenizer = new PathTokenizer(toProcess);
649         while (tokenizer.hasMoreTokens()) {
650             String JavaDoc pathComponent = tokenizer.nextToken();
651             pathComponent = pathComponent.replace('/', File.separatorChar);
652             pathComponent = pathComponent.replace('\\', File.separatorChar);
653             if (path.length() != 0) {
654                 path.append(File.pathSeparatorChar);
655             }
656             path.append(pathComponent);
657         }
658         return path.toString();
659     }
660
661     /**
662      * &quot;Normalize&quot; the given absolute path.
663      *
664      * <p>This includes:
665      * <ul>
666      * <li>Uppercase the drive letter if there is one.</li>
667      * <li>Remove redundant slashes after the drive spec.</li>
668      * <li>Resolve all ./, .\, ../ and ..\ sequences.</li>
669      * <li>DOS style paths that start with a drive letter will have
670      * \ as the separator.</li>
671      * </ul>
672      * Unlike {@link File#getCanonicalPath()} this method
673      * specifically does not resolve symbolic links.
674      *
675      * @param path the path to be normalized.
676      * @return the normalized version of the path.
677      *
678      * @throws java.lang.NullPointerException if path is null.
679      */

680     public File JavaDoc normalize(final String JavaDoc path) {
681         Stack JavaDoc s = new Stack JavaDoc();
682         String JavaDoc[] dissect = dissect(path);
683         s.push(dissect[0]);
684
685         StringTokenizer tok = new StringTokenizer(dissect[1], File.separator);
686         while (tok.hasMoreTokens()) {
687             String JavaDoc thisToken = tok.nextToken();
688             if (".".equals(thisToken)) {
689                 continue;
690             } else if ("..".equals(thisToken)) {
691                 if (s.size() < 2) {
692                     // Cannot resolve it, so skip it.
693
return new File JavaDoc(path);
694                 }
695                 s.pop();
696             } else { // plain component
697
s.push(thisToken);
698             }
699         }
700         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
701         for (int i = 0; i < s.size(); i++) {
702             if (i > 1) {
703                 // not before the filesystem root and not after it, since root
704
// already contains one
705
sb.append(File.separatorChar);
706             }
707             sb.append(s.elementAt(i));
708         }
709         return new File JavaDoc(sb.toString());
710     }
711
712     /**
713      * Dissect the specified absolute path.
714      * @param path the path to dissect.
715      * @return String[] {root, remaining path}.
716      * @throws java.lang.NullPointerException if path is null.
717      * @since Ant 1.7
718      */

719     public String JavaDoc[] dissect(String JavaDoc path) {
720         char sep = File.separatorChar;
721         path = path.replace('/', sep).replace('\\', sep);
722
723         // make sure we are dealing with an absolute path
724
if (!isAbsolutePath(path)) {
725             throw new BuildException(path + " is not an absolute path");
726         }
727         String JavaDoc root = null;
728         int colon = path.indexOf(':');
729         if (colon > 0 && (onDos || onNetWare)) {
730
731             int next = colon + 1;
732             root = path.substring(0, next);
733             char[] ca = path.toCharArray();
734             root += sep;
735             //remove the initial separator; the root has it.
736
next = (ca[next] == sep) ? next + 1 : next;
737
738             StringBuffer JavaDoc sbPath = new StringBuffer JavaDoc();
739             // Eliminate consecutive slashes after the drive spec:
740
for (int i = next; i < ca.length; i++) {
741                 if (ca[i] != sep || ca[i - 1] != sep) {
742                     sbPath.append(ca[i]);
743                 }
744             }
745             path = sbPath.toString();
746         } else if (path.length() > 1 && path.charAt(1) == sep) {
747             // UNC drive
748
int nextsep = path.indexOf(sep, 2);
749             nextsep = path.indexOf(sep, nextsep + 1);
750             root = (nextsep > 2) ? path.substring(0, nextsep + 1) : path;
751             path = path.substring(root.length());
752         } else {
753             root = File.separator;
754             path = path.substring(1);
755         }
756         return new String JavaDoc[] {root, path};
757     }
758
759     /**
760      * Returns a VMS String representation of a <code>File</code> object.
761      * This is useful since the JVM by default internally converts VMS paths
762      * to Unix style.
763      * The returned String is always an absolute path.
764      *
765      * @param f The <code>File</code> to get the VMS path for.
766      * @return The absolute VMS path to <code>f</code>.
767      */

768     public String JavaDoc toVMSPath(File JavaDoc f) {
769         // format: "DEVICE:[DIR.SUBDIR]FILE"
770
String JavaDoc osPath;
771         String JavaDoc path = normalize(f.getAbsolutePath()).getPath();
772         String JavaDoc name = f.getName();
773         boolean isAbsolute = path.charAt(0) == File.separatorChar;
774         // treat directories specified using .DIR syntax as files
775
boolean isDirectory = f.isDirectory()
776             && !name.regionMatches(true, name.length() - 4, ".DIR", 0, 4);
777
778         String JavaDoc device = null;
779         StringBuffer JavaDoc directory = null;
780         String JavaDoc file = null;
781
782         int index = 0;
783
784         if (isAbsolute) {
785             index = path.indexOf(File.separatorChar, 1);
786             if (index == -1) {
787                 return path.substring(1) + ":[000000]";
788             } else {
789                 device = path.substring(1, index++);
790             }
791         }
792         if (isDirectory) {
793             directory = new StringBuffer JavaDoc(path.substring(index).
794                                          replace(File.separatorChar, '.'));
795         } else {
796             int dirEnd =
797                 path.lastIndexOf(File.separatorChar, path.length());
798             if (dirEnd == -1 || dirEnd < index) {
799                 file = path.substring(index);
800             } else {
801                 directory = new StringBuffer JavaDoc(path.substring(index, dirEnd).
802                                              replace(File.separatorChar, '.'));
803                 index = dirEnd + 1;
804                 if (path.length() > index) {
805                     file = path.substring(index);
806                 }
807             }
808         }
809         if (!isAbsolute && directory != null) {
810             directory.insert(0, '.');
811         }
812         osPath = ((device != null) ? device + ":" : "")
813             + ((directory != null) ? "[" + directory + "]" : "")
814             + ((file != null) ? file : "");
815         return osPath;
816     }
817
818     /**
819      * Create a temporary file in a given directory.
820      *
821      * <p>The file denoted by the returned abstract pathname did not
822      * exist before this method was invoked, any subsequent invocation
823      * of this method will yield a different file name.</p>
824      * <p>
825      * The filename is prefixNNNNNsuffix where NNNN is a random number.
826      * </p>
827      * <p>This method is different from File.createTempFile() of JDK 1.2
828      * as it doesn't create the file itself. It uses the location pointed
829      * to by java.io.tmpdir when the parentDir attribute is null.</p>
830      *
831      * @param prefix prefix before the random number.
832      * @param suffix file extension; include the '.'.
833      * @param parentDir Directory to create the temporary file in;
834      * java.io.tmpdir used if not specified.
835      *
836      * @return a File reference to the new temporary file.
837      * @since Ant 1.5
838      */

839     public File JavaDoc createTempFile(String JavaDoc prefix, String JavaDoc suffix, File JavaDoc parentDir) {
840         return createTempFile(prefix, suffix, parentDir, false);
841     }
842
843     /**
844      * Create a temporary file in a given directory.
845      *
846      * <p>The file denoted by the returned abstract pathname did not
847      * exist before this method was invoked, any subsequent invocation
848      * of this method will yield a different file name.</p>
849      * <p>
850      * The filename is prefixNNNNNsuffix where NNNN is a random number.
851      * </p>
852      * <p>This method is different from File.createTempFile() of JDK 1.2
853      * as it doesn't create the file itself. It uses the location pointed
854      * to by java.io.tmpdir when the parentDir attribute is null.</p>
855      *
856      * @param prefix prefix before the random number.
857      * @param suffix file extension; include the '.'.
858      * @param parentDir Directory to create the temporary file in;
859      * @param deleteOnExit whether to set the tempfile for deletion on
860      * normal VM exit.
861      * java.io.tmpdir used if not specified.
862      *
863      * @return a File reference to the new temporary file.
864      * @since Ant 1.7
865      */

866     public File JavaDoc createTempFile(String JavaDoc prefix, String JavaDoc suffix, File JavaDoc parentDir,
867                                boolean deleteOnExit) {
868         File JavaDoc result = null;
869         String JavaDoc parent = (parentDir == null)
870             ? System.getProperty("java.io.tmpdir")
871             : parentDir.getPath();
872
873         DecimalFormat JavaDoc fmt = new DecimalFormat JavaDoc("#####");
874         synchronized (rand) {
875             do {
876                 result = new File JavaDoc(parent,
877                                   prefix + fmt.format(Math.abs(rand.nextInt()))
878                                   + suffix);
879             } while (result.exists());
880         }
881         if (deleteOnExit) {
882             result.deleteOnExit();
883         }
884         return result;
885     }
886
887     /**
888      * Compares the contents of two files.
889      *
890      * @param f1 the file whose content is to be compared.
891      * @param f2 the other file whose content is to be compared.
892      *
893      * @return true if the content of the files is the same.
894      *
895      * @throws IOException if the files cannot be read.
896      */

897     public boolean contentEquals(File JavaDoc f1, File JavaDoc f2) throws IOException JavaDoc {
898         return contentEquals(f1, f2, false);
899     }
900
901     /**
902      * Compares the contents of two files.
903      *
904      * @param f1 the file whose content is to be compared.
905      * @param f2 the other file whose content is to be compared.
906      * @param textfile true if the file is to be treated as a text file and
907      * differences in kind of line break are to be ignored.
908      *
909      * @return true if the content of the files is the same.
910      *
911      * @throws IOException if the files cannot be read.
912      * @since Ant 1.6.3
913      */

914     public boolean contentEquals(File JavaDoc f1, File JavaDoc f2, boolean textfile) throws IOException JavaDoc {
915         return ResourceUtils.contentEquals(
916             new FileResource(f1), new FileResource(f2), textfile);
917     }
918
919     /**
920      * This was originally an emulation of {@link File#getParentFile} for JDK 1.1,
921      * but it is now implemented using that method (Ant 1.6.3 onwards).
922      * @param f the file whose parent is required.
923      * @return the given file's parent, or null if the file does not have a
924      * parent.
925      * @since 1.10
926      * @deprecated since 1.7.
927      * Just use {@link File#getParentFile} directly.
928      */

929     public File JavaDoc getParentFile(File JavaDoc f) {
930         return (f == null) ? null : f.getParentFile();
931     }
932
933     /**
934      * Read from reader till EOF.
935      * @param rdr the reader from which to read.
936      * @return the contents read out of the given reader.
937      *
938      * @throws IOException if the contents could not be read out from the
939      * reader.
940      */

941     public static final String JavaDoc readFully(Reader JavaDoc rdr) throws IOException JavaDoc {
942         return readFully(rdr, BUF_SIZE);
943     }
944
945     /**
946      * Read from reader till EOF.
947      *
948      * @param rdr the reader from which to read.
949      * @param bufferSize the buffer size to use when reading.
950      *
951      * @return the contents read out of the given reader.
952      *
953      * @throws IOException if the contents could not be read out from the
954      * reader.
955      */

956     public static final String JavaDoc readFully(Reader JavaDoc rdr, int bufferSize)
957         throws IOException JavaDoc {
958         if (bufferSize <= 0) {
959             throw new IllegalArgumentException JavaDoc("Buffer size must be greater "
960                                                + "than 0");
961         }
962         final char[] buffer = new char[bufferSize];
963         int bufferLength = 0;
964         StringBuffer JavaDoc textBuffer = null;
965         while (bufferLength != -1) {
966             bufferLength = rdr.read(buffer);
967             if (bufferLength > 0) {
968                 textBuffer = (textBuffer == null) ? new StringBuffer JavaDoc() : textBuffer;
969                 textBuffer.append(new String JavaDoc(buffer, 0, bufferLength));
970             }
971         }
972         return (textBuffer == null) ? null : textBuffer.toString();
973     }
974
975     /**
976      * This was originally an emulation of File.createNewFile for JDK 1.1,
977      * but it is now implemented using that method (Ant 1.6.3 onwards).
978      *
979      * <p>This method has historically <strong>not</strong> guaranteed that the
980      * operation was atomic. In its current implementation it is.
981      *
982      * @param f the file to be created.
983      * @return true if the file did not exist already.
984      * @throws IOException on error.
985      * @since Ant 1.5
986      */

987     public boolean createNewFile(File JavaDoc f) throws IOException JavaDoc {
988         return f.createNewFile();
989     }
990
991     /**
992      * Create a new file, optionally creating parent directories.
993      *
994      * @param f the file to be created.
995      * @param mkdirs <code>boolean</code> whether to create parent directories.
996      * @return true if the file did not exist already.
997      * @throws IOException on error.
998      * @since Ant 1.6.3
999      */

1000    public boolean createNewFile(File JavaDoc f, boolean mkdirs) throws IOException JavaDoc {
1001        File JavaDoc parent = f.getParentFile();
1002        if (mkdirs && !(parent.exists())) {
1003            parent.mkdirs();
1004        }
1005        return f.createNewFile();
1006    }
1007
1008    /**
1009     * Checks whether a given file is a symbolic link.
1010     *
1011     * <p>It doesn't really test for symbolic links but whether the
1012     * canonical and absolute paths of the file are identical--this
1013     * may lead to false positives on some platforms.</p>
1014     *
1015     * @param parent the parent directory of the file to test
1016     * @param name the name of the file to test.
1017     *
1018     * @return true if the file is a symbolic link.
1019     * @throws IOException on error.
1020     * @since Ant 1.5
1021     */

1022    public boolean isSymbolicLink(File JavaDoc parent, String JavaDoc name)
1023        throws IOException JavaDoc {
1024        if (parent == null) {
1025            File JavaDoc f = new File JavaDoc(name);
1026            parent = f.getParentFile();
1027            name = f.getName();
1028        }
1029        File JavaDoc toTest = new File JavaDoc(parent.getCanonicalPath(), name);
1030        return !toTest.getAbsolutePath().equals(toTest.getCanonicalPath());
1031    }
1032
1033    /**
1034     * Removes a leading path from a second path.
1035     *
1036     * @param leading The leading path, must not be null, must be absolute.
1037     * @param path The path to remove from, must not be null, must be absolute.
1038     *
1039     * @return path's normalized absolute if it doesn't start with
1040     * leading; path's path with leading's path removed otherwise.
1041     *
1042     * @since Ant 1.5
1043     */

1044    public String JavaDoc removeLeadingPath(File JavaDoc leading, File JavaDoc path) {
1045        String JavaDoc l = normalize(leading.getAbsolutePath()).getAbsolutePath();
1046        String JavaDoc p = normalize(path.getAbsolutePath()).getAbsolutePath();
1047        if (l.equals(p)) {
1048            return "";
1049        }
1050
1051        // ensure that l ends with a /
1052
// so we never think /foo was a parent directory of /foobar
1053
if (!l.endsWith(File.separator)) {
1054            l += File.separator;
1055        }
1056        return (p.startsWith(l)) ? p.substring(l.length()) : p;
1057    }
1058
1059    /**
1060     * Learn whether one path "leads" another.
1061     * @param leading The leading path, must not be null, must be absolute.
1062     * @param path The path to remove from, must not be null, must be absolute.
1063     * @return true if path starts with leading; false otherwise.
1064     * @since Ant 1.7
1065     */

1066    public boolean isLeadingPath(File JavaDoc leading, File JavaDoc path) {
1067        String JavaDoc l = normalize(leading.getAbsolutePath()).getAbsolutePath();
1068        String JavaDoc p = normalize(path.getAbsolutePath()).getAbsolutePath();
1069        if (l.equals(p)) {
1070            return true;
1071        }
1072        // ensure that l ends with a /
1073
// so we never think /foo was a parent directory of /foobar
1074
if (!l.endsWith(File.separator)) {
1075            l += File.separator;
1076        }
1077        return p.startsWith(l);
1078    }
1079
1080    /**
1081     * Constructs a <code>file:</code> URI that represents the
1082     * external form of the given pathname.
1083     *
1084     * <p>Will be an absolute URI if the given path is absolute.</p>
1085     *
1086     * <p>This code encodes non ASCII characters too.</p>
1087     *
1088     * <p>The coding of the output is the same as what File.toURI().toASCIIString() produces</p>
1089     *
1090     * See <a HREF="http://www.w3.org/TR/xml11/#dt-sysid">dt-sysid</a>
1091     * which makes some mention of how
1092     * characters not supported by URI Reference syntax should be escaped.
1093     *
1094     * @param path the path in the local file system.
1095     * @return the URI version of the local path.
1096     * @since Ant 1.6
1097     */

1098    public String JavaDoc toURI(String JavaDoc path) {
1099        // #8031: first try Java 1.4.
1100
Class JavaDoc uriClazz = null;
1101        try {
1102            uriClazz = Class.forName("java.net.URI");
1103        } catch (ClassNotFoundException JavaDoc e) {
1104            // OK, Java 1.3.
1105
}
1106        if (uriClazz != null) {
1107            try {
1108                File JavaDoc f = new File JavaDoc(path).getAbsoluteFile();
1109                java.lang.reflect.Method JavaDoc toURIMethod = File JavaDoc.class.getMethod("toURI", new Class JavaDoc[0]);
1110                Object JavaDoc uriObj = toURIMethod.invoke(f, new Object JavaDoc[] {});
1111                java.lang.reflect.Method JavaDoc toASCIIStringMethod
1112                    = uriClazz.getMethod("toASCIIString", new Class JavaDoc[0]);
1113                return (String JavaDoc) toASCIIStringMethod.invoke(uriObj, new Object JavaDoc[] {});
1114            } catch (Exception JavaDoc e) {
1115                // Reflection problems? Should not happen, debug.
1116
e.printStackTrace();
1117            }
1118        }
1119        boolean isDir = new File JavaDoc(path).isDirectory();
1120
1121        StringBuffer JavaDoc sb = new StringBuffer JavaDoc("file:");
1122
1123        path = resolveFile(null, path).getPath();
1124        sb.append("//");
1125        // add an extra slash for filesystems with drive-specifiers
1126
if (!path.startsWith(File.separator)) {
1127            sb.append("/");
1128        }
1129        path = path.replace('\\', '/');
1130        try {
1131            sb.append(Locator.encodeURI(path));
1132        } catch (UnsupportedEncodingException JavaDoc exc) {
1133            throw new BuildException(exc);
1134        }
1135        if (isDir && !path.endsWith("/")) {
1136            sb.append('/');
1137        }
1138        return sb.toString();
1139    }
1140
1141    /**
1142     * Constructs a file path from a <code>file:</code> URI.
1143     *
1144     * <p>Will be an absolute path if the given URI is absolute.</p>
1145     *
1146     * <p>Swallows '%' that are not followed by two characters,
1147     * doesn't deal with non-ASCII characters.</p>
1148     *
1149     * @param uri the URI designating a file in the local filesystem.
1150     * @return the local file system path for the file.
1151     * @since Ant 1.6
1152     */

1153    public String JavaDoc fromURI(String JavaDoc uri) {
1154        synchronized (cacheFromUriLock) {
1155            if (uri.equals(cacheFromUriRequest)) {
1156                return cacheFromUriResponse;
1157            }
1158            String JavaDoc path = Locator.fromURI(uri);
1159            String JavaDoc ret = isAbsolutePath(path)
1160                ? normalize(path).getAbsolutePath() : path;
1161            cacheFromUriRequest = uri;
1162            cacheFromUriResponse = ret;
1163            return ret;
1164        }
1165    }
1166
1167    /**
1168     * Compares two filenames.
1169     *
1170     * <p>Unlike java.io.File#equals this method will try to compare
1171     * the absolute paths and &quot;normalize&quot; the filenames
1172     * before comparing them.</p>
1173     *
1174     * @param f1 the file whose name is to be compared.
1175     * @param f2 the other file whose name is to be compared.
1176     *
1177     * @return true if the file are for the same file.
1178     *
1179     * @since Ant 1.5.3
1180     */

1181    public boolean fileNameEquals(File JavaDoc f1, File JavaDoc f2) {
1182        return normalize(f1.getAbsolutePath())
1183            .equals(normalize(f2.getAbsolutePath()));
1184    }
1185
1186    /**
1187     * Renames a file, even if that involves crossing file system boundaries.
1188     *
1189     * <p>This will remove <code>to</code> (if it exists), ensure that
1190     * <code>to</code>'s parent directory exists and move
1191     * <code>from</code>, which involves deleting <code>from</code> as
1192     * well.</p>
1193     *
1194     * @param from the file to move.
1195     * @param to the new file name.
1196     *
1197     * @throws IOException if anything bad happens during this
1198     * process. Note that <code>to</code> may have been deleted
1199     * already when this happens.
1200     *
1201     * @since Ant 1.6
1202     */

1203    public void rename(File JavaDoc from, File JavaDoc to) throws IOException JavaDoc {
1204        if (to.exists() && !to.delete()) {
1205            throw new IOException JavaDoc("Failed to delete " + to
1206                                  + " while trying to rename " + from);
1207        }
1208        File JavaDoc parent = to.getParentFile();
1209        if (parent != null && !parent.exists() && !parent.mkdirs()) {
1210            throw new IOException JavaDoc("Failed to create directory " + parent
1211                                  + " while trying to rename " + from);
1212        }
1213        if (!from.renameTo(to)) {
1214            copyFile(from, to);
1215            if (!from.delete()) {
1216                throw new IOException JavaDoc("Failed to delete " + from
1217                                      + " while trying to rename it.");
1218            }
1219        }
1220    }
1221
1222    /**
1223     * Get the granularity of file timestamps.
1224     * The choice is made based on OS, which is incorrect--it should really be
1225     * by filesystem. We do not have an easy way to probe for file systems,
1226     * however, so this heuristic gives us a decent default.
1227     * @return the difference, in milliseconds, which two file timestamps must have
1228     * in order for the two files to be considered to have different timestamps.
1229     */

1230    public long getFileTimestampGranularity() {
1231        if (onWin9x) {
1232            return FAT_FILE_TIMESTAMP_GRANULARITY;
1233        } else if (onWindows) {
1234            return NTFS_FILE_TIMESTAMP_GRANULARITY;
1235        } else if (onDos) {
1236            return FAT_FILE_TIMESTAMP_GRANULARITY;
1237        }
1238        return UNIX_FILE_TIMESTAMP_GRANULARITY;
1239    }
1240
1241    /**
1242     * Returns true if the source is older than the dest.
1243     * If the dest file does not exist, then the test returns false; it is
1244     * implicitly not up do date.
1245     * @param source source file (should be the older).
1246     * @param dest dest file (should be the newer).
1247     * @param granularity an offset added to the source time.
1248     * @return true if the source is older than the dest after accounting
1249     * for granularity.
1250     * @since Ant 1.6.3
1251     */

1252    public boolean isUpToDate(File JavaDoc source, File JavaDoc dest, long granularity) {
1253        //do a check for the destination file existing
1254
if (!dest.exists()) {
1255            //if it does not, then the file is not up to date.
1256
return false;
1257        }
1258        long sourceTime = source.lastModified();
1259        long destTime = dest.lastModified();
1260        return isUpToDate(sourceTime, destTime, granularity);
1261    }
1262
1263    /**
1264     * Returns true if the source is older than the dest.
1265     * @param source source file (should be the older).
1266     * @param dest dest file (should be the newer).
1267     * @return true if the source is older than the dest, taking the granularity into account.
1268     * @since Ant 1.6.3
1269     */

1270    public boolean isUpToDate(File JavaDoc source, File JavaDoc dest) {
1271        return isUpToDate(source, dest, getFileTimestampGranularity());
1272    }
1273
1274    /**
1275     * Compare two timestamps for being up to date using
1276     * the specified granularity.
1277     *
1278     * @param sourceTime timestamp of source file.
1279     * @param destTime timestamp of dest file.
1280     * @param granularity os/filesys granularity.
1281     * @return true if the dest file is considered up to date.
1282     */

1283    public boolean isUpToDate(long sourceTime, long destTime, long granularity) {
1284        if (destTime == -1) {
1285            return false;
1286        }
1287        return destTime >= sourceTime + granularity;
1288    }
1289
1290    /**
1291     * Compare two timestamps for being up to date using the
1292     * current granularity.
1293     *
1294     * @param sourceTime timestamp of source file.
1295     * @param destTime timestamp of dest file.
1296     * @return true if the dest file is considered up to date.
1297     */

1298    public boolean isUpToDate(long sourceTime, long destTime) {
1299        return isUpToDate(sourceTime, destTime, getFileTimestampGranularity());
1300    }
1301
1302    /**
1303     * Close a Writer without throwing any exception if something went wrong.
1304     * Do not attempt to close it if the argument is null.
1305     * @param device output writer, can be null.
1306     */

1307    public static void close(Writer JavaDoc device) {
1308        if (device != null) {
1309            try {
1310                device.close();
1311            } catch (IOException JavaDoc ioex) {
1312                //ignore
1313
}
1314        }
1315    }
1316
1317    /**
1318     * Close a stream without throwing any exception if something went wrong.
1319     * Do not attempt to close it if the argument is null.
1320     *
1321     * @param device Reader, can be null.
1322     */

1323    public static void close(Reader JavaDoc device) {
1324        if (device != null) {
1325            try {
1326                device.close();
1327            } catch (IOException JavaDoc ioex) {
1328                //ignore
1329
}
1330        }
1331    }
1332
1333    /**
1334     * Close a stream without throwing any exception if something went wrong.
1335     * Do not attempt to close it if the argument is null.
1336     *
1337     * @param device stream, can be null.
1338     */

1339    public static void close(OutputStream JavaDoc device) {
1340        if (device != null) {
1341            try {
1342                device.close();
1343            } catch (IOException JavaDoc ioex) {
1344                //ignore
1345
}
1346        }
1347    }
1348
1349    /**
1350     * Close a stream without throwing any exception if something went wrong.
1351     * Do not attempt to close it if the argument is null.
1352     *
1353     * @param device stream, can be null.
1354     */

1355    public static void close(InputStream JavaDoc device) {
1356        if (device != null) {
1357            try {
1358                device.close();
1359            } catch (IOException JavaDoc ioex) {
1360                //ignore
1361
}
1362        }
1363    }
1364
1365    /**
1366     * Delete the file with {@link File#delete()} if the argument is not null.
1367     * Do nothing on a null argument.
1368     * @param file file to delete.
1369     */

1370    public static void delete(File JavaDoc file) {
1371        if (file != null) {
1372            file.delete();
1373        }
1374    }
1375
1376    /**
1377     * Calculates the relative path between two files.
1378     * <p>
1379     * Implementation note:<br/> This function my throw an IOException if an
1380     * I/O error occurs because its use of the canonical pathname may require
1381     * filesystem queries.
1382     * </p>
1383     *
1384     * @param fromFile
1385     * the <code>File</code> to calculate the path from
1386     * @param toFile
1387     * the <code>File</code> to calculate the path to
1388     * @return the relative path between the files
1389     * @throws Exception for undocumented reasons
1390     * @see File#getCanonicalPath()
1391     *
1392     * @since Ant 1.7
1393     */

1394    public static String JavaDoc getRelativePath(
1395        File JavaDoc fromFile,
1396        File JavaDoc toFile
1397    ) throws Exception JavaDoc {
1398        String JavaDoc fromPath = fromFile.getCanonicalPath();
1399        String JavaDoc toPath = toFile.getCanonicalPath();
1400
1401        // build the path stack info to compare
1402
String JavaDoc[] fromPathStack = getPathStack(fromPath);
1403        String JavaDoc[] toPathStack = getPathStack(toPath);
1404
1405        if (0 < toPathStack.length && 0 < fromPathStack.length) {
1406            if (!fromPathStack[0].equals(toPathStack[0])) {
1407                // not the same device (would be "" on Linux/Unix)
1408

1409                return getPath(Arrays.asList(toPathStack));
1410            }
1411        } else {
1412            // no comparison possible
1413
return getPath(Arrays.asList(toPathStack));
1414        }
1415
1416        int minLength = Math
1417                .min(fromPathStack.length, toPathStack.length);
1418
1419        int same = 1;
1420
1421        // get index of parts which are equal
1422
for (; same < minLength; same++) {
1423            if (!fromPathStack[same].equals(toPathStack[same])) {
1424                break;
1425            }
1426        }
1427
1428        List JavaDoc relativePathStack = new ArrayList JavaDoc();
1429
1430        // if "from" part is longer, fill it up with ".."
1431
// to reach path which is equal to both paths
1432
for (int i = same; i < fromPathStack.length; i++) {
1433            relativePathStack.add("..");
1434        }
1435
1436        // fill it up path with parts which were not equal
1437
for (int i = same; i < toPathStack.length; i++) {
1438            relativePathStack.add(toPathStack[i]);
1439        }
1440
1441        return getPath(relativePathStack);
1442    }
1443
1444    /**
1445     * Gets all names of the path as an array of <code>String</code>s.
1446     *
1447     * @param path
1448     * to get names from
1449     * @return <code>String</code>s, never <code>null</code>
1450     *
1451     * @since Ant 1.7
1452     */

1453    public static String JavaDoc[] getPathStack(String JavaDoc path) {
1454        String JavaDoc normalizedPath = path.replace(File.separatorChar, '/');
1455
1456        // since Java 1.4
1457
//return normalizedPath.split("/");
1458
// workaround for Java 1.2-1.3
1459
Object JavaDoc[] tokens = StringUtils.split(normalizedPath, '/').toArray();
1460        String JavaDoc[] rv = new String JavaDoc[tokens.length];
1461        System.arraycopy(tokens, 0, rv, 0, tokens.length);
1462
1463        return rv;
1464    }
1465
1466    /**
1467     * Gets path from a <code>List</code> of <code>String</code>s.
1468     *
1469     * @param pathStack
1470     * <code>List</code> of <code>String</code>s to be concated
1471     * as a path.
1472     * @return <code>String</code>, never <code>null</code>
1473     *
1474     * @since Ant 1.7
1475     */

1476    public static String JavaDoc getPath(List JavaDoc pathStack) {
1477        // can safely use '/' because Windows understands '/' as separator
1478
return getPath(pathStack, '/');
1479    }
1480
1481    /**
1482     * Gets path from a <code>List</code> of <code>String</code>s.
1483     *
1484     * @param pathStack
1485     * <code>List</code> of <code>String</code>s to be concated
1486     * as a path.
1487     * @param separatorChar
1488     * <code>char</code> to be used as separator between names in
1489     * path
1490     * @return <code>String</code>, never <code>null</code>
1491     *
1492     * @since Ant 1.7
1493     */

1494    public static String JavaDoc getPath(final List JavaDoc pathStack, final char separatorChar) {
1495        final StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
1496
1497        final Iterator JavaDoc iter = pathStack.iterator();
1498        if (iter.hasNext()) {
1499            buffer.append(iter.next());
1500        }
1501
1502        while (iter.hasNext()) {
1503            buffer.append(separatorChar);
1504            buffer.append(iter.next());
1505        }
1506
1507        return buffer.toString();
1508    }
1509
1510    /**
1511     * Get the default encoding.
1512     * This is done by opening an InputStreamReader on
1513     * a dummy InputStream and getting the encoding.
1514     * Could use System.getProperty("file.encoding"), but cannot
1515     * see where this is documented.
1516     * @return the default file encoding.
1517     */

1518    public String JavaDoc getDefaultEncoding() {
1519        InputStreamReader JavaDoc is = new InputStreamReader JavaDoc(
1520            new InputStream JavaDoc() {
1521                public int read() {
1522                    return -1;
1523                }
1524            });
1525        try {
1526            return is.getEncoding();
1527        } finally {
1528            close(is);
1529        }
1530    }
1531}
1532
Popular Tags