Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 1,072   Methods: 32
NCLOC: 613   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
FileUtils.java 82.9% 90.8% 78.1% 87.4%
 1   
 /*
 2   
  * The Apache Software License, Version 1.1
 3   
  *
 4   
  * Copyright (c) 2001-2003 The Apache Software Foundation.  All rights
 5   
  * reserved.
 6   
  *
 7   
  * Redistribution and use in source and binary forms, with or without
 8   
  * modification, are permitted provided that the following conditions
 9   
  * are met:
 10   
  *
 11   
  * 1. Redistributions of source code must retain the above copyright
 12   
  *    notice, this list of conditions and the following disclaimer.
 13   
  *
 14   
  * 2. Redistributions in binary form must reproduce the above copyright
 15   
  *    notice, this list of conditions and the following disclaimer in
 16   
  *    the documentation and/or other materials provided with the
 17   
  *    distribution.
 18   
  *
 19   
  * 3. The end-user documentation included with the redistribution, if
 20   
  *    any, must include the following acknowlegement:
 21   
  *       "This product includes software developed by the
 22   
  *        Apache Software Foundation (http://www.apache.org/)."
 23   
  *    Alternately, this acknowlegement may appear in the software itself,
 24   
  *    if and wherever such third-party acknowlegements normally appear.
 25   
  *
 26   
  * 4. The names "Ant" and "Apache Software
 27   
  *    Foundation" must not be used to endorse or promote products derived
 28   
  *    from this software without prior written permission. For written
 29   
  *    permission, please contact apache@apache.org.
 30   
  *
 31   
  * 5. Products derived from this software may not be called "Apache"
 32   
  *    nor may "Apache" appear in their names without prior written
 33   
  *    permission of the Apache Group.
 34   
  *
 35   
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 36   
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 37   
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 38   
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 39   
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40   
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41   
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 42   
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 43   
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 44   
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 45   
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 46   
  * SUCH DAMAGE.
 47   
  * ====================================================================
 48   
  *
 49   
  * This software consists of voluntary contributions made by many
 50   
  * individuals on behalf of the Apache Software Foundation.  For more
 51   
  * information on the Apache Software Foundation, please see
 52   
  * <http://www.apache.org/>.
 53   
  */
 54   
 
 55   
 package org.apache.tools.ant.util;
 56   
 
 57   
 import java.io.BufferedInputStream;
 58   
 import java.io.BufferedReader;
 59   
 import java.io.BufferedWriter;
 60   
 import java.io.File;
 61   
 import java.io.FileInputStream;
 62   
 import java.io.FileOutputStream;
 63   
 import java.io.FileReader;
 64   
 import java.io.FileWriter;
 65   
 import java.io.IOException;
 66   
 import java.io.InputStream;
 67   
 import java.io.InputStreamReader;
 68   
 import java.io.OutputStreamWriter;
 69   
 import java.io.Reader;
 70   
 import java.lang.reflect.Method;
 71   
 import java.net.MalformedURLException;
 72   
 import java.net.URL;
 73   
 import java.text.CharacterIterator;
 74   
 import java.text.DecimalFormat;
 75   
 import java.text.StringCharacterIterator;
 76   
 import java.util.Random;
 77   
 import java.util.Stack;
 78   
 import java.util.StringTokenizer;
 79   
 import java.util.Vector;
 80   
 import org.apache.tools.ant.BuildException;
 81   
 import org.apache.tools.ant.Project;
 82   
 import org.apache.tools.ant.filters.util.ChainReaderHelper;
 83   
 import org.apache.tools.ant.taskdefs.condition.Os;
 84   
 import org.apache.tools.ant.types.FilterSetCollection;
 85   
 
 86   
 /**
 87   
  * This class also encapsulates methods which allow Files to be
 88   
  * refered to using abstract path names which are translated to native
 89   
  * system file paths at runtime as well as copying files or setting
 90   
  * there last modification time.
 91   
  *
 92   
  * @author duncan@x180.com
 93   
  * @author Conor MacNeill
 94   
  * @author Stefan Bodewig
 95   
  * @author Magesh Umasankar
 96   
  * @author <a href="mailto:jtulley@novell.com">Jeff Tulley</a>
 97   
  *
 98   
  * @version $Revision: 1.40 $
 99   
  */
 100   
 
 101   
 public class FileUtils {
 102   
     private static Random rand = new Random(System.currentTimeMillis());
 103   
     private static Object lockReflection = new Object();
 104   
     private static java.lang.reflect.Method setLastModified = null;
 105   
 
 106   
     private boolean onNetWare = Os.isFamily("netware");
 107   
 
 108   
     // for toURI
 109   
     private static boolean[] isSpecial = new boolean[256];
 110   
     private static char[] escapedChar1 = new char[256];
 111   
     private static char[] escapedChar2 = new char[256];
 112   
 
 113   
 
 114   
     // stolen from FilePathToURI of the Xerces-J team
 115   
     static {
 116  1
         for (int i = 0; i <= 0x20; i++) {
 117  33
             isSpecial[i] = true;
 118  33
             escapedChar1[i] = Character.forDigit(i >> 4, 16);
 119  33
             escapedChar2[i] = Character.forDigit(i & 0xf, 16);
 120   
         }
 121  1
         isSpecial[0x7f] = true;
 122  1
         escapedChar1[0x7f] = '7';
 123  1
         escapedChar2[0x7f] = 'F';
 124  1
         char[] escChs = {'<', '>', '#', '%', '"', '{', '}',
 125   
                          '|', '\\', '^', '~', '[', ']', '`'};
 126  1
         int len = escChs.length;
 127  1
         char ch;
 128  1
         for (int i = 0; i < len; i++) {
 129  14
             ch = escChs[i];
 130  14
             isSpecial[ch] = true;
 131  14
             escapedChar1[ch] = Character.forDigit(ch >> 4, 16);
 132  14
             escapedChar2[ch] = Character.forDigit(ch & 0xf, 16);
 133   
         }
 134   
     }
 135   
 
 136   
     /**
 137   
      * Factory method.
 138   
      */
 139  23690
     public static FileUtils newFileUtils() {
 140  23690
         return new FileUtils();
 141   
     }
 142   
 
 143   
     /**
 144   
      * Empty constructor.
 145   
      */
 146  23690
     protected FileUtils() {}
 147   
 
 148   
     /**
 149   
      * Get the URL for a file taking into account # characters
 150   
      *
 151   
      * @param file the file whose URL representation is required.
 152   
      * @return The FileURL value
 153   
      * @throws MalformedURLException if the URL representation cannot be
 154   
      *      formed.
 155   
      */
 156  133
     public URL getFileURL(File file) throws MalformedURLException {
 157  133
         return new URL(toURI(file.getAbsolutePath()));
 158   
     }
 159   
 
 160   
     /**
 161   
      * Convienence method to copy a file from a source to a destination.
 162   
      * No filtering is performed.
 163   
      *
 164   
      * @throws IOException
 165   
      */
 166  0
     public void copyFile(String sourceFile, String destFile) throws IOException {
 167  0
         copyFile(new File(sourceFile), new File(destFile), null, false, false);
 168   
     }
 169   
 
 170   
     /**
 171   
      * Convienence method to copy a file from a source to a destination
 172   
      * specifying if token filtering must be used.
 173   
      *
 174   
      * @throws IOException
 175   
      */
 176  0
     public void copyFile(String sourceFile, String destFile, FilterSetCollection filters)
 177   
         throws IOException {
 178  0
         copyFile(new File(sourceFile), new File(destFile), filters, false, false);
 179   
     }
 180   
 
 181   
     /**
 182   
      * Convienence method to copy a file from a source to a
 183   
      * destination specifying if token filtering must be used and if
 184   
      * source files may overwrite newer destination files.
 185   
      *
 186   
      * @throws IOException
 187   
      */
 188  688
     public void copyFile(String sourceFile, String destFile, FilterSetCollection filters,
 189   
                          boolean overwrite) throws IOException {
 190  688
         copyFile(new File(sourceFile), new File(destFile), filters,
 191   
                  overwrite, false);
 192   
     }
 193   
 
 194   
     /**
 195   
      * Convienence method to copy a file from a source to a
 196   
      * destination specifying if token filtering must be used, if
 197   
      * source files may overwrite newer destination files and the
 198   
      * last modified time of <code>destFile</code> file should be made equal
 199   
      * to the last modified time of <code>sourceFile</code>.
 200   
      *
 201   
      * @throws IOException
 202   
      */
 203  0
     public void copyFile(String sourceFile, String destFile, FilterSetCollection filters,
 204   
                          boolean overwrite, boolean preserveLastModified)
 205   
         throws IOException {
 206  0
         copyFile(new File(sourceFile), new File(destFile), filters,
 207   
                  overwrite, preserveLastModified);
 208   
     }
 209   
 
 210   
     /**
 211   
      * Convienence method to copy a file from a source to a
 212   
      * destination specifying if token filtering must be used, if
 213   
      * source files may overwrite newer destination files and the
 214   
      * last modified time of <code>destFile</code> file should be made equal
 215   
      * to the last modified time of <code>sourceFile</code>.
 216   
      *
 217   
      * @throws IOException
 218   
      *
 219   
      * @since 1.14, Ant 1.5
 220   
      */
 221  0
     public void copyFile(String sourceFile, String destFile,
 222   
                          FilterSetCollection filters, boolean overwrite,
 223   
                          boolean preserveLastModified, String encoding)
 224   
         throws IOException {
 225  0
         copyFile(new File(sourceFile), new File(destFile), filters,
 226   
                  overwrite, preserveLastModified, encoding);
 227   
     }
 228   
 
 229   
     /**
 230   
      * Convienence method to copy a file from a source to a
 231   
      * destination specifying if token filtering must be used, if
 232   
      * filter chains must be used, if source files may overwrite
 233   
      * newer destination files and the last modified time of
 234   
      * <code>destFile</code> file should be made equal
 235   
      * to the last modified time of <code>sourceFile</code>.
 236   
      *
 237   
      * @throws IOException
 238   
      *
 239   
      * @since 1.15, Ant 1.5
 240   
      */
 241  0
     public void copyFile(String sourceFile, String destFile,
 242   
                          FilterSetCollection filters, Vector filterChains,
 243   
                          boolean overwrite, boolean preserveLastModified,
 244   
                          String encoding, Project project)
 245   
         throws IOException {
 246  0
         copyFile(new File(sourceFile), new File(destFile), filters,
 247   
                  filterChains, overwrite, preserveLastModified,
 248   
                  encoding, project);
 249   
     }
 250   
 
 251   
     /**
 252   
      * Convienence method to copy a file from a source to a
 253   
      * destination specifying if token filtering must be used, if
 254   
      * filter chains must be used, if source files may overwrite
 255   
      * newer destination files and the last modified time of
 256   
      * <code>destFile</code> file should be made equal
 257   
      * to the last modified time of <code>sourceFile</code>.
 258   
      *
 259   
      * @throws IOException
 260   
      *
 261   
      * @since Ant 1.6
 262   
      */
 263  319
     public void copyFile(String sourceFile, String destFile,
 264   
                          FilterSetCollection filters, Vector filterChains,
 265   
                          boolean overwrite, boolean preserveLastModified,
 266   
                          String inputEncoding, String outputEncoding, 
 267   
                          Project project)
 268   
         throws IOException {
 269  319
         copyFile(new File(sourceFile), new File(destFile), filters,
 270   
                  filterChains, overwrite, preserveLastModified,
 271   
                  inputEncoding, outputEncoding, project);
 272   
     }
 273   
 
 274   
     /**
 275   
      * Convienence method to copy a file from a source to a destination.
 276   
      * No filtering is performed.
 277   
      *
 278   
      * @throws IOException
 279   
      */
 280  0
     public void copyFile(File sourceFile, File destFile) throws IOException {
 281  0
         copyFile(sourceFile, destFile, null, false, false);
 282   
     }
 283   
 
 284   
     /**
 285   
      * Convienence method to copy a file from a source to a destination
 286   
      * specifying if token filtering must be used.
 287   
      *
 288   
      * @throws IOException
 289   
      */
 290  0
     public void copyFile(File sourceFile, File destFile, FilterSetCollection filters)
 291   
         throws IOException {
 292  0
         copyFile(sourceFile, destFile, filters, false, false);
 293   
     }
 294   
 
 295   
     /**
 296   
      * Convienence method to copy a file from a source to a
 297   
      * destination specifying if token filtering must be used and if
 298   
      * source files may overwrite newer destination files.
 299   
      *
 300   
      * @throws IOException
 301   
      */
 302  2
     public void copyFile(File sourceFile, File destFile, FilterSetCollection filters,
 303   
                          boolean overwrite) throws IOException {
 304  2
         copyFile(sourceFile, destFile, filters, overwrite, false);
 305   
     }
 306   
 
 307   
     /**
 308   
      * Convienence method to copy a file from a source to a
 309   
      * destination specifying if token filtering must be used, if
 310   
      * source files may overwrite newer destination files and the
 311   
      * last modified time of <code>destFile</code> file should be made equal
 312   
      * to the last modified time of <code>sourceFile</code>.
 313   
      *
 314   
      * @throws IOException
 315   
      */
 316  690
     public void copyFile(File sourceFile, File destFile, FilterSetCollection filters,
 317   
                          boolean overwrite, boolean preserveLastModified)
 318   
         throws IOException {
 319  690
         copyFile(sourceFile, destFile, filters, overwrite,
 320   
                  preserveLastModified, null);
 321   
     }
 322   
 
 323   
     /**
 324   
      * Convienence method to copy a file from a source to a
 325   
      * destination specifying if token filtering must be used, if
 326   
      * source files may overwrite newer destination files, the last
 327   
      * modified time of <code>destFile</code> file should be made
 328   
      * equal to the last modified time of <code>sourceFile</code> and
 329   
      * which character encoding to assume.
 330   
      *
 331   
      * @throws IOException
 332   
      *
 333   
      * @since 1.14, Ant 1.5
 334   
      */
 335  690
     public void copyFile(File sourceFile, File destFile,
 336   
                          FilterSetCollection filters, boolean overwrite,
 337   
                          boolean preserveLastModified, String encoding)
 338   
         throws IOException {
 339  690
         copyFile(sourceFile, destFile, filters, null, overwrite,
 340   
                  preserveLastModified, encoding, null);
 341   
     }
 342   
 
 343   
     /**
 344   
      * Convienence method to copy a file from a source to a
 345   
      * destination specifying if token filtering must be used, if
 346   
      * filter chains must be used, if source files may overwrite
 347   
      * newer destination files and the last modified time of
 348   
      * <code>destFile</code> file should be made equal
 349   
      * to the last modified time of <code>sourceFile</code>.
 350   
      *
 351   
      * @throws IOException
 352   
      *
 353   
      * @since 1.15, Ant 1.5
 354   
      */
 355  690
     public void copyFile(File sourceFile, File destFile,
 356   
                          FilterSetCollection filters, Vector filterChains,
 357   
                          boolean overwrite, boolean preserveLastModified,
 358   
                          String encoding, Project project)
 359   
         throws IOException {
 360  690
         copyFile(sourceFile, destFile, filters, filterChains,
 361   
                  overwrite, preserveLastModified, encoding, encoding, project);
 362   
     }
 363   
 
 364   
     /**
 365   
      * Convienence method to copy a file from a source to a
 366   
      * destination specifying if token filtering must be used, if
 367   
      * filter chains must be used, if source files may overwrite
 368   
      * newer destination files and the last modified time of
 369   
      * <code>destFile</code> file should be made equal
 370   
      * to the last modified time of <code>sourceFile</code>.
 371   
      *
 372   
      * @throws IOException
 373   
      *
 374   
      * @since 1.15, Ant 1.6
 375   
      */
 376  1011
     public void copyFile(File sourceFile, File destFile,
 377   
                          FilterSetCollection filters, Vector filterChains,
 378   
                          boolean overwrite, boolean preserveLastModified,
 379   
                          String inputEncoding, String outputEncoding,
 380   
                          Project project)
 381   
         throws IOException {
 382   
 
 383  1011
         if (overwrite || !destFile.exists() ||
 384   
             destFile.lastModified() < sourceFile.lastModified()) {
 385   
 
 386  1011
             if (destFile.exists() && destFile.isFile()) {
 387  23
                 destFile.delete();
 388   
             }
 389   
 
 390   
             // ensure that parent dir of dest file exists!
 391   
             // not using getParentFile method to stay 1.1 compat
 392  1011
             File parent = getParentFile(destFile);
 393  1011
             if (!parent.exists()) {
 394  111
                 parent.mkdirs();
 395   
             }
 396   
 
 397  1011
             final boolean filterSetsAvailable = (filters != null
 398   
                                                  && filters.hasFilters());
 399  1011
             final boolean filterChainsAvailable = (filterChains != null
 400   
                                                    && filterChains.size() > 0);
 401   
 
 402  1011
             if (filterSetsAvailable || filterChainsAvailable
 403   
                 || (inputEncoding != null 
 404   
                     && !inputEncoding.equals(outputEncoding))
 405   
                 || (inputEncoding == null && outputEncoding != null)) {
 406  18
                 BufferedReader in = null;
 407  18
                 BufferedWriter out = null;
 408   
 
 409  18
                 try {
 410  18
                     if (inputEncoding == null) {
 411  14
                         in = new BufferedReader(new FileReader(sourceFile));
 412   
                     } else {
 413  4
                         in =
 414   
                             new BufferedReader(new InputStreamReader(
 415   
                                                                      new FileInputStream(sourceFile),
 416   
                                                                      inputEncoding));
 417   
                     }
 418   
 
 419  18
                     if (outputEncoding == null) {
 420  14
                         out = new BufferedWriter(new FileWriter(destFile));
 421   
                     } else {
 422  4
                         out =
 423   
                             new BufferedWriter(new OutputStreamWriter(
 424   
                                                                       new FileOutputStream(destFile),
 425   
                                                                       outputEncoding));
 426   
                     }
 427   
 
 428  18
                     if (filterChainsAvailable) {
 429  7
                         ChainReaderHelper crh = new ChainReaderHelper();
 430  7
                         crh.setBufferSize(8192);
 431  7
                         crh.setPrimaryReader(in);
 432  7
                         crh.setFilterChains(filterChains);
 433  7
                         crh.setProject(project);
 434  7
                         Reader rdr = crh.getAssembledReader();
 435  7
                         in = new BufferedReader(rdr);
 436   
                     }
 437   
 
 438  18
                     String newline = null;
 439  18
                     String line = in.readLine();
 440  18
                     while (line != null) {
 441  61
                         if (line.length() == 0) {
 442  7
                             out.newLine();
 443   
                         } else {
 444  54
                             if (filterSetsAvailable) {
 445  21
                                 newline = filters.replaceTokens(line);
 446   
                             } else {
 447  33
                                 newline = line;
 448   
                             }
 449  54
                             out.write(newline);
 450  54
                             out.newLine();
 451   
                         }
 452  61
                         line = in.readLine();
 453   
                     }
 454   
                 } finally {
 455  18
                     if (out != null) {
 456  18
                         out.close();
 457   
                     }
 458  18
                     if (in != null) {
 459  18
                         in.close();
 460   
                     }
 461   
                 }
 462   
             } else {
 463  993
                 FileInputStream in = null;
 464  993
                 FileOutputStream out = null;
 465  993
                 try {
 466  993
                     in = new FileInputStream(sourceFile);
 467  993
                     out = new FileOutputStream(destFile);
 468   
 
 469  991
                     byte[] buffer = new byte[8 * 1024];
 470  991
                     int count = 0;
 471  991
                     do {
 472  2837
                         out.write(buffer, 0, count);
 473  2837
                         count = in.read(buffer, 0, buffer.length);
 474  2837
                     } while (count != -1);
 475   
                 } finally {
 476  993
                     if (out != null) {
 477  991
                         out.close();
 478   
                     }
 479  993
                     if (in != null) {
 480  993
                         in.close();
 481   
                     }
 482   
                 }
 483   
             }
 484   
 
 485  1009
             if (preserveLastModified) {
 486  2
                 setFileLastModified(destFile, sourceFile.lastModified());
 487   
             }
 488   
         }
 489   
     }
 490   
 
 491   
     /**
 492   
      * see whether we have a setLastModified method in File and return it.
 493   
      */
 494  231
     protected final Method getSetLastModified() {
 495  231
         if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
 496  0
             return null;
 497   
         }
 498  231
         if (setLastModified == null) {
 499  1
             synchronized (lockReflection) {
 500  1
                 if (setLastModified == null) {
 501  1
                     try {
 502  1
                         setLastModified =
 503   
                             java.io.File.class.getMethod("setLastModified",
 504   
                                                          new Class[] {Long.TYPE});
 505   
                     } catch (NoSuchMethodException nse) {
 506  0
                         throw new BuildException("File.setlastModified not in JDK > 1.1?",
 507   
                                                  nse);
 508   
                     }
 509   
                 }
 510   
             }
 511   
         }
 512  231
         return setLastModified;
 513   
     }
 514   
 
 515   
     /**
 516   
      * Calls File.setLastModified(long time) in a Java 1.1 compatible way.
 517   
      */
 518  231
     public void setFileLastModified(File file, long time) throws BuildException {
 519  231
         if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
 520  0
             return;
 521   
         }
 522  231
         Long[] times = new Long[1];
 523  231
         if (time < 0) {
 524  1
             times[0] = new Long(System.currentTimeMillis());
 525   
         } else {
 526  230
             times[0] = new Long(time);
 527   
         }
 528   
 
 529  231
         try {
 530  231
             getSetLastModified().invoke(file, times);
 531   
         } catch (java.lang.reflect.InvocationTargetException ite) {
 532  0
             Throwable nested = ite.getTargetException();
 533  0
             throw new BuildException("Exception setting the modification time "
 534   
                                      + "of " + file, nested);
 535   
         } catch (Throwable other) {
 536  0
             throw new BuildException("Exception setting the modification time "
 537   
                                      + "of " + file, other);
 538   
         }
 539   
     }
 540   
 
 541   
     /**
 542   
      * Interpret the filename as a file relative to the given file -
 543   
      * unless the filename already represents an absolute filename.
 544   
      *
 545   
      * @param file the "reference" file for relative paths. This
 546   
      * instance must be an absolute file and must not contain
 547   
      * &quot;./&quot; or &quot;../&quot; sequences (same for \ instead
 548   
      * of /).  If it is null, this call is equivalent to
 549   
      * <code>new java.io.File(filename)</code>.
 550   
      *
 551   
      * @param filename a file name
 552   
      *
 553   
      * @return an absolute file that doesn't contain &quot;./&quot; or
 554   
      * &quot;../&quot; sequences and uses the correct separator for
 555   
      * the current platform.
 556   
      */
 557  25964
     public File resolveFile(File file, String filename) {
 558  25964
         filename = filename.replace('/', File.separatorChar)
 559   
             .replace('\\', File.separatorChar);
 560   
 
 561   
         // deal with absolute files
 562  25964
         if (!onNetWare) {
 563  25964
             if (filename.startsWith(File.separator)
 564   
                 || (filename.length() >= 2
 565   
                     && Character.isLetter(filename.charAt(0))
 566   
                     && filename.charAt(1) == ':')) {
 567  17507
                 return normalize(filename);
 568   
             }
 569   
         } else {
 570   
             // the assumption that the : will appear as the second character in
 571   
             // the path name breaks down when NetWare is a supported platform.
 572   
             // Netware volumes are of the pattern: "data:\"
 573  0
             int colon = filename.indexOf(":");
 574  0
             if (filename.startsWith(File.separator)
 575   
                 || (colon > -1)) {
 576  0
                 return normalize(filename);
 577   
             }
 578   
         }
 579   
 
 580  8457
         if (file == null) {
 581  3
             return new File(filename);
 582   
         }
 583   
 
 584  8454
         File helpFile = new File(file.getAbsolutePath());
 585  8454
         StringTokenizer tok = new StringTokenizer(filename, File.separator);
 586  8454
         while (tok.hasMoreTokens()) {
 587  22365
             String part = tok.nextToken();
 588  22365
             if (part.equals("..")) {
 589  274
                 helpFile = getParentFile(helpFile);
 590  274
                 if (helpFile == null) {
 591  1
                     String msg = "The file or path you specified ("
 592   
                         + filename + ") is invalid relative to "
 593   
                         + file.getPath();
 594  1
                     throw new BuildException(msg);
 595   
                 }
 596  22091
             } else if (part.equals(".")) {
 597   
                 // Do nothing here
 598   
             } else {
 599  21232
                 helpFile = new File(helpFile, part);
 600   
             }
 601   
         }
 602   
 
 603  8453
         return new File(helpFile.getAbsolutePath());
 604   
     }
 605   
 
 606   
     /**
 607   
      * &quot;normalize&quot; the given absolute path.
 608   
      *
 609   
      * <p>This includes:
 610   
      * <ul>
 611   
      *   <li>Uppercase the drive letter if there is one.</li>
 612   
      *   <li>Remove redundant slashes after the drive spec.</li>
 613   
      *   <li>resolve all ./, .\, ../ and ..\ sequences.</li>
 614   
      *   <li>DOS style paths that start with a drive letter will have
 615   
      *     \ as the separator.</li>
 616   
      * </ul>
 617   
      *
 618   
      * @throws java.lang.NullPointerException if the file path is
 619   
      * equal to null.
 620   
      */
 621  41378
     public File normalize(String path) {
 622  41378
         String orig = path;
 623   
 
 624  41378
         path = path.replace('/', File.separatorChar)
 625   
             .replace('\\', File.separatorChar);
 626   
 
 627   
         // make sure we are dealing with an absolute path
 628  41377
         int colon = path.indexOf(":");
 629   
 
 630  41377
         if (!onNetWare) {
 631  41377
             if (!path.startsWith(File.separator) &&
 632   
                 !(path.length() >= 2 &&
 633   
                   Character.isLetter(path.charAt(0)) &&
 634   
                   colon == 1)) {
 635  11
                 String msg = path + " is not an absolute path";
 636  11
                 throw new BuildException(msg);
 637   
             }
 638   
         } else {
 639  0
             if (!path.startsWith(File.separator)
 640   
                 && (colon == -1)) {
 641  0
                 String msg = path + " is not an absolute path";
 642  0
                 throw new BuildException(msg);
 643   
             }
 644   
         }
 645   
 
 646  41366
         boolean dosWithDrive = false;
 647  41366
         String root = null;
 648   
         // Eliminate consecutive slashes after the drive spec
 649  41366
         if ((!onNetWare &&
 650   
              path.length() >= 2 &&
 651   
              Character.isLetter(path.charAt(0)) &&
 652   
              path.charAt(1) == ':') ||
 653   
             (onNetWare && colon > -1)) {
 654   
 
 655  20
             dosWithDrive = true;
 656   
 
 657  20
             char[] ca = path.replace('/', '\\').toCharArray();
 658  20
             StringBuffer sbRoot = new StringBuffer();
 659  20
             for (int i = 0; i < colon; i++) {
 660  20
                 sbRoot.append(Character.toUpperCase(ca[i]));
 661   
             }
 662  20
             sbRoot.append(':');
 663  20
             if (colon + 1 < path.length()) {
 664  19
                 sbRoot.append(File.separatorChar);
 665   
             }
 666  20
             root = sbRoot.toString();
 667   
 
 668   
             // Eliminate consecutive slashes after the drive spec
 669  20
             StringBuffer sbPath = new StringBuffer();
 670  20
             for (int i = colon + 1; i < ca.length; i++) {
 671  60
                 if ((ca[i] != '\\') ||
 672   
                     (ca[i] == '\\' && ca[i - 1] != '\\')) {
 673  33
                     sbPath.append(ca[i]);
 674   
                 }
 675   
             }
 676  20
             path = sbPath.toString().replace('\\', File.separatorChar);
 677   
 
 678   
         } else {
 679  41346
             if (path.length() == 1) {
 680  12
                 root = File.separator;
 681  12
                 path = "";
 682  41334
             } else if (path.charAt(1) == File.separatorChar) {
 683   
                 // UNC drive
 684  0
                 root = File.separator + File.separator;
 685  0
                 path = path.substring(2);
 686   
             } else {
 687  41334
                 root = File.separator;
 688  41334
                 path = path.substring(1);
 689   
             }
 690   
         }
 691   
 
 692  41366
         Stack s = new Stack();
 693  41366
         s.push(root);
 694  41366
         StringTokenizer tok = new StringTokenizer(path, File.separator);
 695  41366
         while (tok.hasMoreTokens()) {
 696  331728
             String thisToken = tok.nextToken();
 697  331728
             if (".".equals(thisToken)) {
 698  49
                 continue;
 699  331679
             } else if ("..".equals(thisToken)) {
 700  21
                 if (s.size() < 2) {
 701  1
                     throw new BuildException("Cannot resolve path " + orig);
 702   
                 } else {
 703  20
                     s.pop();
 704   
                 }
 705   
             } else { // plain component
 706  331658
                 s.push(thisToken);
 707   
             }
 708   
         }
 709   
 
 710  41365
         StringBuffer sb = new StringBuffer();
 711  41365
         for (int i = 0; i < s.size(); i++) {
 712  373003
             if (i > 1) {
 713   
                 // not before the filesystem root and not after it, since root
 714   
                 // already contains one
 715  290304
                 sb.append(File.separatorChar);
 716   
             }
 717  373003
             sb.append(s.elementAt(i));
 718   
         }
 719   
 
 720   
 
 721  41365
         path = sb.toString();
 722  41365
         if (dosWithDrive) {
 723  20
             path = path.replace('/', '\\');
 724   
         }
 725  41365
         return new File(path);
 726   
     }
 727   
 
 728   
     /**
 729   
      * Create a temporary file in a given directory.
 730   
      *
 731   
      * <p>The file denoted by the returned abstract pathname did not
 732   
      * exist before this method was invoked, any subsequent invocation
 733   
      * of this method will yield a different file name.</p>
 734   
      *
 735   
      * <p>This method is different to File.createTempFile of JDK 1.2
 736   
      * as it doesn't create the file itself and doesn't use platform
 737   
      * specific temporary directory when the parentDir attribute is
 738   
      * null.</p>
 739   
      *
 740   
      * @param parentDir Directory to create the temporary file in -
 741   
      * current working directory will be assumed if this parameter is
 742   
      * null.
 743   
      *
 744   
      * @since 1.8
 745   
      */
 746  46
     public File createTempFile(String prefix, String suffix, File parentDir) {
 747   
 
 748  46
         File result = null;
 749  46
         String parent = null;
 750  46
         if (parentDir != null) {
 751  45
             parent = parentDir.getPath();
 752   
         }
 753  46
         DecimalFormat fmt = new DecimalFormat("#####");
 754  46
         synchronized (rand) {
 755  46
             do {
 756  46
                 result = new File(parent,
 757   
                                   prefix + fmt.format(Math.abs(rand.nextInt()))
 758   
                                   + suffix);
 759  46
             } while (result.exists());
 760   
         }
 761  46
         return result;
 762   
     }
 763   
 
 764   
     /**
 765   
      * Compares the contents of two files.
 766   
      *
 767   
      * <p>simple but sub-optimal comparision algorithm.  written for
 768   
      * working rather than fast. Better would be a block read into
 769   
      * buffers followed by long comparisions apart from the final 1-7
 770   
      * bytes.</p>
 771   
      *
 772   
      * @since 1.9
 773   
      */
 774  45
     public boolean contentEquals(File f1, File f2) throws IOException {
 775  45
         if (f1.exists() != f2.exists()) {
 776  3
             return false;
 777   
         }
 778   
 
 779  42
         if (!f1.exists()) {
 780   
             // two not existing files are equal
 781  1
             return true;
 782   
         }
 783   
 
 784  41
         if (f1.isDirectory() || f2.isDirectory()) {
 785   
             // don't want to compare directory contents for now
 786  1
             return false;
 787   
         }
 788   
 
 789  40
         if (fileNameEquals(f1, f2)) {
 790   
             // same filename => true
 791  2
             return true;
 792   
         }
 793   
 
 794  38
         if (f1.length() != f2.length()) {
 795   
             // different size =>false
 796  4
             return false;
 797   
         }
 798   
 
 799  34
         InputStream in1 = null;
 800  34
         InputStream in2 = null;
 801  34
         try {
 802  34
             in1 = new BufferedInputStream(new FileInputStream(f1));
 803  34
             in2 = new BufferedInputStream(new FileInputStream(f2));
 804   
 
 805  34
             int expectedByte = in1.read();
 806  34
             while (expectedByte != -1) {
 807  3347849
                 if (expectedByte != in2.read()) {
 808  1
                     return false;
 809   
                 }
 810  3347848
                 expectedByte = in1.read();
 811   
             }
 812  33
             if (in2.read() != -1) {
 813  0
                 return false;
 814   
             }
 815  33
             return true;
 816   
         } finally {
 817  34
             if (in1 != null) {
 818  34
                 try {
 819  34
                     in1.close();
 820   
                 } catch (IOException e) {}
 821   
             }
 822  34
             if (in2 != null) {
 823  34
                 try {
 824  34
                     in2.close();
 825   
                 } catch (IOException e) {}
 826   
             }
 827   
         }
 828   
     }
 829   
 
 830   
     /**
 831   
      * Emulation of File.getParentFile for JDK 1.1
 832   
      *
 833   
      * @since 1.10
 834   
      */
 835  1690
     public File getParentFile(File f) {
 836  1690
         if (f != null) {
 837  1690
             String p = f.getParent();
 838  1690
             if (p != null) {
 839  1685
                 return new File(p);
 840   
             }
 841   
         }
 842  5
         return null;
 843   
     }
 844   
 
 845   
     /**
 846   
      * Read from reader till EOF
 847   
      */
 848  1
     public static final String readFully(Reader rdr) throws IOException {
 849  1
         return readFully(rdr, 8192);
 850   
     }
 851   
 
 852   
     /**
 853   
      * Read from reader till EOF
 854   
      */
 855  14
     public static final String readFully(Reader rdr, int bufferSize) throws IOException {
 856  14
         if (bufferSize <= 0) {
 857  0
             throw new IllegalArgumentException("Buffer size must be greater "
 858   
                                                + "than 0");
 859   
         }
 860  14
         final char[] buffer = new char[bufferSize];
 861  14
         int bufferLength = 0;
 862  14
         String text = null;
 863  14
         StringBuffer textBuffer = null;
 864  14
         while (bufferLength != -1) {
 865  26
             bufferLength = rdr.read(buffer);
 866  26
             if (bufferLength != -1) {
 867  12
                 if (textBuffer == null) {
 868  11
                     textBuffer = new StringBuffer(
 869   
                                                   new String(buffer, 0, bufferLength));
 870   
                 } else {
 871  1
                     textBuffer.append(new String(buffer, 0, bufferLength));
 872   
                 }
 873   
             }
 874   
         }
 875  14
         if (textBuffer != null) {
 876  11
             text = textBuffer.toString();
 877   
         }
 878  14
         return text;
 879   
     }
 880   
 
 881   
     /**
 882   
      * Emulation of File.createNewFile for JDK 1.1.
 883   
      *
 884   
      * <p>This method does <strong>not</strong> guarantee that the
 885   
      * operation is atomic.</p>
 886   
      *
 887   
      * @since 1.21, Ant 1.5
 888   
      */
 889  60
     public boolean createNewFile(File f) throws IOException {
 890  60
         if (f != null) {
 891  60
             if (f.exists()) {
 892  0
                 return false;
 893   
             }
 894   
 
 895  60
             FileOutputStream fos = null;
 896  60
             try {
 897  60
                 fos = new FileOutputStream(f);
 898  60
                 fos.write(new byte[0]);
 899   
             } finally {
 900  60
                 if (fos != null) {
 901  60
                     fos.close();
 902   
                 }
 903   
             }
 904   
 
 905  60
             return true;
 906   
         }
 907  0
         return false;
 908   
     }
 909   
 
 910   
     /**
 911   
      * Checks whether a given file is a symbolic link.
 912   
      *
 913   
      * <p>It doesn't really test for symbolic links but whether the
 914   
      * canonical and absolute paths of the file are identical - this
 915   
      * may lead to false positives on some platforms.</p>
 916   
      *
 917   
      * @param parent the parent directory of the file to test
 918   
      * @param name the name of the file to test.
 919   
      *
 920   
      * @since Ant 1.5
 921   
      */
 922  1021
     public boolean isSymbolicLink(File parent, String name)
 923   
         throws IOException {
 924  1021
         File resolvedParent = new File(parent.getCanonicalPath());
 925  1021
         File toTest = new File(resolvedParent, name);
 926  1021
         return !toTest.getAbsolutePath().equals(toTest.getCanonicalPath());
 927   
     }
 928   
 
 929   
     /**
 930   
      * Removes a leading path from a second path.
 931   
      *
 932   
      * @param leading The leading path, must not be null, must be absolute.
 933   
      * @param path The path to remove from, must not be null, must be absolute.
 934   
      *
 935   
      * @return path's normalized absolute if it doesn't start with
 936   
      * leading, path's path with leading's path removed otherwise.
 937   
      *
 938   
      * @since Ant 1.5
 939   
      */
 940  74
     public String removeLeadingPath(File leading, File path) {
 941   
         // if leading's path ends with a slash, it will be stripped by
 942   
         // normalize - we always add one so we never think /foo was a
 943   
         // parent directory of /foobar
 944  74
         String l = normalize(leading.getAbsolutePath()).getAbsolutePath()
 945   
             + File.separator;
 946  74
         String p = normalize(path.getAbsolutePath()).getAbsolutePath();
 947  74
         if (p.startsWith(l)) {
 948  67
             return p.substring(l.length());
 949   
         } else {
 950  7
             return p;
 951   
         }
 952   
     }
 953   
 
 954   
     /**
 955   
      * Constructs a <code>file:</code> URI that represents the
 956   
      * external form of the given pathname.
 957   
      *
 958   
      * <p>Will be an absolute URI if the given path is absolute.</p>
 959   
      *
 960   
      * <p>This code doesn't handle non-ASCII characters properly.</p>
 961   
      *
 962   
      * @since Ant 1.6
 963   
      */
 964  777
     public String toURI(String path) {
 965  777
         boolean isDir = (new File(path)).isDirectory();
 966   
 
 967  777
         StringBuffer sb = new StringBuffer("file:");
 968   
 
 969   
         // catch exception if normalize thinks this is not an absolute path
 970  777
         try {
 971  777
             path = normalize(path).getAbsolutePath();
 972  775
             sb.append("//");
 973   
             // add an extra slash for filesystems with drive-specifiers
 974  775
             if (!path.startsWith("/")) {
 975  0
                 sb.append("/");
 976   
             }
 977   
 
 978   
         } catch (BuildException e) {
 979   
             // relative path
 980   
         }
 981   
 
 982  777
         path = path.replace('\\', '/');
 983   
         
 984  777
         CharacterIterator iter = new StringCharacterIterator(path);
 985  777
         for (char c = iter.first(); c != CharacterIterator.DONE;
 986   
              c = iter.next()) {
 987  52754
             if (isSpecial[c]) {
 988  14
                 sb.append('%');
 989  14
                 sb.append(escapedChar1[c]);
 990  14
                 sb.append(escapedChar2[c]);
 991   
             } else {
 992  52740
                 sb.append(c);
 993   
             }
 994   
         }
 995  777
         if (isDir && !path.endsWith("/")) {
 996  16
             sb.append('/');
 997   
         }
 998  777
         return sb.toString();
 999   
     }
 1000   
 
 1001   
     /**
 1002   
      * Constructs a file path from a <code>file:</code> URI.
 1003   
      *
 1004   
      * <p>Will be an absolute path if the given URI is absolute.</p>
 1005   
      *
 1006   
      * <p>Swallows '%' that are not followed by two characters,
 1007   
      * doesn't deal with non-ASCII characters.</p>
 1008   
      *
 1009   
      * @since Ant 1.6
 1010   
      */
 1011  22106
     public String fromURI(String uri) {
 1012  22106
         if (!uri.startsWith("file:")) {
 1013  0
             throw new IllegalArgumentException("Can only handle file: URIs");
 1014   
         }
 1015  22106
         if (uri.startsWith("file://")) {
 1016  22093
             uri = uri.substring(7);
 1017   
         } else {
 1018  13
             uri = uri.substring(5);
 1019   
         }
 1020   
 
 1021  22106
         uri = uri.replace('/', File.separatorChar);
 1022  22106
         if (Os.isFamily("dos") && uri.startsWith("\\") && uri.length() > 2
 1023   
             && Character.isLetter(uri.charAt(1)) && uri.charAt(2) == ':') {
 1024  0
             uri = uri.substring(1);
 1025   
         }
 1026   
 
 1027  22106
         StringBuffer sb = new StringBuffer();
 1028  22106
         CharacterIterator iter = new StringCharacterIterator(uri);
 1029  22106
         for (char c = iter.first(); c != CharacterIterator.DONE;
 1030   
              c = iter.next()) {
 1031  1617876
             if (c == '%') {
 1032  10
                 char c1 = iter.next();
 1033  10
                 if (c1 != CharacterIterator.DONE) {
 1034  10
                     int i1 = Character.digit(c1, 16);
 1035  10
                     char c2 = iter.next();
 1036  10
                     if (c2 != CharacterIterator.DONE) {
 1037  10
                         int i2 = Character.digit(c2, 16);
 1038  10
                         sb.append((char) ((i1 << 4) + i2));
 1039   
                     }
 1040   
                 }
 1041   
             } else {
 1042  1617866
                 sb.append(c);
 1043   
             }
 1044   
         }
 1045   
 
 1046  22106
         String path = sb.toString();
 1047   
         // catch exception if normalize thinks this is not an absolute path
 1048  22106
         try {
 1049  22106
             path = normalize(path).getAbsolutePath();
 1050   
         } catch (BuildException e) {
 1051   
             // relative path
 1052   
         }
 1053  22106
         return path;
 1054   
     }
 1055   
 
 1056   
     /**
 1057   
      * Compares two filenames.
 1058   
      *
 1059   
      * <p>Unlike java.io.File#equals this method will try to compare
 1060   
      * the absolute paths and &quot;normalize&quot; the filenames
 1061   
      * before comparing them.</p>
 1062   
      *
 1063   
      * @since Ant 1.5.3
 1064   
      */
 1065  49
     public boolean fileNameEquals(File f1, File f2) {
 1066  49
         return normalize(f1.getAbsolutePath())
 1067   
             .equals(normalize(f2.getAbsolutePath()));
 1068   
     }
 1069   
 
 1070   
 }
 1071   
 
 1072