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