Fileクラスは古くからファイルシステム上のディレクトリやファイルを扱うクラスとして非常によく使われてきたものである。元のソースコードは2000行を超える巨大なものになっている。
インスタンス変数としては,次のようなものが定義されている。
- fs ローカルのファイルシステム
- path 対象のディレクトリやファイルのパス文字列
- status パスの状態を表すenum値(有効/無効)
- separatorChar 区切り文字(Windowsは’\’,LinuxやMacOSでは’/’)
- separator 区切り文字列
- pathSeparatorChar 複数のパスを表す際の区切り文字(Windowsは’;’,LinuxやMacOSは’:’)
- separator 複数のパスを表す際の区切り文字列
publicなコンストラクタとしては,以下のようなものがオーバーロードされている。
File(String pathname)
File(String parent, String child)
File(File parent, String child)
File(URI uri)
getName()メソッドは,ディレクトリ,または,ファイルの名前のみを取得するメソッドである。path文字列から,lastIndexOf(separatorChar)で最後の区切文字の位置を探し出して,それ以降の部分文字列を切り出している。getParent()メソッドはこれと逆の操作をすることで,親ディレクトリのパスを取得している。
privateなメソッドであるが,slashify(String path, boolean isDirectory)メソッドは注目に価する。区切り文字が’/’ではない場合,すなわち,Windowsの場合,は区切り文字を’/’に変換したパス文字列を返す。このようにしてJavaはOSの違いを吸収しているようだ。
canRead()メソッド,canWrite()メソッド,canExecute() メソッドはSecurityManagerクラスを使って,それぞれ,そのパスに対する読取権限,書込権限,実行権限があるかチェックする。
exists()メソッドはディレクトリ,あるいは,ファイルが存在するかどうかをチェックするものである。isDirectory()メソッドは表しているオブジェクトがディレクトリであるかどうかをチェックする。isFile()メソッドは,表しているオブジェクトがファイルであるかどうかをチェックする。isHidden()メソッドは表しているオブジェクトが所謂「隠しファイル/ディレクトリ」であるかどうかをチェックする。
lastModified()メソッドではファイルが最後に更新されたタイムスタンプをlong型で返す。length()メソッドではファイルサイズをlong型で返す。
createNewFile()メソッドは,ファイルを新しく作成する。delete()メソッドはファイルを削除する。deleteOnExit()メソッドはプロセス終了時にファイルを削除させるフックメソッドであり,DeleteOnExitHook.add(path)を実行して,プロセス終了時の削除対象にパスを登録する。mkdir()メソッドはディレクトリを作成する。mkdirs()メソッドは,存在しない親ディレクトリも作成した上でディレクトリを一度に作成する。
list()メソッドはディレクトリ内のサブディレクトリまたはファイルをString[]型で返す。list(FilenameFilter filter)メソッドは,ディレクトリ内のファイルのうち,filterで指定された条件に当てはまるものだけをString[]型で返す。
listFiles()メソッドもlist()メソッドと同様の処理を行う(内部でlist()メソッドを呼んでいる)が,戻り値型がFileクラスの配列である点が異なる。listFiles( FilenameFilter filter )メソッドも同様である。listFiles( FileFilter filter)メソッドは引数のフィルタリングのためのクラスが異なる。
renameTo(File dest)メソッドは対象のディレクトリ,または,ファイルの名称を変更する。setLastModified(long time)は最終更新のタイムスタンプを変更する。setReadOnly()メソッドは,読取専用の設定を行う。setWritable(boolean writable, boolean ownerOnly)メソッドは書込権限の設定を行う。2つ目の引数はファイルのオーナーのみ書込ができるようにするフラグである。setWritable(boolean writable)メソッドは書込権をセットするもので,内部ではsetWritable(writable, true)を呼び出している。setReadable(boolean readable, boolean ownerOnly)メソッドとsetReadable(boolean readable)メソッドは読込権限について同様のことを行う。setExecutable(boolean executable, boolean ownerOnly)メソッドとsetExecutable(boolean executable)メソッドは実行権限について,同様のことを行う。
/*
* Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.io;
import java.net.URI;
import java.net.URL;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import sun.security.action.GetPropertyAction;
public class File
implements Serializable, Comparable<File>
{
private static final FileSystem fs = DefaultFileSystem.getFileSystem();
private final String path;
private static enum PathStatus { INVALID, CHECKED };
private transient PathStatus status = null;
final boolean isInvalid() {
if (status == null) {
status = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED
: PathStatus.INVALID;
}
return status == PathStatus.INVALID;
}
private final transient int prefixLength;
int getPrefixLength() {
return prefixLength;
}
public static final char separatorChar = fs.getSeparator();
public static final String separator = "" + separatorChar;
public static final char pathSeparatorChar = fs.getPathSeparator();
public static final String pathSeparator = "" + pathSeparatorChar;
/* — Constructors — */
private File(String pathname, int prefixLength) {
this.path = pathname;
this.prefixLength = prefixLength;
}
private File(String child, File parent) {
assert parent.path != null;
assert (!parent.path.isEmpty());
this.path = fs.resolve(parent.path, child);
this.prefixLength = parent.prefixLength;
}
public File(String pathname) {
if (pathname == null) {
throw new NullPointerException();
}
this.path = fs.normalize(pathname);
this.prefixLength = fs.prefixLength(this.path);
}
/* Note: The two-argument File constructors do not interpret an empty
parent abstract pathname as the current user directory. An empty parent
instead causes the child to be resolved against the system-dependent
directory defined by the FileSystem.getDefaultParent method. On Unix
this default is "/", while on Microsoft Windows it is "\\". This is required for
compatibility with the original behavior of this class. */
public File(String parent, String child) {
if (child == null) {
throw new NullPointerException();
}
if (parent != null) {
if (parent.isEmpty()) {
this.path = fs.resolve(fs.getDefaultParent(),
fs.normalize(child));
} else {
this.path = fs.resolve(fs.normalize(parent),
fs.normalize(child));
}
} else {
this.path = fs.normalize(child);
}
this.prefixLength = fs.prefixLength(this.path);
}
public File(File parent, String child) {
if (child == null) {
throw new NullPointerException();
}
if (parent != null) {
if (parent.path.isEmpty()) {
this.path = fs.resolve(fs.getDefaultParent(),
fs.normalize(child));
} else {
this.path = fs.resolve(parent.path,
fs.normalize(child));
}
} else {
this.path = fs.normalize(child);
}
this.prefixLength = fs.prefixLength(this.path);
}
public File(URI uri) {
// Check our many preconditions
if (!uri.isAbsolute())
throw new IllegalArgumentException("URI is not absolute");
if (uri.isOpaque())
throw new IllegalArgumentException("URI is not hierarchical");
String scheme = uri.getScheme();
if ((scheme == null) || !scheme.equalsIgnoreCase("file"))
throw new IllegalArgumentException("URI scheme is not \"file\"");
if (uri.getRawAuthority() != null)
throw new IllegalArgumentException("URI has an authority component");
if (uri.getRawFragment() != null)
throw new IllegalArgumentException("URI has a fragment component");
if (uri.getRawQuery() != null)
throw new IllegalArgumentException("URI has a query component");
String p = uri.getPath();
if (p.isEmpty())
throw new IllegalArgumentException("URI path component is empty");
// Okay, now initialize
p = fs.fromURIPath(p);
if (File.separatorChar != '/')
p = p.replace('/', File.separatorChar);
this.path = fs.normalize(p);
this.prefixLength = fs.prefixLength(this.path);
}
/* — Path-component accessors — */
public String getName() {
int index = path.lastIndexOf(separatorChar);
if (index < prefixLength) return path.substring(prefixLength);
return path.substring(index + 1);
}
public String getParent() {
int index = path.lastIndexOf(separatorChar);
if (index < prefixLength) {
if ((prefixLength > 0) && (path.length() > prefixLength))
return path.substring(0, prefixLength);
return null;
}
return path.substring(0, index);
}
public File getParentFile() {
String p = this.getParent();
if (p == null) return null;
return new File(p, this.prefixLength);
}
public String getPath() {
return path;
}
/* — Path operations — */
public boolean isAbsolute() {
return fs.isAbsolute(this);
}
public String getAbsolutePath() {
return fs.resolve(this);
}
public File getAbsoluteFile() {
String absPath = getAbsolutePath();
return new File(absPath, fs.prefixLength(absPath));
}
public String getCanonicalPath() throws IOException {
if (isInvalid()) {
throw new IOException("Invalid file path");
}
return fs.canonicalize(fs.resolve(this));
}
public File getCanonicalFile() throws IOException {
String canonPath = getCanonicalPath();
return new File(canonPath, fs.prefixLength(canonPath));
}
private static String slashify(String path, boolean isDirectory) {
String p = path;
if (File.separatorChar != '/')
p = p.replace(File.separatorChar, '/');
if (!p.startsWith("/"))
p = "/" + p;
if (!p.endsWith("/") && isDirectory)
p = p + "/";
return p;
}
@Deprecated
public URL toURL() throws MalformedURLException {
if (isInvalid()) {
throw new MalformedURLException("Invalid file path");
}
return new URL("file", "", slashify(getAbsolutePath(), isDirectory()));
}
public URI toURI() {
try {
File f = getAbsoluteFile();
String sp = slashify(f.getPath(), f.isDirectory());
if (sp.startsWith("//"))
sp = "//" + sp;
return new URI("file", null, sp, null);
} catch (URISyntaxException x) {
throw new Error(x); // Can't happen
}
}
/* — Attribute accessors — */
public boolean canRead() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(path);
}
if (isInvalid()) {
return false;
}
return fs.checkAccess(this, FileSystem.ACCESS_READ);
}
public boolean canWrite() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkWrite(path);
}
if (isInvalid()) {
return false;
}
return fs.checkAccess(this, FileSystem.ACCESS_WRITE);
}
public boolean exists() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(path);
}
if (isInvalid()) {
return false;
}
return ((fs.getBooleanAttributes(this) & FileSystem.BA_EXISTS) != 0);
}
public boolean isDirectory() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(path);
}
if (isInvalid()) {
return false;
}
return ((fs.getBooleanAttributes(this) & FileSystem.BA_DIRECTORY)
!= 0);
}
public boolean isFile() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(path);
}
if (isInvalid()) {
return false;
}
return ((fs.getBooleanAttributes(this) & FileSystem.BA_REGULAR) != 0);
}
public boolean isHidden() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(path);
}
if (isInvalid()) {
return false;
}
return ((fs.getBooleanAttributes(this) & FileSystem.BA_HIDDEN) != 0);
}
public long lastModified() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(path);
}
if (isInvalid()) {
return 0L;
}
return fs.getLastModifiedTime(this);
}
public long length() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(path);
}
if (isInvalid()) {
return 0L;
}
return fs.getLength(this);
}
/* — File operations — */
public boolean createNewFile() throws IOException {
SecurityManager security = System.getSecurityManager();
if (security != null) security.checkWrite(path);
if (isInvalid()) {
throw new IOException("Invalid file path");
}
return fs.createFileExclusively(path);
}
public boolean delete() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkDelete(path);
}
if (isInvalid()) {
return false;
}
return fs.delete(this);
}
public void deleteOnExit() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkDelete(path);
}
if (isInvalid()) {
return;
}
DeleteOnExitHook.add(path);
}
public String[] list() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(path);
}
if (isInvalid()) {
return null;
}
return fs.list(this);
}
public String[] list(FilenameFilter filter) {
String names[] = list();
if ((names == null) || (filter == null)) {
return names;
}
List<String> v = new ArrayList<>();
for (int i = 0 ; i < names.length ; i++) {
if (filter.accept(this, names[i])) {
v.add(names[i]);
}
}
return v.toArray(new String[v.size()]);
}
public File[] listFiles() {
String[] ss = list();
if (ss == null) return null;
int n = ss.length;
File[] fs = new File[n];
for (int i = 0; i < n; i++) {
fs[i] = new File(ss[i], this);
}
return fs;
}
public File[] listFiles(FilenameFilter filter) {
String ss[] = list();
if (ss == null) return null;
ArrayList<File> files = new ArrayList<>();
for (String s : ss)
if ((filter == null) || filter.accept(this, s))
files.add(new File(s, this));
return files.toArray(new File[files.size()]);
}
public File[] listFiles(FileFilter filter) {
String ss[] = list();
if (ss == null) return null;
ArrayList<File> files = new ArrayList<>();
for (String s : ss) {
File f = new File(s, this);
if ((filter == null) || filter.accept(f))
files.add(f);
}
return files.toArray(new File[files.size()]);
}
public boolean mkdir() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkWrite(path);
}
if (isInvalid()) {
return false;
}
return fs.createDirectory(this);
}
public boolean mkdirs() {
if (exists()) {
return false;
}
if (mkdir()) {
return true;
}
File canonFile = null;
try {
canonFile = getCanonicalFile();
} catch (IOException e) {
return false;
}
File parent = canonFile.getParentFile();
return (parent != null && (parent.mkdirs() || parent.exists()) &&
canonFile.mkdir());
}
public boolean renameTo(File dest) {
if (dest == null) {
throw new NullPointerException();
}
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkWrite(path);
security.checkWrite(dest.path);
}
if (this.isInvalid() || dest.isInvalid()) {
return false;
}
return fs.rename(this, dest);
}
public boolean setLastModified(long time) {
if (time < 0) throw new IllegalArgumentException("Negative time");
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkWrite(path);
}
if (isInvalid()) {
return false;
}
return fs.setLastModifiedTime(this, time);
}
public boolean setReadOnly() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkWrite(path);
}
if (isInvalid()) {
return false;
}
return fs.setReadOnly(this);
}
public boolean setWritable(boolean writable, boolean ownerOnly) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkWrite(path);
}
if (isInvalid()) {
return false;
}
return fs.setPermission(this, FileSystem.ACCESS_WRITE, writable, ownerOnly);
}
public boolean setWritable(boolean writable) {
return setWritable(writable, true);
}
public boolean setReadable(boolean readable, boolean ownerOnly) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkWrite(path);
}
if (isInvalid()) {
return false;
}
return fs.setPermission(this, FileSystem.ACCESS_READ, readable, ownerOnly);
}
public boolean setReadable(boolean readable) {
return setReadable(readable, true);
}
public boolean setExecutable(boolean executable, boolean ownerOnly) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkWrite(path);
}
if (isInvalid()) {
return false;
}
return fs.setPermission(this, FileSystem.ACCESS_EXECUTE, executable, ownerOnly);
}
public boolean setExecutable(boolean executable) {
return setExecutable(executable, true);
}
public boolean canExecute() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkExec(path);
}
if (isInvalid()) {
return false;
}
return fs.checkAccess(this, FileSystem.ACCESS_EXECUTE);
}
/* — Filesystem interface — */
public static File[] listRoots() {
return fs.listRoots();
}
/* — Disk usage — */
public long getTotalSpace() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("getFileSystemAttributes"));
sm.checkRead(path);
}
if (isInvalid()) {
return 0L;
}
return fs.getSpace(this, FileSystem.SPACE_TOTAL);
}
public long getFreeSpace() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("getFileSystemAttributes"));
sm.checkRead(path);
}
if (isInvalid()) {
return 0L;
}
return fs.getSpace(this, FileSystem.SPACE_FREE);
}
public long getUsableSpace() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("getFileSystemAttributes"));
sm.checkRead(path);
}
if (isInvalid()) {
return 0L;
}
return fs.getSpace(this, FileSystem.SPACE_USABLE);
}
/* — Temporary files — */
private static class TempDirectory {
private TempDirectory() { }
// temporary directory location
private static final File tmpdir = new File(
GetPropertyAction.privilegedGetProperty("java.io.tmpdir"));
static File location() {
return tmpdir;
}
// file name generation
private static final SecureRandom random = new SecureRandom();
private static int shortenSubName(int subNameLength, int excess,
int nameMin) {
int newLength = Math.max(nameMin, subNameLength – excess);
if (newLength < subNameLength) {
return newLength;
}
return subNameLength;
}
static File generateFile(String prefix, String suffix, File dir)
throws IOException
{
long n = random.nextLong();
String nus = Long.toUnsignedString(n);
// Use only the file name from the supplied prefix
prefix = (new File(prefix)).getName();
int prefixLength = prefix.length();
int nusLength = nus.length();
int suffixLength = suffix.length();;
String name;
int nameMax = fs.getNameMax(dir.getPath());
int excess = prefixLength + nusLength + suffixLength – nameMax;
if (excess <= 0) {
name = prefix + nus + suffix;
} else {
// Name exceeds the maximum path component length: shorten it
// Attempt to shorten the prefix length to no less then 3
prefixLength = shortenSubName(prefixLength, excess, 3);
excess = prefixLength + nusLength + suffixLength – nameMax;
if (excess > 0) {
// Attempt to shorten the suffix length to no less than
// 0 or 4 depending on whether it begins with a dot ('.')
suffixLength = shortenSubName(suffixLength, excess,
suffix.indexOf(".") == 0 ? 4 : 0);
suffixLength = shortenSubName(suffixLength, excess, 3);
excess = prefixLength + nusLength + suffixLength – nameMax;
}
if (excess > 0 && excess <= nusLength – 5) {
// Attempt to shorten the random character string length
// to no less than 5
nusLength = shortenSubName(nusLength, excess, 5);
}
StringBuilder sb =
new StringBuilder(prefixLength + nusLength + suffixLength);
sb.append(prefixLength < prefix.length() ?
prefix.substring(0, prefixLength) : prefix);
sb.append(nusLength < nus.length() ?
nus.substring(0, nusLength) : nus);
sb.append(suffixLength < suffix.length() ?
suffix.substring(0, suffixLength) : suffix);
name = sb.toString();
}
// Normalize the path component
name = fs.normalize(name);
File f = new File(dir, name);
if (!name.equals(f.getName()) || f.isInvalid()) {
if (System.getSecurityManager() != null)
throw new IOException("Unable to create temporary file");
else
throw new IOException("Unable to create temporary file, "
+ name);
}
return f;
}
}
public static File createTempFile(String prefix, String suffix,
File directory)
throws IOException
{
if (prefix.length() < 3) {
throw new IllegalArgumentException("Prefix string \"" + prefix +
"\" too short: length must be at least 3");
}
if (suffix == null)
suffix = ".tmp";
File tmpdir = (directory != null) ? directory
: TempDirectory.location();
SecurityManager sm = System.getSecurityManager();
File f;
do {
f = TempDirectory.generateFile(prefix, suffix, tmpdir);
if (sm != null) {
try {
sm.checkWrite(f.getPath());
} catch (SecurityException se) {
// don't reveal temporary directory location
if (directory == null)
throw new SecurityException("Unable to create temporary file");
throw se;
}
}
} while ((fs.getBooleanAttributes(f) & FileSystem.BA_EXISTS) != 0);
if (!fs.createFileExclusively(f.getPath()))
throw new IOException("Unable to create temporary file");
return f;
}
public static File createTempFile(String prefix, String suffix)
throws IOException
{
return createTempFile(prefix, suffix, null);
}
/* — Basic infrastructure — */
public int compareTo(File pathname) {
return fs.compare(this, pathname);
}
public boolean equals(Object obj) {
if ((obj != null) && (obj instanceof File)) {
return compareTo((File)obj) == 0;
}
return false;
}
public int hashCode() {
return fs.hashCode(this);
}
public String toString() {
return getPath();
}
private synchronized void writeObject(java.io.ObjectOutputStream s)
throws IOException
{
s.defaultWriteObject();
s.writeChar(separatorChar); // Add the separator character
}
private synchronized void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
ObjectInputStream.GetField fields = s.readFields();
String pathField = (String)fields.get("path", null);
char sep = s.readChar(); // read the previous separator char
if (sep != separatorChar)
pathField = pathField.replace(sep, separatorChar);
String path = fs.normalize(pathField);
UNSAFE.putReference(this, PATH_OFFSET, path);
UNSAFE.putIntVolatile(this, PREFIX_LENGTH_OFFSET, fs.prefixLength(path));
}
private static final jdk.internal.misc.Unsafe UNSAFE
= jdk.internal.misc.Unsafe.getUnsafe();
private static final long PATH_OFFSET
= UNSAFE.objectFieldOffset(File.class, "path");
private static final long PREFIX_LENGTH_OFFSET
= UNSAFE.objectFieldOffset(File.class, "prefixLength");
private static final long serialVersionUID = 301077366599181567L;
// — Integration with java.nio.file —
private transient volatile Path filePath;
public Path toPath() {
Path result = filePath;
if (result == null) {
synchronized (this) {
result = filePath;
if (result == null) {
result = FileSystems.getDefault().getPath(path);
filePath = result;
}
}
}
return result;
}
}