001/* File.java -- Class representing a file on disk
002   Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2012
003   Free Software Foundation, Inc.
004
005This file is part of GNU Classpath.
006
007GNU Classpath is free software; you can redistribute it and/or modify
008it under the terms of the GNU General Public License as published by
009the Free Software Foundation; either version 2, or (at your option)
010any later version.
011 
012GNU Classpath is distributed in the hope that it will be useful, but
013WITHOUT ANY WARRANTY; without even the implied warranty of
014MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015General Public License for more details.
016
017You should have received a copy of the GNU General Public License
018along with GNU Classpath; see the file COPYING.  If not, write to the
019Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02002110-1301 USA.
021
022Linking this library statically or dynamically with other modules is
023making a combined work based on this library.  Thus, the terms and
024conditions of the GNU General Public License cover the whole
025combination.
026
027As a special exception, the copyright holders of this library give you
028permission to link this library with independent modules to produce an
029executable, regardless of the license terms of these independent
030modules, and to copy and distribute the resulting executable under
031terms of your choice, provided that you also meet, for each linked
032independent module, the terms and conditions of the license of that
033module.  An independent module is a module which is not derived from
034or based on this library.  If you modify this library, you may extend
035this exception to your version of the library, but you are not
036obligated to do so.  If you do not wish to do so, delete this
037exception statement from your version. */
038
039
040package java.io;
041
042import java.net.MalformedURLException;
043import java.net.URI;
044import java.net.URISyntaxException;
045import java.net.URL;
046import gnu.classpath.Configuration;
047
048/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
049 * "The Java Language Specification", ISBN 0-201-63451-1
050 * Status:  Complete to version 1.3.
051 */
052
053/**
054 * This class represents a file or directory on a local disk.  It provides
055 * facilities for dealing with a variety of systems that use various
056 * types of path separators ("/" versus "\", for example).  It also
057 * contains method useful for creating and deleting files and directories.
058 *
059 * @author Aaron M. Renn (arenn@urbanophile.com)
060 * @author Tom Tromey (tromey@cygnus.com)
061 */
062public class File implements Serializable, Comparable<File>
063{
064  private static final long serialVersionUID = 301077366599181567L;
065        
066  // QUERY arguments to access function.
067  private final static int READ = 0;
068  private final static int WRITE = 1;
069  private final static int EXISTS = 2;
070  private final static int EXEC = 3;
071
072  // QUERY arguments to stat function.
073  private final static int DIRECTORY = 0;
074  private final static int ISFILE = 1;
075  private final static int ISHIDDEN = 2;
076
077  // QUERY arguments to attr function.
078  private final static int MODIFIED = 0;
079  private final static int LENGTH = 1;
080  
081  private final native long attr (int query);
082  private final native boolean access (int query);
083  private final native boolean stat (int query);
084
085  /**
086   * This is the path separator string for the current host. This field
087   * contains the value of the <code>file.separator</code> system property.
088   * An example separator string would be "/" on the GNU system.
089   */
090  public static final String separator = System.getProperty("file.separator");
091  private static final String dupSeparator = separator + separator;
092
093  /**
094   * This is the first character of the file separator string.  On many
095   * hosts (for example, on the GNU system), this represents the entire 
096   * separator string.  The complete separator string is obtained from the
097   * <code>file.separator</code>system property.
098   */
099  public static final char separatorChar = separator.charAt(0);
100  
101  /**
102   * This is the string that is used to separate the host name from the
103   * path name in paths that include the host name.  It is the value of
104   * the <code>path.separator</code> system property.
105   */
106  public static final String pathSeparator
107    = System.getProperty("path.separator");
108  
109  /**
110   * This is the first character of the string used to separate the host name
111   * from the path name in paths that include a host.  The separator string
112   * is taken from the <code>path.separator</code> system property.
113   */
114  public static final char pathSeparatorChar = pathSeparator.charAt(0);
115
116  static final String tmpdir = System.getProperty("java.io.tmpdir");
117  /* If 0, then the system doesn't have a file name length limit.  */
118  static int maxPathLen;
119  static boolean caseSensitive;
120  
121  static
122  {
123    if (Configuration.INIT_LOAD_LIBRARY)
124      {
125        System.loadLibrary("javaio");
126      }
127    
128    init_native();
129  }
130  
131  // Native function called at class initialization. This should should
132  // set the maxPathLen and caseSensitive variables.
133  private static native void init_native();
134
135  /**
136   * This is the path to the file set when the object is created.  It
137   * may be an absolute or relative path name.
138   */
139  private String path;
140
141  // We keep a counter for use by createTempFile.  We choose the first
142  // value randomly to try to avoid clashes with other VMs.
143  private static long counter = Double.doubleToLongBits (Math.random());
144
145  /**
146   * This method tests whether or not the current thread is allowed to
147   * to read the file pointed to by this object.  This will be true if and
148   * and only if 1) the file exists and 2) the <code>SecurityManager</code>
149   * (if any) allows access to the file via it's <code>checkRead</code>
150   * method 3) the file is readable.
151   *
152   * @return <code>true</code> if reading is allowed, 
153   * <code>false</code> otherwise
154   *
155   * @exception SecurityException If the <code>SecurityManager</code> 
156   * does not allow access to the file
157   */
158  public boolean canRead()
159  {
160    checkRead();
161    return access (READ);
162  }
163
164  /**
165   * This method test whether or not the current thread is allowed to
166   * write to this object.  This will be true if and only if 1) The
167   * <code>SecurityManager</code> (if any) allows write access to the
168   * file and 2) The file exists and 3) The file is writable.  To determine
169   * whether or not a non-existent file can be created, check the parent
170   * directory for write access.
171   *
172   * @return <code>true</code> if writing is allowed, <code>false</code> 
173   * otherwise
174   *
175   * @exception SecurityException If the <code>SecurityManager</code> 
176   * does not allow access to the file
177   */
178  public boolean canWrite()
179  {
180    checkWrite();
181    return access (WRITE);
182  }
183  
184  /**
185   * This method tests whether or not the current thread is allowed to
186   * to execute the file pointed to by this object. This will be true if and
187   * and only if 1) the file exists and 2) the <code>SecurityManager</code>
188   * (if any) allows access to the file via it's <code>checkExec</code>
189   * method 3) the file is executable.
190   *
191   * @return <code>true</code> if execution is allowed, 
192   * <code>false</code> otherwise
193   *
194   * @exception SecurityException If the <code>SecurityManager</code> 
195   * does not allow access to the file
196   */
197  public boolean canExecute()
198  {
199    if (!exists())
200      return false;
201    checkExec();
202    return access (EXEC);
203  }
204
205  private native boolean performCreate() throws IOException;
206
207  /**
208   * This method creates a new file of zero length with the same name as
209   * the path of this <code>File</code> object if an only if that file
210   * does not already exist.
211   * <p>
212   * A <code>SecurityManager.checkWrite</code> check is done prior
213   * to performing this action.
214   *
215   * @return <code>true</code> if the file was created, <code>false</code> if
216   * the file alread existed.
217   *
218   * @exception IOException If an I/O error occurs
219   * @exception SecurityException If the <code>SecurityManager</code> will
220   * not allow this operation to be performed.
221   *
222   * @since 1.2
223   */
224  public boolean createNewFile() throws IOException
225  {
226    checkWrite();
227    return performCreate();
228  }
229 
230  /*
231   * This native method handles the actual deleting of the file
232   */
233  private native boolean performDelete();
234
235  /**
236   * This method deletes the file represented by this object.  If this file
237   * is a directory, it must be empty in order for the delete to succeed.
238   *
239   * @return <code>true</code> if the file was deleted, <code>false</code> 
240   * otherwise
241   *
242   * @exception SecurityException If deleting of the file is not allowed
243   */
244  public synchronized boolean delete()
245  {
246    SecurityManager s = System.getSecurityManager();
247    
248    if (s != null)
249      s.checkDelete(path);
250    
251    return performDelete();
252  }
253
254  /**
255   * This method tests two <code>File</code> objects for equality by 
256   * comparing the path of the specified <code>File</code> against the path
257   * of this object.  The two objects are equal if an only if 1) The
258   * argument is not null 2) The argument is a <code>File</code> object and
259   * 3) The path of the <code>File</code>argument is equal to the path
260   * of this object.
261   * <p>
262   * The paths of the files are determined by calling the 
263   * <code>getPath()</code>
264   * method on each object.
265   *
266   * @return <code>true</code> if the two objects are equal, 
267   * <code>false</code> otherwise.
268   */
269  public boolean equals(Object obj)
270  {
271    if (! (obj instanceof File))
272      return false;
273    
274    File other = (File) obj;
275
276    if (caseSensitive)
277      return path.equals(other.path);
278    else
279      return path.equalsIgnoreCase(other.path);
280  }
281
282  /*
283   * This method tests whether or not the file represented by the
284   * object actually exists on the filesystem.
285   */
286  private boolean internalExists()
287  {
288    return access (EXISTS);
289  }
290  
291  /**
292   * This method tests whether or not the file represented by the object
293   * actually exists on the filesystem.
294   *
295   * @return <code>true</code> if the file exists, <code>false</code>otherwise.
296   *
297   * @exception SecurityException If reading of the file is not permitted
298   */
299  public boolean exists()
300  {
301    checkRead();
302    return internalExists();
303  }
304
305  /**
306   * This method initializes a new <code>File</code> object to represent
307   * a file with the specified path.
308   *
309   * @param name The path name of the file
310   */
311  public File(String name)
312  {
313    path = normalizePath (name);
314  }
315
316  // Remove duplicate and redundant separator characters.
317  private String normalizePath(String p)
318  {
319    // On Windows, convert any '/' to '\'.  This appears to be the same logic
320    // that Sun's Win32 Java performs.
321    if (separatorChar == '\\')
322      {
323        p = p.replace ('/', '\\');
324        // We have to special case the "\c:" prefix.
325        if (p.length() > 2 && p.charAt(0) == '\\' &&
326            ((p.charAt(1) >= 'a' && p.charAt(1) <= 'z') ||
327            (p.charAt(1) >= 'A' && p.charAt(1) <= 'Z')) &&
328            p.charAt(2) == ':')
329          p = p.substring(1);
330      }
331
332    int dupIndex = p.indexOf(dupSeparator);
333    int plen = p.length();
334
335    // Special case: permit Windows UNC path prefix.
336    if (dupSeparator.equals("\\\\") && dupIndex == 0)
337      dupIndex = p.indexOf(dupSeparator, 1);
338
339    if (dupIndex == -1)
340      {
341        // Ignore trailing separator (though on Windows "a:\", for
342        // example, is a valid and minimal path).
343        if (plen > 1 && p.charAt (plen - 1) == separatorChar)
344          {
345            if (! (separatorChar == '\\' && plen == 3 && p.charAt (1) == ':'))
346              return p.substring (0, plen - 1);
347          }
348        else
349          return p;
350      }
351    
352    StringBuffer newpath = new StringBuffer(plen);
353    int last = 0;
354    while (dupIndex != -1)
355      {
356        newpath.append(p.substring(last, dupIndex));
357        // Ignore the duplicate path characters.
358        while (p.charAt(dupIndex) == separatorChar)
359          {
360            dupIndex++;
361            if (dupIndex == plen)
362              return newpath.toString();
363          }
364        newpath.append(separatorChar);
365        last = dupIndex;
366        dupIndex = p.indexOf(dupSeparator, last);
367      }
368    
369    // Again, ignore possible trailing separator (except special cases
370    // like "a:\" on Windows).
371    int end;
372    if (plen > 1 && p.charAt (plen - 1) == separatorChar)
373    {
374      if (separatorChar == '\\' && plen == 3 && p.charAt (1) == ':')
375        end = plen;
376      else
377        end = plen - 1;
378    }
379    else
380      end = plen;
381    newpath.append(p.substring(last, end));
382    
383    return newpath.toString();
384  }
385 
386  /**
387   * This method initializes a new <code>File</code> object to represent
388   * a file in the specified named directory.  The path name to the file
389   * will be the directory name plus the separator string plus the file
390   * name.  If the directory path name ends in the separator string, another
391   * separator string will still be appended.
392   *
393   * @param dirPath The path to the directory the file resides in
394   * @param name The name of the file
395   */
396  public File(String dirPath, String name)
397  {
398    if (name == null)
399      throw new NullPointerException();
400    if (dirPath != null)
401      {
402        if (dirPath.length() > 0)
403          {
404            // Try to be smart about the number of separator characters.
405            if (dirPath.charAt(dirPath.length() - 1) == separatorChar
406                || name.length() == 0)
407              path = normalizePath(dirPath + name);
408            else
409              path = normalizePath(dirPath + separatorChar + name);
410          }
411        else
412          {
413            // If dirPath is empty, use a system dependant
414            // default prefix.
415            // Note that the leading separators in name have
416            // to be chopped off, to prevent them forming
417            // a UNC prefix on Windows.
418            if (separatorChar == '\\' /* TODO use ON_WINDOWS */)
419              {
420                int skip = 0;
421                while(name.length() > skip
422                    && (name.charAt(skip) == separatorChar
423                    || name.charAt(skip) == '/'))
424                  {
425                    skip++;
426                  }
427                name = name.substring(skip);
428              }
429            path = normalizePath(separatorChar + name);
430          }
431      }
432    else
433      path = normalizePath(name);
434  }
435
436  /**
437   * This method initializes a new <code>File</code> object to represent
438   * a file in the specified directory.  If the <code>directory</code>
439   * argument is <code>null</code>, the file is assumed to be in the
440   * current directory as specified by the <code>user.dir</code> system
441   * property
442   *
443   * @param directory The directory this file resides in
444   * @param name The name of the file
445   */
446  public File(File directory, String name)
447  {
448    this (directory == null ? null : directory.path, name);
449  }
450
451  /**
452   * This method initializes a new <code>File</code> object to represent
453   * a file corresponding to the specified <code>file:</code> protocol URI.
454   *
455   * @param uri The URI
456   * @throws IllegalArgumentException if the URI is not hierarchical
457   */
458  public File(URI uri)
459  {
460    if (uri == null)
461        throw new NullPointerException("uri is null");
462
463    if (!uri.getScheme().equals("file"))
464        throw new IllegalArgumentException("invalid uri protocol");
465
466    String name = uri.getPath();
467    if (name == null)
468      throw new IllegalArgumentException("URI \"" + uri
469                     + "\" is not hierarchical");
470    path = normalizePath(name);
471  }
472
473  /**
474   * This method returns the path of this file as an absolute path name.
475   * If the path name is already absolute, then it is returned.  Otherwise
476   * the value returned is the current directory plus the separatory
477   * string plus the path of the file.  The current directory is determined
478   * from the <code>user.dir</code> system property.
479   *
480   * @return The absolute path of this file
481   */
482  public String getAbsolutePath()
483  {
484    if (isAbsolute())
485      return path;
486    else if (separatorChar == '\\' 
487             && path.length() > 0 && path.charAt (0) == '\\')
488      {
489        // On Windows, even if the path starts with a '\\' it is not
490        // really absolute until we prefix the drive specifier from
491        // the current working directory to it.
492        return System.getProperty ("user.dir").substring (0, 2) + path;
493      }
494    else if (separatorChar == '\\' 
495             && path.length() > 1 && path.charAt (1) == ':'
496             && ((path.charAt (0) >= 'a' && path.charAt (0) <= 'z')
497                 || (path.charAt (0) >= 'A' && path.charAt (0) <= 'Z')))
498      {
499        // On Windows, a process has a current working directory for
500        // each drive and a path like "G:foo\bar" would mean the 
501        // absolute path "G:\wombat\foo\bar" if "\wombat" is the 
502        // working directory on the G drive.
503        String drvDir = null;
504        try
505          {
506            drvDir = new File (path.substring (0, 2)).getCanonicalPath();
507          }
508        catch (IOException e)
509          {
510            drvDir = path.substring (0, 2) + "\\";
511          }
512        
513        // Note: this would return "C:\\." for the path "C:.", if "\"
514        // is the working folder on the C drive, but this is 
515        // consistent with what Sun's JRE 1.4.1.01 actually returns!
516        if (path.length() > 2)
517          return drvDir + '\\' + path.substring (2, path.length());
518        else
519          return drvDir;
520      }
521    else
522      return System.getProperty ("user.dir") + separatorChar + path;
523  }
524
525  /**
526   * This method returns a <code>File</code> object representing the
527   * absolute path of this object.
528   *
529   * @return A <code>File</code> with the absolute path of the object.
530   *
531   * @since 1.2
532   */
533  public File getAbsoluteFile()
534  {
535    return new File(getAbsolutePath());
536  }
537
538  /**
539   * This method returns a canonical representation of the pathname of
540   * this file.  The actual form of the canonical representation is
541   * system-dependent.  On the GNU system, conversion to canonical
542   * form involves the removal of redundant separators, references to
543   * "." and "..", and symbolic links.
544   * <p>
545   * Note that this method, unlike the other methods which return path
546   * names, can throw an IOException.  This is because native method 
547   * might be required in order to resolve the canonical path
548   *
549   * @exception IOException If an error occurs
550   */
551  public native String getCanonicalPath() throws IOException;
552
553  /**
554   * This method returns a <code>File</code> object representing the
555   * canonical path of this object.
556   *
557   * @return A <code>File</code> instance representing the canonical path of
558   * this object.
559   *
560   * @exception IOException If an error occurs.
561   *
562   * @since 1.2
563   */
564  public File getCanonicalFile() throws IOException
565  {
566    return new File(getCanonicalPath());
567  }
568
569  /**
570   * This method returns the name of the file.  This is everything in the
571   * complete path of the file after the last instance of the separator
572   * string.
573   *
574   * @return The file name
575   */
576  public String getName()
577  {
578    int nameSeqIndex = 0;
579
580    if (separatorChar == '\\' && path.length() > 1)
581      {
582        // On Windows, ignore the drive specifier or the leading '\\'
583        // of a UNC network path, if any (a.k.a. the "prefix").
584        if ((path.charAt (0) == '\\' && path.charAt (1) == '\\')
585            || (((path.charAt (0) >= 'a' && path.charAt (0) <= 'z')
586                 || (path.charAt (0) >= 'A' && path.charAt (0) <= 'Z'))
587                && path.charAt (1) == ':'))
588          {
589            if (path.length() > 2)
590              nameSeqIndex = 2;
591            else
592              return "";
593          }
594      }
595
596    String nameSeq 
597      = (nameSeqIndex > 0 ? path.substring (nameSeqIndex) : path);
598
599    int last = nameSeq.lastIndexOf (separatorChar);
600
601    return nameSeq.substring (last + 1);
602  }
603
604  /**
605   * This method returns a <code>String</code> the represents this file's
606   * parent.  <code>null</code> is returned if the file has no parent.  The
607   * parent is determined via a simple operation which removes the name
608   * after the last file separator character, as determined by the platform.
609   *
610   * @return The parent directory of this file
611   */
612  public String getParent()
613  {
614    String prefix = null;
615    int nameSeqIndex = 0;
616
617    // The "prefix", if present, is the leading "/" on UNIX and 
618    // either the drive specifier (e.g. "C:") or the leading "\\"
619    // of a UNC network path on Windows.
620    if (separatorChar == '/' && path.charAt (0) == '/')
621      {
622        prefix = "/";
623        nameSeqIndex = 1;
624      }
625    else if (separatorChar == '\\' && path.length() > 1)
626      {
627        if ((path.charAt (0) == '\\' && path.charAt (1) == '\\')
628            || (((path.charAt (0) >= 'a' && path.charAt (0) <= 'z')
629                 || (path.charAt (0) >= 'A' && path.charAt (0) <= 'Z'))
630                && path.charAt (1) == ':'))
631          {
632            prefix = path.substring (0, 2);
633            nameSeqIndex = 2;
634          }
635      }
636
637    // According to the JDK docs, the returned parent path is the 
638    // portion of the name sequence before the last separator
639    // character, if found, prefixed by the prefix, otherwise null.
640    if (nameSeqIndex < path.length())
641      {
642        String nameSeq = path.substring (nameSeqIndex, path.length());
643        int last = nameSeq.lastIndexOf (separatorChar);
644        if (last == -1)
645          return prefix;
646        else if (last == (nameSeq.length() - 1))
647          // Note: The path would not have a trailing separator
648          // except for cases like "C:\" on Windows (see 
649          // normalizePath( )), where Sun's JRE 1.4 returns null.
650          return null;
651        else if (last == 0)
652          last++;
653
654        if (prefix != null)
655          return prefix + nameSeq.substring (0, last);
656        else
657          return nameSeq.substring (0, last);
658      }
659    else
660      // Sun's JRE 1.4 returns null if the prefix is the only 
661      // component of the path - so "/" gives null on UNIX and 
662      // "C:", "\\", etc. return null on Windows.
663      return null;
664  }
665
666  /**
667   * This method returns a <code>File</code> object representing the parent
668   * file of this one.
669   *
670   * @return a <code>File</code> for the parent of this object.  
671   * <code>null</code>
672   * will be returned if this object does not have a parent.
673   *
674   * @since 1.2
675   */
676  public File getParentFile()
677  {
678    String parent = getParent();
679    return parent != null ? new File(parent) : null;
680  }
681
682  /**
683   * Returns the path name that represents this file.  May be a relative
684   * or an absolute path name
685   *
686   * @return The pathname of this file
687   */
688  public String getPath()
689  {
690    return path;
691  }
692
693  /**
694   * This method returns a hash code representing this file.  It is the
695   * hash code of the path of this file (as returned by <code>getPath()</code>)
696   * exclusived or-ed with the value 1234321.
697   *
698   * @return The hash code for this object
699   */
700  public int hashCode()
701  {
702    if (caseSensitive)
703      return path.hashCode() ^ 1234321;
704    else
705      return path.toLowerCase().hashCode() ^ 1234321;
706  }
707
708  /**
709   * This method returns true if this object represents an absolute file
710   * path and false if it does not.  The definition of an absolute path varies
711   * by system.  As an example, on GNU systems, a path is absolute if it starts
712   * with a "/".
713   *
714   * @return <code>true</code> if this object represents an absolute 
715   * file name, <code>false</code> otherwise.
716   */
717  public native boolean isAbsolute();
718
719  /*
720   * This method tests whether or not the file represented by this
721   * object is a directory.
722   */
723  private boolean internalIsDirectory()
724  {
725    return stat (DIRECTORY);
726  }
727  
728  /**
729   * This method tests whether or not the file represented by this object
730   * is a directory.  In order for this method to return <code>true</code>,
731   * the file represented by this object must exist and be a directory.
732   * 
733   * @return <code>true</code> if this file is a directory, <code>false</code>
734   * otherwise
735   *
736   * @exception SecurityException If reading of the file is not permitted
737   */
738  public boolean isDirectory()
739  {
740    checkRead();
741    return internalIsDirectory();
742  }
743
744  /**
745   * This method tests whether or not the file represented by this object
746   * is a "plain" file.  A file is a plain file if and only if it 1) Exists,
747   * 2) Is not a directory or other type of special file.
748   *
749   * @return <code>true</code> if this is a plain file, <code>false</code> 
750   * otherwise
751   *
752   * @exception SecurityException If reading of the file is not permitted
753   */
754  public boolean isFile()
755  {
756    checkRead();
757    return stat (ISFILE);
758  }
759
760  /**
761   * This method tests whether or not this file represents a "hidden" file.
762   * On GNU systems, a file is hidden if its name begins with a "."
763   * character.  Files with these names are traditionally not shown with
764   * directory listing tools.
765   *
766   * @return <code>true</code> if the file is hidden, <code>false</code>
767   * otherwise.
768   *
769   * @since 1.2
770   */
771  public boolean isHidden()
772  {
773    checkRead();
774    return stat (ISHIDDEN);
775  }
776
777  /**
778   * This method returns the last modification time of this file.  The
779   * time value returned is an abstract value that should not be interpreted
780   * as a specified time value.  It is only useful for comparing to other
781   * such time values returned on the same system.  In that case, the larger
782   * value indicates a more recent modification time. 
783   * <p>
784   * If the file does not exist, then a value of 0 is returned.
785   *
786   * @return The last modification time of the file
787   *
788   * @exception SecurityException If reading of the file is not permitted
789   */
790  public long lastModified()
791  {
792    checkRead();
793    return attr (MODIFIED);
794  }
795
796  /**
797   * This method returns the length of the file represented by this object,
798   * or 0 if the specified file does not exist.
799   *
800   * @return The length of the file
801   *
802   * @exception SecurityException If reading of the file is not permitted
803   */
804  public long length()
805  {
806    checkRead();
807    return attr (LENGTH);
808  }
809
810  /*
811   * This native function actually produces the list of file in this
812   * directory
813   */
814  private final native Object[] performList (FilenameFilter filter,
815                                             FileFilter fileFilter,
816                                             Class result_type);
817
818  /**
819   * This method returns a array of <code>String</code>'s representing the
820   * list of files is then directory represented by this object.  If this
821   * object represents a non-directory file or a non-existent file, then
822   * <code>null</code> is returned.  The list of files will not contain
823   * any names such as "." or ".." which indicate the current or parent
824   * directory.  Also, the names are not guaranteed to be sorted.
825   * <p>
826   * In this form of the <code>list()</code> method, a filter is specified
827   * that allows the caller to control which files are returned in the
828   * list.  The <code>FilenameFilter</code> specified is called for each
829   * file returned to determine whether or not that file should be included
830   * in the list.
831   * <p>
832   * A <code>SecurityManager</code> check is made prior to reading the
833   * directory.  If read access to the directory is denied, an exception
834   * will be thrown.
835   *
836   * @param filter An object which will identify files to exclude from 
837   * the directory listing.
838   *
839   * @return An array of files in the directory, or <code>null</code> 
840   * if this object does not represent a valid directory.
841   * 
842   * @exception SecurityException If read access is not allowed to the 
843   * directory by the <code>SecurityManager</code>
844   */
845  public String[] list(FilenameFilter filter)
846  {
847    checkRead();
848    return (String[]) performList (filter, null, String.class);
849  }
850
851  /**
852   * This method returns a array of <code>String</code>'s representing the
853   * list of files is then directory represented by this object.  If this
854   * object represents a non-directory file or a non-existent file, then
855   * <code>null</code> is returned.  The list of files will not contain
856   * any names such as "." or ".." which indicate the current or parent
857   * directory.  Also, the names are not guaranteed to be sorted.
858   * <p>
859   * A <code>SecurityManager</code> check is made prior to reading the
860   * directory.  If read access to the directory is denied, an exception
861   * will be thrown.
862   *
863   * @return An array of files in the directory, or <code>null</code> if 
864   * this object does not represent a valid directory.
865   * 
866   * @exception SecurityException If read access is not allowed to the 
867   * directory by the <code>SecurityManager</code>
868   */
869  public String[] list()
870  {
871    checkRead();
872    return (String[]) performList (null, null, String.class);
873  }
874
875  /**
876   * This method returns an array of <code>File</code> objects representing
877   * all the files in the directory represented by this object. If this
878   * object does not represent a directory, <code>null</code> is returned.
879   * Each of the returned <code>File</code> object is constructed with this
880   * object as its parent.
881   * <p>
882   * A <code>SecurityManager</code> check is made prior to reading the
883   * directory.  If read access to the directory is denied, an exception
884   * will be thrown.
885   *
886   * @return An array of <code>File</code> objects for this directory.
887   *
888   * @exception SecurityException If the <code>SecurityManager</code> denies
889   * access to this directory.
890   *
891   * @since 1.2
892   */
893  public File[] listFiles()
894  {
895    checkRead();
896    return (File[]) performList (null, null, File.class);
897  }
898  
899  /**
900   * This method returns an array of <code>File</code> objects representing
901   * all the files in the directory represented by this object. If this
902   * object does not represent a directory, <code>null</code> is returned.
903   * Each of the returned <code>File</code> object is constructed with this
904   * object as its parent.
905   * <p> 
906   * In this form of the <code>listFiles()</code> method, a filter is specified
907   * that allows the caller to control which files are returned in the
908   * list.  The <code>FilenameFilter</code> specified is called for each
909   * file returned to determine whether or not that file should be included
910   * in the list.
911   * <p>
912   * A <code>SecurityManager</code> check is made prior to reading the
913   * directory.  If read access to the directory is denied, an exception
914   * will be thrown.
915   *
916   * @return An array of <code>File</code> objects for this directory.
917   *
918   * @exception SecurityException If the <code>SecurityManager</code> denies
919   * access to this directory.
920   *
921   * @since 1.2
922   */
923  public File[] listFiles(FilenameFilter filter)
924  {
925    checkRead();
926    return (File[]) performList (filter, null, File.class);
927  }
928
929  /**
930   * This method returns an array of <code>File</code> objects representing
931   * all the files in the directory represented by this object. If this
932   * object does not represent a directory, <code>null</code> is returned.
933   * Each of the returned <code>File</code> object is constructed with this
934   * object as its parent.
935   * <p> 
936   * In this form of the <code>listFiles()</code> method, a filter is specified
937   * that allows the caller to control which files are returned in the
938   * list.  The <code>FileFilter</code> specified is called for each
939   * file returned to determine whether or not that file should be included
940   * in the list.
941   * <p>
942   * A <code>SecurityManager</code> check is made prior to reading the
943   * directory.  If read access to the directory is denied, an exception
944   * will be thrown.
945   *
946   * @return An array of <code>File</code> objects for this directory.
947   *
948   * @exception SecurityException If the <code>SecurityManager</code> denies
949   * access to this directory.
950   *
951   * @since 1.2
952   */
953  public File[] listFiles(FileFilter filter)
954  {
955    checkRead();
956    return (File[]) performList (null, filter, File.class);
957  }
958
959  /**
960   * This method returns a <code>String</code> that is the path name of the
961   * file as returned by <code>getPath</code>.
962   *
963   * @return A <code>String</code> representation of this file
964   */
965  public String toString()
966  {
967    return path;
968  }
969
970  /**
971   * @return A <code>URI</code> for this object.
972   */
973  public URI toURI()
974  {
975    String abspath = getAbsolutePath();
976
977    if (isDirectory())
978      abspath = abspath + separator;
979        
980    try
981      {
982        return new URI("file", abspath.replace(separatorChar, '/'), null);
983      }
984    catch (URISyntaxException use)
985      {
986        // Can't happen.
987        throw new RuntimeException(use);
988      }
989  }
990
991  /**
992   * This method returns a <code>URL</code> with the <code>file:</code>
993   * protocol that represents this file.  The exact form of this URL is
994   * system dependent.
995   *
996   * @return A <code>URL</code> for this object.
997   *
998   * @exception MalformedURLException If the URL cannot be created 
999   * successfully.
1000   */
1001  public URL toURL() throws MalformedURLException
1002  {
1003    // On Win32, Sun's JDK returns URLs of the form "file:/c:/foo/bar.txt",
1004    // while on UNIX, it returns URLs of the form "file:/foo/bar.txt". 
1005    if (separatorChar == '\\')
1006      return new URL ("file:/" + getAbsolutePath().replace ('\\', '/')
1007                      + (isDirectory() ? "/" : ""));
1008    else
1009      return new URL ("file:" + getAbsolutePath()
1010                      + (isDirectory() ? "/" : ""));
1011  }
1012
1013  /*
1014   * This native method actually creates the directory
1015   */
1016  private final native boolean performMkdir();
1017
1018  /**
1019   * This method creates a directory for the path represented by this object.
1020   *
1021   * @return <code>true</code> if the directory was created, 
1022   * <code>false</code> otherwise
1023   *
1024   * @exception SecurityException If write access is not allowed to this file
1025   */
1026  public boolean mkdir()
1027  {
1028    checkWrite();
1029    return performMkdir();
1030  }
1031
1032  private static boolean mkdirs (File x)
1033  {
1034    if (x.isDirectory())
1035      return true;
1036    String p = x.getPath();
1037    String parent = x.getParent();
1038    if (parent != null)
1039      {
1040        x.path = parent;
1041        if (! mkdirs (x))
1042          return false;
1043        x.path = p;
1044      }
1045    return x.mkdir();
1046  }
1047
1048  /**
1049   * This method creates a directory for the path represented by this file.
1050   * It will also create any intervening parent directories if necessary.
1051   *
1052   * @return <code>true</code> if the directory was created, 
1053   * <code>false</code> otherwise
1054   *
1055   * @exception SecurityException If write access is not allowed to this file
1056   */
1057  public boolean mkdirs()
1058  {
1059    checkWrite();
1060    if (isDirectory())
1061      return false;
1062    return mkdirs (new File (path));
1063  }
1064
1065  private static synchronized String nextValue()
1066  {
1067    return Long.toString(counter++, Character.MAX_RADIX);
1068  }
1069
1070  /**
1071   * This method creates a temporary file in the specified directory.  If 
1072   * the directory name is null, then this method uses the system temporary 
1073   * directory. The files created are guaranteed not to currently exist and 
1074   * the same file name will never be used twice in the same virtual 
1075   * machine instance.  
1076   * The system temporary directory is determined by examinging the 
1077   * <code>java.io.tmpdir</code> system property.
1078   * <p>
1079   * The <code>prefix</code> parameter is a sequence of at least three
1080   * characters that are used as the start of the generated filename.  The
1081   * <code>suffix</code> parameter is a sequence of characters that is used
1082   * to terminate the file name.  This parameter may be <code>null</code>
1083   * and if it is, the suffix defaults to ".tmp".
1084   * <p>
1085   * If a <code>SecurityManager</code> exists, then its <code>checkWrite</code>
1086   * method is used to verify that this operation is permitted.
1087   *
1088   * @param prefix The character prefix to use in generating the path name.
1089   * @param suffix The character suffix to use in generating the path name.
1090   * @param directory The directory to create the file in, or 
1091   * <code>null</code> for the default temporary directory
1092   *
1093   * @exception IllegalArgumentException If the patterns is not valid
1094   * @exception SecurityException If there is no permission to perform 
1095   * this operation
1096   * @exception IOException If an error occurs
1097   *
1098   * @since 1.2
1099   */
1100  public static File createTempFile(String prefix, String suffix,
1101                                    File directory)
1102    throws IOException
1103  {
1104    // Grab the system temp directory if necessary
1105    if (directory == null)
1106      {
1107        String dirname = tmpdir;
1108        if (dirname == null)
1109          throw new IOException("Cannot determine system temporary directory"); 
1110        
1111        directory = new File(dirname);
1112        if (!directory.internalExists())
1113          throw new IOException("System temporary directory "
1114                                + directory.getName() + " does not exist.");
1115        if (!directory.internalIsDirectory())
1116          throw new IOException("System temporary directory "
1117                                + directory.getName()
1118                                + " is not really a directory.");
1119      }
1120
1121    // Check if prefix is at least 3 characters long
1122    if (prefix.length() < 3)
1123      throw new IllegalArgumentException("Prefix too short: " + prefix);
1124
1125    // Set default value of suffix
1126    if (suffix == null)
1127      suffix = ".tmp";
1128
1129    // Truncation rules.
1130    // `6' is the number of characters we generate.
1131    // If maxPathLen equals zero, then the system doesn't have a limit
1132    // on the file name, so there is nothing to truncate.
1133    if (maxPathLen > 0 && prefix.length() + 6 + suffix.length() > maxPathLen)
1134      {
1135        int suf_len = 0;
1136        if (suffix.charAt(0) == '.')
1137          suf_len = 4;
1138        suffix = suffix.substring(0, suf_len);
1139        if (prefix.length() + 6 + suf_len > maxPathLen)
1140          prefix = prefix.substring(0, maxPathLen - 6 - suf_len);
1141      }
1142
1143    File f;
1144
1145    // How many times should we try?  We choose 100.
1146    for (int i = 0; i < 100; ++i)
1147      {
1148        // This is ugly.
1149        String t = "ZZZZZZ" + nextValue();
1150        String l = prefix + t.substring(t.length() - 6) + suffix;
1151        try
1152          {
1153            f = new File(directory, l);
1154            if (f.createNewFile())
1155              return f;
1156          }
1157        catch (IOException ignored)
1158          {
1159          }
1160      }
1161
1162    throw new IOException ("cannot create temporary file");
1163  }
1164
1165  /*
1166   * This native method sets file permissions.
1167   */
1168  private native boolean setFilePermissions(boolean enable, boolean ownerOnly,
1169                                            int permissions);
1170
1171  /**
1172   * This method sets the owner's read permission for the File represented by
1173   * this object.
1174   * 
1175   * It is the same as calling <code>setReadable(readable, true)</code>.
1176   * 
1177   * @param <code>readable</code> <code>true</code> to set read permission,
1178   * <code>false</code> to unset the read permission.
1179   * @return <code>true</code> if the file permissions are changed,
1180   * <code>false</code> otherwise.
1181   * @exception SecurityException If write access of the file is not permitted.
1182   * @see #setReadable(boolean, boolean)
1183   * @since 1.6
1184   */
1185  public boolean setReadable(boolean readable)
1186  {
1187    return setReadable(readable, true);
1188  }
1189  
1190  /**
1191   * This method sets the read permissions for the File represented by
1192   * this object.
1193   * 
1194   * If <code>ownerOnly</code> is set to <code>true</code> then only the
1195   * read permission bit for the owner of the file is changed.
1196   * 
1197   * If <code>ownerOnly</code> is set to <code>false</code>, the file
1198   * permissions are changed so that the file can be read by everyone.
1199   * 
1200   * On unix like systems this sets the <code>user</code>, <code>group</code>
1201   * and <code>other</code> read bits and is equal to call
1202   * <code>chmod a+r</code> on the file.
1203   * 
1204   * @param <code>readable</code> <code>true</code> to set read permission,
1205   * <code>false</code> to unset the read permission.
1206   * @param <code>ownerOnly</code> <code>true</code> to set read permission
1207   * for owner only, <code>false</code> for all.
1208   * @return <code>true</code> if the file permissions are changed,
1209   * <code>false</code> otherwise.
1210   * @exception SecurityException If write access of the file is not permitted.
1211   * @see #setReadable(boolean)
1212   * @since 1.6
1213   */
1214  public boolean setReadable(boolean readable, boolean ownerOnly)
1215  {
1216    checkWrite();
1217    return setFilePermissions(readable, ownerOnly, READ);
1218  }
1219  
1220  /**
1221   * This method sets the owner's write permission for the File represented by
1222   * this object.
1223   * 
1224   * It is the same as calling <code>setWritable(readable, true)</code>. 
1225   * 
1226   * @param <code>writable</code> <code>true</code> to set write permission,
1227   * <code>false</code> to unset write permission.
1228   * @return <code>true</code> if the file permissions are changed,
1229   * <code>false</code> otherwise.
1230   * @exception SecurityException If write access of the file is not permitted.
1231   * @see #setWritable(boolean, boolean)
1232   * @since 1.6
1233   */
1234  public boolean setWritable(boolean writable)
1235  {
1236    return setWritable(writable, true);
1237  }
1238  
1239  /**
1240   * This method sets the write permissions for the File represented by
1241   * this object.
1242   * 
1243   * If <code>ownerOnly</code> is set to <code>true</code> then only the
1244   * write permission bit for the owner of the file is changed.
1245   * 
1246   * If <code>ownerOnly</code> is set to <code>false</code>, the file
1247   * permissions are changed so that the file can be written by everyone.
1248   * 
1249   * On unix like systems this set the <code>user</code>, <code>group</code>
1250   * and <code>other</code> write bits and is equal to call
1251   * <code>chmod a+w</code> on the file.
1252   * 
1253   * @param <code>writable</code> <code>true</code> to set write permission,
1254   * <code>false</code> to unset write permission.
1255   * @param <code>ownerOnly</code> <code>true</code> to set write permission
1256   * for owner only, <code>false</code> for all. 
1257   * @return <code>true</code> if the file permissions are changed,
1258   * <code>false</code> otherwise.
1259   * @exception SecurityException If write access of the file is not permitted.
1260   * @see #setWritable(boolean)
1261   * @since 1.6
1262   */
1263  public boolean setWritable(boolean writable, boolean ownerOnly)
1264  {
1265    checkWrite();
1266    return setFilePermissions(writable, ownerOnly, WRITE);
1267  }
1268  
1269  /**
1270   * This method sets the owner's execute permission for the File represented
1271   * by this object.
1272   * 
1273   * It is the same as calling <code>setExecutable(readable, true)</code>. 
1274   * 
1275   * @param <code>executable</code> <code>true</code> to set execute permission,
1276   * <code>false</code> to unset execute permission.
1277   * @return <code>true</code> if the file permissions are changed,
1278   * <code>false</code> otherwise.
1279   * @exception SecurityException If write access of the file is not permitted.
1280   * @see #setExecutable(boolean, boolean)
1281   * @since 1.6
1282   */
1283  public boolean setExecutable(boolean executable) 
1284  {
1285    return setExecutable(executable, true);
1286  }
1287  
1288  /**
1289   * This method sets the execute permissions for the File represented by
1290   * this object.
1291   * 
1292   * If <code>ownerOnly</code> is set to <code>true</code> then only the
1293   * execute permission bit for the owner of the file is changed.
1294   * 
1295   * If <code>ownerOnly</code> is set to <code>false</code>, the file
1296   * permissions are changed so that the file can be executed by everyone.
1297   * 
1298   * On unix like systems this set the <code>user</code>, <code>group</code>
1299   * and <code>other</code> write bits and is equal to call
1300   * <code>chmod a+x</code> on the file.
1301   * 
1302   * @param <code>executable</code> <code>true</code> to set write permission,
1303   * <code>false</code> to unset write permission.
1304   * @param <code>ownerOnly</code> <code>true</code> to set write permission
1305   * for owner only, <code>false</code> for all. 
1306   * @return <code>true</code> if the file permissions are changed,
1307   * <code>false</code> otherwise.
1308   * @exception SecurityException If write access of the file is not permitted.
1309   * @see #setExecutable(boolean)
1310   * @since 1.6
1311   */
1312  public boolean setExecutable(boolean executable, boolean ownerOnly)
1313  {
1314    checkWrite();
1315    return setFilePermissions(executable, ownerOnly, EXEC);
1316  }
1317
1318  /*
1319   * This native method sets the permissions to make the file read only.
1320   */
1321  private native boolean performSetReadOnly();
1322
1323  /**
1324   * This method sets the file represented by this object to be read only.
1325   * A read only file or directory cannot be modified.  Please note that 
1326   * GNU systems allow read only files to be deleted if the directory it
1327   * is contained in is writable.
1328   *
1329   * @return <code>true</code> if the operation succeeded, <code>false</code>
1330   * otherwise.
1331   *
1332   * @exception SecurityException If the <code>SecurityManager</code> does
1333   * not allow this operation.
1334   *
1335   * @since 1.2
1336   */
1337  public boolean setReadOnly()
1338  {
1339    // Do a security check before trying to do anything else.
1340    checkWrite();
1341    return performSetReadOnly();
1342  }
1343
1344  private static native File[] performListRoots();
1345
1346  /**
1347   * This method returns an array of filesystem roots.  Some operating systems
1348   * have volume oriented filesystem.  This method provides a mechanism for
1349   * determining which volumes exist.  GNU systems use a single hierarchical
1350   * filesystem, so will have only one "/" filesystem root.
1351   *
1352   * @return An array of <code>File</code> objects for each filesystem root
1353   * available.
1354   *
1355   * @since 1.2
1356   */
1357  public static File[] listRoots()
1358  {
1359    File[] roots = performListRoots();
1360    
1361    SecurityManager s = System.getSecurityManager();
1362    if (s != null)
1363      {
1364        // Only return roots to which the security manager permits read access.
1365        int count = roots.length;
1366        for (int i = 0; i < roots.length; i++)
1367          {
1368            try
1369              {
1370                s.checkRead (roots[i].path);            
1371              }
1372            catch (SecurityException sx)
1373              {
1374                roots[i] = null;
1375                count--;
1376              }
1377          }
1378        if (count != roots.length)
1379          {
1380            File[] newRoots = new File[count];
1381            int k = 0;
1382            for (int i=0; i < roots.length; i++)
1383              {
1384                if (roots[i] != null)
1385                  newRoots[k++] = roots[i];
1386              }
1387            roots = newRoots;
1388          }
1389      }
1390    return roots;
1391  }
1392
1393  /**
1394   * This method creates a temporary file in the system temporary directory. 
1395   * The files created are guaranteed not to currently exist and the same file
1396   * name will never be used twice in the same virtual machine instance.  The
1397   * system temporary directory is determined by examinging the 
1398   * <code>java.io.tmpdir</code> system property.
1399   * <p>
1400   * The <code>prefix</code> parameter is a sequence of at least three
1401   * characters that are used as the start of the generated filename.  The
1402   * <code>suffix</code> parameter is a sequence of characters that is used
1403   * to terminate the file name.  This parameter may be <code>null</code>
1404   * and if it is, the suffix defaults to ".tmp".
1405   * <p>
1406   * If a <code>SecurityManager</code> exists, then its <code>checkWrite</code>
1407   * method is used to verify that this operation is permitted.
1408   * <p>
1409   * This method is identical to calling 
1410   * <code>createTempFile(prefix, suffix, null)</code>.
1411   *
1412   * @param prefix The character prefix to use in generating the path name.
1413   * @param suffix The character suffix to use in generating the path name.
1414   *
1415   * @exception IllegalArgumentException If the prefix or suffix are not valid.
1416   * @exception SecurityException If there is no permission to perform 
1417   * this operation
1418   * @exception IOException If an error occurs
1419   */
1420  public static File createTempFile(String prefix, String suffix)
1421    throws IOException
1422  {
1423    return createTempFile(prefix, suffix, null);
1424  }
1425
1426  /**
1427   * This method compares the specified <code>File</code> to this one
1428   * to test for equality.  It does this by comparing the canonical path names
1429   * of the files. 
1430   * <p>
1431   * The canonical paths of the files are determined by calling the
1432   * <code>getCanonicalPath</code> method on each object.
1433   * <p>
1434   * This method returns a 0 if the specified <code>Object</code> is equal
1435   * to this one, a negative value if it is less than this one 
1436   * a positive value if it is greater than this one.
1437   *
1438   * @return An integer as described above
1439   *
1440   * @since 1.2
1441   */
1442  public int compareTo(File other)
1443  {
1444    if (caseSensitive)
1445      return path.compareTo (other.path);
1446    else
1447      return path.compareToIgnoreCase (other.path);
1448  }
1449
1450  /*
1451   * This native method actually performs the rename.
1452   */
1453  private native boolean performRenameTo (File dest);
1454
1455  /**
1456   * This method renames the file represented by this object to the path
1457   * of the file represented by the argument <code>File</code>.
1458   *
1459   * @param dest The <code>File</code> object representing the target name
1460   *
1461   * @return <code>true</code> if the rename succeeds, <code>false</code> 
1462   * otherwise.
1463   *
1464   * @exception SecurityException If write access is not allowed to the 
1465   * file by the <code>SecurityMananger</code>.
1466   */
1467  public synchronized boolean renameTo(File dest)
1468  {
1469    SecurityManager s = System.getSecurityManager();
1470    if (s != null)
1471      {
1472        s.checkWrite (getPath());
1473        s.checkWrite (dest.getPath());
1474      }
1475    return performRenameTo (dest);
1476  }
1477
1478  /*
1479   * This method does the actual setting of the modification time.
1480   */
1481  private native boolean performSetLastModified(long time);
1482 
1483  /**
1484   * This method sets the modification time on the file to the specified
1485   * value.  This is specified as the number of seconds since midnight
1486   * on January 1, 1970 GMT.
1487   *
1488   * @param time The desired modification time.
1489   *
1490   * @return <code>true</code> if the operation succeeded, <code>false</code>
1491   * otherwise.
1492   *
1493   * @exception IllegalArgumentException If the specified time is negative.
1494   * @exception SecurityException If the <code>SecurityManager</code> will
1495   * not allow this operation.
1496   *
1497   * @since 1.2
1498   */
1499  public boolean setLastModified(long time) 
1500  {
1501    if (time < 0)
1502      throw new IllegalArgumentException("Negative modification time: " + time);
1503
1504    checkWrite();
1505    return performSetLastModified(time);
1506  }
1507
1508  private void checkWrite()
1509  {
1510    // Check the SecurityManager
1511    SecurityManager s = System.getSecurityManager();
1512    
1513    if (s != null)
1514      s.checkWrite(path);
1515  }
1516
1517  private void checkRead()
1518  {
1519    // Check the SecurityManager
1520    SecurityManager s = System.getSecurityManager();
1521    
1522    if (s != null)
1523      s.checkRead(path);
1524  }
1525
1526  private void checkExec()
1527  {
1528    // Check the SecurityManager
1529    SecurityManager s = System.getSecurityManager();
1530    
1531    if (s != null)
1532      s.checkExec(path);
1533  }
1534
1535  /** 
1536   * Calling this method requests that the file represented by this object
1537   * be deleted when the virtual machine exits.  Note that this request cannot
1538   * be cancelled.  Also, it will only be carried out if the virtual machine
1539   * exits normally.
1540   *
1541   * @exception SecurityException If deleting of the file is not allowed
1542   *
1543   * @since 1.2 
1544   */
1545  // FIXME: This should use the ShutdownHook API once we implement that.
1546  public void deleteOnExit()
1547  {
1548    // Check the SecurityManager
1549    SecurityManager sm = System.getSecurityManager();
1550    if (sm != null)
1551      sm.checkDelete (getPath());
1552
1553    DeleteFileHelper.add(this);
1554  }
1555
1556  private void writeObject(ObjectOutputStream oos) throws IOException
1557  {
1558    oos.defaultWriteObject();
1559    oos.writeChar(separatorChar);
1560  }
1561
1562  private void readObject(ObjectInputStream ois)
1563    throws ClassNotFoundException, IOException
1564  {
1565    ois.defaultReadObject();
1566
1567    // If the file was from an OS with a different dir separator,
1568    // fixup the path to use the separator on this OS.
1569    char oldSeparatorChar = ois.readChar();
1570    
1571    if (oldSeparatorChar != separatorChar)
1572      path = path.replace(oldSeparatorChar, separatorChar);
1573  }
1574  
1575} // class File
1576