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