1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18// BEGIN android-note 19// We've dropped Windows support, except where it's exposed: we still support 20// non-Unix separators in serialized File objects, for example, but we don't 21// have any code for UNC paths or case-insensitivity. 22// We've also changed the JNI interface to better match what the Java actually wants. 23// (The JNI implementation is also much simpler.) 24// Some methods have been rewritten to reduce unnecessary allocation. 25// Some duplication has been factored out. 26// END android-note 27 28package java.io; 29 30import java.net.URI; 31import java.net.URISyntaxException; 32import java.net.URL; 33import java.nio.ByteBuffer; 34import java.nio.charset.Charset; 35import java.security.AccessController; 36import java.security.SecureRandom; 37import java.util.ArrayList; 38import java.util.List; 39 40import org.apache.harmony.luni.util.DeleteOnExit; 41import org.apache.harmony.luni.util.Msg; 42import org.apache.harmony.luni.util.PriviAction; 43import org.apache.harmony.luni.util.Util; 44 45/** 46 * An "abstract" representation of a file system entity identified by a 47 * pathname. The pathname may be absolute (relative to the root directory 48 * of the file system) or relative to the current directory in which the program 49 * is running. 50 * <p> 51 * The actual file referenced by a {@code File} may or may not exist. It may 52 * also, despite the name {@code File}, be a directory or other non-regular 53 * file. 54 * <p> 55 * This class provides limited functionality for getting/setting file 56 * permissions, file type, and last modified time. 57 * <p> 58 * Although Java doesn't specify a character encoding for filenames, on Android 59 * Java strings are converted to UTF-8 byte sequences when sending filenames to 60 * the operating system, and byte sequences returned by the operating system 61 * (from the various {@code list} methods) are converted to Java strings by 62 * decoding them as UTF-8 byte sequences. 63 * 64 * @see java.io.Serializable 65 * @see java.lang.Comparable 66 */ 67public class File implements Serializable, Comparable<File> { 68 69 private static final long serialVersionUID = 301077366599181567L; 70 71 private static final String EMPTY_STRING = ""; //$NON-NLS-1$ 72 73 // Caches the UTF-8 Charset for newCString. 74 private static final Charset UTF8 = Charset.forName("UTF-8"); 75 76 /** 77 * The system dependent file separator character. 78 * This field is initialized from the system property "file.separator". 79 * Later changes to that property will have no effect on this field or this class. 80 */ 81 public static final char separatorChar; 82 83 /** 84 * The system dependent file separator string. 85 * This field is a single-character string equal to String.valueOf(separatorChar). 86 */ 87 public static final String separator; 88 89 /** 90 * The system dependent path separator character. 91 * This field is initialized from the system property "path.separator". 92 * Later changes to that property will have no effect on this field or this class. 93 */ 94 public static final char pathSeparatorChar; 95 96 /** 97 * The system dependent path separator string. 98 * This field is a single-character string equal to String.valueOf(pathSeparatorChar). 99 */ 100 public static final String pathSeparator; 101 102 /* Temp file counter */ 103 private static int counter; 104 105 /** 106 * The path we return from getPath. This is almost the path we were 107 * given, but without duplicate adjacent slashes and without trailing 108 * slashes (except for the special case of the root directory). This 109 * path may be the empty string. 110 */ 111 private String path; 112 113 /** 114 * The cached UTF-8 byte sequence corresponding to 'path'. 115 * This is suitable for direct use by our JNI, and includes a trailing NUL. 116 * For non-absolute paths, the "user.dir" property is prepended: that is, 117 * this byte sequence usually represents an absolute path (the exception 118 * being if the user overwrites the "user.dir" property with a non-absolute 119 * path). 120 */ 121 transient byte[] pathBytes; 122 123 static { 124 // The default protection domain grants access to these properties. 125 separatorChar = System.getProperty("file.separator", "/").charAt(0); //$NON-NLS-1$ //$NON-NLS-2$ 126 pathSeparatorChar = System.getProperty("path.separator", ":").charAt(0); //$NON-NLS-1$//$NON-NLS-2$ 127 separator = String.valueOf(separatorChar); 128 pathSeparator = String.valueOf(pathSeparatorChar); 129 } 130 131 /** 132 * Constructs a new file using the specified directory and name. 133 * 134 * @param dir 135 * the directory where the file is stored. 136 * @param name 137 * the file's name. 138 * @throws NullPointerException 139 * if {@code name} is {@code null}. 140 */ 141 public File(File dir, String name) { 142 this(dir == null ? null : dir.getPath(), name); 143 } 144 145 /** 146 * Constructs a new file using the specified path. 147 * 148 * @param path 149 * the path to be used for the file. 150 */ 151 public File(String path) { 152 init(path); 153 } 154 155 /** 156 * Constructs a new File using the specified directory path and file name, 157 * placing a path separator between the two. 158 * 159 * @param dirPath 160 * the path to the directory where the file is stored. 161 * @param name 162 * the file's name. 163 * @throws NullPointerException 164 * if {@code name} is {@code null}. 165 */ 166 public File(String dirPath, String name) { 167 if (name == null) { 168 throw new NullPointerException(); 169 } 170 if (dirPath == null || dirPath.length() == 0) { 171 init(name); 172 } else if (name.length() == 0) { 173 init(dirPath); 174 } else { 175 init(join(dirPath, name)); 176 } 177 } 178 179 /** 180 * Constructs a new File using the path of the specified URI. {@code uri} 181 * needs to be an absolute and hierarchical Unified Resource Identifier with 182 * file scheme and non-empty path component, but with undefined authority, 183 * query or fragment components. 184 * 185 * @param uri 186 * the Unified Resource Identifier that is used to construct this 187 * file. 188 * @throws IllegalArgumentException 189 * if {@code uri} does not comply with the conditions above. 190 * @see #toURI 191 * @see java.net.URI 192 */ 193 public File(URI uri) { 194 // check pre-conditions 195 checkURI(uri); 196 init(uri.getPath()); 197 } 198 199 private void init(String dirtyPath) { 200 // Keep a copy of the cleaned-up string path. 201 this.path = fixSlashes(dirtyPath); 202 // Cache the UTF-8 bytes we need for the JNI. 203 // TODO: we shouldn't do this caching at all; the RI demonstrably doesn't. 204 if (path.length() > 0 && path.charAt(0) == separatorChar) { // http://b/2486943 205 this.pathBytes = newCString(path); 206 return; 207 } 208 String userDir = AccessController.doPrivileged( 209 new PriviAction<String>("user.dir")); //$NON-NLS-1$ 210 this.pathBytes = newCString(path.length() == 0 ? userDir : join(userDir, path)); 211 } 212 213 private byte[] newCString(String s) { 214 ByteBuffer buffer = UTF8.encode(s); 215 // Add a trailing NUL, because this byte[] is going to be used as a char*. 216 int byteCount = buffer.limit() + 1; 217 byte[] bytes = new byte[byteCount]; 218 buffer.get(bytes, 0, byteCount - 1); 219 // This is an awful mistake, because '\' is a perfectly acceptable 220 // character on Linux/Android. But we've shipped so many versions 221 // that behaved like this, I'm too scared to change it. 222 for (int i = 0; i < bytes.length; ++i) { 223 if (bytes[i] == '\\') { 224 bytes[i] = '/'; 225 } 226 } 227 return bytes; 228 } 229 230 // Removes duplicate adjacent slashes and any trailing slash. 231 private String fixSlashes(String origPath) { 232 // Remove duplicate adjacent slashes. 233 boolean lastWasSlash = false; 234 char[] newPath = origPath.toCharArray(); 235 int length = newPath.length; 236 int newLength = 0; 237 for (int i = 0; i < length; ++i) { 238 char ch = newPath[i]; 239 if (ch == '/') { 240 if (!lastWasSlash) { 241 newPath[newLength++] = separatorChar; 242 lastWasSlash = true; 243 } 244 } else { 245 newPath[newLength++] = ch; 246 lastWasSlash = false; 247 } 248 } 249 // Remove any trailing slash (unless this is the root of the file system). 250 if (lastWasSlash && newLength > 1) { 251 newLength--; 252 } 253 // Reuse the original string if possible. 254 return (newLength != length) ? new String(newPath, 0, newLength) : origPath; 255 } 256 257 // Joins two path components, adding a separator only if necessary. 258 private String join(String prefix, String suffix) { 259 int prefixLength = prefix.length(); 260 boolean haveSlash = (prefixLength > 0 && prefix.charAt(prefixLength - 1) == separatorChar); 261 if (!haveSlash) { 262 haveSlash = (suffix.length() > 0 && suffix.charAt(0) == separatorChar); 263 } 264 return haveSlash ? (prefix + suffix) : (prefix + separatorChar + suffix); 265 } 266 267 @SuppressWarnings("nls") 268 private void checkURI(URI uri) { 269 if (!uri.isAbsolute()) { 270 throw new IllegalArgumentException(Msg.getString("K031a", uri)); 271 } else if (!uri.getRawSchemeSpecificPart().startsWith("/")) { 272 throw new IllegalArgumentException(Msg.getString("K031b", uri)); 273 } 274 275 String temp = uri.getScheme(); 276 if (temp == null || !temp.equals("file")) { 277 throw new IllegalArgumentException(Msg.getString("K031c", uri)); 278 } 279 280 temp = uri.getRawPath(); 281 if (temp == null || temp.length() == 0) { 282 throw new IllegalArgumentException(Msg.getString("K031d", uri)); 283 } 284 285 if (uri.getRawAuthority() != null) { 286 throw new IllegalArgumentException(Msg.getString("K031e", 287 new String[] { "authority", uri.toString() })); 288 } 289 290 if (uri.getRawQuery() != null) { 291 throw new IllegalArgumentException(Msg.getString("K031e", 292 new String[] { "query", uri.toString() })); 293 } 294 295 if (uri.getRawFragment() != null) { 296 throw new IllegalArgumentException(Msg.getString("K031e", 297 new String[] { "fragment", uri.toString() })); 298 } 299 } 300 301 /** 302 * Lists the file system roots. The Java platform may support zero or more 303 * file systems, each with its own platform-dependent root. Further, the 304 * canonical pathname of any file on the system will always begin with one 305 * of the returned file system roots. 306 * 307 * @return the array of file system roots. 308 */ 309 public static File[] listRoots() { 310 return new File[] { new File("/") }; 311 } 312 313 /** 314 * Indicates whether the current context is allowed to read from this file. 315 * 316 * @return {@code true} if this file can be read, {@code false} otherwise. 317 * @throws SecurityException 318 * if a {@code SecurityManager} is installed and it denies the 319 * read request. 320 */ 321 public boolean canRead() { 322 if (path.length() == 0) { 323 return false; 324 } 325 SecurityManager security = System.getSecurityManager(); 326 if (security != null) { 327 security.checkRead(path); 328 } 329 return isReadableImpl(pathBytes); 330 } 331 332 private native boolean isReadableImpl(byte[] filePath); 333 334 /** 335 * Indicates whether the current context is allowed to write to this file. 336 * 337 * @return {@code true} if this file can be written, {@code false} 338 * otherwise. 339 * @throws SecurityException 340 * if a {@code SecurityManager} is installed and it denies the 341 * write request. 342 */ 343 public boolean canWrite() { 344 if (path.length() == 0) { 345 return false; 346 } 347 SecurityManager security = System.getSecurityManager(); 348 if (security != null) { 349 security.checkWrite(path); 350 } 351 return isWritableImpl(pathBytes); 352 } 353 354 private native boolean isWritableImpl(byte[] filePath); 355 356 /** 357 * Returns the relative sort ordering of the paths for this file and the 358 * file {@code another}. The ordering is platform dependent. 359 * 360 * @param another 361 * a file to compare this file to 362 * @return an int determined by comparing the two paths. Possible values are 363 * described in the Comparable interface. 364 * @see Comparable 365 */ 366 public int compareTo(File another) { 367 return this.getPath().compareTo(another.getPath()); 368 } 369 370 /** 371 * Deletes this file. Directories must be empty before they will be deleted. 372 * 373 * @return {@code true} if this file was deleted, {@code false} otherwise. 374 * @throws SecurityException 375 * if a {@code SecurityManager} is installed and it denies the 376 * request. 377 * @see java.lang.SecurityManager#checkDelete 378 */ 379 public boolean delete() { 380 if (path.length() == 0) { 381 return false; 382 } 383 SecurityManager security = System.getSecurityManager(); 384 if (security != null) { 385 security.checkDelete(path); 386 } 387 return deleteImpl(pathBytes); 388 } 389 390 private native boolean deleteImpl(byte[] filePath); 391 392 /** 393 * Schedules this file to be automatically deleted once the virtual machine 394 * terminates. This will only happen when the virtual machine terminates 395 * normally as described by the Java Language Specification section 12.9. 396 * 397 * @throws SecurityException 398 * if a {@code SecurityManager} is installed and it denies the 399 * request. 400 */ 401 public void deleteOnExit() { 402 SecurityManager security = System.getSecurityManager(); 403 if (security != null) { 404 security.checkDelete(path); 405 } 406 DeleteOnExit.getInstance().addFile(getAbsoluteName()); 407 } 408 409 /** 410 * Compares {@code obj} to this file and returns {@code true} if they 411 * represent the <em>same</em> object using a path specific comparison. 412 * 413 * @param obj 414 * the object to compare this file with. 415 * @return {@code true} if {@code obj} is the same as this object, 416 * {@code false} otherwise. 417 */ 418 @Override 419 public boolean equals(Object obj) { 420 if (!(obj instanceof File)) { 421 return false; 422 } 423 return path.equals(((File) obj).getPath()); 424 } 425 426 /** 427 * Returns a boolean indicating whether this file can be found on the 428 * underlying file system. 429 * 430 * @return {@code true} if this file exists, {@code false} otherwise. 431 * @throws SecurityException 432 * if a {@code SecurityManager} is installed and it denies read 433 * access to this file. 434 * @see #getPath 435 * @see java.lang.SecurityManager#checkRead(FileDescriptor) 436 */ 437 public boolean exists() { 438 if (path.length() == 0) { 439 return false; 440 } 441 SecurityManager security = System.getSecurityManager(); 442 if (security != null) { 443 security.checkRead(path); 444 } 445 return existsImpl(pathBytes); 446 } 447 448 private native boolean existsImpl(byte[] filePath); 449 450 /** 451 * Returns the absolute path of this file. 452 * 453 * @return the absolute file path. 454 */ 455 public String getAbsolutePath() { 456 return Util.toUTF8String(pathBytes, 0, pathBytes.length - 1); 457 } 458 459 /** 460 * Returns a new file constructed using the absolute path of this file. 461 * 462 * @return a new file from this file's absolute path. 463 * @see java.lang.SecurityManager#checkPropertyAccess 464 */ 465 public File getAbsoluteFile() { 466 return new File(this.getAbsolutePath()); 467 } 468 469 /** 470 * Returns the absolute path of this file with all references resolved. An 471 * <em>absolute</em> path is one that begins at the root of the file 472 * system. The canonical path is one in which all references have been 473 * resolved. For the cases of '..' and '.', where the file system supports 474 * parent and working directory respectively, these are removed and replaced 475 * with a direct directory reference. If the file does not exist, 476 * getCanonicalPath() may not resolve any references and simply returns an 477 * absolute path name or throws an IOException. 478 * 479 * @return the canonical path of this file. 480 * @throws IOException 481 * if an I/O error occurs. 482 */ 483 public String getCanonicalPath() throws IOException { 484 // BEGIN android-removed 485 // Caching the canonical path is bogus. Users facing specific 486 // performance problems can perform their own caching, with 487 // eviction strategies that are appropriate for their application. 488 // A VM-wide cache with no mechanism to evict stale elements is a 489 // disservice to applications that need up-to-date data. 490 // String canonPath = FileCanonPathCache.get(absPath); 491 // if (canonPath != null) { 492 // return canonPath; 493 // } 494 // END android-removed 495 496 byte[] result = pathBytes; 497 if(separatorChar == '/') { 498 // resolve the full path first 499 result = resolveLink(result, result.length, false); 500 // resolve the parent directories 501 result = resolve(result); 502 } 503 int numSeparators = 1; 504 for (int i = 0; i < result.length; i++) { 505 if (result[i] == separatorChar) { 506 numSeparators++; 507 } 508 } 509 int sepLocations[] = new int[numSeparators]; 510 int rootLoc = 0; 511 if (separatorChar != '/') { 512 if (result[0] == '\\') { 513 rootLoc = (result.length > 1 && result[1] == '\\') ? 1 : 0; 514 } else { 515 rootLoc = 2; // skip drive i.e. c: 516 } 517 } 518 byte newResult[] = new byte[result.length + 1]; 519 int newLength = 0, lastSlash = 0, foundDots = 0; 520 sepLocations[lastSlash] = rootLoc; 521 for (int i = 0; i <= result.length; i++) { 522 if (i < rootLoc) { 523 newResult[newLength++] = result[i]; 524 } else { 525 if (i == result.length || result[i] == separatorChar) { 526 if (i == result.length && foundDots == 0) { 527 break; 528 } 529 if (foundDots == 1) { 530 /* Don't write anything, just reset and continue */ 531 foundDots = 0; 532 continue; 533 } 534 if (foundDots > 1) { 535 /* Go back N levels */ 536 lastSlash = lastSlash > (foundDots - 1) ? lastSlash 537 - (foundDots - 1) : 0; 538 newLength = sepLocations[lastSlash] + 1; 539 foundDots = 0; 540 continue; 541 } 542 sepLocations[++lastSlash] = newLength; 543 newResult[newLength++] = (byte) separatorChar; 544 continue; 545 } 546 if (result[i] == '.') { 547 foundDots++; 548 continue; 549 } 550 /* Found some dots within text, write them out */ 551 if (foundDots > 0) { 552 for (int j = 0; j < foundDots; j++) { 553 newResult[newLength++] = (byte) '.'; 554 } 555 } 556 newResult[newLength++] = result[i]; 557 foundDots = 0; 558 } 559 } 560 // remove trailing slash 561 if (newLength > (rootLoc + 1) 562 && newResult[newLength - 1] == separatorChar) { 563 newLength--; 564 } 565 newResult[newLength] = 0; 566 newResult = getCanonImpl(newResult); 567 newLength = newResult.length; 568 569 // BEGIN android-changed 570 // caching the canonical path is completely bogus 571 return Util.toUTF8String(newResult, 0, newLength); 572 // FileCanonPathCache.put(absPath, canonPath); 573 // return canonPath; 574 // END android-changed 575 } 576 577 /* 578 * Resolve symbolic links in the parent directories. 579 */ 580 private byte[] resolve(byte[] newResult) throws IOException { 581 int last = 1, nextSize, linkSize; 582 byte[] linkPath = newResult, bytes; 583 boolean done, inPlace; 584 for (int i = 1; i <= newResult.length; i++) { 585 if (i == newResult.length || newResult[i] == separatorChar) { 586 done = i >= newResult.length - 1; 587 // if there is only one segment, do nothing 588 if (done && linkPath.length == 1) { 589 return newResult; 590 } 591 inPlace = false; 592 if (linkPath == newResult) { 593 bytes = newResult; 594 // if there are no symbolic links, terminate the C string 595 // instead of copying 596 if (!done) { 597 inPlace = true; 598 newResult[i] = '\0'; 599 } 600 } else { 601 nextSize = i - last + 1; 602 linkSize = linkPath.length; 603 if (linkPath[linkSize - 1] == separatorChar) { 604 linkSize--; 605 } 606 bytes = new byte[linkSize + nextSize]; 607 System.arraycopy(linkPath, 0, bytes, 0, linkSize); 608 System.arraycopy(newResult, last - 1, bytes, linkSize, 609 nextSize); 610 // the full path has already been resolved 611 } 612 if (done) { 613 return bytes; 614 } 615 linkPath = resolveLink(bytes, inPlace ? i : bytes.length, true); 616 if (inPlace) { 617 newResult[i] = '/'; 618 } 619 last = i + 1; 620 } 621 } 622 throw new InternalError(); 623 } 624 625 /* 626 * Resolve a symbolic link. While the path resolves to an existing path, 627 * keep resolving. If an absolute link is found, resolve the parent 628 * directories if resolveAbsolute is true. 629 */ 630 private byte[] resolveLink(byte[] pathBytes, int length, 631 boolean resolveAbsolute) throws IOException { 632 boolean restart = false; 633 byte[] linkBytes, temp; 634 do { 635 linkBytes = getLinkImpl(pathBytes); 636 if (linkBytes == pathBytes) { 637 break; 638 } 639 if (linkBytes[0] == separatorChar) { 640 // link to an absolute path, if resolving absolute paths, 641 // resolve the parent dirs again 642 restart = resolveAbsolute; 643 pathBytes = linkBytes; 644 } else { 645 int last = length - 1; 646 while (pathBytes[last] != separatorChar) { 647 last--; 648 } 649 last++; 650 temp = new byte[last + linkBytes.length]; 651 System.arraycopy(pathBytes, 0, temp, 0, last); 652 System.arraycopy(linkBytes, 0, temp, last, linkBytes.length); 653 pathBytes = temp; 654 } 655 length = pathBytes.length; 656 } while (existsImpl(pathBytes)); 657 // resolve the parent directories 658 if (restart) { 659 return resolve(pathBytes); 660 } 661 return pathBytes; 662 } 663 664 private native byte[] getLinkImpl(byte[] filePath); 665 666 /** 667 * Returns a new file created using the canonical path of this file. 668 * Equivalent to {@code new File(this.getCanonicalPath())}. 669 * 670 * @return the new file constructed from this file's canonical path. 671 * @throws IOException 672 * if an I/O error occurs. 673 * @see java.lang.SecurityManager#checkPropertyAccess 674 */ 675 public File getCanonicalFile() throws IOException { 676 return new File(getCanonicalPath()); 677 } 678 679 private native byte[] getCanonImpl(byte[] filePath); 680 681 /** 682 * Returns the name of the file or directory represented by this file. 683 * 684 * @return this file's name or an empty string if there is no name part in 685 * the file's path. 686 */ 687 public String getName() { 688 int separatorIndex = path.lastIndexOf(separator); 689 return (separatorIndex < 0) ? path : path.substring(separatorIndex + 1, 690 path.length()); 691 } 692 693 /** 694 * Returns the pathname of the parent of this file. This is the path up to 695 * but not including the last name. {@code null} is returned if there is no 696 * parent. 697 * 698 * @return this file's parent pathname or {@code null}. 699 */ 700 public String getParent() { 701 int length = path.length(), firstInPath = 0; 702 if (separatorChar == '\\' && length > 2 && path.charAt(1) == ':') { 703 firstInPath = 2; 704 } 705 int index = path.lastIndexOf(separatorChar); 706 if (index == -1 && firstInPath > 0) { 707 index = 2; 708 } 709 if (index == -1 || path.charAt(length - 1) == separatorChar) { 710 return null; 711 } 712 if (path.indexOf(separatorChar) == index 713 && path.charAt(firstInPath) == separatorChar) { 714 return path.substring(0, index + 1); 715 } 716 return path.substring(0, index); 717 } 718 719 /** 720 * Returns a new file made from the pathname of the parent of this file. 721 * This is the path up to but not including the last name. {@code null} is 722 * returned when there is no parent. 723 * 724 * @return a new file representing this file's parent or {@code null}. 725 */ 726 public File getParentFile() { 727 String tempParent = getParent(); 728 if (tempParent == null) { 729 return null; 730 } 731 return new File(tempParent); 732 } 733 734 /** 735 * Returns the path of this file. 736 * 737 * @return this file's path. 738 */ 739 public String getPath() { 740 return path; 741 } 742 743 /** 744 * Returns an integer hash code for the receiver. Any two objects for which 745 * {@code equals} returns {@code true} must return the same hash code. 746 * 747 * @return this files's hash value. 748 * @see #equals 749 */ 750 @Override 751 public int hashCode() { 752 return getPath().hashCode() ^ 1234321; 753 } 754 755 /** 756 * Indicates if this file's pathname is absolute. Whether a pathname is 757 * absolute is platform specific. On Android, absolute paths start with 758 * the character '/'. 759 * 760 * @return {@code true} if this file's pathname is absolute, {@code false} 761 * otherwise. 762 * @see #getPath 763 */ 764 public boolean isAbsolute() { 765 return path.length() > 0 && path.charAt(0) == separatorChar; 766 } 767 768 /** 769 * Indicates if this file represents a <em>directory</em> on the 770 * underlying file system. 771 * 772 * @return {@code true} if this file is a directory, {@code false} 773 * otherwise. 774 * @throws SecurityException 775 * if a {@code SecurityManager} is installed and it denies read 776 * access to this file. 777 */ 778 public boolean isDirectory() { 779 if (path.length() == 0) { 780 return false; 781 } 782 SecurityManager security = System.getSecurityManager(); 783 if (security != null) { 784 security.checkRead(path); 785 } 786 return isDirectoryImpl(pathBytes); 787 } 788 789 private native boolean isDirectoryImpl(byte[] filePath); 790 791 /** 792 * Indicates if this file represents a <em>file</em> on the underlying 793 * file system. 794 * 795 * @return {@code true} if this file is a file, {@code false} otherwise. 796 * @throws SecurityException 797 * if a {@code SecurityManager} is installed and it denies read 798 * access to this file. 799 */ 800 public boolean isFile() { 801 if (path.length() == 0) { 802 return false; 803 } 804 SecurityManager security = System.getSecurityManager(); 805 if (security != null) { 806 security.checkRead(path); 807 } 808 return isFileImpl(pathBytes); 809 } 810 811 private native boolean isFileImpl(byte[] filePath); 812 813 /** 814 * Returns whether or not this file is a hidden file as defined by the 815 * operating system. The notion of "hidden" is system-dependent. For Unix 816 * systems a file is considered hidden if its name starts with a ".". For 817 * Windows systems there is an explicit flag in the file system for this 818 * purpose. 819 * 820 * @return {@code true} if the file is hidden, {@code false} otherwise. 821 * @throws SecurityException 822 * if a {@code SecurityManager} is installed and it denies read 823 * access to this file. 824 */ 825 public boolean isHidden() { 826 if (path.length() == 0) { 827 return false; 828 } 829 SecurityManager security = System.getSecurityManager(); 830 if (security != null) { 831 security.checkRead(path); 832 } 833 return getName().startsWith("."); 834 } 835 836 /** 837 * Returns the time when this file was last modified, measured in 838 * milliseconds since January 1st, 1970, midnight. 839 * Returns 0 if the file does not exist. 840 * 841 * @return the time when this file was last modified. 842 * @throws SecurityException 843 * if a {@code SecurityManager} is installed and it denies read 844 * access to this file. 845 */ 846 public long lastModified() { 847 if (path.length() == 0) { 848 return 0; 849 } 850 SecurityManager security = System.getSecurityManager(); 851 if (security != null) { 852 security.checkRead(path); 853 } 854 return lastModifiedImpl(pathBytes); 855 } 856 857 private native long lastModifiedImpl(byte[] filePath); 858 859 /** 860 * Sets the time this file was last modified, measured in milliseconds since 861 * January 1st, 1970, midnight. 862 * 863 * @param time 864 * the last modification time for this file. 865 * @return {@code true} if the operation is successful, {@code false} 866 * otherwise. 867 * @throws IllegalArgumentException 868 * if {@code time < 0}. 869 * @throws SecurityException 870 * if a {@code SecurityManager} is installed and it denies write 871 * access to this file. 872 */ 873 public boolean setLastModified(long time) { 874 if (path.length() == 0) { 875 return false; 876 } 877 if (time < 0) { 878 throw new IllegalArgumentException(Msg.getString("K006a")); //$NON-NLS-1$ 879 } 880 SecurityManager security = System.getSecurityManager(); 881 if (security != null) { 882 security.checkWrite(path); 883 } 884 return setLastModifiedImpl(pathBytes, time); 885 } 886 887 private native boolean setLastModifiedImpl(byte[] path, long time); 888 889 /** 890 * Marks this file or directory to be read-only as defined by the operating 891 * system. 892 * 893 * @return {@code true} if the operation is successful, {@code false} 894 * otherwise. 895 * @throws SecurityException 896 * if a {@code SecurityManager} is installed and it denies write 897 * access to this file. 898 */ 899 public boolean setReadOnly() { 900 if (path.length() == 0) { 901 return false; 902 } 903 SecurityManager security = System.getSecurityManager(); 904 if (security != null) { 905 security.checkWrite(path); 906 } 907 return setReadOnlyImpl(pathBytes); 908 } 909 910 private native boolean setReadOnlyImpl(byte[] path); 911 912 /** 913 * Returns the length of this file in bytes. 914 * Returns 0 if the file does not exist. 915 * The result for a directory is not defined. 916 * 917 * @return the number of bytes in this file. 918 * @throws SecurityException 919 * if a {@code SecurityManager} is installed and it denies read 920 * access to this file. 921 */ 922 public long length() { 923 SecurityManager security = System.getSecurityManager(); 924 if (security != null) { 925 security.checkRead(path); 926 } 927 return lengthImpl(pathBytes); 928 } 929 930 private native long lengthImpl(byte[] filePath); 931 932 /** 933 * Returns an array of strings with the file names in the directory 934 * represented by this file. The result is {@code null} if this file is not 935 * a directory. 936 * <p> 937 * The entries {@code .} and {@code ..} representing the current and parent 938 * directory are not returned as part of the list. 939 * 940 * @return an array of strings with file names or {@code null}. 941 * @throws SecurityException 942 * if a {@code SecurityManager} is installed and it denies read 943 * access to this file. 944 * @see #isDirectory 945 * @see java.lang.SecurityManager#checkRead(FileDescriptor) 946 */ 947 public String[] list() { 948 SecurityManager security = System.getSecurityManager(); 949 if (security != null) { 950 security.checkRead(path); 951 } 952 if (path.length() == 0) { 953 return null; 954 } 955 return listImpl(pathBytes); 956 } 957 958 private native String[] listImpl(byte[] path); 959 960 /** 961 * Gets a list of the files in the directory represented by this file. This 962 * list is then filtered through a FilenameFilter and the names of files 963 * with matching names are returned as an array of strings. Returns 964 * {@code null} if this file is not a directory. If {@code filter} is 965 * {@code null} then all filenames match. 966 * <p> 967 * The entries {@code .} and {@code ..} representing the current and parent 968 * directories are not returned as part of the list. 969 * 970 * @param filter 971 * the filter to match names against, may be {@code null}. 972 * @return an array of files or {@code null}. 973 * @throws SecurityException 974 * if a {@code SecurityManager} is installed and it denies read 975 * access to this file. 976 * @see #getPath 977 * @see #isDirectory 978 * @see java.lang.SecurityManager#checkRead(FileDescriptor) 979 */ 980 public String[] list(FilenameFilter filter) { 981 String[] filenames = list(); 982 if (filter == null || filenames == null) { 983 return filenames; 984 } 985 List<String> result = new ArrayList<String>(filenames.length); 986 for (String filename : filenames) { 987 if (filter.accept(this, filename)) { 988 result.add(filename); 989 } 990 } 991 return result.toArray(new String[result.size()]); 992 } 993 994 /** 995 * Returns an array of files contained in the directory represented by this 996 * file. The result is {@code null} if this file is not a directory. The 997 * paths of the files in the array are absolute if the path of this file is 998 * absolute, they are relative otherwise. 999 * 1000 * @return an array of files or {@code null}. 1001 * @throws SecurityException 1002 * if a {@code SecurityManager} is installed and it denies read 1003 * access to this file. 1004 * @see #list 1005 * @see #isDirectory 1006 */ 1007 public File[] listFiles() { 1008 return filenamesToFiles(list()); 1009 } 1010 1011 /** 1012 * Gets a list of the files in the directory represented by this file. This 1013 * list is then filtered through a FilenameFilter and files with matching 1014 * names are returned as an array of files. Returns {@code null} if this 1015 * file is not a directory. If {@code filter} is {@code null} then all 1016 * filenames match. 1017 * <p> 1018 * The entries {@code .} and {@code ..} representing the current and parent 1019 * directories are not returned as part of the list. 1020 * 1021 * @param filter 1022 * the filter to match names against, may be {@code null}. 1023 * @return an array of files or {@code null}. 1024 * @throws SecurityException 1025 * if a {@code SecurityManager} is installed and it denies read 1026 * access to this file. 1027 * @see #list(FilenameFilter filter) 1028 * @see #getPath 1029 * @see #isDirectory 1030 * @see java.lang.SecurityManager#checkRead(FileDescriptor) 1031 */ 1032 public File[] listFiles(FilenameFilter filter) { 1033 return filenamesToFiles(list(filter)); 1034 } 1035 1036 /** 1037 * Gets a list of the files in the directory represented by this file. This 1038 * list is then filtered through a FileFilter and matching files are 1039 * returned as an array of files. Returns {@code null} if this file is not a 1040 * directory. If {@code filter} is {@code null} then all files match. 1041 * <p> 1042 * The entries {@code .} and {@code ..} representing the current and parent 1043 * directories are not returned as part of the list. 1044 * 1045 * @param filter 1046 * the filter to match names against, may be {@code null}. 1047 * @return an array of files or {@code null}. 1048 * @throws SecurityException 1049 * if a {@code SecurityManager} is installed and it denies read 1050 * access to this file. 1051 * @see #getPath 1052 * @see #isDirectory 1053 * @see java.lang.SecurityManager#checkRead(FileDescriptor) 1054 */ 1055 public File[] listFiles(FileFilter filter) { 1056 File[] files = listFiles(); 1057 if (filter == null || files == null) { 1058 return files; 1059 } 1060 List<File> result = new ArrayList<File>(files.length); 1061 for (File file : files) { 1062 if (filter.accept(file)) { 1063 result.add(file); 1064 } 1065 } 1066 return result.toArray(new File[result.size()]); 1067 } 1068 1069 /** 1070 * Converts a String[] containing filenames to a File[]. 1071 * Note that the filenames must not contain slashes. 1072 * This method is to remove duplication in the implementation 1073 * of File.list's overloads. 1074 */ 1075 private File[] filenamesToFiles(String[] filenames) { 1076 if (filenames == null) { 1077 return null; 1078 } 1079 int count = filenames.length; 1080 File[] result = new File[count]; 1081 for (int i = 0; i < count; ++i) { 1082 result[i] = new File(this, filenames[i]); 1083 } 1084 return result; 1085 } 1086 1087 /** 1088 * Creates the directory named by the trailing filename of this file. Does 1089 * not create the complete path required to create this directory. 1090 * 1091 * @return {@code true} if the directory has been created, {@code false} 1092 * otherwise. 1093 * @throws SecurityException 1094 * if a {@code SecurityManager} is installed and it denies write 1095 * access for this file. 1096 * @see #mkdirs 1097 */ 1098 public boolean mkdir() { 1099 SecurityManager security = System.getSecurityManager(); 1100 if (security != null) { 1101 security.checkWrite(path); 1102 } 1103 return mkdirImpl(pathBytes); 1104 } 1105 1106 private native boolean mkdirImpl(byte[] filePath); 1107 1108 /** 1109 * Creates the directory named by the trailing filename of this file, 1110 * including the complete directory path required to create this directory. 1111 * 1112 * @return {@code true} if the necessary directories have been created, 1113 * {@code false} if the target directory already exists or one of 1114 * the directories can not be created. 1115 * @throws SecurityException 1116 * if a {@code SecurityManager} is installed and it denies write 1117 * access for this file. 1118 * @see #mkdir 1119 */ 1120 public boolean mkdirs() { 1121 /* If the terminal directory already exists, answer false */ 1122 if (exists()) { 1123 return false; 1124 } 1125 1126 /* If the receiver can be created, answer true */ 1127 if (mkdir()) { 1128 return true; 1129 } 1130 1131 String parentDir = getParent(); 1132 /* If there is no parent and we were not created, answer false */ 1133 if (parentDir == null) { 1134 return false; 1135 } 1136 1137 /* Otherwise, try to create a parent directory and then this directory */ 1138 return (new File(parentDir).mkdirs() && mkdir()); 1139 } 1140 1141 /** 1142 * Creates a new, empty file on the file system according to the path 1143 * information stored in this file. 1144 * 1145 * @return {@code true} if the file has been created, {@code false} if it 1146 * already exists. 1147 * @throws IOException if it's not possible to create the file. 1148 * @throws SecurityException 1149 * if a {@code SecurityManager} is installed and it denies write 1150 * access for this file. 1151 */ 1152 public boolean createNewFile() throws IOException { 1153 SecurityManager security = System.getSecurityManager(); 1154 if (security != null) { 1155 security.checkWrite(path); 1156 } 1157 if (path.length() == 0) { 1158 throw new IOException(Msg.getString("KA012")); //$NON-NLS-1$ 1159 } 1160 return createNewFileImpl(pathBytes); 1161 } 1162 1163 private native boolean createNewFileImpl(byte[] filePath); 1164 1165 /** 1166 * Creates an empty temporary file using the given prefix and suffix as part 1167 * of the file name. If suffix is {@code null}, {@code .tmp} is used. This 1168 * method is a convenience method that calls 1169 * {@link #createTempFile(String, String, File)} with the third argument 1170 * being {@code null}. 1171 * 1172 * @param prefix 1173 * the prefix to the temp file name. 1174 * @param suffix 1175 * the suffix to the temp file name. 1176 * @return the temporary file. 1177 * @throws IOException 1178 * if an error occurs when writing the file. 1179 */ 1180 public static File createTempFile(String prefix, String suffix) 1181 throws IOException { 1182 return createTempFile(prefix, suffix, null); 1183 } 1184 1185 /** 1186 * Creates an empty temporary file in the given directory using the given 1187 * prefix and suffix as part of the file name. 1188 * 1189 * @param prefix 1190 * the prefix to the temp file name. 1191 * @param suffix 1192 * the suffix to the temp file name. 1193 * @param directory 1194 * the location to which the temp file is to be written, or 1195 * {@code null} for the default location for temporary files, 1196 * which is taken from the "java.io.tmpdir" system property. It 1197 * may be necessary to set this property to an existing, writable 1198 * directory for this method to work properly. 1199 * @return the temporary file. 1200 * @throws IllegalArgumentException 1201 * if the length of {@code prefix} is less than 3. 1202 * @throws IOException 1203 * if an error occurs when writing the file. 1204 */ 1205 @SuppressWarnings("nls") 1206 public static File createTempFile(String prefix, String suffix, 1207 File directory) throws IOException { 1208 // Force a prefix null check first 1209 if (prefix.length() < 3) { 1210 throw new IllegalArgumentException(Msg.getString("K006b")); 1211 } 1212 String newSuffix = suffix == null ? ".tmp" : suffix; 1213 File tmpDirFile; 1214 if (directory == null) { 1215 String tmpDir = AccessController.doPrivileged( 1216 new PriviAction<String>("java.io.tmpdir", ".")); 1217 tmpDirFile = new File(tmpDir); 1218 } else { 1219 tmpDirFile = directory; 1220 } 1221 File result; 1222 do { 1223 result = genTempFile(prefix, newSuffix, tmpDirFile); 1224 } while (!result.createNewFile()); 1225 return result; 1226 } 1227 1228 private static File genTempFile(String prefix, String suffix, File directory) { 1229 if (counter == 0) { 1230 // TODO: this doesn't make a lot of sense. SecureRandom for the seed, but then always just add one? 1231 int newInt = new SecureRandom().nextInt(); 1232 counter = ((newInt / 65535) & 0xFFFF) + 0x2710; 1233 } 1234 StringBuilder newName = new StringBuilder(); 1235 newName.append(prefix); 1236 newName.append(counter++); 1237 newName.append(suffix); 1238 return new File(directory, newName.toString()); 1239 } 1240 1241 /** 1242 * Renames this file to the name represented by the {@code dest} file. This 1243 * works for both normal files and directories. 1244 * 1245 * @param dest 1246 * the file containing the new name. 1247 * @return {@code true} if the File was renamed, {@code false} otherwise. 1248 * @throws SecurityException 1249 * if a {@code SecurityManager} is installed and it denies write 1250 * access for this file or the {@code dest} file. 1251 */ 1252 public boolean renameTo(java.io.File dest) { 1253 if (path.length() == 0 || dest.path.length() == 0) { 1254 return false; 1255 } 1256 SecurityManager security = System.getSecurityManager(); 1257 if (security != null) { 1258 security.checkWrite(path); 1259 security.checkWrite(dest.path); 1260 } 1261 return renameToImpl(pathBytes, dest.pathBytes); 1262 } 1263 1264 private native boolean renameToImpl(byte[] pathExist, byte[] pathNew); 1265 1266 /** 1267 * Returns a string containing a concise, human-readable description of this 1268 * file. 1269 * 1270 * @return a printable representation of this file. 1271 */ 1272 @Override 1273 public String toString() { 1274 return path; 1275 } 1276 1277 /** 1278 * Returns a Uniform Resource Identifier for this file. The URI is system 1279 * dependent and may not be transferable between different operating / file 1280 * systems. 1281 * 1282 * @return an URI for this file. 1283 */ 1284 @SuppressWarnings("nls") 1285 public URI toURI() { 1286 String name = getAbsoluteName(); 1287 try { 1288 if (!name.startsWith("/")) { 1289 // start with sep. 1290 return new URI("file", null, new StringBuilder( 1291 name.length() + 1).append('/').append(name).toString(), 1292 null, null); 1293 } else if (name.startsWith("//")) { 1294 return new URI("file", "", name, null); // UNC path 1295 } 1296 return new URI("file", null, name, null, null); 1297 } catch (URISyntaxException e) { 1298 // this should never happen 1299 return null; 1300 } 1301 } 1302 1303 /** 1304 * Returns a Uniform Resource Locator for this file. The URL is system 1305 * dependent and may not be transferable between different operating / file 1306 * systems. 1307 * 1308 * @return a URL for this file. 1309 * @throws java.net.MalformedURLException 1310 * if the path cannot be transformed into a URL. 1311 */ 1312 @SuppressWarnings("nls") 1313 public URL toURL() throws java.net.MalformedURLException { 1314 String name = getAbsoluteName(); 1315 if (!name.startsWith("/")) { 1316 // start with sep. 1317 return new URL( 1318 "file", EMPTY_STRING, -1, new StringBuilder(name.length() + 1) //$NON-NLS-1$ 1319 .append('/').append(name).toString(), null); 1320 } else if (name.startsWith("//")) { 1321 return new URL("file:" + name); // UNC path 1322 } 1323 return new URL("file", EMPTY_STRING, -1, name, null); 1324 } 1325 1326 private String getAbsoluteName() { 1327 File f = getAbsoluteFile(); 1328 String name = f.getPath(); 1329 1330 if (f.isDirectory() && name.charAt(name.length() - 1) != separatorChar) { 1331 // Directories must end with a slash 1332 name = new StringBuilder(name.length() + 1).append(name) 1333 .append('/').toString(); 1334 } 1335 if (separatorChar != '/') { // Must convert slashes. 1336 name = name.replace(separatorChar, '/'); 1337 } 1338 return name; 1339 } 1340 1341 private void writeObject(ObjectOutputStream stream) throws IOException { 1342 stream.defaultWriteObject(); 1343 stream.writeChar(separatorChar); 1344 } 1345 1346 private void readObject(ObjectInputStream stream) throws IOException, 1347 ClassNotFoundException { 1348 stream.defaultReadObject(); 1349 char inSeparator = stream.readChar(); 1350 init(path.replace(inSeparator, separatorChar)); 1351 } 1352} 1353