Source for java.io.File

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