OSDN Git Service

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