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