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 package java.io;
19
20 import java.net.URI;
21 import java.net.URISyntaxException;
22 import java.net.URL;
23 import java.security.AccessController;
24 import java.security.SecureRandom;
25 import java.util.ArrayList;
26 import java.util.List;
27
28 import org.apache.harmony.luni.internal.io.FileCanonPathCache;
29 import org.apache.harmony.luni.util.DeleteOnExit;
30 import org.apache.harmony.luni.internal.nls.Messages;
31 import org.apache.harmony.luni.util.PriviAction;
32 import org.apache.harmony.luni.util.Util;
33
34 /**
35 * An "abstract" representation of a file system entity identified by a
36 * pathname. The pathname may be absolute (relative to the root directory
37 * of the file system) or relative to the current directory in which the program
38 * is running.
39 * <p>
40 * This class provides methods for querying/changing information about the file
41 * as well as directory listing capabilities if the file represents a directory.
42 * <p>
43 * When manipulating file paths, the static fields of this class may be used to
44 * determine the platform specific separators.
45 *
46 * @see java.io.Serializable
47 * @see java.lang.Comparable
48 */
49 public class File implements Serializable, Comparable<File> {
50
51 private static final long serialVersionUID = 301077366599181567L;
52
53 private static final String EMPTY_STRING = ""; //$NON-NLS-1$
54
55 private String path;
56
57 transient byte[] properPath;
58
59 /**
60 * The system dependent file separator character.
61 */
62 public static final char separatorChar;
63
64 /**
65 * The system dependent file separator string. The initial value of this
66 * field is the system property "file.separator".
67 */
68 public static final String separator;
69
70 /**
71 * The system dependent path separator character.
72 */
73 public static final char pathSeparatorChar;
74
75 /**
76 * The system dependent path separator string. The initial value of this
77 * field is the system property "path.separator".
78 */
79 public static final String pathSeparator;
80
81 /* Temp file counter */
82 private static int counter;
83
84 private static boolean caseSensitive;
85
86 private static native void oneTimeInitialization();
87
88 static {
89 oneTimeInitialization();
90
91 // The default protection domain grants access to these properties
92 separatorChar = System.getProperty("file.separator", "\\").charAt(0); //$NON-NLS-1$ //$NON-NLS-2$
93 pathSeparatorChar = System.getProperty("path.separator", ";").charAt(0); //$NON-NLS-1$//$NON-NLS-2$
94 separator = new String(new char[] { separatorChar }, 0, 1);
95 pathSeparator = new String(new char[] { pathSeparatorChar }, 0, 1);
96 caseSensitive = isCaseSensitiveImpl();
97 }
98
99 /**
100 * Constructs a new file using the specified directory and name.
101 *
102 * @param dir
103 * the directory where the file is stored.
104 * @param name
105 * the file's name.
106 * @throws NullPointerException
107 * if {@code name} is {@code null}.
108 */
109 public File(File dir, String name) {
110 if (name == null) {
111 throw new NullPointerException();
112 }
113 if (dir == null) {
114 this.path = fixSlashes(name);
115 } else {
116 this.path = calculatePath(dir.getPath(), name);
117 }
118 }
119
120 /**
121 * Constructs a new file using the specified path.
122 *
123 * @param path
124 * the path to be used for the file.
125 */
126 public File(String path) {
127 // path == null check & NullPointerException thrown by fixSlashes
128 this.path = fixSlashes(path);
129 }
130
131 /**
132 * Constructs a new File using the specified directory path and file name,
133 * placing a path separator between the two.
134 *
135 * @param dirPath
136 * the path to the directory where the file is stored.
137 * @param name
138 * the file's name.
139 * @throws NullPointerException
140 * if {@code name} is {@code null}.
141 */
142 public File(String dirPath, String name) {
143 if (name == null) {
144 throw new NullPointerException();
145 }
146 if (dirPath == null) {
147 this.path = fixSlashes(name);
148 } else {
149 this.path = calculatePath(dirPath, name);
150 }
151 }
152
153 /**
154 * Constructs a new File using the path of the specified URI. {@code uri}
155 * needs to be an absolute and hierarchical Unified Resource Identifier with
156 * file scheme and non-empty path component, but with undefined authority,
157 * query or fragment components.
158 *
159 * @param uri
160 * the Unified Resource Identifier that is used to construct this
161 * file.
162 * @throws IllegalArgumentException
163 * if {@code uri} does not comply with the conditions above.
164 * @see #toURI
165 * @see java.net.URI
166 */
167 public File(URI uri) {
168 // check pre-conditions
169 checkURI(uri);
170 this.path = fixSlashes(uri.getPath());
171 }
172
173 private String calculatePath(String dirPath, String name) {
174 dirPath = fixSlashes(dirPath);
175 if (!name.equals(EMPTY_STRING) || dirPath.equals(EMPTY_STRING)) {
176 // Remove all the proceeding separator chars from name
177 name = fixSlashes(name);
178
179 int separatorIndex = 0;
180 while ((separatorIndex < name.length())
181 && (name.charAt(separatorIndex) == separatorChar)) {
182 separatorIndex++;
183 }
184 if (separatorIndex > 0) {
185 name = name.substring(separatorIndex, name.length());
186 }
187
188 // Ensure there is a separator char between dirPath and name
189 if (dirPath.length() > 0
190 && (dirPath.charAt(dirPath.length() - 1) == separatorChar)) {
191 return dirPath + name;
192 }
193 return dirPath + separatorChar + name;
194 }
195
196 return dirPath;
197 }
198
199 @SuppressWarnings("nls")
200 private void checkURI(URI uri) {
201 if (!uri.isAbsolute()) {
202 throw new IllegalArgumentException(Messages.getString("luni.AD", uri));
203 } else if (!uri.getRawSchemeSpecificPart().startsWith("/")) {
204 throw new IllegalArgumentException(Messages.getString("luni.AE", uri));
205 }
206
207 String temp = uri.getScheme();
208 if (temp == null || !temp.equals("file")) {
209 throw new IllegalArgumentException(Messages.getString("luni.AF", uri));
210 }
211
212 temp = uri.getRawPath();
213 if (temp == null || temp.length() == 0) {
214 throw new IllegalArgumentException(Messages.getString("luni.B0", uri));
215 }
216
217 if (uri.getRawAuthority() != null) {
218 throw new IllegalArgumentException(Messages.getString("luni.B1",
219 new String[] { "authority", uri.toString() }));
220 }
221
222 if (uri.getRawQuery() != null) {
223 throw new IllegalArgumentException(Messages.getString("luni.B1",
224 new String[] { "query", uri.toString() }));
225 }
226
227 if (uri.getRawFragment() != null) {
228 throw new IllegalArgumentException(Messages.getString("luni.B1",
229 new String[] { "fragment", uri.toString() }));
230 }
231 }
232
233 private static native byte[][] rootsImpl();
234
235 private static native boolean isCaseSensitiveImpl();
236
237 /**
238 * Lists the file system roots. The Java platform may support zero or more
239 * file systems, each with its own platform-dependent root. Further, the
240 * canonical pathname of any file on the system will always begin with one
241 * of the returned file system roots.
242 *
243 * @return the array of file system roots.
244 */
245 public static File[] listRoots() {
246 byte[][] rootsList = rootsImpl();
247 if (rootsList == null) {
248 return new File[0];
249 }
250 File result[] = new File[rootsList.length];
251 for (int i = 0; i < rootsList.length; i++) {
252 result[i] = new File(Util.toString(rootsList[i]));
253 }
254 return result;
255 }
256
257 /**
258 * The purpose of this method is to take a path and fix the slashes up. This
259 * includes changing them all to the current platforms fileSeparator and
260 * removing duplicates.
261 */
262 private String fixSlashes(String origPath) {
263 int uncIndex = 1;
264 int length = origPath.length(), newLength = 0;
265 if (separatorChar == '/') {
266 uncIndex = 0;
267 } else if (length > 2 && origPath.charAt(1) == ':') {
268 uncIndex = 2;
269 }
270
271 boolean foundSlash = false;
272 char newPath[] = origPath.toCharArray();
273 for (int i = 0; i < length; i++) {
274 char pathChar = newPath[i];
275 if ((separatorChar == '\\' && pathChar == '\\')
276 || pathChar == '/') {
277 /* UNC Name requires 2 leading slashes */
278 if ((foundSlash && i == uncIndex) || !foundSlash) {
279 newPath[newLength++] = separatorChar;
280 foundSlash = true;
281 }
282 } else {
283 // check for leading slashes before a drive
284 if (pathChar == ':'
285 && uncIndex > 0
286 && (newLength == 2 || (newLength == 3 && newPath[1] == separatorChar))
287 && newPath[0] == separatorChar) {
288 newPath[0] = newPath[newLength - 1];
289 newLength = 1;
290 // allow trailing slash after drive letter
291 uncIndex = 2;
292 }
293 newPath[newLength++] = pathChar;
294 foundSlash = false;
295 }
296 }
297 // remove trailing slash
298 if (foundSlash
299 && (newLength > (uncIndex + 1) || (newLength == 2 && newPath[0] != separatorChar))) {
300 newLength--;
301 }
302
303 return new String(newPath, 0, newLength);
304 }
305
306 /**
307 * Indicates whether the current context is allowed to read from this file.
308 *
309 * @return {@code true} if this file can be read, {@code false} otherwise.
310 * @throws SecurityException
311 * if a {@code SecurityManager} is installed and it denies the
312 * read request.
313 */
314 public boolean canRead() {
315 if (path.length() == 0) {
316 return false;
317 }
318 SecurityManager security = System.getSecurityManager();
319 if (security != null) {
320 security.checkRead(path);
321 }
322 byte[] pp = properPath(true);
323 return existsImpl(pp) && !isWriteOnlyImpl(pp);
324 }
325
326 /**
327 * Indicates whether the current context is allowed to write to this file.
328 *
329 * @return {@code true} if this file can be written, {@code false}
330 * otherwise.
331 * @throws SecurityException
332 * if a {@code SecurityManager} is installed and it denies the
333 * write request.
334 */
335 public boolean canWrite() {
336 SecurityManager security = System.getSecurityManager();
337 if (security != null) {
338 security.checkWrite(path);
339 }
340
341 // Cannot use exists() since that does an unwanted read-check.
342 boolean exists = false;
343 if (path.length() > 0) {
344 exists = existsImpl(properPath(true));
345 }
346 return exists && !isReadOnlyImpl(properPath(true));
347 }
348
349 /**
350 * Returns the relative sort ordering of the paths for this file and the
351 * file {@code another}. The ordering is platform dependent.
352 *
353 * @param another
354 * a file to compare this file to
355 * @return an int determined by comparing the two paths. Possible values are
356 * described in the Comparable interface.
357 * @see Comparable
358 */
359 public int compareTo(File another) {
360 if (caseSensitive) {
361 return this.getPath().compareTo(another.getPath());
362 }
363 return this.getPath().compareToIgnoreCase(another.getPath());
364 }
365
366 /**
367 * Deletes this file. Directories must be empty before they will be deleted.
368 *
369 * @return {@code true} if this file was deleted, {@code false} otherwise.
370 * @throws SecurityException
371 * if a {@code SecurityManager} is installed and it denies the
372 * request.
373 * @see java.lang.SecurityManager#checkDelete
374 */
375 public boolean delete() {
376 SecurityManager security = System.getSecurityManager();
377 if (security != null) {
378 security.checkDelete(path);
379 }
380 byte[] propPath = properPath(true);
381 if ((path.length() != 0) && isDirectoryImpl(propPath)) {
382 return deleteDirImpl(propPath);
383 }
384 return deleteFileImpl(propPath);
385 }
386
387 private native boolean deleteDirImpl(byte[] filePath);
388
389 private native boolean deleteFileImpl(byte[] filePath);
390
391 /**
392 * Schedules this file to be automatically deleted once the virtual machine
393 * terminates. This will only happen when the virtual machine terminates
394 * normally as described by the Java Language Specification section 12.9.
395 *
396 * @throws SecurityException
397 * if a {@code SecurityManager} is installed and it denies the
398 * request.
399 */
400 public void deleteOnExit() {
401 SecurityManager security = System.getSecurityManager();
402 if (security != null) {
403 security.checkDelete(path);
404 }
405
406 DeleteOnExit.addFile(Util.toUTF8String(properPath(true)));
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 if (!caseSensitive) {
424 return path.equalsIgnoreCase(((File) obj).getPath());
425 }
426 return path.equals(((File) obj).getPath());
427 }
428
429 /**
430 * Returns a boolean indicating whether this file can be found on the
431 * underlying file system.
432 *
433 * @return {@code true} if this file exists, {@code false} otherwise.
434 * @throws SecurityException
435 * if a {@code SecurityManager} is installed and it denies read
436 * access to this file.
437 * @see #getPath
438 * @see java.lang.SecurityManager#checkRead(FileDescriptor)
439 */
440 public boolean exists() {
441 if (path.length() == 0) {
442 return false;
443 }
444 SecurityManager security = System.getSecurityManager();
445 if (security != null) {
446 security.checkRead(path);
447 }
448 return existsImpl(properPath(true));
449 }
450
451 private native boolean existsImpl(byte[] filePath);
452
453 /**
454 * Returns the absolute path of this file.
455 *
456 * @return the absolute file path.
457 *
458 * @see java.lang.SecurityManager#checkPropertyAccess
459 */
460 public String getAbsolutePath() {
461 byte[] absolute = properPath(false);
462 return Util.toUTF8String(absolute);
463 }
464
465 /**
466 * Returns a new file constructed using the absolute path of this file.
467 *
468 * @return a new file from this file's absolute path.
469 * @see java.lang.SecurityManager#checkPropertyAccess
470 */
471 public File getAbsoluteFile() {
472 return new File(this.getAbsolutePath());
473 }
474
475 /**
476 * Returns the absolute path of this file with all references resolved. An
477 * <em>absolute</em> path is one that begins at the root of the file
478 * system. The canonical path is one in which all references have been
479 * resolved. For the cases of '..' and '.', where the file system supports
480 * parent and working directory respectively, these are removed and replaced
481 * with a direct directory reference. If the file does not exist,
482 * getCanonicalPath() may not resolve any references and simply returns an
483 * absolute path name or throws an IOException.
484 *
485 * @return the canonical path of this file.
486 * @throws IOException
487 * if an I/O error occurs.
488 * @see java.lang.SecurityManager#checkPropertyAccess
489 */
490 public String getCanonicalPath() throws IOException {
491 byte[] result = properPath(false);
492 String absPath = Util.toUTF8String(result);
493 String canonPath = FileCanonPathCache.get(absPath);
494
495 if (canonPath != null) {
496 return canonPath;
497 }
498 if(separatorChar == '/') {
499 // resolve the full path first
500 result = resolveLink(result, result.length, false);
501 // resolve the parent directories
502 result = resolve(result);
503 }
504 int numSeparators = 1;
505 for (int i = 0; i < result.length; i++) {
506 if (result[i] == separatorChar) {
507 numSeparators++;
508 }
509 }
510 int sepLocations[] = new int[numSeparators];
511 int rootLoc = 0;
512 if (separatorChar != '/') {
513 if (result[0] == '\\') {
514 rootLoc = (result.length > 1 && result[1] == '\\') ? 1 : 0;
515 } else {
516 rootLoc = 2; // skip drive i.e. c:
517 }
518 }
519 byte newResult[] = new byte[result.length + 1];
520 int newLength = 0, lastSlash = 0, foundDots = 0;
521 sepLocations[lastSlash] = rootLoc;
522 for (int i = 0; i <= result.length; i++) {
523 if (i < rootLoc) {
524 newResult[newLength++] = result[i];
525 } else {
526 if (i == result.length || result[i] == separatorChar) {
527 if (i == result.length && foundDots == 0) {
528 break;
529 }
530 if (foundDots == 1) {
531 /* Don't write anything, just reset and continue */
532 foundDots = 0;
533 continue;
534 }
535 if (foundDots > 1) {
536 /* Go back N levels */
537 lastSlash = lastSlash > (foundDots - 1) ? lastSlash
538 - (foundDots - 1) : 0;
539 newLength = sepLocations[lastSlash] + 1;
540 foundDots = 0;
541 continue;
542 }
543 sepLocations[++lastSlash] = newLength;
544 newResult[newLength++] = (byte) separatorChar;
545 continue;
546 }
547 if (result[i] == '.') {
548 foundDots++;
549 continue;
550 }
551 /* Found some dots within text, write them out */
552 if (foundDots > 0) {
553 for (int j = 0; j < foundDots; j++) {
554 newResult[newLength++] = (byte) '.';
555 }
556 }
557 newResult[newLength++] = result[i];
558 foundDots = 0;
559 }
560 }
561 // remove trailing slash
562 if (newLength > (rootLoc + 1)
563 && newResult[newLength - 1] == separatorChar) {
564 newLength--;
565 }
566 newResult[newLength] = 0;
567 newResult = getCanonImpl(newResult);
568 newLength = newResult.length;
569 canonPath = Util.toUTF8String(newResult, 0, newLength);
570 FileCanonPathCache.put(absPath, canonPath);
571 return canonPath;
572 }
573
574 /*
575 * Resolve symbolic links in the parent directories.
576 */
577 private byte[] resolve(byte[] newResult) throws IOException {
578 int last = 1, nextSize, linkSize;
579 byte[] linkPath = newResult, bytes;
580 boolean done, inPlace;
581 for (int i = 1; i <= newResult.length; i++) {
582 if (i == newResult.length || newResult[i] == separatorChar) {
583 done = i >= newResult.length - 1;
584 // if there is only one segment, do nothing
585 if (done && linkPath.length == 1) {
586 return newResult;
587 }
588 inPlace = false;
589 if (linkPath == newResult) {
590 bytes = newResult;
591 // if there are no symbolic links, terminate the C string
592 // instead of copying
593 if (!done) {
594 inPlace = true;
595 newResult[i] = '\0';
596 }
597 } else {
598 nextSize = i - last + 1;
599 linkSize = linkPath.length;
600 if (linkPath[linkSize - 1] == separatorChar) {
601 linkSize--;
602 }
603 bytes = new byte[linkSize + nextSize];
604 System.arraycopy(linkPath, 0, bytes, 0, linkSize);
605 System.arraycopy(newResult, last - 1, bytes, linkSize,
606 nextSize);
607 // the full path has already been resolved
608 }
609 if (done) {
610 return bytes;
611 }
612 linkPath = resolveLink(bytes, inPlace ? i : bytes.length, true);
613 if (inPlace) {
614 newResult[i] = '/';
615 }
616 last = i + 1;
617 }
618 }
619 throw new InternalError();
620 }
621
622 /*
623 * Resolve a symbolic link. While the path resolves to an existing path,
624 * keep resolving. If an absolute link is found, resolve the parent
625 * directories if resolveAbsolute is true.
626 */
627 private byte[] resolveLink(byte[] pathBytes, int length,
628 boolean resolveAbsolute) throws IOException {
629 boolean restart = false;
630 byte[] linkBytes, temp;
631 do {
632 linkBytes = getLinkImpl(pathBytes);
633 if (linkBytes == pathBytes) {
634 break;
635 }
636 if (linkBytes[0] == separatorChar) {
637 // link to an absolute path, if resolving absolute paths,
638 // resolve the parent dirs again
639 restart = resolveAbsolute;
640 pathBytes = linkBytes;
641 } else {
642 int last = length - 1;
643 while (pathBytes[last] != separatorChar) {
644 last--;
645 }
646 last++;
647 temp = new byte[last + linkBytes.length];
648 System.arraycopy(pathBytes, 0, temp, 0, last);
649 System.arraycopy(linkBytes, 0, temp, last, linkBytes.length);
650 pathBytes = temp;
651 }
652 length = pathBytes.length;
653 } while (existsImpl(pathBytes));
654 // resolve the parent directories
655 if (restart) {
656 return resolve(pathBytes);
657 }
658 return pathBytes;
659 }
660
661 /**
662 * Returns a new file created using the canonical path of this file.
663 * Equivalent to {@code new File(this.getCanonicalPath())}.
664 *
665 * @return the new file constructed from this file's canonical path.
666 * @throws IOException
667 * if an I/O error occurs.
668 * @see java.lang.SecurityManager#checkPropertyAccess
669 */
670 public File getCanonicalFile() throws IOException {
671 return new File(getCanonicalPath());
672 }
673
674 private native byte[] getCanonImpl(byte[] filePath);
675
676 /**
677 * Returns the name of the file or directory represented by this file.
678 *
679 * @return this file's name or an empty string if there is no name part in
680 * the file's path.
681 */
682 public String getName() {
683 int separatorIndex = path.lastIndexOf(separator);
684 return (separatorIndex < 0) ? path : path.substring(separatorIndex + 1,
685 path.length());
686 }
687
688 /**
689 * Returns the pathname of the parent of this file. This is the path up to
690 * but not including the last name. {@code null} is returned if there is no
691 * parent.
692 *
693 * @return this file's parent pathname or {@code null}.
694 */
695 public String getParent() {
696 int length = path.length(), firstInPath = 0;
697 if (separatorChar == '\\' && length > 2 && path.charAt(1) == ':') {
698 firstInPath = 2;
699 }
700 int index = path.lastIndexOf(separatorChar);
701 if (index == -1 && firstInPath > 0) {
702 index = 2;
703 }
704 if (index == -1 || path.charAt(length - 1) == separatorChar) {
705 return null;
706 }
707 if (path.indexOf(separatorChar) == index
708 && path.charAt(firstInPath) == separatorChar) {
709 return path.substring(0, index + 1);
710 }
711 return path.substring(0, index);
712 }
713
714 /**
715 * Returns a new file made from the pathname of the parent of this file.
716 * This is the path up to but not including the last name. {@code null} is
717 * returned when there is no parent.
718 *
719 * @return a new file representing this file's parent or {@code null}.
720 */
721 public File getParentFile() {
722 String tempParent = getParent();
723 if (tempParent == null) {
724 return null;
725 }
726 return new File(tempParent);
727 }
728
729 /**
730 * Returns the path of this file.
731 *
732 * @return this file's path.
733 */
734 public String getPath() {
735 return path;
736 }
737
738 /**
739 * Answers the partition size indicated by the path name.
740 *
741 * @return the partition size counted in bytes, 0L if the path does not
742 * exist
743 */
744 public long getTotalSpace() {
745 SecurityManager security = System.getSecurityManager();
746 if (security != null) {
747 security.checkPermission(new RuntimePermission(
748 "getFileSystemAttributes")); //$NON-NLS-1$
749 }
750 return getTotalSpaceImpl(properPath(true));
751 }
752
753 private native long getTotalSpaceImpl(byte[] filePath);
754
755 /**
756 * Answers the available bytes of the partition indicated by the path name.
757 * This method checks write permission and other restrictions and offers a
758 * more precise estimate than getFreeSpace(). If the operating system does
759 * not support this information, the result will be the same as the one
760 * returned by getFreeSpace().
761 *
762 * @return the available bytes of the partition considering platform
763 * dependent permissions
764 */
765 public long getUsableSpace() {
766 SecurityManager security = System.getSecurityManager();
767 if (security != null) {
768 security.checkPermission(new RuntimePermission(
769 "getFileSystemAttributes")); //$NON-NLS-1$
770 }
771 return getUsableSpaceImpl(properPath(true));
772 }
773
774 private native long getUsableSpaceImpl(byte[] filePath);
775
776 /**
777 * Answers the available bytes of the partition indicated by the path name.
778 *
779 * @return the available bytes of the partition
780 */
781 public long getFreeSpace() {
782 SecurityManager security = System.getSecurityManager();
783 if (security != null) {
784 security.checkPermission(new RuntimePermission(
785 "getFileSystemAttributes")); //$NON-NLS-1$
786 }
787 return getFreeSpaceImpl(properPath(true));
788 }
789
790 private native long getFreeSpaceImpl(byte[] filePath);
791
792 /**
793 * Returns an integer hash code for the receiver. Any two objects for which
794 * {@code equals} returns {@code true} must return the same hash code.
795 *
796 * @return this files's hash value.
797 * @see #equals
798 */
799 @Override
800 public int hashCode() {
801 if (caseSensitive) {
802 return path.hashCode() ^ 1234321;
803 }
804 return path.toLowerCase().hashCode() ^ 1234321;
805 }
806
807 /**
808 * Indicates if this file's pathname is absolute. Whether a pathname is
809 * absolute is platform specific. On UNIX, absolute paths must start with
810 * the character '/'; on Windows it is absolute if either it starts with
811 * '\\' (to represent a file server), or a letter followed by a colon.
812 *
813 * @return {@code true} if this file's pathname is absolute, {@code false}
814 * otherwise.
815 * @see #getPath
816 */
817 public boolean isAbsolute() {
818 if (File.separatorChar == '\\') {
819 // for windows
820 if (path.length() > 1 && path.charAt(0) == File.separatorChar
821 && path.charAt(1) == File.separatorChar) {
822 return true;
823 }
824 if (path.length() > 2) {
825 if (Character.isLetter(path.charAt(0)) && path.charAt(1) == ':'
826 && (path.charAt(2) == '/' || path.charAt(2) == '\\')) {
827 return true;
828 }
829 }
830 return false;
831 }
832
833 // for Linux
834 return (path.length() > 0 && path.charAt(0) == File.separatorChar);
835 }
836
837 /**
838 * Indicates if this file represents a <em>directory</em> on the
839 * underlying file system.
840 *
841 * @return {@code true} if this file is a directory, {@code false}
842 * otherwise.
843 * @throws SecurityException
844 * if a {@code SecurityManager} is installed and it denies read
845 * access to this file.
846 */
847 public boolean isDirectory() {
848 if (path.length() == 0) {
849 return false;
850 }
851 SecurityManager security = System.getSecurityManager();
852 if (security != null) {
853 security.checkRead(path);
854 }
855 return isDirectoryImpl(properPath(true));
856 }
857
858 private native boolean isDirectoryImpl(byte[] filePath);
859
860 /**
861 * Indicates if this file represents a <em>file</em> on the underlying
862 * file system.
863 *
864 * @return {@code true} if this file is a file, {@code false} otherwise.
865 * @throws SecurityException
866 * if a {@code SecurityManager} is installed and it denies read
867 * access to this file.
868 */
869 public boolean isFile() {
870 if (path.length() == 0) {
871 return false;
872 }
873 SecurityManager security = System.getSecurityManager();
874 if (security != null) {
875 security.checkRead(path);
876 }
877 return isFileImpl(properPath(true));
878 }
879
880 private native boolean isFileImpl(byte[] filePath);
881
882 /**
883 * Returns whether or not this file is a hidden file as defined by the
884 * operating system. The notion of "hidden" is system-dependent. For Unix
885 * systems a file is considered hidden if its name starts with a ".". For
886 * Windows systems there is an explicit flag in the file system for this
887 * purpose.
888 *
889 * @return {@code true} if the file is hidden, {@code false} otherwise.
890 * @throws SecurityException
891 * if a {@code SecurityManager} is installed and it denies read
892 * access to this file.
893 */
894 public boolean isHidden() {
895 if (path.length() == 0) {
896 return false;
897 }
898 SecurityManager security = System.getSecurityManager();
899 if (security != null) {
900 security.checkRead(path);
901 }
902 return isHiddenImpl(properPath(true));
903 }
904
905 private native boolean isHiddenImpl(byte[] filePath);
906
907 private native boolean isReadOnlyImpl(byte[] filePath);
908
909 private native boolean isWriteOnlyImpl(byte[] filePath);
910
911 private native byte[] getLinkImpl(byte[] filePath);
912
913 /**
914 * Returns the time when this file was last modified, measured in
915 * milliseconds since January 1st, 1970, midnight.
916 *
917 * @return the time when this file was last modified.
918 * @throws SecurityException
919 * if a {@code SecurityManager} is installed and it denies read
920 * access to this file.
921 */
922 public long lastModified() {
923 SecurityManager security = System.getSecurityManager();
924 if (security != null) {
925 security.checkRead(path);
926 }
927 long result = lastModifiedImpl(properPath(true));
928 /* Temporary code to handle both return cases until natives fixed */
929 if (result == -1 || result == 0) {
930 return 0;
931 }
932 return result;
933 }
934
935 private native long lastModifiedImpl(byte[] filePath);
936
937 /**
938 * Sets the time this file was last modified, measured in milliseconds since
939 * January 1st, 1970, midnight.
940 *
941 * @param time
942 * the last modification time for this file.
943 * @return {@code true} if the operation is successful, {@code false}
944 * otherwise.
945 * @throws IllegalArgumentException
946 * if {@code time < 0}.
947 * @throws SecurityException
948 * if a {@code SecurityManager} is installed and it denies write
949 * access to this file.
950 */
951 public boolean setLastModified(long time) {
952 if (time < 0) {
953 throw new IllegalArgumentException(Messages.getString("luni.B2")); //$NON-NLS-1$
954 }
955 SecurityManager security = System.getSecurityManager();
956 if (security != null) {
957 security.checkWrite(path);
958 }
959 return (setLastModifiedImpl(properPath(true), time));
960 }
961
962 private native boolean setLastModifiedImpl(byte[] path, long time);
963
964 /**
965 * Marks this file or directory to be read-only as defined by the operating
966 * system.
967 *
968 * @return {@code true} if the operation is successful, {@code false}
969 * otherwise.
970 * @throws SecurityException
971 * if a {@code SecurityManager} is installed and it denies write
972 * access to this file.
973 */
974 public boolean setReadOnly() {
975 SecurityManager security = System.getSecurityManager();
976 if (security != null) {
977 security.checkWrite(path);
978 }
979 return (setReadOnlyImpl(properPath(true)));
980 }
981
982 private native boolean setReadOnlyImpl(byte[] path);
983
984 /**
985 * Manipulates the read permission of the abstract path designated by this
986 * file object.
987 *
988 * @param readable
989 * To allow read permission if true, otherwise disallow
990 * @param ownerOnly
991 * To manipulate read permission only for owner if true,
992 * otherwise for everyone. The manipulation will apply to
993 * everyone regardless of this value if the underlying system
994 * does not distinguish owner and other users.
995 * @return true if and only if the operation succeeded. If the user does not
996 * have permission to change the access permissions of this abstract
997 * pathname the operation will fail. If the underlying file system
998 * does not support read permission and the value of readable is
999 * false, this operation will fail.
1000 * @throws SecurityException -
1001 * If a security manager exists and
1002 * SecurityManager.checkWrite(java.lang.String) disallows write
1003 * permission to this file object
1004 * @since 1.6
1005 */
1006 public boolean setReadable(boolean readable, boolean ownerOnly) {
1007 checkWrite();
1008 return (setReadableImpl(properPath(true), readable, ownerOnly));
1009 }
1010
1011 /**
1012 * A convenience method for setReadable(boolean, boolean). An invocation of
1013 * this method is the same as file.setReadable(readable, true).
1014 *
1015 * @param readable
1016 * To allow read permission if true, otherwise disallow
1017 * @return true if and only if the operation succeeded. If the user does not
1018 * have permission to change the access permissions of this abstract
1019 * pathname the operation will fail. If the underlying file system
1020 * does not support read permission and the value of readable is
1021 * false, this operation will fail.
1022 * @throws SecurityException -
1023 * If a security manager exists and
1024 * SecurityManager.checkWrite(java.lang.String) disallows write
1025 * permission to this file object
1026 * @since 1.6
1027 */
1028 public boolean setReadable(boolean readable) {
1029 return setReadable(readable, true);
1030 }
1031
1032 private native boolean setReadableImpl(byte[] path, boolean readable,
1033 boolean ownerOnly);
1034
1035 /**
1036 * Manipulates the write permission of the abstract path designated by this
1037 * file object.
1038 *
1039 * @param writable
1040 * To allow write permission if true, otherwise disallow
1041 * @param ownerOnly
1042 * To manipulate write permission only for owner if true,
1043 * otherwise for everyone. The manipulation will apply to
1044 * everyone regardless of this value if the underlying system
1045 * does not distinguish owner and other users.
1046 * @return true if and only if the operation succeeded. If the user does not
1047 * have permission to change the access permissions of this abstract
1048 * pathname the operation will fail.
1049 * @throws SecurityException -
1050 * If a security manager exists and
1051 * SecurityManager.checkWrite(java.lang.String) disallows write
1052 * permission to this file object
1053 * @since 1.6
1054 */
1055 public boolean setWritable(boolean writable, boolean ownerOnly) {
1056 checkWrite();
1057 return (setWritableImpl(properPath(true), writable, ownerOnly));
1058 }
1059
1060 private native boolean setWritableImpl(byte[] path, boolean writable,
1061 boolean ownerOnly);
1062
1063 /**
1064 * A convenience method for setWritable(boolean, boolean). An invocation of
1065 * this method is the same as file.setWritable(writable, true).
1066 *
1067 * @param writable
1068 * To allow write permission if true, otherwise disallow
1069 * @return true if and only if the operation succeeded. If the user does not
1070 * have permission to change the access permissions of this abstract
1071 * pathname the operation will fail.
1072 * @throws SecurityException -
1073 * If a security manager exists and
1074 * SecurityManager.checkWrite(java.lang.String) disallows write
1075 * permission to this file object
1076 * @since 1.6
1077 */
1078 public boolean setWritable(boolean writable) {
1079 return setWritable(writable, true);
1080 }
1081
1082 /**
1083 * Returns the length of this file in bytes.
1084 *
1085 * @return the number of bytes in this file.
1086 * @throws SecurityException
1087 * if a {@code SecurityManager} is installed and it denies read
1088 * access to this file.
1089 */
1090 public long length() {
1091 SecurityManager security = System.getSecurityManager();
1092 if (security != null) {
1093 security.checkRead(path);
1094 }
1095 return lengthImpl(properPath(true));
1096 }
1097
1098 private native long lengthImpl(byte[] filePath);
1099
1100 /**
1101 * Returns an array of strings with the file names in the directory
1102 * represented by this file. The result is {@code null} if this file is not
1103 * a directory.
1104 * <p>
1105 * The entries {@code .} and {@code ..} representing the current and parent
1106 * directory are not returned as part of the list.
1107 *
1108 * @return an array of strings with file names or {@code null}.
1109 * @throws SecurityException
1110 * if a {@code SecurityManager} is installed and it denies read
1111 * access to this file.
1112 * @see #isDirectory
1113 * @see java.lang.SecurityManager#checkRead(FileDescriptor)
1114 */
1115 public java.lang.String[] list() {
1116 SecurityManager security = System.getSecurityManager();
1117 if (security != null) {
1118 security.checkRead(path);
1119 }
1120
1121 if (path.length() == 0) {
1122 return null;
1123 }
1124
1125 byte[] bs = properPath(true);
1126 if (!isDirectoryImpl(bs) || !existsImpl(bs) || isWriteOnlyImpl(bs)) {
1127 return null;
1128 }
1129
1130 byte[][] implList = listImpl(bs);
1131 if (implList == null) {
1132 // empty list
1133 return new String[0];
1134 }
1135 String result[] = new String[implList.length];
1136 for (int index = 0; index < implList.length; index++) {
1137 result[index] = Util.toUTF8String(implList[index]);
1138 }
1139 return result;
1140 }
1141
1142 /**
1143 * Returns an array of files contained in the directory represented by this
1144 * file. The result is {@code null} if this file is not a directory. The
1145 * paths of the files in the array are absolute if the path of this file is
1146 * absolute, they are relative otherwise.
1147 *
1148 * @return an array of files or {@code null}.
1149 * @throws SecurityException
1150 * if a {@code SecurityManager} is installed and it denies read
1151 * access to this file.
1152 * @see #list
1153 * @see #isDirectory
1154 */
1155 public File[] listFiles() {
1156 String[] tempNames = list();
1157 if (tempNames == null) {
1158 return null;
1159 }
1160 int resultLength = tempNames.length;
1161 File results[] = new File[resultLength];
1162 for (int i = 0; i < resultLength; i++) {
1163 results[i] = new File(this, tempNames[i]);
1164 }
1165 return results;
1166 }
1167
1168 /**
1169 * Gets a list of the files in the directory represented by this file. This
1170 * list is then filtered through a FilenameFilter and files with matching
1171 * names are returned as an array of files. Returns {@code null} if this
1172 * file is not a directory. If {@code filter} is {@code null} then all
1173 * filenames match.
1174 * <p>
1175 * The entries {@code .} and {@code ..} representing the current and parent
1176 * directories are not returned as part of the list.
1177 *
1178 * @param filter
1179 * the filter to match names against, may be {@code null}.
1180 * @return an array of files or {@code null}.
1181 * @throws SecurityException
1182 * if a {@code SecurityManager} is installed and it denies read
1183 * access to this file.
1184 * @see #list(FilenameFilter filter)
1185 * @see #getPath
1186 * @see #isDirectory
1187 * @see java.lang.SecurityManager#checkRead(FileDescriptor)
1188 */
1189 public File[] listFiles(FilenameFilter filter) {
1190 String[] tempNames = list(filter);
1191 if (tempNames == null) {
1192 return null;
1193 }
1194 int resultLength = tempNames.length;
1195 File results[] = new File[resultLength];
1196 for (int i = 0; i < resultLength; i++) {
1197 results[i] = new File(this, tempNames[i]);
1198 }
1199 return results;
1200 }
1201
1202 /**
1203 * Gets a list of the files in the directory represented by this file. This
1204 * list is then filtered through a FileFilter and matching files are
1205 * returned as an array of files. Returns {@code null} if this file is not a
1206 * directory. If {@code filter} is {@code null} then all files match.
1207 * <p>
1208 * The entries {@code .} and {@code ..} representing the current and parent
1209 * directories are not returned as part of the list.
1210 *
1211 * @param filter
1212 * the filter to match names against, may be {@code null}.
1213 * @return an array of files or {@code null}.
1214 * @throws SecurityException
1215 * if a {@code SecurityManager} is installed and it denies read
1216 * access to this file.
1217 * @see #getPath
1218 * @see #isDirectory
1219 * @see java.lang.SecurityManager#checkRead(FileDescriptor)
1220 */
1221 public File[] listFiles(FileFilter filter) {
1222 SecurityManager security = System.getSecurityManager();
1223 if (security != null) {
1224 security.checkRead(path);
1225 }
1226
1227 if (path.length() == 0) {
1228 return null;
1229 }
1230
1231 byte[] bs = properPath(true);
1232 if (!isDirectoryImpl(bs) || !existsImpl(bs) || isWriteOnlyImpl(bs)) {
1233 return null;
1234 }
1235
1236 byte[][] implList = listImpl(bs);
1237 if (implList == null) {
1238 return new File[0];
1239 }
1240 List<File> tempResult = new ArrayList<File>();
1241 for (int index = 0; index < implList.length; index++) {
1242 String aName = Util.toString(implList[index]);
1243 File aFile = new File(this, aName);
1244 if (filter == null || filter.accept(aFile)) {
1245 tempResult.add(aFile);
1246 }
1247 }
1248 return tempResult.toArray(new File[tempResult.size()]);
1249 }
1250
1251 /**
1252 * Gets a list of the files in the directory represented by this file. This
1253 * list is then filtered through a FilenameFilter and the names of files
1254 * with matching names are returned as an array of strings. Returns
1255 * {@code null} if this file is not a directory. If {@code filter} is
1256 * {@code null} then all filenames match.
1257 * <p>
1258 * The entries {@code .} and {@code ..} representing the current and parent
1259 * directories are not returned as part of the list.
1260 *
1261 * @param filter
1262 * the filter to match names against, may be {@code null}.
1263 * @return an array of files or {@code null}.
1264 * @throws SecurityException
1265 * if a {@code SecurityManager} is installed and it denies read
1266 * access to this file.
1267 * @see #getPath
1268 * @see #isDirectory
1269 * @see java.lang.SecurityManager#checkRead(FileDescriptor)
1270 */
1271 public java.lang.String[] list(FilenameFilter filter) {
1272 SecurityManager security = System.getSecurityManager();
1273 if (security != null) {
1274 security.checkRead(path);
1275 }
1276
1277 if (path.length() == 0) {
1278 return null;
1279 }
1280
1281 byte[] bs = properPath(true);
1282 if (!isDirectoryImpl(bs) || !existsImpl(bs) || isWriteOnlyImpl(bs)) {
1283 return null;
1284 }
1285
1286 byte[][] implList = listImpl(bs);
1287 if (implList == null) {
1288 // empty list
1289 return new String[0];
1290 }
1291 List<String> tempResult = new ArrayList<String>();
1292 for (int index = 0; index < implList.length; index++) {
1293 String aName = Util.toString(implList[index]);
1294 if (filter == null || filter.accept(this, aName)) {
1295 tempResult.add(aName);
1296 }
1297 }
1298
1299 return tempResult.toArray(new String[tempResult.size()]);
1300 }
1301
1302 private synchronized static native byte[][] listImpl(byte[] path);
1303
1304 /**
1305 * Creates the directory named by the trailing filename of this file. Does
1306 * not create the complete path required to create this directory.
1307 *
1308 * @return {@code true} if the directory has been created, {@code false}
1309 * otherwise.
1310 * @throws SecurityException
1311 * if a {@code SecurityManager} is installed and it denies write
1312 * access for this file.
1313 * @see #mkdirs
1314 */
1315 public boolean mkdir() {
1316 SecurityManager security = System.getSecurityManager();
1317 if (security != null) {
1318 security.checkWrite(path);
1319 }
1320 return mkdirImpl(properPath(true));
1321 }
1322
1323 private native boolean mkdirImpl(byte[] filePath);
1324
1325 /**
1326 * Creates the directory named by the trailing filename of this file,
1327 * including the complete directory path required to create this directory.
1328 *
1329 * @return {@code true} if the necessary directories have been created,
1330 * {@code false} if the target directory already exists or one of
1331 * the directories can not be created.
1332 * @throws SecurityException
1333 * if a {@code SecurityManager} is installed and it denies write
1334 * access for this file.
1335 * @see #mkdir
1336 */
1337 public boolean mkdirs() {
1338 /* If the terminal directory already exists, answer false */
1339 if (exists()) {
1340 return false;
1341 }
1342
1343 /* If the receiver can be created, answer true */
1344 if (mkdir()) {
1345 return true;
1346 }
1347
1348 String parentDir = getParent();
1349 /* If there is no parent and we were not created, answer false */
1350 if (parentDir == null) {
1351 return false;
1352 }
1353
1354 /* Otherwise, try to create a parent directory and then this directory */
1355 return (new File(parentDir).mkdirs() && mkdir());
1356 }
1357
1358 /**
1359 * Creates a new, empty file on the file system according to the path
1360 * information stored in this file.
1361 *
1362 * @return {@code true} if the file has been created, {@code false} if it
1363 * already exists.
1364 * @throws IOException
1365 * if an I/O error occurs or the directory does not exist where
1366 * the file should have been created.
1367 * @throws SecurityException
1368 * if a {@code SecurityManager} is installed and it denies write
1369 * access for this file.
1370 */
1371 public boolean createNewFile() throws IOException {
1372 SecurityManager security = System.getSecurityManager();
1373 if (security != null) {
1374 security.checkWrite(path);
1375 }
1376 if (0 == path.length()) {
1377 throw new IOException(Messages.getString("luni.B3")); //$NON-NLS-1$
1378 }
1379 int result = newFileImpl(properPath(true));
1380 switch (result) {
1381 case 0:
1382 return true;
1383 case 1:
1384 return false;
1385 default:
1386 throw new IOException(Messages.getString("luni.B4", path)); //$NON-NLS-1$
1387 }
1388 }
1389
1390 private native int newFileImpl(byte[] filePath);
1391
1392 /**
1393 * Creates an empty temporary file using the given prefix and suffix as part
1394 * of the file name. If suffix is {@code null}, {@code .tmp} is used. This
1395 * method is a convenience method that calls
1396 * {@link #createTempFile(String, String, File)} with the third argument
1397 * being {@code null}.
1398 *
1399 * @param prefix
1400 * the prefix to the temp file name.
1401 * @param suffix
1402 * the suffix to the temp file name.
1403 * @return the temporary file.
1404 * @throws IOException
1405 * if an error occurs when writing the file.
1406 */
1407 public static File createTempFile(String prefix, String suffix)
1408 throws IOException {
1409 return createTempFile(prefix, suffix, null);
1410 }
1411
1412 /**
1413 * Creates an empty temporary file in the given directory using the given
1414 * prefix and suffix as part of the file name.
1415 *
1416 * @param prefix
1417 * the prefix to the temp file name.
1418 * @param suffix
1419 * the suffix to the temp file name.
1420 * @param directory
1421 * the location to which the temp file is to be written, or
1422 * {@code null} for the default location for temporary files,
1423 * which is taken from the "java.io.tmpdir" system property. It
1424 * may be necessary to set this property to an existing, writable
1425 * directory for this method to work properly.
1426 * @return the temporary file.
1427 * @throws IllegalArgumentException
1428 * if the length of {@code prefix} is less than 3.
1429 * @throws IOException
1430 * if an error occurs when writing the file.
1431 */
1432 @SuppressWarnings("nls")
1433 public static File createTempFile(String prefix, String suffix,
1434 File directory) throws IOException {
1435 // Force a prefix null check first
1436 if (prefix.length() < 3) {
1437 throw new IllegalArgumentException(Messages.getString("luni.B5"));
1438 }
1439 String newSuffix = suffix == null ? ".tmp" : suffix;
1440 File tmpDirFile;
1441 if (directory == null) {
1442 String tmpDir = AccessController.doPrivileged(
1443 new PriviAction<String>("java.io.tmpdir", "."));
1444 tmpDirFile = new File(tmpDir);
1445 } else {
1446 tmpDirFile = directory;
1447 }
1448 File result;
1449 do {
1450 result = genTempFile(prefix, newSuffix, tmpDirFile);
1451 } while (!result.createNewFile());
1452 return result;
1453 }
1454
1455 private static File genTempFile(String prefix, String suffix, File directory) {
1456 if (counter == 0) {
1457 int newInt = new SecureRandom().nextInt();
1458 counter = ((newInt / 65535) & 0xFFFF) + 0x2710;
1459 }
1460 StringBuilder newName = new StringBuilder();
1461 newName.append(prefix);
1462 newName.append(counter++);
1463 newName.append(suffix);
1464 return new File(directory, newName.toString());
1465 }
1466
1467 /**
1468 * Returns a string representing the proper path for this file. If this file
1469 * path is absolute, the user.dir property is not prepended, otherwise it
1470 * is.
1471 *
1472 * @param internal
1473 * is user.dir internal.
1474 * @return the proper path.
1475 */
1476 byte[] properPath(boolean internal) {
1477 if (properPath != null) {
1478 return properPath;
1479 }
1480
1481 if (isAbsolute()) {
1482 byte[] pathBytes = Util.getUTF8Bytes(path);
1483 return properPath = pathBytes;
1484 }
1485 // Check security by getting user.dir when the path is not absolute
1486 String userdir;
1487 if (internal) {
1488 userdir = AccessController.doPrivileged(new PriviAction<String>(
1489 "user.dir")); //$NON-NLS-1$
1490 } else {
1491 userdir = System.getProperty("user.dir"); //$NON-NLS-1$
1492 }
1493
1494 if (path.length() == 0) {
1495 return properPath = Util.getUTF8Bytes(userdir);
1496 }
1497 int length = userdir.length();
1498
1499 // Handle windows-like path
1500 if (path.charAt(0) == '\\') {
1501 if (length > 1 && userdir.charAt(1) == ':') {
1502 return properPath = Util.getUTF8Bytes(userdir.substring(0, 2)
1503 + path);
1504 }
1505 path = path.substring(1);
1506 }
1507
1508 // Handle separator
1509 String result = userdir;
1510 if (userdir.charAt(length - 1) != separatorChar) {
1511 if (path.charAt(0) != separatorChar) {
1512 result += separator;
1513 }
1514 } else if (path.charAt(0) == separatorChar) {
1515 result = result.substring(0, length - 2);
1516
1517 }
1518 result += path;
1519 return properPath = Util.getUTF8Bytes(result);
1520 }
1521
1522 /**
1523 * Renames this file to the name represented by the {@code dest} file. This
1524 * works for both normal files and directories.
1525 *
1526 * @param dest
1527 * the file containing the new name.
1528 * @return {@code true} if the File was renamed, {@code false} otherwise.
1529 * @throws SecurityException
1530 * if a {@code SecurityManager} is installed and it denies write
1531 * access for this file or the {@code dest} file.
1532 */
1533 public boolean renameTo(java.io.File dest) {
1534 SecurityManager security = System.getSecurityManager();
1535 if (security != null) {
1536 security.checkWrite(path);
1537 security.checkWrite(dest.path);
1538 }
1539 return renameToImpl(properPath(true), dest.properPath(true));
1540 }
1541
1542 private native boolean renameToImpl(byte[] pathExist, byte[] pathNew);
1543
1544 /**
1545 * Returns a string containing a concise, human-readable description of this
1546 * file.
1547 *
1548 * @return a printable representation of this file.
1549 */
1550 @Override
1551 public String toString() {
1552 return path;
1553 }
1554
1555 /**
1556 * Returns a Uniform Resource Identifier for this file. The URI is system
1557 * dependent and may not be transferable between different operating / file
1558 * systems.
1559 *
1560 * @return an URI for this file.
1561 */
1562 @SuppressWarnings("nls")
1563 public URI toURI() {
1564 String name = getAbsoluteName();
1565 try {
1566 if (!name.startsWith("/")) {
1567 // start with sep.
1568 return new URI("file", null, new StringBuilder(
1569 name.length() + 1).append('/').append(name).toString(),
1570 null, null);
1571 } else if (name.startsWith("//")) {
1572 return new URI("file", "", name, null); // UNC path
1573 }
1574 return new URI("file", null, name, null, null);
1575 } catch (URISyntaxException e) {
1576 // this should never happen
1577 return null;
1578 }
1579 }
1580
1581 /**
1582 * Returns a Uniform Resource Locator for this file. The URL is system
1583 * dependent and may not be transferable between different operating / file
1584 * systems.
1585 *
1586 * @return a URL for this file.
1587 * @throws java.net.MalformedURLException
1588 * if the path cannot be transformed into a URL.
1589 *
1590 * @deprecated use {@link #toURI} and {@link java.net.URI#toURL} to get
1591 * correct escaping of illegal characters.
1592 */
1593 @Deprecated
1594 @SuppressWarnings("nls")
1595 public URL toURL() throws java.net.MalformedURLException {
1596 String name = getAbsoluteName();
1597 if (!name.startsWith("/")) {
1598 // start with sep.
1599 return new URL(
1600 "file", EMPTY_STRING, -1, new StringBuilder(name.length() + 1) //$NON-NLS-1$
1601 .append('/').append(name).toString(), null);
1602 } else if (name.startsWith("//")) {
1603 return new URL("file:" + name); // UNC path
1604 }
1605 return new URL("file", EMPTY_STRING, -1, name, null);
1606 }
1607
1608 private String getAbsoluteName() {
1609 File f = getAbsoluteFile();
1610 String name = f.getPath();
1611
1612 if (f.isDirectory() && name.charAt(name.length() - 1) != separatorChar) {
1613 // Directories must end with a slash
1614 name = new StringBuilder(name.length() + 1).append(name)
1615 .append('/').toString();
1616 }
1617 if (separatorChar != '/') { // Must convert slashes.
1618 name = name.replace(separatorChar, '/');
1619 }
1620 return name;
1621 }
1622
1623 private void writeObject(ObjectOutputStream stream) throws IOException {
1624 stream.defaultWriteObject();
1625 stream.writeChar(separatorChar);
1626
1627 }
1628
1629 private void readObject(ObjectInputStream stream) throws IOException,
1630 ClassNotFoundException {
1631 stream.defaultReadObject();
1632 char inSeparator = stream.readChar();
1633 path = path.replace(inSeparator, separatorChar);
1634 }
1635
1636 /**
1637 * Manipulates the execute permission of the abstract path designated by
1638 * this file object.
1639 *
1640 * @param executable
1641 * To allow execute permission if true, otherwise disallow
1642 * @param ownerOnly
1643 * To manipulate execute permission only for owner if true,
1644 * otherwise for everyone. The manipulation will apply to
1645 * everyone regardless of this value if the underlying system
1646 * does not distinguish owner and other users.
1647 * @return true if and only if the operation succeeded. If the user does not
1648 * have permission to change the access permissions of this abstract
1649 * pathname the operation will fail. If the underlying file system
1650 * does not support execute permission and the value of executable
1651 * is false, this operation will fail.
1652 * @throws SecurityException -
1653 * If a security manager exists and
1654 * SecurityManager.checkWrite(java.lang.String) disallows write
1655 * permission to this file object
1656 * @since 1.6
1657 */
1658 public boolean setExecutable(boolean executable, boolean ownerOnly) {
1659 checkWrite();
1660 return (setExecutableImpl(properPath(true), executable, ownerOnly));
1661 }
1662
1663 /**
1664 * A convenience method for setExecutable(boolean, boolean). An invocation
1665 * of this method is the same as file.setExecutable(executable, true).
1666 *
1667 * @param executable
1668 * To allow execute permission if true, otherwise disallow
1669 * @return true if and only if the operation succeeded. If the user does not
1670 * have permission to change the access permissions of this abstract
1671 * pathname the operation will fail. If the underlying file system
1672 * does not support execute permission and the value of executable
1673 * is false, this operation will fail.
1674 * @throws SecurityException -
1675 * If a security manager exists and
1676 * SecurityManager.checkWrite(java.lang.String) disallows write
1677 * permission to this file object
1678 * @since 1.6
1679 */
1680 public boolean setExecutable(boolean executable) {
1681 return setExecutable(executable, true);
1682 }
1683
1684 private native boolean setExecutableImpl(byte[] path, boolean executable,
1685 boolean ownerOnly);
1686
1687
1688
1689 /**
1690 * Answers a boolean indicating whether or not the current context is
1691 * allowed to execute this File.
1692 *
1693 * @return <code>true</code> if this File can be executed,
1694 * <code>false</code> otherwise.
1695 *
1696 * @throws SecurityException
1697 * If a security manager exists and
1698 * SecurityManager.checkExec(java.lang.String) disallows read
1699 * permission to this file object
1700 * @see java.lang.SecurityManager#checkExec(String)
1701 *
1702 * @since 1.6
1703 */
1704 public boolean canExecute() {
1705 checkExec();
1706 return exists() && isExecutableImpl(properPath(true));
1707 }
1708
1709 private native boolean isExecutableImpl(byte[] filePath);
1710
1711 private void checkExec() {
1712 SecurityManager security = System.getSecurityManager();
1713 if (security != null) {
1714 security.checkExec(path);
1715 }
1716 }
1717
1718 private void checkWrite() {
1719 SecurityManager security = System.getSecurityManager();
1720 if (security != null) {
1721 security.checkWrite(path);
1722 }
1723 }
1724
1725 }