More java.io.File cleanup.
Remove the duplication between the various list and listFiles methods.
This also makes the variants that take a filter no less efficient than
those that don't, for the special case of the null filter.
The upstream code sometimes assumed returned filenames were UTF-8, and
at other times assumed returned filenames were in the platform encoding.
The new code always uses UTF-8, to match filenames sent to the operating
system, which were and are always sent as UTF-8 byte sequences.
This patch does not alter the native listImpl method. That's for the
next patch.
A couple of unrelated cosmetic changes have been made in this patch:
declarations of native methods have been moved next to the methods
they implement, and a minor simplification has been made to the
documentation of isAbsolute.
Bug: 2281992
diff --git a/libcore/luni/src/main/java/java/io/File.java b/libcore/luni/src/main/java/java/io/File.java
index 9d7de8c..62ca603 100644
--- a/libcore/luni/src/main/java/java/io/File.java
+++ b/libcore/luni/src/main/java/java/io/File.java
@@ -48,11 +48,18 @@
* of the file system) or relative to the current directory in which the program
* is running.
* <p>
- * This class provides methods for querying/changing information about the file
- * as well as directory listing capabilities if the file represents a directory.
+ * The actual file referenced by a {@code File} may or may not exist. It may
+ * also, despite the name {@code File}, be a directory or other non-regular
+ * file.
* <p>
- * When manipulating file paths, the static fields of this class may be used to
- * determine the platform specific separators.
+ * This class provides limited functionality for getting/setting file
+ * permissions, file type, and last modified time.
+ * <p>
+ * Although Java doesn't specify a character encoding for filenames, on Android
+ * Java strings are converted to UTF-8 byte sequences when sending filenames to
+ * the operating system, and byte sequences returned by the operating system
+ * (from the various {@code list} methods) are converted to Java strings by
+ * decoding them as UTF-8 byte sequences.
*
* @see java.io.Serializable
* @see java.lang.Comparable
@@ -321,6 +328,8 @@
return isReadableImpl(pathBytes);
}
+ private native boolean isReadableImpl(byte[] filePath);
+
/**
* Indicates whether the current context is allowed to write to this file.
*
@@ -341,6 +350,8 @@
return isWritableImpl(pathBytes);
}
+ private native boolean isWritableImpl(byte[] filePath);
+
/**
* Returns the relative sort ordering of the paths for this file and the
* file {@code another}. The ordering is platform dependent.
@@ -649,6 +660,8 @@
return pathBytes;
}
+ private native byte[] getLinkImpl(byte[] filePath);
+
/**
* Returns a new file created using the canonical path of this file.
* Equivalent to {@code new File(this.getCanonicalPath())}.
@@ -740,9 +753,8 @@
/**
* Indicates if this file's pathname is absolute. Whether a pathname is
- * absolute is platform specific. On UNIX, absolute paths must start with
- * the character '/'; on Windows it is absolute if either it starts with
- * '\\' (to represent a file server), or a letter followed by a colon.
+ * absolute is platform specific. On Android, absolute paths start with
+ * the character '/'.
*
* @return {@code true} if this file's pathname is absolute, {@code false}
* otherwise.
@@ -820,12 +832,6 @@
return getName().startsWith(".");
}
- private native boolean isReadableImpl(byte[] filePath);
-
- private native boolean isWritableImpl(byte[] filePath);
-
- private native byte[] getLinkImpl(byte[] filePath);
-
/**
* Returns the time when this file was last modified, measured in
* milliseconds since January 1st, 1970, midnight.
@@ -937,7 +943,7 @@
* @see #isDirectory
* @see java.lang.SecurityManager#checkRead(FileDescriptor)
*/
- public java.lang.String[] list() {
+ public String[] list() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(path);
@@ -947,23 +953,58 @@
return null;
}
- byte[] bs = pathBytes;
- if (!isDirectoryImpl(bs) || !existsImpl(bs) || !isReadableImpl(bs)) {
+ // TODO: rewrite the JNI so the rest of this method is just "return listImpl(pathBytes);"
+ if (!isDirectoryImpl(pathBytes) || !existsImpl(pathBytes) || !isReadableImpl(pathBytes)) {
return null;
}
-
- byte[][] implList = listImpl(bs);
+ byte[][] implList = listImpl(pathBytes);
if (implList == null) {
// empty list
return new String[0];
}
- String result[] = new String[implList.length];
+ String[] result = new String[implList.length];
for (int index = 0; index < implList.length; index++) {
result[index] = Util.toUTF8String(implList[index]);
}
return result;
}
+ private synchronized static native byte[][] listImpl(byte[] path);
+
+ /**
+ * Gets a list of the files in the directory represented by this file. This
+ * list is then filtered through a FilenameFilter and the names of files
+ * with matching names are returned as an array of strings. Returns
+ * {@code null} if this file is not a directory. If {@code filter} is
+ * {@code null} then all filenames match.
+ * <p>
+ * The entries {@code .} and {@code ..} representing the current and parent
+ * directories are not returned as part of the list.
+ *
+ * @param filter
+ * the filter to match names against, may be {@code null}.
+ * @return an array of files or {@code null}.
+ * @throws SecurityException
+ * if a {@code SecurityManager} is installed and it denies read
+ * access to this file.
+ * @see #getPath
+ * @see #isDirectory
+ * @see java.lang.SecurityManager#checkRead(FileDescriptor)
+ */
+ public String[] list(FilenameFilter filter) {
+ String[] filenames = list();
+ if (filter == null || filenames == null) {
+ return filenames;
+ }
+ List<String> result = new ArrayList<String>(filenames.length);
+ for (String filename : filenames) {
+ if (filter.accept(this, filename)) {
+ result.add(filename);
+ }
+ }
+ return result.toArray(new String[result.size()]);
+ }
+
/**
* Returns an array of files contained in the directory represented by this
* file. The result is {@code null} if this file is not a directory. The
@@ -978,16 +1019,7 @@
* @see #isDirectory
*/
public File[] listFiles() {
- String[] tempNames = list();
- if (tempNames == null) {
- return null;
- }
- int resultLength = tempNames.length;
- File results[] = new File[resultLength];
- for (int i = 0; i < resultLength; i++) {
- results[i] = new File(this, tempNames[i]);
- }
- return results;
+ return filenamesToFiles(list());
}
/**
@@ -1012,16 +1044,7 @@
* @see java.lang.SecurityManager#checkRead(FileDescriptor)
*/
public File[] listFiles(FilenameFilter filter) {
- String[] tempNames = list(filter);
- if (tempNames == null) {
- return null;
- }
- int resultLength = tempNames.length;
- File results[] = new File[resultLength];
- for (int i = 0; i < resultLength; i++) {
- results[i] = new File(this, tempNames[i]);
- }
- return results;
+ return filenamesToFiles(list(filter));
}
/**
@@ -1044,88 +1067,37 @@
* @see java.lang.SecurityManager#checkRead(FileDescriptor)
*/
public File[] listFiles(FileFilter filter) {
- SecurityManager security = System.getSecurityManager();
- if (security != null) {
- security.checkRead(path);
+ File[] files = listFiles();
+ if (filter == null || files == null) {
+ return files;
}
-
- if (path.length() == 0) {
- return null;
- }
-
- byte[] bs = pathBytes;
- if (!isDirectoryImpl(bs) || !existsImpl(bs) || !isReadableImpl(bs)) {
- return null;
- }
-
- byte[][] implList = listImpl(bs);
- if (implList == null) {
- return new File[0];
- }
- List<File> tempResult = new ArrayList<File>();
- for (int index = 0; index < implList.length; index++) {
- String aName = Util.toString(implList[index]);
- File aFile = new File(this, aName);
- if (filter == null || filter.accept(aFile)) {
- tempResult.add(aFile);
+ List<File> result = new ArrayList<File>(files.length);
+ for (File file : files) {
+ if (filter.accept(file)) {
+ result.add(file);
}
}
- return tempResult.toArray(new File[tempResult.size()]);
+ return result.toArray(new File[result.size()]);
}
/**
- * Gets a list of the files in the directory represented by this file. This
- * list is then filtered through a FilenameFilter and the names of files
- * with matching names are returned as an array of strings. Returns
- * {@code null} if this file is not a directory. If {@code filter} is
- * {@code null} then all filenames match.
- * <p>
- * The entries {@code .} and {@code ..} representing the current and parent
- * directories are not returned as part of the list.
- *
- * @param filter
- * the filter to match names against, may be {@code null}.
- * @return an array of files or {@code null}.
- * @throws SecurityException
- * if a {@code SecurityManager} is installed and it denies read
- * access to this file.
- * @see #getPath
- * @see #isDirectory
- * @see java.lang.SecurityManager#checkRead(FileDescriptor)
+ * Converts a String[] containing filenames to a File[].
+ * Note that the filenames must not contain slashes.
+ * This method is to remove duplication in the implementation
+ * of File.list's overloads.
*/
- public java.lang.String[] list(FilenameFilter filter) {
- SecurityManager security = System.getSecurityManager();
- if (security != null) {
- security.checkRead(path);
- }
-
- if (path.length() == 0) {
+ private File[] filenamesToFiles(String[] filenames) {
+ if (filenames == null) {
return null;
}
-
- byte[] bs = pathBytes;
- if (!isDirectoryImpl(bs) || !existsImpl(bs) || !isReadableImpl(bs)) {
- return null;
+ int count = filenames.length;
+ File[] result = new File[count];
+ for (int i = 0; i < count; ++i) {
+ result[i] = new File(this, filenames[i]);
}
-
- byte[][] implList = listImpl(bs);
- if (implList == null) {
- // empty list
- return new String[0];
- }
- List<String> tempResult = new ArrayList<String>();
- for (int index = 0; index < implList.length; index++) {
- String aName = Util.toString(implList[index]);
- if (filter == null || filter.accept(this, aName)) {
- tempResult.add(aName);
- }
- }
-
- return tempResult.toArray(new String[tempResult.size()]);
+ return result;
}
- private synchronized static native byte[][] listImpl(byte[] path);
-
/**
* Creates the directory named by the trailing filename of this file. Does
* not create the complete path required to create this directory.