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    
005    This file is part of GNU Classpath.
006    
007    GNU Classpath is free software; you can redistribute it and/or modify
008    it under the terms of the GNU General Public License as published by
009    the Free Software Foundation; either version 2, or (at your option)
010    any later version.
011     
012    GNU Classpath is distributed in the hope that it will be useful, but
013    WITHOUT ANY WARRANTY; without even the implied warranty of
014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015    General Public License for more details.
016    
017    You should have received a copy of the GNU General Public License
018    along with GNU Classpath; see the file COPYING.  If not, write to the
019    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
020    02110-1301 USA.
021    
022    Linking this library statically or dynamically with other modules is
023    making a combined work based on this library.  Thus, the terms and
024    conditions of the GNU General Public License cover the whole
025    combination.
026    
027    As a special exception, the copyright holders of this library give you
028    permission to link this library with independent modules to produce an
029    executable, regardless of the license terms of these independent
030    modules, and to copy and distribute the resulting executable under
031    terms of your choice, provided that you also meet, for each linked
032    independent module, the terms and conditions of the license of that
033    module.  An independent module is a module which is not derived from
034    or based on this library.  If you modify this library, you may extend
035    this exception to your version of the library, but you are not
036    obligated to do so.  If you do not wish to do so, delete this
037    exception statement from your version. */
038    
039    
040    package java.io;
041    
042    import java.net.MalformedURLException;
043    import java.net.URI;
044    import java.net.URISyntaxException;
045    import java.net.URL;
046    import 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     */
062    public 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