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