OSDN Git Service

2003-03-24 Michael Koch <konqueror@gmx.de>
[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 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10  
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package java.io;
40
41 import java.util.*;
42 import java.net.*;
43 import gnu.gcj.runtime.FileDeleter;
44
45 /**
46  * @author Tom Tromey <tromey@cygnus.com>
47  * @date September 24, 1998 
48  */
49
50 /* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
51  * "The Java Language Specification", ISBN 0-201-63451-1
52  * Status:  Complete to version 1.3.
53  */
54
55 public class File implements Serializable, Comparable
56 {
57   public boolean canRead ()
58   {
59     checkRead();
60     return _access (READ);
61   }
62
63   public boolean canWrite ()
64   {
65     checkWrite();
66     return _access (WRITE);
67   }
68   
69   private native boolean performCreate() throws IOException;
70
71   /** @since 1.2 */
72   public boolean createNewFile() throws IOException
73   {
74     checkWrite();
75     return performCreate();
76   }
77   
78   private native boolean performDelete ();
79   public boolean delete ()
80   {
81     SecurityManager s = System.getSecurityManager();
82     String name = path;
83     if (s != null)
84       s.checkDelete(path);
85     return performDelete ();
86   }
87
88   public boolean equals (Object obj)
89   {
90     if (! (obj instanceof File))
91       return false;
92     File other = (File) obj;
93     if (caseSensitive)
94       return (path.equals(other.path));
95     else
96       return (path.equalsIgnoreCase(other.path));      
97   }
98
99   public boolean exists ()
100   {
101     checkRead();
102     return _access (EXISTS);
103   }
104
105   public File (String p)
106   {
107     path = normalizePath(p);
108   }
109
110   // Remove duplicate and redundant separator characters.
111   private String normalizePath(String p)
112   {
113     // On Windows, convert any '/' to '\'.  This appears to be the same logic
114     // that Sun's Win32 Java performs.
115     if (separatorChar == '\\')
116       p = p.replace ('/', '\\');
117
118     int dupIndex = p.indexOf(dupSeparator);
119     int plen = p.length();
120
121     // Special case: permit Windows UNC path prefix.
122     if (dupSeparator.equals("\\\\") && dupIndex == 0)
123       dupIndex = p.indexOf(dupSeparator, 1);
124
125     if (dupIndex == -1)
126       {
127         // Ignore trailing separator (though on Windows "a:\", for
128         // example, is a valid and minimal path).
129         if (plen > 1 && p.charAt (plen - 1) == separatorChar)
130           {
131             if (! (separatorChar == '\\' && plen == 3 && p.charAt (1) == ':'))
132               return p.substring (0, plen - 1);
133           }
134         else
135           return p;
136       }
137     
138     StringBuffer newpath = new StringBuffer(plen);
139     int last = 0;
140     while (dupIndex != -1)
141       {
142         newpath.append(p.substring(last, dupIndex));
143         // Ignore the duplicate path characters.
144         while (p.charAt(dupIndex) == separatorChar)
145           {
146             dupIndex++;
147             if (dupIndex == plen)
148               return newpath.toString();
149           }
150         newpath.append(separatorChar);
151         last = dupIndex;
152         dupIndex = p.indexOf(dupSeparator, last);
153       }
154     
155     // Again, ignore possible trailing separator (except special cases
156     // like "a:\" on Windows).
157     int end;
158     if (plen > 1 && p.charAt (plen - 1) == separatorChar)
159     {
160       if (separatorChar == '\\' && plen == 3 && p.charAt (1) == ':')
161         end = plen;
162       else
163         end = plen - 1;
164     }
165     else
166       end = plen;
167     newpath.append(p.substring(last, end));
168     
169     return newpath.toString();
170   }
171   
172   public File (String dirPath, String name)
173   {
174     if (name == null)
175       throw new NullPointerException ();
176     if (dirPath != null && dirPath.length() > 0)
177       {
178         // Try to be smart about the number of separator characters.
179         if (dirPath.charAt(dirPath.length() - 1) == separatorChar
180             || name.length() == 0)
181           path = normalizePath(dirPath + name);
182         else
183           path = normalizePath(dirPath + separatorChar + name);
184       }
185     else
186       path = normalizePath(name);
187   }
188
189   public File (File dir, String name)
190   {
191     this (dir == null ? null : dir.path, name);
192   }
193
194   public String getAbsolutePath ()
195   {
196     if (isAbsolute ())
197       return path;
198     else if (separatorChar == '\\' 
199              && path.length () > 0 && path.charAt (0) == '\\')
200       {
201         // On Windows, even if the path starts with a '\\' it is not
202         // really absolute until we prefix the drive specifier from
203         // the current working directory to it.
204         return System.getProperty ("user.dir").substring (0, 2) + path;
205       }
206     else
207       return System.getProperty ("user.dir") + separatorChar + path;
208   }
209
210   /** @since 1.2 */
211   public File getAbsoluteFile ()
212   {
213     return new File (getAbsolutePath());
214   }
215
216   public native String getCanonicalPath () throws IOException;
217
218   /** @since 1.2 */
219   public File getCanonicalFile () throws IOException
220   {
221     return new File (getCanonicalPath());
222   }
223
224   public String getName ()
225   {
226     int last = path.lastIndexOf(separatorChar);
227     return path.substring(last + 1);
228   }
229
230   public String getParent ()
231   {
232     int last = path.lastIndexOf(separatorChar);
233     if (last == -1)
234       return null;
235     // FIXME: POSIX assumption.
236     if (last == 0 && path.charAt (0) == '/')
237       ++last;
238     return path.substring(0, last);
239   }
240
241   /** @since 1.2 */
242   public File getParentFile ()
243   {
244     String parent = getParent ();
245     return (parent == null ? null : new File (parent));
246   }
247
248   public String getPath ()
249   {
250     return path;
251   }
252
253   public int hashCode ()
254   {
255     if (caseSensitive)
256       return (path.hashCode() ^ 1234321);
257     else
258       return (path.toLowerCase().hashCode() ^ 1234321);
259   }
260
261   public native boolean isAbsolute ();
262
263   public boolean isDirectory ()
264   {
265     checkRead();
266     return _stat (DIRECTORY);
267   }
268
269   public boolean isFile ()
270   {
271     checkRead();
272     return _stat (ISFILE);
273   }
274
275   /** @since 1.2 */
276   public boolean isHidden()
277   {
278     checkRead();
279     return _stat (ISHIDDEN);
280   }
281
282   public long lastModified ()
283   {
284     checkRead();
285     return attr (MODIFIED);
286   }
287
288   public long length ()
289   {
290     checkRead();
291     return attr (LENGTH);
292   }
293     
294   private final native Object[] performList (FilenameFilter filter,
295                                              FileFilter fileFilter,
296                                              Class result_type);
297
298   public String[] list (FilenameFilter filter)
299   {
300     checkRead();
301     return (String[]) performList (filter, null, String.class);
302   }
303
304   public String[] list ()
305   {
306     checkRead();
307     return (String[]) performList (null, null, String.class);
308   }
309
310   /** @since 1.2 */
311   public File[] listFiles()
312   {
313     checkRead();
314     return (File[]) performList (null, null, File.class);
315   }
316   
317   /** @since 1.2 */
318   public File[] listFiles(FilenameFilter filter)
319   {
320     checkRead();
321     return (File[]) performList (filter, null, File.class);
322   }
323   
324   /** @since 1.2 */
325   public File[] listFiles(FileFilter filter)
326   {
327     checkRead();
328     return (File[]) performList (null, filter, File.class);
329   }
330
331   public String toString ()
332   {
333     return path;
334   }
335
336   public URL toURL () throws MalformedURLException
337   {
338     // On Win32, Sun's JDK returns URLs of the form "file:/c:/foo/bar.txt",
339     // while on UNIX, it returns URLs of the form "file:/foo/bar.txt". 
340     if (separatorChar == '\\')
341       return new URL ("file:/" + getAbsolutePath ().replace ('\\', '/')
342                       + (isDirectory() ? "/" : ""));
343     else
344       return new URL ("file:" + getAbsolutePath ()
345                       + (isDirectory() ? "/" : ""));
346   }
347
348   private final native boolean performMkdir ();
349
350   public boolean mkdir ()
351   {
352     checkWrite();
353     return performMkdir ();
354   }
355
356   private static boolean mkdirs (File x)
357   {
358     if (x.isDirectory())
359       return true;
360     String p = x.getPath();
361     String parent = x.getParent();
362     if (parent != null)
363       {
364         x.path = parent;
365         if (! mkdirs (x))
366           return false;
367         x.path = p;
368       }
369     return x.mkdir();
370   }
371
372   public boolean mkdirs ()
373   {
374     checkWrite();
375     if (isDirectory ())
376       return false;
377     return mkdirs (new File (path));
378   }
379
380   private static synchronized String nextValue ()
381   {
382     return Long.toString(counter++, Character.MAX_RADIX);
383   }
384
385   /** @since 1.2 */
386   public static File createTempFile (String prefix, String suffix,
387                                      File directory)
388     throws IOException
389   {
390     // Grab the system temp directory if necessary
391     if (directory == null)
392       {
393         String dirname = tmpdir;
394         if (dirname == null)
395           throw 
396             new IOException("Cannot determine system temporary directory"); 
397         
398         directory = new File(dirname);
399         if (!directory.exists())
400           throw new IOException("System temporary directory " 
401                                 + directory.getName() + " does not exist.");
402         if (!directory.isDirectory())
403           throw new IOException("System temporary directory " 
404                                 + directory.getName() 
405                                 + " is not really a directory.");
406       }
407
408     if (prefix.length () < 3)
409       throw new IllegalArgumentException ("Prefix too short: " + prefix);
410     if (suffix == null)
411       suffix = ".tmp";
412
413     // Truncation rules.
414     // `6' is the number of characters we generate.
415     if (prefix.length () + 6 + suffix.length () > maxPathLen)
416       {
417         int suf_len = 0;
418         if (suffix.charAt(0) == '.')
419           suf_len = 4;
420         suffix = suffix.substring(0, suf_len);
421         if (prefix.length () + 6 + suf_len > maxPathLen)
422           prefix = prefix.substring(0, maxPathLen - 6 - suf_len);
423       }
424
425     File f;
426
427     // How many times should we try?  We choose 100.
428     for (int i = 0; i < 100; ++i)
429       {
430         // This is ugly.
431         String t = "ZZZZZZ" + nextValue ();
432         String l = prefix + t.substring(t.length() - 6) + suffix;
433         try
434           {
435             f = new File(directory, l);
436             if (f.createNewFile())
437               return f;
438           }
439         catch (IOException ignored)
440           {
441           }
442       }
443
444     throw new IOException ("cannot create temporary file");
445   }
446
447   private native boolean performSetReadOnly();
448
449   /** @since 1.2 */
450   public boolean setReadOnly()
451   {
452     checkWrite();
453     return performSetReadOnly();
454   }
455
456   private static native File[] performListRoots();
457
458   /** @since 1.2 */
459   public static File[] listRoots()
460   {
461     File[] roots = performListRoots();
462     
463     SecurityManager s = System.getSecurityManager();
464     if (s != null)
465       {
466         // Only return roots to which the security manager permits read access.
467         int count = roots.length;
468         for (int i = 0; i < roots.length; i++)
469           {
470             try
471               {
472                 s.checkRead(roots[i].path);             
473               }
474             catch (SecurityException sx)
475               {
476                 roots[i] = null;
477                 count--;
478               }
479           }
480         if (count != roots.length)
481           {
482             File[] newRoots = new File[count];
483             int k = 0;
484             for (int i=0; i < roots.length; i++)
485               {
486                 if (roots[i] != null)
487                   newRoots[k++] = roots[i];
488               }
489             roots = newRoots;
490           }
491       }
492     return roots;
493   }
494
495   public static File createTempFile (String prefix, String suffix)
496     throws IOException
497   {
498     return createTempFile (prefix, suffix, null);
499   }
500
501   /** @since 1.2 */
502   public int compareTo(File other)
503   {
504     if (caseSensitive)
505       return path.compareTo (other.path);
506     else
507       return path.compareToIgnoreCase (other.path);
508   }
509
510   /** @since 1.2 */
511   public int compareTo(Object o)
512   {
513     File other = (File) o;
514     return compareTo (other);
515   }
516
517   private native boolean performRenameTo (File dest);
518   public boolean renameTo (File dest)
519   {
520     SecurityManager s = System.getSecurityManager();
521     String sname = getName();
522     String dname = dest.getName();
523     if (s != null)
524       {
525         s.checkWrite(sname);
526         s.checkWrite(dname);
527       }
528     return performRenameTo (dest);
529   }
530
531   private native boolean performSetLastModified(long time);
532   
533   /** @since 1.2 */
534   public boolean setLastModified(long time)
535   {
536     checkWrite();
537     return performSetLastModified(time);
538   }
539
540   public static final String pathSeparator
541     = System.getProperty("path.separator");
542   public static final char pathSeparatorChar = pathSeparator.charAt(0);
543   public static final String separator = System.getProperty("file.separator");
544   public static final char separatorChar = separator.charAt(0);
545
546   static final String tmpdir = System.getProperty("java.io.tmpdir");
547   static int maxPathLen;
548   static boolean caseSensitive;
549   static String dupSeparator = separator + separator;
550   
551   static
552   {
553     init_native();
554   }
555   
556   // Native function called at class initialization. This should should
557   // set the maxPathLen and caseSensitive variables.
558   private static native void init_native();
559
560   // The path.
561   private String path;
562
563   // We keep a counter for use by createTempFile.  We choose the first
564   // value randomly to try to avoid clashes with other VMs.
565   private static long counter = Double.doubleToLongBits (Math.random ());
566
567   private void checkWrite ()
568   {
569     SecurityManager s = System.getSecurityManager();
570     if (s != null)
571       s.checkWrite(path);
572   }
573
574   private void checkRead ()
575   {
576     SecurityManager s = System.getSecurityManager();
577     if (s != null)
578       s.checkRead(path);
579   }
580
581   /** 
582     * Add this File to the set of files to be deleted upon normal
583     * termination.
584     *
585     * @since 1.2 
586     */
587   // FIXME: This should use the ShutdownHook API once we implement that.
588   public void deleteOnExit ()
589   {
590     SecurityManager sm = System.getSecurityManager ();
591     if (sm != null)
592       sm.checkDelete (getName ());
593
594     FileDeleter.add (this);
595   }
596
597   private void writeObject (ObjectOutputStream oos) throws IOException
598   {
599     oos.defaultWriteObject ();
600     oos.writeChar (separatorChar);
601   }
602
603   private void readObject (ObjectInputStream ois)
604     throws ClassNotFoundException, IOException
605   {
606     ois.defaultReadObject ();
607
608     // If the file was from an OS with a different dir separator,
609     // fixup the path to use the separator on this OS.
610     char oldSeparatorChar = ois.readChar ();
611     if (oldSeparatorChar != separatorChar)
612       path = path.replace (oldSeparatorChar, separatorChar);
613   }
614
615   // QUERY arguments to access function.
616   private final static int READ = 0;
617   private final static int WRITE = 1;
618   private final static int EXISTS = 2;
619
620   // QUERY arguments to stat function.
621   private final static int DIRECTORY = 0;
622   private final static int ISFILE = 1;
623   private final static int ISHIDDEN = 2;
624
625   // QUERY arguments to attr function.
626   private final static int MODIFIED = 0;
627   private final static int LENGTH = 1;
628   
629   private final native long attr (int query);
630   // On OSF1 V5.0, `stat' is a macro.  It is easiest to use the name
631   // `_stat' instead.  We do the same thing for `_access' just in
632   // case.
633   private final native boolean _access (int query);
634   private final native boolean _stat (int query);
635
636   private static final long serialVersionUID = 301077366599181567L;
637 }