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