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