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

Save This Page
Home » apache-ant-1.7.1-src » org.apache.tools » ant » util » [javadoc | source]