source: other-projects/trunk/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/util/FileUtils.java@ 14627

Last change on this file since 14627 was 14627, checked in by oranfry, 17 years ago

initial import of the gs3-release-maker

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