8147460: Clean-up jrtfs implementation
Reviewed-by: alanb, jlaskey, sundar
This commit is contained in:
parent
965536262b
commit
f4a9612da3
@ -136,7 +136,7 @@ public class ImageReader extends BasicImageReader {
|
||||
private final BasicFileAttributes fileAttrs;
|
||||
private boolean completed;
|
||||
|
||||
Node(String name, BasicFileAttributes fileAttrs) {
|
||||
protected Node(String name, BasicFileAttributes fileAttrs) {
|
||||
this.name = Objects.requireNonNull(name);
|
||||
this.fileAttrs = Objects.requireNonNull(fileAttrs);
|
||||
}
|
||||
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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 jdk.internal.jrtfs;
|
||||
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Formatter;
|
||||
|
||||
/**
|
||||
* Base class for file attributes supported by jrt file systems.
|
||||
*
|
||||
* @implNote This class needs to maintain JDK 8 source compatibility.
|
||||
*
|
||||
* It is used internally in the JDK to implement jimage/jrtfs access,
|
||||
* but also compiled and delivered as part of the jrtfs.jar to support access
|
||||
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
|
||||
*/
|
||||
public abstract class AbstractJrtFileAttributes implements BasicFileAttributes {
|
||||
|
||||
// jrt fs specific attributes
|
||||
/**
|
||||
* Compressed resource file. If not available or not applicable, 0L is
|
||||
* returned.
|
||||
*
|
||||
* @return the compressed resource size for compressed resources.
|
||||
*/
|
||||
public abstract long compressedSize();
|
||||
|
||||
/**
|
||||
* "file" extension of a file resource.
|
||||
*
|
||||
* @return extension string for the file resource
|
||||
*/
|
||||
public abstract String extension();
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
StringBuilder sb = new StringBuilder(1024);
|
||||
try (Formatter fm = new Formatter(sb)) {
|
||||
if (creationTime() != null) {
|
||||
fm.format(" creationTime : %tc%n", creationTime().toMillis());
|
||||
} else {
|
||||
fm.format(" creationTime : null%n");
|
||||
}
|
||||
|
||||
if (lastAccessTime() != null) {
|
||||
fm.format(" lastAccessTime : %tc%n", lastAccessTime().toMillis());
|
||||
} else {
|
||||
fm.format(" lastAccessTime : null%n");
|
||||
}
|
||||
fm.format(" lastModifiedTime: %tc%n", lastModifiedTime().toMillis());
|
||||
fm.format(" isRegularFile : %b%n", isRegularFile());
|
||||
fm.format(" isDirectory : %b%n", isDirectory());
|
||||
fm.format(" isSymbolicLink : %b%n", isSymbolicLink());
|
||||
fm.format(" isOther : %b%n", isOther());
|
||||
fm.format(" fileKey : %s%n", fileKey());
|
||||
fm.format(" size : %d%n", size());
|
||||
fm.format(" compressedSize : %d%n", compressedSize());
|
||||
fm.format(" extension : %s%n", extension());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -1,372 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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 jdk.internal.jrtfs;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.NonWritableChannelException;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.ClosedFileSystemException;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.FileStore;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystemNotFoundException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.PathMatcher;
|
||||
import java.nio.file.ReadOnlyFileSystemException;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.WatchService;
|
||||
import java.nio.file.attribute.FileAttribute;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.nio.file.attribute.UserPrincipalLookupService;
|
||||
import java.nio.file.spi.FileSystemProvider;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Base class for jrt file systems. jrt filesystem implementations are currently
|
||||
* available on top of .jimage file and on top "exploded" build directories.
|
||||
*
|
||||
* @implNote This class needs to maintain JDK 8 source compatibility.
|
||||
*
|
||||
* It is used internally in the JDK to implement jimage/jrtfs access,
|
||||
* but also compiled and delivered as part of the jrtfs.jar to support access
|
||||
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
|
||||
*/
|
||||
abstract class AbstractJrtFileSystem extends FileSystem {
|
||||
|
||||
private final JrtFileSystemProvider provider;
|
||||
|
||||
AbstractJrtFileSystem(JrtFileSystemProvider provider, Map<String, ?> options) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
private static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
|
||||
// static utility methods
|
||||
static ReadOnlyFileSystemException readOnly() {
|
||||
return new ReadOnlyFileSystemException();
|
||||
}
|
||||
|
||||
// if a Path does not exist, throw exception
|
||||
static void checkExists(Path path) {
|
||||
if (Files.notExists(path)) {
|
||||
throw new FileSystemNotFoundException(path.toString());
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] getBytes(String name) {
|
||||
return name.getBytes(UTF_8);
|
||||
}
|
||||
|
||||
static String getString(byte[] name) {
|
||||
return new String(name, UTF_8);
|
||||
}
|
||||
|
||||
// do the supplied options imply that we have to chase symlinks?
|
||||
static boolean followLinks(LinkOption... options) {
|
||||
if (options != null) {
|
||||
for (LinkOption lo : options) {
|
||||
if (lo == LinkOption.NOFOLLOW_LINKS) {
|
||||
return false;
|
||||
} else if (lo == null) {
|
||||
throw new NullPointerException();
|
||||
} else {
|
||||
throw new AssertionError("should not reach here");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// check that the options passed are supported by (read-only) jrt file system
|
||||
static void checkOptions(Set<? extends OpenOption> options) {
|
||||
// check for options of null type and option is an intance of StandardOpenOption
|
||||
for (OpenOption option : options) {
|
||||
if (option == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (!(option instanceof StandardOpenOption)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
if (options.contains(StandardOpenOption.WRITE)
|
||||
|| options.contains(StandardOpenOption.APPEND)) {
|
||||
throw readOnly();
|
||||
}
|
||||
}
|
||||
|
||||
// FileSystem method implementations
|
||||
@Override
|
||||
public FileSystemProvider provider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Path> getRootDirectories() {
|
||||
ArrayList<Path> pathArr = new ArrayList<>();
|
||||
pathArr.add(getRootPath());
|
||||
return pathArr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractJrtPath getPath(String first, String... more) {
|
||||
String path;
|
||||
if (more.length == 0) {
|
||||
path = first;
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(first);
|
||||
for (String segment : more) {
|
||||
if (segment.length() > 0) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append('/');
|
||||
}
|
||||
sb.append(segment);
|
||||
}
|
||||
}
|
||||
path = sb.toString();
|
||||
}
|
||||
return getRootPath().newJrtPath(getBytes(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isReadOnly() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final UserPrincipalLookupService getUserPrincipalLookupService() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final WatchService newWatchService() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Iterable<FileStore> getFileStores() {
|
||||
ArrayList<FileStore> list = new ArrayList<>(1);
|
||||
list.add(getFileStore(getRootPath()));
|
||||
return list;
|
||||
}
|
||||
|
||||
private static final Set<String> supportedFileAttributeViews
|
||||
= Collections.unmodifiableSet(
|
||||
new HashSet<String>(Arrays.asList("basic", "jrt")));
|
||||
|
||||
@Override
|
||||
public final Set<String> supportedFileAttributeViews() {
|
||||
return supportedFileAttributeViews;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return "jrt:/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getSeparator() {
|
||||
return "/";
|
||||
}
|
||||
|
||||
private static final String GLOB_SYNTAX = "glob";
|
||||
private static final String REGEX_SYNTAX = "regex";
|
||||
|
||||
@Override
|
||||
public PathMatcher getPathMatcher(String syntaxAndInput) {
|
||||
int pos = syntaxAndInput.indexOf(':');
|
||||
if (pos <= 0 || pos == syntaxAndInput.length()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
String syntax = syntaxAndInput.substring(0, pos);
|
||||
String input = syntaxAndInput.substring(pos + 1);
|
||||
String expr;
|
||||
if (syntax.equalsIgnoreCase(GLOB_SYNTAX)) {
|
||||
expr = JrtUtils.toRegexPattern(input);
|
||||
} else {
|
||||
if (syntax.equalsIgnoreCase(REGEX_SYNTAX)) {
|
||||
expr = input;
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Syntax '" + syntax
|
||||
+ "' not recognized");
|
||||
}
|
||||
}
|
||||
// return matcher
|
||||
final Pattern pattern = Pattern.compile(expr);
|
||||
return (Path path) -> pattern.matcher(path.toString()).matches();
|
||||
}
|
||||
|
||||
// These methods throw read only file system exception
|
||||
final void setTimes(AbstractJrtPath jrtPath, FileTime mtime, FileTime atime, FileTime ctime)
|
||||
throws IOException {
|
||||
throw readOnly();
|
||||
}
|
||||
|
||||
final void createDirectory(AbstractJrtPath jrtPath, FileAttribute<?>... attrs) throws IOException {
|
||||
throw readOnly();
|
||||
}
|
||||
|
||||
final void deleteFile(AbstractJrtPath jrtPath, boolean failIfNotExists)
|
||||
throws IOException {
|
||||
throw readOnly();
|
||||
}
|
||||
|
||||
final OutputStream newOutputStream(AbstractJrtPath jrtPath, OpenOption... options)
|
||||
throws IOException {
|
||||
throw readOnly();
|
||||
}
|
||||
|
||||
final void copyFile(boolean deletesrc, AbstractJrtPath srcPath, AbstractJrtPath dstPath, CopyOption... options)
|
||||
throws IOException {
|
||||
throw readOnly();
|
||||
}
|
||||
|
||||
final FileChannel newFileChannel(AbstractJrtPath jrtPath,
|
||||
Set<? extends OpenOption> options,
|
||||
FileAttribute<?>... attrs)
|
||||
throws IOException {
|
||||
throw new UnsupportedOperationException("newFileChannel");
|
||||
}
|
||||
|
||||
final InputStream newInputStream(AbstractJrtPath jrtPath) throws IOException {
|
||||
return new ByteArrayInputStream(getFileContent(jrtPath));
|
||||
}
|
||||
|
||||
final SeekableByteChannel newByteChannel(AbstractJrtPath jrtPath,
|
||||
Set<? extends OpenOption> options,
|
||||
FileAttribute<?>... attrs)
|
||||
throws IOException {
|
||||
checkOptions(options);
|
||||
|
||||
byte[] buf = getFileContent(jrtPath);
|
||||
final ReadableByteChannel rbc
|
||||
= Channels.newChannel(new ByteArrayInputStream(buf));
|
||||
final long size = buf.length;
|
||||
return new SeekableByteChannel() {
|
||||
long read = 0;
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return rbc.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long position() throws IOException {
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel position(long pos)
|
||||
throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuffer dst) throws IOException {
|
||||
int n = rbc.read(dst);
|
||||
if (n > 0) {
|
||||
read += n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel truncate(long size)
|
||||
throws IOException {
|
||||
throw new NonWritableChannelException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(ByteBuffer src) throws IOException {
|
||||
throw new NonWritableChannelException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() throws IOException {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
rbc.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
final JrtFileStore getFileStore(AbstractJrtPath jrtPath) {
|
||||
return new JrtFileStore(jrtPath);
|
||||
}
|
||||
|
||||
final void ensureOpen() throws IOException {
|
||||
if (!isOpen()) {
|
||||
throw new ClosedFileSystemException();
|
||||
}
|
||||
}
|
||||
|
||||
// abstract methods to be implemented by a particular jrt file system
|
||||
abstract AbstractJrtPath getRootPath();
|
||||
|
||||
abstract boolean isSameFile(AbstractJrtPath jrtPath1, AbstractJrtPath jrtPath2) throws IOException;
|
||||
|
||||
abstract boolean isLink(AbstractJrtPath jrtPath) throws IOException;
|
||||
|
||||
abstract AbstractJrtPath resolveLink(AbstractJrtPath jrtPath) throws IOException;
|
||||
|
||||
abstract AbstractJrtFileAttributes getFileAttributes(AbstractJrtPath jrtPath, LinkOption... options) throws IOException;
|
||||
|
||||
abstract boolean exists(AbstractJrtPath jrtPath) throws IOException;
|
||||
|
||||
abstract boolean isDirectory(AbstractJrtPath jrtPath, boolean resolveLinks) throws IOException;
|
||||
|
||||
/**
|
||||
* returns the list of child paths of the given directory "path"
|
||||
*
|
||||
* @param path name of the directory whose content is listed
|
||||
* @return iterator for child paths of the given directory path
|
||||
*/
|
||||
abstract Iterator<Path> iteratorOf(AbstractJrtPath jrtPath) throws IOException;
|
||||
|
||||
// returns the content of the file resource specified by the path
|
||||
abstract byte[] getFileContent(AbstractJrtPath jrtPath) throws IOException;
|
||||
}
|
@ -1,935 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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 jdk.internal.jrtfs;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.channels.*;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.DirectoryStream.Filter;
|
||||
import java.nio.file.attribute.*;
|
||||
import java.util.*;
|
||||
import static java.nio.file.StandardOpenOption.*;
|
||||
import static java.nio.file.StandardCopyOption.*;
|
||||
|
||||
/**
|
||||
* Base class for Path implementation of jrt file systems.
|
||||
*
|
||||
* @implNote This class needs to maintain JDK 8 source compatibility.
|
||||
*
|
||||
* It is used internally in the JDK to implement jimage/jrtfs access,
|
||||
* but also compiled and delivered as part of the jrtfs.jar to support access
|
||||
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
|
||||
*/
|
||||
abstract class AbstractJrtPath implements Path {
|
||||
|
||||
protected final AbstractJrtFileSystem jrtfs;
|
||||
private final byte[] path;
|
||||
private volatile int[] offsets;
|
||||
private int hashcode = 0; // cached hashcode (created lazily)
|
||||
|
||||
AbstractJrtPath(AbstractJrtFileSystem jrtfs, byte[] path) {
|
||||
this(jrtfs, path, false);
|
||||
this.resolved = null;
|
||||
}
|
||||
|
||||
AbstractJrtPath(AbstractJrtFileSystem jrtfs, byte[] path, boolean normalized) {
|
||||
this.resolved = null;
|
||||
this.jrtfs = jrtfs;
|
||||
if (normalized) {
|
||||
this.path = path;
|
||||
} else {
|
||||
this.path = normalize(path);
|
||||
}
|
||||
}
|
||||
|
||||
// factory methods to create subtypes of AbstractJrtPath
|
||||
protected abstract AbstractJrtPath newJrtPath(byte[] path);
|
||||
|
||||
protected abstract AbstractJrtPath newJrtPath(byte[] path, boolean normalized);
|
||||
|
||||
final byte[] getName() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final AbstractJrtPath getRoot() {
|
||||
if (this.isAbsolute()) {
|
||||
return jrtfs.getRootPath();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final AbstractJrtPath getFileName() {
|
||||
initOffsets();
|
||||
int count = offsets.length;
|
||||
if (count == 0) {
|
||||
return null; // no elements so no name
|
||||
}
|
||||
if (count == 1 && path[0] != '/') {
|
||||
return this;
|
||||
}
|
||||
int lastOffset = offsets[count - 1];
|
||||
int len = path.length - lastOffset;
|
||||
byte[] result = new byte[len];
|
||||
System.arraycopy(path, lastOffset, result, 0, len);
|
||||
return newJrtPath(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final AbstractJrtPath getParent() {
|
||||
initOffsets();
|
||||
int count = offsets.length;
|
||||
if (count == 0) // no elements so no parent
|
||||
{
|
||||
return null;
|
||||
}
|
||||
int len = offsets[count - 1] - 1;
|
||||
if (len <= 0) // parent is root only (may be null)
|
||||
{
|
||||
return getRoot();
|
||||
}
|
||||
byte[] result = new byte[len];
|
||||
System.arraycopy(path, 0, result, 0, len);
|
||||
return newJrtPath(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getNameCount() {
|
||||
initOffsets();
|
||||
return offsets.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final AbstractJrtPath getName(int index) {
|
||||
initOffsets();
|
||||
if (index < 0 || index >= offsets.length) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
int begin = offsets[index];
|
||||
int len;
|
||||
if (index == (offsets.length - 1)) {
|
||||
len = path.length - begin;
|
||||
} else {
|
||||
len = offsets[index + 1] - begin - 1;
|
||||
}
|
||||
// construct result
|
||||
byte[] result = new byte[len];
|
||||
System.arraycopy(path, begin, result, 0, len);
|
||||
return newJrtPath(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final AbstractJrtPath subpath(int beginIndex, int endIndex) {
|
||||
initOffsets();
|
||||
if (beginIndex < 0
|
||||
|| beginIndex >= offsets.length
|
||||
|| endIndex > offsets.length
|
||||
|| beginIndex >= endIndex) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
// starting offset and length
|
||||
int begin = offsets[beginIndex];
|
||||
int len;
|
||||
if (endIndex == offsets.length) {
|
||||
len = path.length - begin;
|
||||
} else {
|
||||
len = offsets[endIndex] - begin - 1;
|
||||
}
|
||||
// construct result
|
||||
byte[] result = new byte[len];
|
||||
System.arraycopy(path, begin, result, 0, len);
|
||||
return newJrtPath(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final AbstractJrtPath toRealPath(LinkOption... options) throws IOException {
|
||||
AbstractJrtPath realPath = newJrtPath(getResolvedPath()).toAbsolutePath();
|
||||
realPath = JrtFileSystem.followLinks(options) ? jrtfs.resolveLink(this) : realPath;
|
||||
realPath.checkAccess();
|
||||
return realPath;
|
||||
}
|
||||
|
||||
final AbstractJrtPath readSymbolicLink() throws IOException {
|
||||
if (!jrtfs.isLink(this)) {
|
||||
throw new IOException("not a symbolic link");
|
||||
}
|
||||
|
||||
return jrtfs.resolveLink(this);
|
||||
}
|
||||
|
||||
final boolean isHidden() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final AbstractJrtPath toAbsolutePath() {
|
||||
if (isAbsolute()) {
|
||||
return this;
|
||||
} else {
|
||||
//add / bofore the existing path
|
||||
byte[] tmp = new byte[path.length + 1];
|
||||
tmp[0] = '/';
|
||||
System.arraycopy(path, 0, tmp, 1, path.length);
|
||||
return newJrtPath(tmp).normalize();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final URI toUri() {
|
||||
try {
|
||||
return new URI("jrt",
|
||||
JrtFileSystem.getString(toAbsolutePath().path),
|
||||
null);
|
||||
} catch (URISyntaxException ex) {
|
||||
throw new AssertionError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean equalsNameAt(AbstractJrtPath other, int index) {
|
||||
int mbegin = offsets[index];
|
||||
int mlen;
|
||||
if (index == (offsets.length - 1)) {
|
||||
mlen = path.length - mbegin;
|
||||
} else {
|
||||
mlen = offsets[index + 1] - mbegin - 1;
|
||||
}
|
||||
int obegin = other.offsets[index];
|
||||
int olen;
|
||||
if (index == (other.offsets.length - 1)) {
|
||||
olen = other.path.length - obegin;
|
||||
} else {
|
||||
olen = other.offsets[index + 1] - obegin - 1;
|
||||
}
|
||||
if (mlen != olen) {
|
||||
return false;
|
||||
}
|
||||
int n = 0;
|
||||
while (n < mlen) {
|
||||
if (path[mbegin + n] != other.path[obegin + n]) {
|
||||
return false;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final AbstractJrtPath relativize(Path other) {
|
||||
final AbstractJrtPath o = checkPath(other);
|
||||
if (o.equals(this)) {
|
||||
return newJrtPath(new byte[0], true);
|
||||
}
|
||||
if (/* this.getFileSystem() != o.getFileSystem() || */this.isAbsolute() != o.isAbsolute()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
int mc = this.getNameCount();
|
||||
int oc = o.getNameCount();
|
||||
int n = Math.min(mc, oc);
|
||||
int i = 0;
|
||||
while (i < n) {
|
||||
if (!equalsNameAt(o, i)) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
int dotdots = mc - i;
|
||||
int len = dotdots * 3 - 1;
|
||||
if (i < oc) {
|
||||
len += (o.path.length - o.offsets[i] + 1);
|
||||
}
|
||||
byte[] result = new byte[len];
|
||||
|
||||
int pos = 0;
|
||||
while (dotdots > 0) {
|
||||
result[pos++] = (byte) '.';
|
||||
result[pos++] = (byte) '.';
|
||||
if (pos < len) // no tailing slash at the end
|
||||
{
|
||||
result[pos++] = (byte) '/';
|
||||
}
|
||||
dotdots--;
|
||||
}
|
||||
if (i < oc) {
|
||||
System.arraycopy(o.path, o.offsets[i],
|
||||
result, pos,
|
||||
o.path.length - o.offsets[i]);
|
||||
}
|
||||
return newJrtPath(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractJrtFileSystem getFileSystem() {
|
||||
return jrtfs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isAbsolute() {
|
||||
return (this.path.length > 0 && path[0] == '/');
|
||||
}
|
||||
|
||||
@Override
|
||||
public final AbstractJrtPath resolve(Path other) {
|
||||
final AbstractJrtPath o = checkPath(other);
|
||||
if (o.isAbsolute()) {
|
||||
return o;
|
||||
}
|
||||
byte[] res;
|
||||
if (this.path[path.length - 1] == '/') {
|
||||
res = new byte[path.length + o.path.length];
|
||||
System.arraycopy(path, 0, res, 0, path.length);
|
||||
System.arraycopy(o.path, 0, res, path.length, o.path.length);
|
||||
} else {
|
||||
res = new byte[path.length + 1 + o.path.length];
|
||||
System.arraycopy(path, 0, res, 0, path.length);
|
||||
res[path.length] = '/';
|
||||
System.arraycopy(o.path, 0, res, path.length + 1, o.path.length);
|
||||
}
|
||||
return newJrtPath(res);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Path resolveSibling(Path other) {
|
||||
if (other == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
Path parent = getParent();
|
||||
return (parent == null) ? other : parent.resolve(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean startsWith(Path other) {
|
||||
final AbstractJrtPath o = checkPath(other);
|
||||
if (o.isAbsolute() != this.isAbsolute()
|
||||
|| o.path.length > this.path.length) {
|
||||
return false;
|
||||
}
|
||||
int olast = o.path.length;
|
||||
for (int i = 0; i < olast; i++) {
|
||||
if (o.path[i] != this.path[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
olast--;
|
||||
return o.path.length == this.path.length
|
||||
|| o.path[olast] == '/'
|
||||
|| this.path[olast + 1] == '/';
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean endsWith(Path other) {
|
||||
final AbstractJrtPath o = checkPath(other);
|
||||
int olast = o.path.length - 1;
|
||||
if (olast > 0 && o.path[olast] == '/') {
|
||||
olast--;
|
||||
}
|
||||
int last = this.path.length - 1;
|
||||
if (last > 0 && this.path[last] == '/') {
|
||||
last--;
|
||||
}
|
||||
if (olast == -1) // o.path.length == 0
|
||||
{
|
||||
return last == -1;
|
||||
}
|
||||
if ((o.isAbsolute() && (!this.isAbsolute() || olast != last))
|
||||
|| (last < olast)) {
|
||||
return false;
|
||||
}
|
||||
for (; olast >= 0; olast--, last--) {
|
||||
if (o.path[olast] != this.path[last]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return o.path[olast + 1] == '/'
|
||||
|| last == -1 || this.path[last] == '/';
|
||||
}
|
||||
|
||||
@Override
|
||||
public final AbstractJrtPath resolve(String other) {
|
||||
return resolve(getFileSystem().getPath(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Path resolveSibling(String other) {
|
||||
return resolveSibling(getFileSystem().getPath(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean startsWith(String other) {
|
||||
return startsWith(getFileSystem().getPath(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean endsWith(String other) {
|
||||
return endsWith(getFileSystem().getPath(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final AbstractJrtPath normalize() {
|
||||
byte[] res = getResolved();
|
||||
if (res == path) // no change
|
||||
{
|
||||
return this;
|
||||
}
|
||||
return newJrtPath(res, true);
|
||||
}
|
||||
|
||||
private AbstractJrtPath checkPath(Path path) {
|
||||
if (path == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (!(path instanceof AbstractJrtPath)) {
|
||||
throw new ProviderMismatchException();
|
||||
}
|
||||
return (AbstractJrtPath) path;
|
||||
}
|
||||
|
||||
// create offset list if not already created
|
||||
private void initOffsets() {
|
||||
if (offsets == null) {
|
||||
int count, index;
|
||||
// count names
|
||||
count = 0;
|
||||
index = 0;
|
||||
while (index < path.length) {
|
||||
byte c = path[index++];
|
||||
if (c != '/') {
|
||||
count++;
|
||||
while (index < path.length && path[index] != '/') {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// populate offsets
|
||||
int[] result = new int[count];
|
||||
count = 0;
|
||||
index = 0;
|
||||
while (index < path.length) {
|
||||
byte c = path[index];
|
||||
if (c == '/') {
|
||||
index++;
|
||||
} else {
|
||||
result[count++] = index++;
|
||||
while (index < path.length && path[index] != '/') {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
synchronized (this) {
|
||||
if (offsets == null) {
|
||||
offsets = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private volatile byte[] resolved;
|
||||
|
||||
final byte[] getResolvedPath() {
|
||||
byte[] r = resolved;
|
||||
if (r == null) {
|
||||
if (isAbsolute()) {
|
||||
r = getResolved();
|
||||
} else {
|
||||
r = toAbsolutePath().getResolvedPath();
|
||||
}
|
||||
resolved = r;
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
// removes redundant slashs, replace "\" to separator "/"
|
||||
// and check for invalid characters
|
||||
private static byte[] normalize(byte[] path) {
|
||||
if (path.length == 0) {
|
||||
return path;
|
||||
}
|
||||
byte prevC = 0;
|
||||
for (int i = 0; i < path.length; i++) {
|
||||
byte c = path[i];
|
||||
if (c == '\\') {
|
||||
return normalize(path, i);
|
||||
}
|
||||
if (c == (byte) '/' && prevC == '/') {
|
||||
return normalize(path, i - 1);
|
||||
}
|
||||
if (c == '\u0000') {
|
||||
throw new InvalidPathException(JrtFileSystem.getString(path),
|
||||
"Path: nul character not allowed");
|
||||
}
|
||||
prevC = c;
|
||||
}
|
||||
|
||||
if (path.length > 1 && path[path.length - 1] == '/') {
|
||||
return Arrays.copyOf(path, path.length - 1);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private static byte[] normalize(byte[] path, int off) {
|
||||
byte[] to = new byte[path.length];
|
||||
int n = 0;
|
||||
while (n < off) {
|
||||
to[n] = path[n];
|
||||
n++;
|
||||
}
|
||||
int m = n;
|
||||
byte prevC = 0;
|
||||
while (n < path.length) {
|
||||
byte c = path[n++];
|
||||
if (c == (byte) '\\') {
|
||||
c = (byte) '/';
|
||||
}
|
||||
if (c == (byte) '/' && prevC == (byte) '/') {
|
||||
continue;
|
||||
}
|
||||
if (c == '\u0000') {
|
||||
throw new InvalidPathException(JrtFileSystem.getString(path),
|
||||
"Path: nul character not allowed");
|
||||
}
|
||||
to[m++] = c;
|
||||
prevC = c;
|
||||
}
|
||||
if (m > 1 && to[m - 1] == '/') {
|
||||
m--;
|
||||
}
|
||||
return (m == to.length) ? to : Arrays.copyOf(to, m);
|
||||
}
|
||||
|
||||
// Remove DotSlash(./) and resolve DotDot (..) components
|
||||
private byte[] getResolved() {
|
||||
if (path.length == 0) {
|
||||
return path;
|
||||
}
|
||||
for (int i = 0; i < path.length; i++) {
|
||||
byte c = path[i];
|
||||
if (c == (byte) '.') {
|
||||
return resolve0();
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
// TBD: performance, avoid initOffsets
|
||||
private byte[] resolve0() {
|
||||
byte[] to = new byte[path.length];
|
||||
int nc = getNameCount();
|
||||
int[] lastM = new int[nc];
|
||||
int lastMOff = -1;
|
||||
int m = 0;
|
||||
for (int i = 0; i < nc; i++) {
|
||||
int n = offsets[i];
|
||||
int len = (i == offsets.length - 1)
|
||||
? (path.length - n) : (offsets[i + 1] - n - 1);
|
||||
if (len == 1 && path[n] == (byte) '.') {
|
||||
if (m == 0 && path[0] == '/') // absolute path
|
||||
{
|
||||
to[m++] = '/';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (len == 2 && path[n] == '.' && path[n + 1] == '.') {
|
||||
if (lastMOff >= 0) {
|
||||
m = lastM[lastMOff--]; // retreat
|
||||
continue;
|
||||
}
|
||||
if (path[0] == '/') { // "/../xyz" skip
|
||||
if (m == 0) {
|
||||
to[m++] = '/';
|
||||
}
|
||||
} else { // "../xyz" -> "../xyz"
|
||||
if (m != 0 && to[m - 1] != '/') {
|
||||
to[m++] = '/';
|
||||
}
|
||||
while (len-- > 0) {
|
||||
to[m++] = path[n++];
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (m == 0 && path[0] == '/' || // absolute path
|
||||
m != 0 && to[m - 1] != '/') { // not the first name
|
||||
to[m++] = '/';
|
||||
}
|
||||
lastM[++lastMOff] = m;
|
||||
while (len-- > 0) {
|
||||
to[m++] = path[n++];
|
||||
}
|
||||
}
|
||||
if (m > 1 && to[m - 1] == '/') {
|
||||
m--;
|
||||
}
|
||||
return (m == to.length) ? to : Arrays.copyOf(to, m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return JrtFileSystem.getString(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
int h = hashcode;
|
||||
if (h == 0) {
|
||||
hashcode = h = Arrays.hashCode(path);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object obj) {
|
||||
return obj != null
|
||||
&& obj instanceof AbstractJrtPath
|
||||
&& this.jrtfs == ((AbstractJrtPath) obj).jrtfs
|
||||
&& compareTo((Path) obj) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int compareTo(Path other) {
|
||||
final AbstractJrtPath o = checkPath(other);
|
||||
int len1 = this.path.length;
|
||||
int len2 = o.path.length;
|
||||
|
||||
int n = Math.min(len1, len2);
|
||||
byte v1[] = this.path;
|
||||
byte v2[] = o.path;
|
||||
|
||||
int k = 0;
|
||||
while (k < n) {
|
||||
int c1 = v1[k] & 0xff;
|
||||
int c2 = v2[k] & 0xff;
|
||||
if (c1 != c2) {
|
||||
return c1 - c2;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
return len1 - len2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final WatchKey register(
|
||||
WatchService watcher,
|
||||
WatchEvent.Kind<?>[] events,
|
||||
WatchEvent.Modifier... modifiers) {
|
||||
if (watcher == null || events == null || modifiers == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events) {
|
||||
return register(watcher, events, new WatchEvent.Modifier[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final File toFile() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Iterator<Path> iterator() {
|
||||
return new Iterator<Path>() {
|
||||
private int i = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return (i < getNameCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path next() {
|
||||
if (i < getNameCount()) {
|
||||
Path result = getName(i);
|
||||
i++;
|
||||
return result;
|
||||
} else {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new ReadOnlyFileSystemException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Helpers for JrtFileSystemProvider and JrtFileSystem
|
||||
final int getPathLength() {
|
||||
return path.length;
|
||||
}
|
||||
|
||||
final void createDirectory(FileAttribute<?>... attrs)
|
||||
throws IOException {
|
||||
jrtfs.createDirectory(this, attrs);
|
||||
}
|
||||
|
||||
final InputStream newInputStream(OpenOption... options) throws IOException {
|
||||
if (options.length > 0) {
|
||||
for (OpenOption opt : options) {
|
||||
if (opt != READ) {
|
||||
throw new UnsupportedOperationException("'" + opt + "' not allowed");
|
||||
}
|
||||
}
|
||||
}
|
||||
return jrtfs.newInputStream(this);
|
||||
}
|
||||
|
||||
final DirectoryStream<Path> newDirectoryStream(Filter<? super Path> filter)
|
||||
throws IOException {
|
||||
return new JrtDirectoryStream(this, filter);
|
||||
}
|
||||
|
||||
final void delete() throws IOException {
|
||||
jrtfs.deleteFile(this, true);
|
||||
}
|
||||
|
||||
final void deleteIfExists() throws IOException {
|
||||
jrtfs.deleteFile(this, false);
|
||||
}
|
||||
|
||||
final AbstractJrtFileAttributes getAttributes(LinkOption... options) throws IOException {
|
||||
AbstractJrtFileAttributes zfas = jrtfs.getFileAttributes(this, options);
|
||||
if (zfas == null) {
|
||||
throw new NoSuchFileException(toString());
|
||||
}
|
||||
return zfas;
|
||||
}
|
||||
|
||||
final void setAttribute(String attribute, Object value, LinkOption... options)
|
||||
throws IOException {
|
||||
String type;
|
||||
String attr;
|
||||
int colonPos = attribute.indexOf(':');
|
||||
if (colonPos == -1) {
|
||||
type = "basic";
|
||||
attr = attribute;
|
||||
} else {
|
||||
type = attribute.substring(0, colonPos++);
|
||||
attr = attribute.substring(colonPos);
|
||||
}
|
||||
JrtFileAttributeView view = JrtFileAttributeView.get(this, type, options);
|
||||
if (view == null) {
|
||||
throw new UnsupportedOperationException("view <" + view + "> is not supported");
|
||||
}
|
||||
view.setAttribute(attr, value);
|
||||
}
|
||||
|
||||
final void setTimes(FileTime mtime, FileTime atime, FileTime ctime)
|
||||
throws IOException {
|
||||
jrtfs.setTimes(this, mtime, atime, ctime);
|
||||
}
|
||||
|
||||
final Map<String, Object> readAttributes(String attributes, LinkOption... options)
|
||||
throws IOException {
|
||||
String view;
|
||||
String attrs;
|
||||
int colonPos = attributes.indexOf(':');
|
||||
if (colonPos == -1) {
|
||||
view = "basic";
|
||||
attrs = attributes;
|
||||
} else {
|
||||
view = attributes.substring(0, colonPos++);
|
||||
attrs = attributes.substring(colonPos);
|
||||
}
|
||||
JrtFileAttributeView jrtfv = JrtFileAttributeView.get(this, view, options);
|
||||
if (jrtfv == null) {
|
||||
throw new UnsupportedOperationException("view not supported");
|
||||
}
|
||||
return jrtfv.readAttributes(attrs);
|
||||
}
|
||||
|
||||
final FileStore getFileStore() throws IOException {
|
||||
// each JrtFileSystem only has one root (as requested for now)
|
||||
if (exists()) {
|
||||
return jrtfs.getFileStore(this);
|
||||
}
|
||||
throw new NoSuchFileException(JrtFileSystem.getString(path));
|
||||
}
|
||||
|
||||
final boolean isSameFile(Path other) throws IOException {
|
||||
if (this.equals(other)) {
|
||||
return true;
|
||||
}
|
||||
if (other == null
|
||||
|| this.getFileSystem() != other.getFileSystem()) {
|
||||
return false;
|
||||
}
|
||||
this.checkAccess();
|
||||
AbstractJrtPath target = (AbstractJrtPath) other;
|
||||
target.checkAccess();
|
||||
return Arrays.equals(this.getResolvedPath(), target.getResolvedPath())
|
||||
|| jrtfs.isSameFile(this, target);
|
||||
}
|
||||
|
||||
final SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,
|
||||
FileAttribute<?>... attrs)
|
||||
throws IOException {
|
||||
return jrtfs.newByteChannel(this, options, attrs);
|
||||
}
|
||||
|
||||
final FileChannel newFileChannel(Set<? extends OpenOption> options,
|
||||
FileAttribute<?>... attrs)
|
||||
throws IOException {
|
||||
return jrtfs.newFileChannel(this, options, attrs);
|
||||
}
|
||||
|
||||
final void checkAccess(AccessMode... modes) throws IOException {
|
||||
boolean w = false;
|
||||
boolean x = false;
|
||||
for (AccessMode mode : modes) {
|
||||
switch (mode) {
|
||||
case READ:
|
||||
break;
|
||||
case WRITE:
|
||||
w = true;
|
||||
break;
|
||||
case EXECUTE:
|
||||
x = true;
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
BasicFileAttributes attrs = jrtfs.getFileAttributes(this);
|
||||
if (attrs == null && (path.length != 1 || path[0] != '/')) {
|
||||
throw new NoSuchFileException(toString());
|
||||
}
|
||||
if (w) {
|
||||
// if (jrtfs.isReadOnly())
|
||||
throw new AccessDeniedException(toString());
|
||||
}
|
||||
if (x) {
|
||||
throw new AccessDeniedException(toString());
|
||||
}
|
||||
}
|
||||
|
||||
final boolean exists() {
|
||||
try {
|
||||
return jrtfs.exists(this);
|
||||
} catch (IOException x) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
final OutputStream newOutputStream(OpenOption... options) throws IOException {
|
||||
if (options.length == 0) {
|
||||
return jrtfs.newOutputStream(this,
|
||||
CREATE_NEW, WRITE);
|
||||
}
|
||||
return jrtfs.newOutputStream(this, options);
|
||||
}
|
||||
|
||||
final void move(AbstractJrtPath target, CopyOption... options)
|
||||
throws IOException {
|
||||
if (this.jrtfs == target.jrtfs) {
|
||||
jrtfs.copyFile(true,
|
||||
this, target,
|
||||
options);
|
||||
} else {
|
||||
copyToTarget(target, options);
|
||||
delete();
|
||||
}
|
||||
}
|
||||
|
||||
final void copy(AbstractJrtPath target, CopyOption... options)
|
||||
throws IOException {
|
||||
if (this.jrtfs == target.jrtfs) {
|
||||
jrtfs.copyFile(false,
|
||||
this, target,
|
||||
options);
|
||||
} else {
|
||||
copyToTarget(target, options);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyToTarget(AbstractJrtPath target, CopyOption... options)
|
||||
throws IOException {
|
||||
boolean replaceExisting = false;
|
||||
boolean copyAttrs = false;
|
||||
for (CopyOption opt : options) {
|
||||
if (opt == REPLACE_EXISTING) {
|
||||
replaceExisting = true;
|
||||
} else if (opt == COPY_ATTRIBUTES) {
|
||||
copyAttrs = true;
|
||||
}
|
||||
}
|
||||
// attributes of source file
|
||||
BasicFileAttributes jrtfas = getAttributes();
|
||||
// check if target exists
|
||||
boolean exists;
|
||||
if (replaceExisting) {
|
||||
try {
|
||||
target.deleteIfExists();
|
||||
exists = false;
|
||||
} catch (DirectoryNotEmptyException x) {
|
||||
exists = true;
|
||||
}
|
||||
} else {
|
||||
exists = target.exists();
|
||||
}
|
||||
if (exists) {
|
||||
throw new FileAlreadyExistsException(target.toString());
|
||||
}
|
||||
|
||||
if (jrtfas.isDirectory()) {
|
||||
// create directory or file
|
||||
target.createDirectory();
|
||||
} else {
|
||||
try (InputStream is = jrtfs.newInputStream(this); OutputStream os = target.newOutputStream()) {
|
||||
byte[] buf = new byte[8192];
|
||||
int n;
|
||||
while ((n = is.read(buf)) != -1) {
|
||||
os.write(buf, 0, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (copyAttrs) {
|
||||
BasicFileAttributeView view
|
||||
= JrtFileAttributeView.get(target, BasicFileAttributeView.class);
|
||||
try {
|
||||
view.setTimes(jrtfas.lastModifiedTime(),
|
||||
jrtfas.lastAccessTime(),
|
||||
jrtfas.creationTime());
|
||||
} catch (IOException x) {
|
||||
// rollback?
|
||||
try {
|
||||
target.delete();
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
throw x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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 jdk.internal.jrtfs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystemException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.internal.jimage.ImageReader.Node;
|
||||
|
||||
/**
|
||||
* A jrt file system built on $JAVA_HOME/modules directory ('exploded modules
|
||||
* build')
|
||||
*
|
||||
* @implNote This class needs to maintain JDK 8 source compatibility.
|
||||
*
|
||||
* It is used internally in the JDK to implement jimage/jrtfs access,
|
||||
* but also compiled and delivered as part of the jrtfs.jar to support access
|
||||
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
|
||||
*/
|
||||
class ExplodedImage extends SystemImage {
|
||||
|
||||
private static final String MODULES = "/modules/";
|
||||
private static final String PACKAGES = "/packages/";
|
||||
private static final int PACKAGES_LEN = PACKAGES.length();
|
||||
|
||||
private final FileSystem defaultFS;
|
||||
private final String separator;
|
||||
private final Map<String, PathNode> nodes = Collections.synchronizedMap(new HashMap<>());
|
||||
private final BasicFileAttributes modulesDirAttrs;
|
||||
|
||||
ExplodedImage(Path modulesDir) throws IOException {
|
||||
defaultFS = FileSystems.getDefault();
|
||||
String str = defaultFS.getSeparator();
|
||||
separator = str.equals("/") ? null : str;
|
||||
modulesDirAttrs = Files.readAttributes(modulesDir, BasicFileAttributes.class);
|
||||
initNodes();
|
||||
}
|
||||
|
||||
// A Node that is backed by actual default file system Path
|
||||
private final class PathNode extends Node {
|
||||
|
||||
// Path in underlying default file system
|
||||
private Path path;
|
||||
private PathNode link;
|
||||
private List<Node> children;
|
||||
|
||||
PathNode(String name, Path path, BasicFileAttributes attrs) { // path
|
||||
super(name, attrs);
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
PathNode(String name, Node link) { // link
|
||||
super(name, link.getFileAttributes());
|
||||
this.link = (PathNode)link;
|
||||
}
|
||||
|
||||
PathNode(String name, List<Node> children) { // dir
|
||||
super(name, modulesDirAttrs);
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
return children != null ||
|
||||
(link == null && getFileAttributes().isDirectory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLink() {
|
||||
return link != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathNode resolveLink(boolean recursive) {
|
||||
if (link == null)
|
||||
return this;
|
||||
return recursive && link.isLink() ? link.resolveLink(true) : link;
|
||||
}
|
||||
|
||||
byte[] getContent() throws IOException {
|
||||
if (!getFileAttributes().isRegularFile())
|
||||
throw new FileSystemException(getName() + " is not file");
|
||||
return Files.readAllBytes(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Node> getChildren() {
|
||||
if (!isDirectory())
|
||||
throw new IllegalArgumentException("not a directory: " + getNameString());
|
||||
if (children == null) {
|
||||
List<Node> list = new ArrayList<>();
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
|
||||
for (Path p : stream) {
|
||||
p = explodedModulesDir.relativize(p);
|
||||
String pName = MODULES + nativeSlashToFrontSlash(p.toString());
|
||||
Node node = findNode(pName);
|
||||
if (node != null) { // findNode may choose to hide certain files!
|
||||
list.add(node);
|
||||
}
|
||||
}
|
||||
} catch (IOException x) {
|
||||
return null;
|
||||
}
|
||||
children = list;
|
||||
}
|
||||
return children;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
nodes.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getResource(Node node) throws IOException {
|
||||
return ((PathNode)node).getContent();
|
||||
}
|
||||
|
||||
// find Node for the given Path
|
||||
@Override
|
||||
public synchronized Node findNode(String str) {
|
||||
Node node = findModulesNode(str);
|
||||
if (node != null) {
|
||||
return node;
|
||||
}
|
||||
// lazily created for paths like /packages/<package>/<module>/xyz
|
||||
// For example /packages/java.lang/java.base/java/lang/
|
||||
if (str.startsWith(PACKAGES)) {
|
||||
// pkgEndIdx marks end of <package> part
|
||||
int pkgEndIdx = str.indexOf('/', PACKAGES_LEN);
|
||||
if (pkgEndIdx != -1) {
|
||||
// modEndIdx marks end of <module> part
|
||||
int modEndIdx = str.indexOf('/', pkgEndIdx + 1);
|
||||
if (modEndIdx != -1) {
|
||||
// make sure we have such module link!
|
||||
// ie., /packages/<package>/<module> is valid
|
||||
Node linkNode = nodes.get(str.substring(0, modEndIdx));
|
||||
if (linkNode == null || !linkNode.isLink()) {
|
||||
return null;
|
||||
}
|
||||
// map to "/modules/zyz" path and return that node
|
||||
// For example, "/modules/java.base/java/lang" for
|
||||
// "/packages/java.lang/java.base/java/lang".
|
||||
String mod = MODULES + str.substring(pkgEndIdx + 1);
|
||||
return findModulesNode(mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// find a Node for a path that starts like "/modules/..."
|
||||
Node findModulesNode(String str) {
|
||||
PathNode node = nodes.get(str);
|
||||
if (node != null) {
|
||||
return node;
|
||||
}
|
||||
// lazily created "/modules/xyz/abc/" Node
|
||||
// This is mapped to default file system path "<JDK_MODULES_DIR>/xyz/abc"
|
||||
Path p = underlyingPath(str);
|
||||
if (p != null) {
|
||||
try {
|
||||
BasicFileAttributes attrs = Files.readAttributes(p, BasicFileAttributes.class);
|
||||
if (attrs.isRegularFile()) {
|
||||
Path f = p.getFileName();
|
||||
if (f.toString().startsWith("_the."))
|
||||
return null;
|
||||
}
|
||||
node = new PathNode(str, p, attrs);
|
||||
nodes.put(str, node);
|
||||
return node;
|
||||
} catch (IOException x) {
|
||||
// does not exists or unable to determine
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Path underlyingPath(String str) {
|
||||
if (str.startsWith(MODULES)) {
|
||||
str = frontSlashToNativeSlash(str.substring("/modules".length()));
|
||||
return defaultFS.getPath(explodedModulesDir.toString(), str);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// convert "/" to platform path separator
|
||||
private String frontSlashToNativeSlash(String str) {
|
||||
return separator == null ? str : str.replace("/", separator);
|
||||
}
|
||||
|
||||
// convert platform path separator to "/"
|
||||
private String nativeSlashToFrontSlash(String str) {
|
||||
return separator == null ? str : str.replace(separator, "/");
|
||||
}
|
||||
|
||||
// convert "/"s to "."s
|
||||
private String slashesToDots(String str) {
|
||||
return str.replace(separator != null ? separator : "/", ".");
|
||||
}
|
||||
|
||||
// initialize file system Nodes
|
||||
private void initNodes() throws IOException {
|
||||
// same package prefix may exist in mutliple modules. This Map
|
||||
// is filled by walking "jdk modules" directory recursively!
|
||||
Map<String, List<String>> packageToModules = new HashMap<>();
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(explodedModulesDir)) {
|
||||
for (Path module : stream) {
|
||||
if (Files.isDirectory(module)) {
|
||||
String moduleName = module.getFileName().toString();
|
||||
// make sure "/modules/<moduleName>" is created
|
||||
findModulesNode(MODULES + moduleName);
|
||||
Files.walk(module).filter(Files::isDirectory).forEach((p) -> {
|
||||
p = module.relativize(p);
|
||||
String pkgName = slashesToDots(p.toString());
|
||||
// skip META-INFO and empty strings
|
||||
if (!pkgName.isEmpty() && !pkgName.startsWith("META-INF")) {
|
||||
List<String> moduleNames = packageToModules.get(pkgName);
|
||||
if (moduleNames == null) {
|
||||
moduleNames = new ArrayList<>();
|
||||
packageToModules.put(pkgName, moduleNames);
|
||||
}
|
||||
moduleNames.add(moduleName);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// create "/modules" directory
|
||||
// "nodes" map contains only /modules/<foo> nodes only so far and so add all as children of /modules
|
||||
PathNode modulesDir = new PathNode("/modules", new ArrayList<>(nodes.values()));
|
||||
nodes.put(modulesDir.getName(), modulesDir);
|
||||
|
||||
// create children under "/packages"
|
||||
List<Node> packagesChildren = new ArrayList<>(packageToModules.size());
|
||||
for (Map.Entry<String, List<String>> entry : packageToModules.entrySet()) {
|
||||
String pkgName = entry.getKey();
|
||||
List<String> moduleNameList = entry.getValue();
|
||||
List<Node> moduleLinkNodes = new ArrayList<>(moduleNameList.size());
|
||||
for (String moduleName : moduleNameList) {
|
||||
Node moduleNode = findModulesNode(MODULES + moduleName);
|
||||
PathNode linkNode = new PathNode(PACKAGES + pkgName + "/" + moduleName, moduleNode);
|
||||
nodes.put(linkNode.getName(), linkNode);
|
||||
moduleLinkNodes.add(linkNode);
|
||||
}
|
||||
PathNode pkgDir = new PathNode(PACKAGES + pkgName, moduleLinkNodes);
|
||||
nodes.put(pkgDir.getName(), pkgDir);
|
||||
packagesChildren.add(pkgDir);
|
||||
}
|
||||
// "/packages" dir
|
||||
PathNode packagesDir = new PathNode("/packages", packagesChildren);
|
||||
nodes.put(packagesDir.getName(), packagesDir);
|
||||
|
||||
// finally "/" dir!
|
||||
List<Node> rootChildren = new ArrayList<>();
|
||||
rootChildren.add(packagesDir);
|
||||
rootChildren.add(modulesDir);
|
||||
PathNode root = new PathNode("/", rootChildren);
|
||||
nodes.put(root.getName(), root);
|
||||
}
|
||||
}
|
@ -30,6 +30,7 @@ import java.nio.file.DirectoryIteratorException;
|
||||
import java.nio.file.NotDirectoryException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.io.IOException;
|
||||
|
||||
@ -44,115 +45,47 @@ import java.io.IOException;
|
||||
*/
|
||||
final class JrtDirectoryStream implements DirectoryStream<Path> {
|
||||
|
||||
private final AbstractJrtFileSystem jrtfs;
|
||||
private final AbstractJrtPath dir;
|
||||
private final JrtPath dir;
|
||||
private final DirectoryStream.Filter<? super Path> filter;
|
||||
private volatile boolean isClosed;
|
||||
private volatile Iterator<Path> itr;
|
||||
|
||||
JrtDirectoryStream(AbstractJrtPath jrtPath,
|
||||
JrtDirectoryStream(JrtPath dir,
|
||||
DirectoryStream.Filter<? super java.nio.file.Path> filter)
|
||||
throws IOException {
|
||||
this.jrtfs = jrtPath.getFileSystem();
|
||||
this.dir = jrtPath;
|
||||
// sanity check
|
||||
if (!jrtfs.isDirectory(dir, true)) {
|
||||
throw new NotDirectoryException(jrtPath.toString());
|
||||
throws IOException
|
||||
{
|
||||
this.dir = dir;
|
||||
if (!dir.jrtfs.isDirectory(dir, true)) { // sanity check
|
||||
throw new NotDirectoryException(dir.toString());
|
||||
}
|
||||
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Iterator<Path> iterator() {
|
||||
if (isClosed) {
|
||||
if (isClosed)
|
||||
throw new ClosedDirectoryStreamException();
|
||||
}
|
||||
if (itr != null) {
|
||||
if (itr != null)
|
||||
throw new IllegalStateException("Iterator has already been returned");
|
||||
}
|
||||
|
||||
try {
|
||||
itr = jrtfs.iteratorOf(dir);
|
||||
itr = dir.jrtfs.iteratorOf(dir, filter);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
return new Iterator<Path>() {
|
||||
/*
|
||||
* next Path value to return from this iterator.
|
||||
* null value means hasNext() not called yet
|
||||
* or last hasNext() returned false or resulted
|
||||
* in exception. If last hasNext() returned true,
|
||||
* then this field has non-null value.
|
||||
*/
|
||||
private Path next;
|
||||
|
||||
// get-and-clear and set-next by these methods
|
||||
private Path getAndClearNext() {
|
||||
assert next != null;
|
||||
Path result = this.next;
|
||||
this.next = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
private void setNext(Path path) {
|
||||
assert path != null;
|
||||
this.next = path;
|
||||
}
|
||||
|
||||
// if hasNext() returns true, 'next' field has non-null Path
|
||||
@Override
|
||||
public synchronized boolean hasNext() {
|
||||
if (next != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isClosed) {
|
||||
if (isClosed)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filter == null) {
|
||||
if (itr.hasNext()) {
|
||||
setNext(itr.next());
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
while (itr.hasNext()) {
|
||||
Path tmpPath = itr.next();
|
||||
try {
|
||||
if (filter.accept(tmpPath)) {
|
||||
setNext(tmpPath);
|
||||
return true;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throw new DirectoryIteratorException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return itr.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Path next() {
|
||||
if (next != null) {
|
||||
return getAndClearNext();
|
||||
}
|
||||
|
||||
if (isClosed) {
|
||||
if (isClosed)
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
if (next == null && itr.hasNext()) {
|
||||
// missing hasNext() between next() calls.
|
||||
if (hasNext()) {
|
||||
return getAndClearNext();
|
||||
}
|
||||
}
|
||||
|
||||
throw new NoSuchElementException();
|
||||
return itr.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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 jdk.internal.jrtfs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import jdk.internal.jrtfs.JrtExplodedFileSystem.Node;
|
||||
|
||||
/**
|
||||
* jrt file system attributes implementation on top of 'exploded file system'
|
||||
* Node.
|
||||
*
|
||||
* @implNote This class needs to maintain JDK 8 source compatibility.
|
||||
*
|
||||
* It is used internally in the JDK to implement jimage/jrtfs access,
|
||||
* but also compiled and delivered as part of the jrtfs.jar to support access
|
||||
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
|
||||
*/
|
||||
final class JrtExplodedFileAttributes extends AbstractJrtFileAttributes {
|
||||
|
||||
private final Node node;
|
||||
private final BasicFileAttributes attrs;
|
||||
|
||||
JrtExplodedFileAttributes(Node node) throws IOException {
|
||||
this.node = node;
|
||||
this.attrs = node.getBasicAttrs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileTime creationTime() {
|
||||
return attrs.creationTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
return node.isDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOther() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegularFile() {
|
||||
return node.isFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileTime lastAccessTime() {
|
||||
return attrs.lastAccessTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileTime lastModifiedTime() {
|
||||
return attrs.lastModifiedTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() {
|
||||
return isRegularFile() ? attrs.size() : 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSymbolicLink() {
|
||||
return node.isLink();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fileKey() {
|
||||
return node.resolveLink(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long compressedSize() {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String extension() {
|
||||
return node.getExtension();
|
||||
}
|
||||
}
|
@ -1,528 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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 jdk.internal.jrtfs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystemException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.NotDirectoryException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static jdk.internal.jrtfs.AbstractJrtFileSystem.getString;
|
||||
|
||||
/**
|
||||
* A jrt file system built on $JAVA_HOME/modules directory ('exploded modules
|
||||
* build')
|
||||
*
|
||||
* @implNote This class needs to maintain JDK 8 source compatibility.
|
||||
*
|
||||
* It is used internally in the JDK to implement jimage/jrtfs access,
|
||||
* but also compiled and delivered as part of the jrtfs.jar to support access
|
||||
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
|
||||
*/
|
||||
class JrtExplodedFileSystem extends AbstractJrtFileSystem {
|
||||
|
||||
private static final String MODULES = "/modules/";
|
||||
private static final String PACKAGES = "/packages/";
|
||||
private static final int PACKAGES_LEN = PACKAGES.length();
|
||||
|
||||
// root path
|
||||
private final JrtExplodedPath rootPath;
|
||||
private volatile boolean isOpen;
|
||||
private final FileSystem defaultFS;
|
||||
private final String separator;
|
||||
private final Map<String, Node> nodes = Collections.synchronizedMap(new HashMap<>());
|
||||
private final BasicFileAttributes modulesDirAttrs;
|
||||
|
||||
JrtExplodedFileSystem(JrtFileSystemProvider provider,
|
||||
Map<String, ?> env)
|
||||
throws IOException {
|
||||
|
||||
super(provider, env);
|
||||
checkExists(SystemImages.explodedModulesDir());
|
||||
byte[] root = new byte[]{'/'};
|
||||
rootPath = new JrtExplodedPath(this, root);
|
||||
isOpen = true;
|
||||
defaultFS = FileSystems.getDefault();
|
||||
String str = defaultFS.getSeparator();
|
||||
separator = str.equals(getSeparator()) ? null : str;
|
||||
modulesDirAttrs = Files.readAttributes(SystemImages.explodedModulesDir(), BasicFileAttributes.class);
|
||||
initNodes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return isOpen;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
cleanup();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
private synchronized void cleanup() {
|
||||
isOpen = false;
|
||||
nodes.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
JrtExplodedPath getRootPath() {
|
||||
return rootPath;
|
||||
}
|
||||
|
||||
// Base class for Nodes of this file system
|
||||
abstract class Node {
|
||||
|
||||
private final String name;
|
||||
|
||||
Node(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
final String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
final String getExtension() {
|
||||
if (isFile()) {
|
||||
final int index = name.lastIndexOf(".");
|
||||
if (index != -1) {
|
||||
return name.substring(index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
BasicFileAttributes getBasicAttrs() throws IOException {
|
||||
return modulesDirAttrs;
|
||||
}
|
||||
|
||||
boolean isLink() {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isDirectory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isFile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] getContent() throws IOException {
|
||||
if (!isFile()) {
|
||||
throw new FileSystemException(name + " is not file");
|
||||
}
|
||||
|
||||
throw new AssertionError("ShouldNotReachHere");
|
||||
}
|
||||
|
||||
List<Node> getChildren() throws IOException {
|
||||
if (!isDirectory()) {
|
||||
throw new NotDirectoryException(name);
|
||||
}
|
||||
|
||||
throw new AssertionError("ShouldNotReachHere");
|
||||
}
|
||||
|
||||
final Node resolveLink() {
|
||||
return resolveLink(false);
|
||||
}
|
||||
|
||||
Node resolveLink(boolean recursive) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
// A Node that is backed by actual default file system Path
|
||||
private final class PathNode extends Node {
|
||||
|
||||
// Path in underlying default file system
|
||||
private final Path path;
|
||||
private final boolean file;
|
||||
// lazily initialized, don't read attributes unless required!
|
||||
private BasicFileAttributes attrs;
|
||||
|
||||
PathNode(String name, Path path) {
|
||||
super(name);
|
||||
this.path = path;
|
||||
this.file = Files.isRegularFile(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
synchronized BasicFileAttributes getBasicAttrs() throws IOException {
|
||||
if (attrs == null) {
|
||||
attrs = Files.readAttributes(path, BasicFileAttributes.class);
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isDirectory() {
|
||||
return !file;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
byte[] getContent() throws IOException {
|
||||
if (!isFile()) {
|
||||
throw new FileSystemException(getName() + " is not file");
|
||||
}
|
||||
|
||||
return Files.readAllBytes(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
List<Node> getChildren() throws IOException {
|
||||
if (!isDirectory()) {
|
||||
throw new NotDirectoryException(getName());
|
||||
}
|
||||
|
||||
List<Node> children = new ArrayList<>();
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
|
||||
for (Path cp : stream) {
|
||||
cp = SystemImages.explodedModulesDir().relativize(cp);
|
||||
String cpName = MODULES + nativeSlashToFrontSlash(cp.toString());
|
||||
try {
|
||||
children.add(findNode(cpName));
|
||||
} catch (NoSuchFileException nsfe) {
|
||||
// findNode may choose to hide certain files!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
}
|
||||
|
||||
// A Node that links to another Node
|
||||
private final class LinkNode extends Node {
|
||||
|
||||
// underlying linked Node
|
||||
private final Node link;
|
||||
|
||||
LinkNode(String name, Node link) {
|
||||
super(name);
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
@Override
|
||||
BasicFileAttributes getBasicAttrs() throws IOException {
|
||||
return link.getBasicAttrs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLink() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
Node resolveLink(boolean recursive) {
|
||||
return recursive && (link instanceof LinkNode) ? ((LinkNode) link).resolveLink(true) : link;
|
||||
}
|
||||
}
|
||||
|
||||
// A directory Node with it's children Nodes
|
||||
private final class DirNode extends Node {
|
||||
|
||||
// children Nodes of this Node.
|
||||
private final List<Node> children;
|
||||
|
||||
DirNode(String name, List<Node> children) {
|
||||
super(name);
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isDirectory() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
List<Node> getChildren() throws IOException {
|
||||
return children;
|
||||
}
|
||||
}
|
||||
|
||||
private JrtExplodedPath toJrtExplodedPath(String path) {
|
||||
return toJrtExplodedPath(getBytes(path));
|
||||
}
|
||||
|
||||
private JrtExplodedPath toJrtExplodedPath(byte[] path) {
|
||||
return new JrtExplodedPath(this, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isSameFile(AbstractJrtPath jrtPath1, AbstractJrtPath jrtPath2) throws IOException {
|
||||
Node n1 = checkNode(jrtPath1);
|
||||
Node n2 = checkNode(jrtPath2);
|
||||
return n1 == n2;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isLink(AbstractJrtPath jrtPath) throws IOException {
|
||||
return checkNode(jrtPath).isLink();
|
||||
}
|
||||
|
||||
@Override
|
||||
AbstractJrtPath resolveLink(AbstractJrtPath jrtPath) throws IOException {
|
||||
String name = checkNode(jrtPath).resolveLink().getName();
|
||||
return toJrtExplodedPath(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
AbstractJrtFileAttributes getFileAttributes(AbstractJrtPath jrtPath, LinkOption... options) throws IOException {
|
||||
Node node = checkNode(jrtPath);
|
||||
if (node.isLink() && followLinks(options)) {
|
||||
node = node.resolveLink(true);
|
||||
}
|
||||
return new JrtExplodedFileAttributes(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean exists(AbstractJrtPath jrtPath) throws IOException {
|
||||
try {
|
||||
checkNode(jrtPath);
|
||||
return true;
|
||||
} catch (NoSuchFileException nsfe) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isDirectory(AbstractJrtPath jrtPath, boolean resolveLinks) throws IOException {
|
||||
Node node = checkNode(jrtPath);
|
||||
return resolveLinks && node.isLink()
|
||||
? node.resolveLink(true).isDirectory()
|
||||
: node.isDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
Iterator<Path> iteratorOf(AbstractJrtPath dir) throws IOException {
|
||||
Node node = checkNode(dir).resolveLink(true);
|
||||
if (!node.isDirectory()) {
|
||||
throw new NotDirectoryException(getString(dir.getName()));
|
||||
}
|
||||
|
||||
Function<Node, Path> nodeToPath =
|
||||
child -> dir.resolve(
|
||||
toJrtExplodedPath(child.getName()).
|
||||
getFileName());
|
||||
|
||||
return node.getChildren().stream().
|
||||
map(nodeToPath).collect(toList()).
|
||||
iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
byte[] getFileContent(AbstractJrtPath jrtPath) throws IOException {
|
||||
return checkNode(jrtPath).getContent();
|
||||
}
|
||||
|
||||
private Node checkNode(AbstractJrtPath jrtPath) throws IOException {
|
||||
return checkNode(jrtPath.getResolvedPath());
|
||||
}
|
||||
|
||||
private Node checkNode(byte[] path) throws IOException {
|
||||
ensureOpen();
|
||||
return findNode(path);
|
||||
}
|
||||
|
||||
synchronized Node findNode(byte[] path) throws IOException {
|
||||
return findNode(getString(path));
|
||||
}
|
||||
|
||||
// find Node for the given Path
|
||||
synchronized Node findNode(String str) throws IOException {
|
||||
Node node = findModulesNode(str);
|
||||
if (node != null) {
|
||||
return node;
|
||||
}
|
||||
|
||||
// lazily created for paths like /packages/<package>/<module>/xyz
|
||||
// For example /packages/java.lang/java.base/java/lang/
|
||||
if (str.startsWith(PACKAGES)) {
|
||||
// pkgEndIdx marks end of <package> part
|
||||
int pkgEndIdx = str.indexOf('/', PACKAGES_LEN);
|
||||
if (pkgEndIdx != -1) {
|
||||
// modEndIdx marks end of <module> part
|
||||
int modEndIdx = str.indexOf('/', pkgEndIdx + 1);
|
||||
if (modEndIdx != -1) {
|
||||
// make sure we have such module link!
|
||||
// ie., /packages/<package>/<module> is valid
|
||||
Node linkNode = nodes.get(str.substring(0, modEndIdx));
|
||||
if (linkNode == null || !linkNode.isLink()) {
|
||||
throw new NoSuchFileException(str);
|
||||
}
|
||||
|
||||
// map to "/modules/zyz" path and return that node
|
||||
// For example, "/modules/java.base/java/lang" for
|
||||
// "/packages/java.lang/java.base/java/lang".
|
||||
String mod = MODULES + str.substring(pkgEndIdx + 1);
|
||||
return findNode(mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new NoSuchFileException(str);
|
||||
}
|
||||
|
||||
// find a Node for a path that starts like "/modules/..."
|
||||
synchronized Node findModulesNode(String str) throws IOException {
|
||||
Node node = nodes.get(str);
|
||||
if (node != null) {
|
||||
return node;
|
||||
}
|
||||
|
||||
// lazily created "/modules/xyz/abc/" Node
|
||||
// This is mapped to default file system path "<JDK_MODULES_DIR>/xyz/abc"
|
||||
Path p = underlyingPath(str);
|
||||
if (p != null) {
|
||||
if (Files.isRegularFile(p)) {
|
||||
Path file = p.getFileName();
|
||||
if (file.toString().startsWith("_the.")) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
node = new PathNode(str, p);
|
||||
nodes.put(str, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Path underlyingPath(String str) {
|
||||
if (str.startsWith(MODULES)) {
|
||||
str = frontSlashToNativeSlash(str.substring("/modules".length()));
|
||||
return defaultFS.getPath(SystemImages.explodedModulesDir().toString(), str);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// convert "/" to platform path separator
|
||||
private String frontSlashToNativeSlash(String str) {
|
||||
return separator == null ? str : str.replace("/", separator);
|
||||
}
|
||||
|
||||
// convert platform path separator to "/"
|
||||
private String nativeSlashToFrontSlash(String str) {
|
||||
return separator == null ? str : str.replace(separator, "/");
|
||||
}
|
||||
|
||||
// convert "/"s to "."s
|
||||
private String slashesToDots(String str) {
|
||||
return str.replace(separator != null ? separator : "/", ".");
|
||||
}
|
||||
|
||||
// initialize file system Nodes
|
||||
private void initNodes() throws IOException {
|
||||
// same package prefix may exist in mutliple modules. This Map
|
||||
// is filled by walking "jdk modules" directory recursively!
|
||||
Map<String, List<String>> packageToModules = new HashMap<>();
|
||||
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(SystemImages.explodedModulesDir())) {
|
||||
for (Path module : stream) {
|
||||
if (Files.isDirectory(module)) {
|
||||
String moduleName = module.getFileName().toString();
|
||||
// make sure "/modules/<moduleName>" is created
|
||||
findModulesNode(MODULES + moduleName);
|
||||
|
||||
Files.walk(module).filter(Files::isDirectory).forEach((p) -> {
|
||||
p = module.relativize(p);
|
||||
String pkgName = slashesToDots(p.toString());
|
||||
// skip META-INFO and empty strings
|
||||
if (!pkgName.isEmpty() && !pkgName.startsWith("META-INF")) {
|
||||
List<String> moduleNames = packageToModules.get(pkgName);
|
||||
if (moduleNames == null) {
|
||||
moduleNames = new ArrayList<>();
|
||||
packageToModules.put(pkgName, moduleNames);
|
||||
}
|
||||
moduleNames.add(moduleName);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create "/modules" directory
|
||||
// "nodes" map contains only /modules/<foo> nodes only so far and so add all as children of /modules
|
||||
DirNode modulesDir = new DirNode("/modules", new ArrayList<>(nodes.values()));
|
||||
nodes.put(modulesDir.getName(), modulesDir);
|
||||
|
||||
// create children under "/packages"
|
||||
List<Node> packagesChildren = new ArrayList<>(packageToModules.size());
|
||||
for (Map.Entry<String, List<String>> entry : packageToModules.entrySet()) {
|
||||
String pkgName = entry.getKey();
|
||||
List<String> moduleNameList = entry.getValue();
|
||||
List<Node> moduleLinkNodes = new ArrayList<>(moduleNameList.size());
|
||||
for (String moduleName : moduleNameList) {
|
||||
Node moduleNode = findModulesNode(MODULES + moduleName);
|
||||
LinkNode linkNode = new LinkNode(PACKAGES + pkgName + "/" + moduleName, moduleNode);
|
||||
nodes.put(linkNode.getName(), linkNode);
|
||||
moduleLinkNodes.add(linkNode);
|
||||
}
|
||||
|
||||
DirNode pkgDir = new DirNode(PACKAGES + pkgName, moduleLinkNodes);
|
||||
nodes.put(pkgDir.getName(), pkgDir);
|
||||
packagesChildren.add(pkgDir);
|
||||
}
|
||||
|
||||
// "/packages" dir
|
||||
DirNode packagesDir = new DirNode("/packages", packagesChildren);
|
||||
nodes.put(packagesDir.getName(), packagesDir);
|
||||
|
||||
// finally "/" dir!
|
||||
List<Node> rootChildren = new ArrayList<>();
|
||||
rootChildren.add(modulesDir);
|
||||
rootChildren.add(packagesDir);
|
||||
DirNode root = new DirNode("/", rootChildren);
|
||||
nodes.put(root.getName(), root);
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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 jdk.internal.jrtfs;
|
||||
|
||||
/**
|
||||
* Path implementation for jrt file system on JDK exploded modules build.
|
||||
*
|
||||
* @implNote This class needs to maintain JDK 8 source compatibility.
|
||||
*
|
||||
* It is used internally in the JDK to implement jimage/jrtfs access,
|
||||
* but also compiled and delivered as part of the jrtfs.jar to support access
|
||||
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
|
||||
*/
|
||||
final class JrtExplodedPath extends AbstractJrtPath {
|
||||
|
||||
JrtExplodedPath(AbstractJrtFileSystem jrtfs, byte[] path) {
|
||||
super(jrtfs, path);
|
||||
}
|
||||
|
||||
JrtExplodedPath(AbstractJrtFileSystem jrtfs, byte[] path, boolean normalized) {
|
||||
super(jrtfs, path, normalized);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractJrtPath newJrtPath(byte[] path) {
|
||||
return new JrtExplodedPath(jrtfs, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractJrtPath newJrtPath(byte[] path, boolean normalized) {
|
||||
return new JrtExplodedPath(jrtfs, path, normalized);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JrtExplodedFileSystem getFileSystem() {
|
||||
return (JrtExplodedFileSystem) jrtfs;
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ import java.nio.file.attribute.*;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* File attribute view for jrt file system.
|
||||
@ -42,7 +43,6 @@ import java.util.Map;
|
||||
final class JrtFileAttributeView implements BasicFileAttributeView {
|
||||
|
||||
private static enum AttrID {
|
||||
|
||||
size,
|
||||
creationTime,
|
||||
lastAccessTime,
|
||||
@ -56,21 +56,19 @@ final class JrtFileAttributeView implements BasicFileAttributeView {
|
||||
extension
|
||||
};
|
||||
|
||||
private final AbstractJrtPath path;
|
||||
private final JrtPath path;
|
||||
private final boolean isJrtView;
|
||||
private final LinkOption[] options;
|
||||
|
||||
private JrtFileAttributeView(AbstractJrtPath path, boolean isJrtView, LinkOption... options) {
|
||||
private JrtFileAttributeView(JrtPath path, boolean isJrtView, LinkOption... options) {
|
||||
this.path = path;
|
||||
this.isJrtView = isJrtView;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked") // Cast to V
|
||||
static <V extends FileAttributeView> V get(AbstractJrtPath path, Class<V> type, LinkOption... options) {
|
||||
if (type == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
static <V extends FileAttributeView> V get(JrtPath path, Class<V> type, LinkOption... options) {
|
||||
Objects.requireNonNull(type);
|
||||
if (type == BasicFileAttributeView.class) {
|
||||
return (V) new JrtFileAttributeView(path, false, options);
|
||||
}
|
||||
@ -80,10 +78,8 @@ final class JrtFileAttributeView implements BasicFileAttributeView {
|
||||
return null;
|
||||
}
|
||||
|
||||
static JrtFileAttributeView get(AbstractJrtPath path, String type, LinkOption... options) {
|
||||
if (type == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
static JrtFileAttributeView get(JrtPath path, String type, LinkOption... options) {
|
||||
Objects.requireNonNull(type);
|
||||
if (type.equals("basic")) {
|
||||
return new JrtFileAttributeView(path, false, options);
|
||||
}
|
||||
@ -99,61 +95,74 @@ final class JrtFileAttributeView implements BasicFileAttributeView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractJrtFileAttributes readAttributes() throws IOException {
|
||||
public JrtFileAttributes readAttributes() throws IOException {
|
||||
return path.getAttributes(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimes(FileTime lastModifiedTime,
|
||||
FileTime lastAccessTime,
|
||||
FileTime createTime)
|
||||
throws IOException {
|
||||
FileTime lastAccessTime,
|
||||
FileTime createTime) throws IOException {
|
||||
path.setTimes(lastModifiedTime, lastAccessTime, createTime);
|
||||
}
|
||||
|
||||
void setAttribute(String attribute, Object value)
|
||||
static void setAttribute(JrtPath path, String attribute, Object value)
|
||||
throws IOException {
|
||||
int colonPos = attribute.indexOf(':');
|
||||
if (colonPos != -1) { // type = "basic", if no ":"
|
||||
String type = attribute.substring(0, colonPos++);
|
||||
if (!type.equals("basic") && !type.equals("jrt")) {
|
||||
throw new UnsupportedOperationException(
|
||||
"view <" + type + "> is not supported");
|
||||
}
|
||||
attribute = attribute.substring(colonPos);
|
||||
}
|
||||
try {
|
||||
if (AttrID.valueOf(attribute) == AttrID.lastModifiedTime) {
|
||||
setTimes((FileTime) value, null, null);
|
||||
}
|
||||
if (AttrID.valueOf(attribute) == AttrID.lastAccessTime) {
|
||||
setTimes(null, (FileTime) value, null);
|
||||
}
|
||||
if (AttrID.valueOf(attribute) == AttrID.creationTime) {
|
||||
setTimes(null, null, (FileTime) value);
|
||||
AttrID id = AttrID.valueOf(attribute);
|
||||
if (id == AttrID.lastModifiedTime) {
|
||||
path.setTimes((FileTime) value, null, null);
|
||||
} else if (id == AttrID.lastAccessTime) {
|
||||
path.setTimes(null, (FileTime) value, null);
|
||||
} else if (id == AttrID.creationTime) {
|
||||
path.setTimes(null, null, (FileTime) value);
|
||||
}
|
||||
return;
|
||||
} catch (IllegalArgumentException x) {
|
||||
}
|
||||
} catch (IllegalArgumentException x) {}
|
||||
throw new UnsupportedOperationException("'" + attribute
|
||||
+ "' is unknown or read-only attribute");
|
||||
}
|
||||
|
||||
Map<String, Object> readAttributes(String attributes)
|
||||
static Map<String, Object> readAttributes(JrtPath path, String attributes,
|
||||
LinkOption... options)
|
||||
throws IOException {
|
||||
AbstractJrtFileAttributes jrtfas = readAttributes();
|
||||
int colonPos = attributes.indexOf(':');
|
||||
boolean isJrtView = false;
|
||||
if (colonPos != -1) { // type = "basic", if no ":"
|
||||
String type = attributes.substring(0, colonPos++);
|
||||
if (!type.equals("basic") && !type.equals("jrt")) {
|
||||
throw new UnsupportedOperationException("view <" + type +
|
||||
"> is not supported");
|
||||
}
|
||||
isJrtView = true;
|
||||
attributes = attributes.substring(colonPos);
|
||||
}
|
||||
JrtFileAttributes jrtfas = path.getAttributes();
|
||||
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
|
||||
if ("*".equals(attributes)) {
|
||||
for (AttrID id : AttrID.values()) {
|
||||
try {
|
||||
map.put(id.name(), attribute(id, jrtfas));
|
||||
} catch (IllegalArgumentException x) {
|
||||
}
|
||||
map.put(id.name(), attribute(id, jrtfas, isJrtView));
|
||||
}
|
||||
} else {
|
||||
String[] as = attributes.split(",");
|
||||
for (String a : as) {
|
||||
try {
|
||||
map.put(a, attribute(AttrID.valueOf(a), jrtfas));
|
||||
} catch (IllegalArgumentException x) {
|
||||
}
|
||||
//throw IllegalArgumentException
|
||||
map.put(a, attribute(AttrID.valueOf(a), jrtfas, isJrtView));
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
Object attribute(AttrID id, AbstractJrtFileAttributes jrtfas) {
|
||||
static Object attribute(AttrID id, JrtFileAttributes jrtfas, boolean isJrtView) {
|
||||
switch (id) {
|
||||
case size:
|
||||
return jrtfas.size();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 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
|
||||
@ -24,7 +24,9 @@
|
||||
*/
|
||||
package jdk.internal.jrtfs;
|
||||
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.util.Formatter;
|
||||
import jdk.internal.jimage.ImageReader.Node;
|
||||
|
||||
/**
|
||||
@ -36,7 +38,7 @@ import jdk.internal.jimage.ImageReader.Node;
|
||||
* but also compiled and delivered as part of the jrtfs.jar to support access
|
||||
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
|
||||
*/
|
||||
final class JrtFileAttributes extends AbstractJrtFileAttributes {
|
||||
final class JrtFileAttributes implements BasicFileAttributes {
|
||||
|
||||
private final Node node;
|
||||
|
||||
@ -90,14 +92,50 @@ final class JrtFileAttributes extends AbstractJrtFileAttributes {
|
||||
return node.resolveLink(true);
|
||||
}
|
||||
|
||||
///////// jrt entry attributes ///////////
|
||||
@Override
|
||||
///////// jrtfs specific attributes ///////////
|
||||
/**
|
||||
* Compressed resource file. If not available or not applicable, 0L is
|
||||
* returned.
|
||||
*
|
||||
* @return the compressed resource size for compressed resources.
|
||||
*/
|
||||
public long compressedSize() {
|
||||
return node.compressedSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* "file" extension of a file resource.
|
||||
*
|
||||
* @return extension string for the file resource
|
||||
*/
|
||||
public String extension() {
|
||||
return node.extension();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
StringBuilder sb = new StringBuilder(1024);
|
||||
try (Formatter fm = new Formatter(sb)) {
|
||||
if (creationTime() != null) {
|
||||
fm.format(" creationTime : %tc%n", creationTime().toMillis());
|
||||
} else {
|
||||
fm.format(" creationTime : null%n");
|
||||
}
|
||||
if (lastAccessTime() != null) {
|
||||
fm.format(" lastAccessTime : %tc%n", lastAccessTime().toMillis());
|
||||
} else {
|
||||
fm.format(" lastAccessTime : null%n");
|
||||
}
|
||||
fm.format(" lastModifiedTime: %tc%n", lastModifiedTime().toMillis());
|
||||
fm.format(" isRegularFile : %b%n", isRegularFile());
|
||||
fm.format(" isDirectory : %b%n", isDirectory());
|
||||
fm.format(" isSymbolicLink : %b%n", isSymbolicLink());
|
||||
fm.format(" isOther : %b%n", isOther());
|
||||
fm.format(" fileKey : %s%n", fileKey());
|
||||
fm.format(" size : %d%n", size());
|
||||
fm.format(" compressedSize : %d%n", compressedSize());
|
||||
fm.format(" extension : %s%n", extension());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import java.nio.file.FileSystem;
|
||||
import java.nio.file.attribute.FileAttributeView;
|
||||
import java.nio.file.attribute.BasicFileAttributeView;
|
||||
import java.nio.file.attribute.FileStoreAttributeView;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* File store implementation for jrt file systems.
|
||||
@ -44,7 +45,7 @@ final class JrtFileStore extends FileStore {
|
||||
|
||||
protected final FileSystem jrtfs;
|
||||
|
||||
JrtFileStore(AbstractJrtPath jrtPath) {
|
||||
JrtFileStore(JrtPath jrtPath) {
|
||||
this.jrtfs = jrtPath.getFileSystem();
|
||||
}
|
||||
|
||||
@ -71,9 +72,7 @@ final class JrtFileStore extends FileStore {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type) {
|
||||
if (type == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
Objects.requireNonNull(type, "type");
|
||||
return (V) null;
|
||||
}
|
||||
|
||||
@ -99,7 +98,7 @@ final class JrtFileStore extends FileStore {
|
||||
|
||||
@Override
|
||||
public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
|
||||
return (type == BasicFileAttributeView.class
|
||||
|| type == JrtFileAttributeView.class);
|
||||
return type == BasicFileAttributeView.class ||
|
||||
type == JrtFileAttributeView.class;
|
||||
}
|
||||
}
|
||||
|
@ -24,23 +24,47 @@
|
||||
*/
|
||||
package jdk.internal.jrtfs;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.NonWritableChannelException;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.ClosedFileSystemException;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.FileStore;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystemException;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.NotDirectoryException;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.PathMatcher;
|
||||
import java.nio.file.ReadOnlyFileSystemException;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.WatchService;
|
||||
import java.nio.file.attribute.FileAttribute;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.nio.file.attribute.UserPrincipalLookupService;
|
||||
import java.nio.file.spi.FileSystemProvider;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import jdk.internal.jimage.ImageReader;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import jdk.internal.jimage.ImageReader.Node;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
/**
|
||||
* jrt file system implementation built on System jimage files.
|
||||
@ -51,33 +75,21 @@ import jdk.internal.jimage.ImageReader.Node;
|
||||
* but also compiled and delivered as part of the jrtfs.jar to support access
|
||||
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
|
||||
*/
|
||||
class JrtFileSystem extends AbstractJrtFileSystem {
|
||||
class JrtFileSystem extends FileSystem {
|
||||
|
||||
// System image reader
|
||||
private ImageReader bootImage;
|
||||
// root path
|
||||
private final JrtPath rootPath;
|
||||
private final JrtFileSystemProvider provider;
|
||||
private final JrtPath rootPath = new JrtPath(this, "/");
|
||||
private volatile boolean isOpen;
|
||||
private volatile boolean isClosable;
|
||||
private SystemImage image;
|
||||
|
||||
// open a .jimage and build directory structure
|
||||
private static ImageReader openImage(Path path) throws IOException {
|
||||
ImageReader image = ImageReader.open(path);
|
||||
image.getRootDirectory();
|
||||
return image;
|
||||
}
|
||||
|
||||
JrtFileSystem(JrtFileSystemProvider provider,
|
||||
Map<String, ?> env)
|
||||
throws IOException {
|
||||
super(provider, env);
|
||||
checkExists(SystemImages.moduleImageFile());
|
||||
|
||||
// open image file
|
||||
this.bootImage = openImage(SystemImages.moduleImageFile());
|
||||
|
||||
byte[] root = new byte[]{'/'};
|
||||
rootPath = new JrtPath(this, root);
|
||||
isOpen = true;
|
||||
JrtFileSystem(JrtFileSystemProvider provider, Map<String, ?> env)
|
||||
throws IOException
|
||||
{
|
||||
this.provider = provider;
|
||||
this.image = SystemImage.open(); // open image file
|
||||
this.isOpen = true;
|
||||
this.isClosable = env != null;
|
||||
}
|
||||
|
||||
// FileSystem method implementations
|
||||
@ -88,6 +100,8 @@ class JrtFileSystem extends AbstractJrtFileSystem {
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (!isClosable)
|
||||
throw new UnsupportedOperationException();
|
||||
cleanup();
|
||||
}
|
||||
|
||||
@ -95,237 +109,397 @@ class JrtFileSystem extends AbstractJrtFileSystem {
|
||||
protected void finalize() throws Throwable {
|
||||
try {
|
||||
cleanup();
|
||||
} catch (IOException ignored) {
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileSystemProvider provider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Path> getRootDirectories() {
|
||||
ArrayList<Path> dirs = new ArrayList<>();
|
||||
dirs.add(getRootPath());
|
||||
return dirs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JrtPath getPath(String first, String... more) {
|
||||
if (more.length == 0) {
|
||||
return new JrtPath(this, first);
|
||||
}
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
// AbstractJrtFileSystem method implementations
|
||||
@Override
|
||||
JrtPath getRootPath() {
|
||||
return rootPath;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(first);
|
||||
for (String path : more) {
|
||||
if (path.length() > 0) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append('/');
|
||||
}
|
||||
sb.append(path);
|
||||
}
|
||||
}
|
||||
return new JrtPath(this, sb.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isSameFile(AbstractJrtPath p1, AbstractJrtPath p2) throws IOException {
|
||||
ensureOpen();
|
||||
Node node1 = findNode(p1);
|
||||
Node node2 = findNode(p2);
|
||||
return node1.equals(node2);
|
||||
public final boolean isReadOnly() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isLink(AbstractJrtPath jrtPath) throws IOException {
|
||||
return checkNode(jrtPath).isLink();
|
||||
public final UserPrincipalLookupService getUserPrincipalLookupService() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
AbstractJrtPath resolveLink(AbstractJrtPath jrtPath) throws IOException {
|
||||
Node node = checkNode(jrtPath);
|
||||
public final WatchService newWatchService() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Iterable<FileStore> getFileStores() {
|
||||
ArrayList<FileStore> list = new ArrayList<>(1);
|
||||
list.add(getFileStore(getRootPath()));
|
||||
return list;
|
||||
}
|
||||
|
||||
private static final Set<String> supportedFileAttributeViews
|
||||
= Collections.unmodifiableSet(
|
||||
new HashSet<String>(Arrays.asList("basic", "jrt")));
|
||||
|
||||
@Override
|
||||
public final Set<String> supportedFileAttributeViews() {
|
||||
return supportedFileAttributeViews;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return "jrt:/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getSeparator() {
|
||||
return "/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathMatcher getPathMatcher(String syntaxAndInput) {
|
||||
int pos = syntaxAndInput.indexOf(':');
|
||||
if (pos <= 0 || pos == syntaxAndInput.length()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
String syntax = syntaxAndInput.substring(0, pos);
|
||||
String input = syntaxAndInput.substring(pos + 1);
|
||||
String expr;
|
||||
if (syntax.equalsIgnoreCase("glob")) {
|
||||
expr = JrtUtils.toRegexPattern(input);
|
||||
} else if (syntax.equalsIgnoreCase("regex")) {
|
||||
expr = input;
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Syntax '" + syntax
|
||||
+ "' not recognized");
|
||||
}
|
||||
// return matcher
|
||||
final Pattern pattern = Pattern.compile(expr);
|
||||
return (Path path) -> pattern.matcher(path.toString()).matches();
|
||||
}
|
||||
|
||||
JrtPath resolveLink(JrtPath path) throws IOException {
|
||||
Node node = checkNode(path);
|
||||
if (node.isLink()) {
|
||||
node = node.resolveLink();
|
||||
return toJrtPath(getBytes(node.getName()));
|
||||
return new JrtPath(this, node.getName()); // TBD, normalized?
|
||||
}
|
||||
|
||||
return jrtPath;
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
JrtFileAttributes getFileAttributes(AbstractJrtPath jrtPath, LinkOption... options)
|
||||
JrtFileAttributes getFileAttributes(JrtPath path, LinkOption... options)
|
||||
throws IOException {
|
||||
Node node = checkNode(jrtPath);
|
||||
Node node = checkNode(path);
|
||||
if (node.isLink() && followLinks(options)) {
|
||||
return new JrtFileAttributes(node.resolveLink(true));
|
||||
}
|
||||
return new JrtFileAttributes(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean exists(AbstractJrtPath jrtPath) throws IOException {
|
||||
/**
|
||||
* returns the list of child paths of the given directory "path"
|
||||
*
|
||||
* @param path name of the directory whose content is listed
|
||||
* @return iterator for child paths of the given directory path
|
||||
*/
|
||||
Iterator<Path> iteratorOf(JrtPath path, DirectoryStream.Filter<? super Path> filter)
|
||||
throws IOException {
|
||||
Node node = checkNode(path).resolveLink(true);
|
||||
if (!node.isDirectory()) {
|
||||
throw new NotDirectoryException(path.getName());
|
||||
}
|
||||
if (filter == null) {
|
||||
return node.getChildren()
|
||||
.stream()
|
||||
.map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName())))
|
||||
.iterator();
|
||||
}
|
||||
return node.getChildren()
|
||||
.stream()
|
||||
.map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName())))
|
||||
.filter(p -> { try { return filter.accept(p);
|
||||
} catch (IOException x) {}
|
||||
return false;
|
||||
})
|
||||
.iterator();
|
||||
}
|
||||
|
||||
// returns the content of the file resource specified by the path
|
||||
byte[] getFileContent(JrtPath path) throws IOException {
|
||||
Node node = checkNode(path);
|
||||
if (node.isDirectory()) {
|
||||
throw new FileSystemException(path + " is a directory");
|
||||
}
|
||||
//assert node.isResource() : "resource node expected here";
|
||||
return image.getResource(node);
|
||||
}
|
||||
|
||||
/////////////// Implementation details below this point //////////
|
||||
|
||||
// static utility methods
|
||||
static ReadOnlyFileSystemException readOnly() {
|
||||
return new ReadOnlyFileSystemException();
|
||||
}
|
||||
|
||||
// do the supplied options imply that we have to chase symlinks?
|
||||
static boolean followLinks(LinkOption... options) {
|
||||
if (options != null) {
|
||||
for (LinkOption lo : options) {
|
||||
Objects.requireNonNull(lo);
|
||||
if (lo == LinkOption.NOFOLLOW_LINKS) {
|
||||
return false;
|
||||
} else {
|
||||
throw new AssertionError("should not reach here");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// check that the options passed are supported by (read-only) jrt file system
|
||||
static void checkOptions(Set<? extends OpenOption> options) {
|
||||
// check for options of null type and option is an intance of StandardOpenOption
|
||||
for (OpenOption option : options) {
|
||||
Objects.requireNonNull(option);
|
||||
if (!(option instanceof StandardOpenOption)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
if (options.contains(StandardOpenOption.WRITE) ||
|
||||
options.contains(StandardOpenOption.APPEND)) {
|
||||
throw readOnly();
|
||||
}
|
||||
}
|
||||
|
||||
// clean up this file system - called from finalize and close
|
||||
void cleanup() throws IOException {
|
||||
if (!isOpen) {
|
||||
return;
|
||||
}
|
||||
synchronized (this) {
|
||||
isOpen = false;
|
||||
// close image reader and null out
|
||||
image.close();
|
||||
image = null;
|
||||
}
|
||||
}
|
||||
|
||||
// These methods throw read only file system exception
|
||||
final void setTimes(JrtPath jrtPath, FileTime mtime, FileTime atime, FileTime ctime)
|
||||
throws IOException {
|
||||
throw readOnly();
|
||||
}
|
||||
|
||||
// These methods throw read only file system exception
|
||||
final void createDirectory(JrtPath jrtPath, FileAttribute<?>... attrs) throws IOException {
|
||||
throw readOnly();
|
||||
}
|
||||
|
||||
final void deleteFile(JrtPath jrtPath, boolean failIfNotExists)
|
||||
throws IOException {
|
||||
throw readOnly();
|
||||
}
|
||||
|
||||
final OutputStream newOutputStream(JrtPath jrtPath, OpenOption... options)
|
||||
throws IOException {
|
||||
throw readOnly();
|
||||
}
|
||||
|
||||
final void copyFile(boolean deletesrc, JrtPath srcPath, JrtPath dstPath, CopyOption... options)
|
||||
throws IOException {
|
||||
throw readOnly();
|
||||
}
|
||||
|
||||
final FileChannel newFileChannel(JrtPath path,
|
||||
Set<? extends OpenOption> options,
|
||||
FileAttribute<?>... attrs)
|
||||
throws IOException {
|
||||
throw new UnsupportedOperationException("newFileChannel");
|
||||
}
|
||||
|
||||
final InputStream newInputStream(JrtPath path) throws IOException {
|
||||
return new ByteArrayInputStream(getFileContent(path));
|
||||
}
|
||||
|
||||
final SeekableByteChannel newByteChannel(JrtPath path,
|
||||
Set<? extends OpenOption> options,
|
||||
FileAttribute<?>... attrs)
|
||||
throws IOException {
|
||||
checkOptions(options);
|
||||
|
||||
byte[] buf = getFileContent(path);
|
||||
final ReadableByteChannel rbc
|
||||
= Channels.newChannel(new ByteArrayInputStream(buf));
|
||||
final long size = buf.length;
|
||||
return new SeekableByteChannel() {
|
||||
long read = 0;
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return rbc.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long position() throws IOException {
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel position(long pos)
|
||||
throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuffer dst) throws IOException {
|
||||
int n = rbc.read(dst);
|
||||
if (n > 0) {
|
||||
read += n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel truncate(long size)
|
||||
throws IOException {
|
||||
throw new NonWritableChannelException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(ByteBuffer src) throws IOException {
|
||||
throw new NonWritableChannelException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() throws IOException {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
rbc.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
final JrtFileStore getFileStore(JrtPath path) {
|
||||
return new JrtFileStore(path);
|
||||
}
|
||||
|
||||
final void ensureOpen() throws IOException {
|
||||
if (!isOpen()) {
|
||||
throw new ClosedFileSystemException();
|
||||
}
|
||||
}
|
||||
|
||||
final JrtPath getRootPath() {
|
||||
return rootPath;
|
||||
}
|
||||
|
||||
boolean isSameFile(JrtPath path1, JrtPath path2) throws IOException {
|
||||
return checkNode(path1) == checkNode(path2);
|
||||
}
|
||||
|
||||
boolean isLink(JrtPath path) throws IOException {
|
||||
return checkNode(path).isLink();
|
||||
}
|
||||
|
||||
boolean exists(JrtPath path) throws IOException {
|
||||
try {
|
||||
checkNode(jrtPath);
|
||||
checkNode(path);
|
||||
} catch (NoSuchFileException exp) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isDirectory(AbstractJrtPath jrtPath, boolean resolveLinks)
|
||||
boolean isDirectory(JrtPath path, boolean resolveLinks)
|
||||
throws IOException {
|
||||
Node node = checkNode(jrtPath);
|
||||
Node node = checkNode(path);
|
||||
return resolveLinks && node.isLink()
|
||||
? node.resolveLink(true).isDirectory()
|
||||
: node.isDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
Iterator<Path> iteratorOf(AbstractJrtPath jrtPath) throws IOException {
|
||||
Node node = checkNode(jrtPath).resolveLink(true);
|
||||
if (!node.isDirectory()) {
|
||||
throw new NotDirectoryException(getString(jrtPath.getName()));
|
||||
JrtPath toRealPath(JrtPath path, LinkOption... options)
|
||||
throws IOException {
|
||||
Node node = checkNode(path);
|
||||
if (followLinks(options) && node.isLink()) {
|
||||
node = node.resolveLink();
|
||||
}
|
||||
|
||||
if (node.isRootDir()) {
|
||||
return rootDirIterator(jrtPath);
|
||||
} else if (node.isModulesDir()) {
|
||||
return modulesDirIterator(jrtPath);
|
||||
} else if (node.isPackagesDir()) {
|
||||
return packagesDirIterator(jrtPath);
|
||||
}
|
||||
|
||||
return nodesToIterator(jrtPath, node.getChildren());
|
||||
// image node holds the real/absolute path name
|
||||
return new JrtPath(this, node.getName(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
byte[] getFileContent(AbstractJrtPath jrtPath) throws IOException {
|
||||
final Node node = checkResource(jrtPath);
|
||||
return bootImage.getResource(node);
|
||||
}
|
||||
|
||||
// Implementation details below this point
|
||||
// clean up this file system - called from finalize and close
|
||||
private void cleanup() throws IOException {
|
||||
if (!isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
isOpen = false;
|
||||
|
||||
// close all image reader and null out
|
||||
bootImage.close();
|
||||
bootImage = null;
|
||||
}
|
||||
}
|
||||
|
||||
private Node lookup(byte[] path) {
|
||||
Node node = null;
|
||||
private Node lookup(String path) {
|
||||
try {
|
||||
node = bootImage.findNode(getString(path));
|
||||
return image.findNode(path);
|
||||
} catch (RuntimeException re) {
|
||||
throw new InvalidPathException(getString(path), re.toString());
|
||||
throw new InvalidPathException(path, re.toString());
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private Node lookupSymbolic(byte[] path) {
|
||||
for (int i = 1; i < path.length; i++) {
|
||||
if (path[i] == (byte) '/') {
|
||||
byte[] prefix = Arrays.copyOfRange(path, 0, i);
|
||||
Node node = lookup(prefix);
|
||||
if (node == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (node.isLink()) {
|
||||
Node link = node.resolveLink(true);
|
||||
// resolved symbolic path concatenated to the rest of the path
|
||||
String resPath = link.getName() + getString(path).substring(i);
|
||||
byte[] resPathBytes = getBytes(resPath);
|
||||
node = lookup(resPathBytes);
|
||||
return node != null ? node : lookupSymbolic(resPathBytes);
|
||||
}
|
||||
private Node lookupSymbolic(String path) {
|
||||
int i = 1;
|
||||
while (i < path.length()) {
|
||||
i = path.indexOf('/', i);
|
||||
if (i == -1) {
|
||||
break;
|
||||
}
|
||||
String prefix = path.substring(0, i);
|
||||
Node node = lookup(prefix);
|
||||
if (node == null) {
|
||||
break;
|
||||
}
|
||||
if (node.isLink()) {
|
||||
Node link = node.resolveLink(true);
|
||||
// resolved symbolic path concatenated to the rest of the path
|
||||
String resPath = link.getName() + path.substring(i);
|
||||
node = lookup(resPath);
|
||||
return node != null ? node : lookupSymbolic(resPath);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Node findNode(AbstractJrtPath jrtPath) throws IOException {
|
||||
return findNode(jrtPath.getResolvedPath());
|
||||
}
|
||||
|
||||
private Node findNode(byte[] path) throws IOException {
|
||||
Node node = lookup(path);
|
||||
Node checkNode(JrtPath path) throws IOException {
|
||||
ensureOpen();
|
||||
String p = path.getResolvedPath();
|
||||
Node node = lookup(p);
|
||||
if (node == null) {
|
||||
node = lookupSymbolic(path);
|
||||
node = lookupSymbolic(p);
|
||||
if (node == null) {
|
||||
throw new NoSuchFileException(getString(path));
|
||||
throw new NoSuchFileException(p);
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private Node checkNode(AbstractJrtPath jrtPath) throws IOException {
|
||||
return checkNode(jrtPath.getResolvedPath());
|
||||
}
|
||||
|
||||
private Node checkNode(byte[] path) throws IOException {
|
||||
ensureOpen();
|
||||
return findNode(path);
|
||||
}
|
||||
|
||||
private Node checkResource(AbstractJrtPath jrtPath) throws IOException {
|
||||
return checkResource(jrtPath.getResolvedPath());
|
||||
}
|
||||
|
||||
private Node checkResource(byte[] path) throws IOException {
|
||||
Node node = checkNode(path);
|
||||
if (node.isDirectory()) {
|
||||
throw new FileSystemException(getString(path) + " is a directory");
|
||||
}
|
||||
|
||||
assert node.isResource() : "resource node expected here";
|
||||
return node;
|
||||
}
|
||||
|
||||
private JrtPath toJrtPath(String path) {
|
||||
return toJrtPath(getBytes(path));
|
||||
}
|
||||
|
||||
private JrtPath toJrtPath(byte[] path) {
|
||||
return new JrtPath(this, path);
|
||||
}
|
||||
|
||||
private Iterator<Path> nodesToIterator(AbstractJrtPath dir, List<Node> childNodes) {
|
||||
Function<Node, Path> nodeToPath =
|
||||
child -> dir.resolve(
|
||||
toJrtPath(child.getNameString()).getFileName());
|
||||
return childNodes.stream().
|
||||
map(nodeToPath).collect(toList()).
|
||||
iterator();
|
||||
}
|
||||
|
||||
private List<Node> rootChildren;
|
||||
|
||||
private synchronized void initRootChildren(AbstractJrtPath jrtPath) throws IOException {
|
||||
if (rootChildren == null) {
|
||||
rootChildren = new ArrayList<>();
|
||||
rootChildren.addAll(findNode(jrtPath).getChildren());
|
||||
}
|
||||
}
|
||||
|
||||
private Iterator<Path> rootDirIterator(AbstractJrtPath jrtPath) throws IOException {
|
||||
initRootChildren(jrtPath);
|
||||
return nodesToIterator(jrtPath, rootChildren);
|
||||
}
|
||||
|
||||
private List<Node> modulesChildren;
|
||||
|
||||
private synchronized void initModulesChildren(AbstractJrtPath jrtPath) throws IOException {
|
||||
if (modulesChildren == null) {
|
||||
modulesChildren = new ArrayList<>();
|
||||
modulesChildren.addAll(findNode(jrtPath).getChildren());
|
||||
}
|
||||
}
|
||||
|
||||
private Iterator<Path> modulesDirIterator(AbstractJrtPath jrtPath) throws IOException {
|
||||
initModulesChildren(jrtPath);
|
||||
return nodesToIterator(jrtPath, modulesChildren);
|
||||
}
|
||||
|
||||
private List<Node> packagesChildren;
|
||||
|
||||
private synchronized void initPackagesChildren(AbstractJrtPath jrtPath) throws IOException {
|
||||
if (packagesChildren == null) {
|
||||
packagesChildren = new ArrayList<>();
|
||||
packagesChildren.addAll(findNode(jrtPath).getChildren());
|
||||
}
|
||||
}
|
||||
|
||||
private Iterator<Path> packagesDirIterator(AbstractJrtPath jrtPath) throws IOException {
|
||||
initPackagesChildren(jrtPath);
|
||||
return nodesToIterator(jrtPath, packagesChildren);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 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
|
||||
@ -70,7 +70,7 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
|
||||
private void checkPermission() {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
String home = SystemImages.RUNTIME_HOME;
|
||||
String home = SystemImage.RUNTIME_HOME;
|
||||
FilePermission perm
|
||||
= new FilePermission(home + File.separator + "-", "read");
|
||||
sm.checkPermission(perm);
|
||||
@ -107,9 +107,7 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
|
||||
if (env != null && env.containsKey("java.home")) {
|
||||
return newFileSystem((String)env.get("java.home"), uri, env);
|
||||
} else {
|
||||
return SystemImages.hasModulesImage()
|
||||
? new JrtFileSystem(this, env)
|
||||
: new JrtExplodedFileSystem(this, env);
|
||||
return new JrtFileSystem(this, env);
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,7 +119,6 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
|
||||
if (Files.notExists(jrtfs)) {
|
||||
throw new IOException(jrtfs.toString() + " not exist");
|
||||
}
|
||||
|
||||
Map<String,?> newEnv = new HashMap<>(env);
|
||||
newEnv.remove("java.home");
|
||||
ClassLoader cl = newJrtFsLoader(jrtfs);
|
||||
@ -139,7 +136,6 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
|
||||
JrtFsLoader(URL[] urls) {
|
||||
super(urls);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String cn, boolean resolve)
|
||||
throws ClassNotFoundException
|
||||
@ -208,21 +204,7 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
|
||||
fs = this.theFileSystem;
|
||||
if (fs == null) {
|
||||
try {
|
||||
if (SystemImages.hasModulesImage()) {
|
||||
this.theFileSystem = fs = new JrtFileSystem(this, null) {
|
||||
@Override
|
||||
public void close() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
this.theFileSystem = fs = new JrtExplodedFileSystem(this, null) {
|
||||
@Override
|
||||
public void close() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
this.theFileSystem = fs = new JrtFileSystem(this, null);
|
||||
} catch (IOException ioe) {
|
||||
throw new InternalError(ioe);
|
||||
}
|
||||
@ -240,69 +222,67 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
|
||||
}
|
||||
|
||||
// Checks that the given file is a JrtPath
|
||||
static final AbstractJrtPath toAbstractJrtPath(Path path) {
|
||||
if (path == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (!(path instanceof AbstractJrtPath)) {
|
||||
static final JrtPath toJrtPath(Path path) {
|
||||
Objects.requireNonNull(path, "path");
|
||||
if (!(path instanceof JrtPath)) {
|
||||
throw new ProviderMismatchException();
|
||||
}
|
||||
return (AbstractJrtPath) path;
|
||||
return (JrtPath) path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkAccess(Path path, AccessMode... modes) throws IOException {
|
||||
toAbstractJrtPath(path).checkAccess(modes);
|
||||
toJrtPath(path).checkAccess(modes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path readSymbolicLink(Path link) throws IOException {
|
||||
return toAbstractJrtPath(link).readSymbolicLink();
|
||||
return toJrtPath(link).readSymbolicLink();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy(Path src, Path target, CopyOption... options)
|
||||
throws IOException {
|
||||
toAbstractJrtPath(src).copy(toAbstractJrtPath(target), options);
|
||||
toJrtPath(src).copy(toJrtPath(target), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createDirectory(Path path, FileAttribute<?>... attrs)
|
||||
throws IOException {
|
||||
toAbstractJrtPath(path).createDirectory(attrs);
|
||||
toJrtPath(path).createDirectory(attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void delete(Path path) throws IOException {
|
||||
toAbstractJrtPath(path).delete();
|
||||
toJrtPath(path).delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V extends FileAttributeView> V
|
||||
getFileAttributeView(Path path, Class<V> type, LinkOption... options) {
|
||||
return JrtFileAttributeView.get(toAbstractJrtPath(path), type, options);
|
||||
return JrtFileAttributeView.get(toJrtPath(path), type, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileStore getFileStore(Path path) throws IOException {
|
||||
return toAbstractJrtPath(path).getFileStore();
|
||||
return toJrtPath(path).getFileStore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden(Path path) {
|
||||
return toAbstractJrtPath(path).isHidden();
|
||||
return toJrtPath(path).isHidden();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameFile(Path path, Path other) throws IOException {
|
||||
return toAbstractJrtPath(path).isSameFile(other);
|
||||
return toJrtPath(path).isSameFile(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move(Path src, Path target, CopyOption... options)
|
||||
throws IOException {
|
||||
toAbstractJrtPath(src).move(toAbstractJrtPath(target), options);
|
||||
toJrtPath(src).move(toJrtPath(target), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -319,13 +299,13 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
|
||||
Set<? extends OpenOption> options,
|
||||
FileAttribute<?>... attrs)
|
||||
throws IOException {
|
||||
return toAbstractJrtPath(path).newByteChannel(options, attrs);
|
||||
return toJrtPath(path).newByteChannel(options, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DirectoryStream<Path> newDirectoryStream(
|
||||
Path path, Filter<? super Path> filter) throws IOException {
|
||||
return toAbstractJrtPath(path).newDirectoryStream(filter);
|
||||
return toJrtPath(path).newDirectoryStream(filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -333,19 +313,19 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
|
||||
Set<? extends OpenOption> options,
|
||||
FileAttribute<?>... attrs)
|
||||
throws IOException {
|
||||
return toAbstractJrtPath(path).newFileChannel(options, attrs);
|
||||
return toJrtPath(path).newFileChannel(options, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream newInputStream(Path path, OpenOption... options)
|
||||
throws IOException {
|
||||
return toAbstractJrtPath(path).newInputStream(options);
|
||||
return toJrtPath(path).newInputStream(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream newOutputStream(Path path, OpenOption... options)
|
||||
throws IOException {
|
||||
return toAbstractJrtPath(path).newOutputStream(options);
|
||||
return toJrtPath(path).newOutputStream(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -354,7 +334,7 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
|
||||
readAttributes(Path path, Class<A> type, LinkOption... options)
|
||||
throws IOException {
|
||||
if (type == BasicFileAttributes.class || type == JrtFileAttributes.class) {
|
||||
return (A) toAbstractJrtPath(path).getAttributes(options);
|
||||
return (A) toJrtPath(path).getAttributes(options);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -363,13 +343,13 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
|
||||
public Map<String, Object>
|
||||
readAttributes(Path path, String attribute, LinkOption... options)
|
||||
throws IOException {
|
||||
return toAbstractJrtPath(path).readAttributes(attribute, options);
|
||||
return toJrtPath(path).readAttributes(attribute, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(Path path, String attribute,
|
||||
Object value, LinkOption... options)
|
||||
throws IOException {
|
||||
toAbstractJrtPath(path).setAttribute(attribute, value, options);
|
||||
toJrtPath(path).setAttribute(attribute, value, options);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 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
|
||||
@ -24,8 +24,30 @@
|
||||
*/
|
||||
package jdk.internal.jrtfs;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.DirectoryStream.Filter;;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.attribute.BasicFileAttributeView;
|
||||
import java.nio.file.attribute.FileAttribute;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import static java.nio.file.StandardOpenOption.*;
|
||||
import static java.nio.file.StandardCopyOption.*;
|
||||
|
||||
/**
|
||||
* jrt Path implementation for jrt on .jimage files.
|
||||
* Base class for Path implementation of jrt file systems.
|
||||
*
|
||||
* @implNote This class needs to maintain JDK 8 source compatibility.
|
||||
*
|
||||
@ -33,23 +55,756 @@ package jdk.internal.jrtfs;
|
||||
* but also compiled and delivered as part of the jrtfs.jar to support access
|
||||
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
|
||||
*/
|
||||
final class JrtPath extends AbstractJrtPath {
|
||||
final class JrtPath implements Path {
|
||||
|
||||
JrtPath(AbstractJrtFileSystem jrtfs, byte[] path) {
|
||||
this(jrtfs, path, false);
|
||||
final JrtFileSystem jrtfs;
|
||||
private final String path;
|
||||
private volatile int[] offsets;
|
||||
|
||||
JrtPath(JrtFileSystem jrtfs, String path) {
|
||||
this.jrtfs = jrtfs;
|
||||
this.path = normalize(path);
|
||||
this.resolved = null;
|
||||
}
|
||||
|
||||
JrtPath(AbstractJrtFileSystem jrtfs, byte[] path, boolean normalized) {
|
||||
super(jrtfs, path, normalized);
|
||||
JrtPath(JrtFileSystem jrtfs, String path, boolean normalized) {
|
||||
this.jrtfs = jrtfs;
|
||||
this.path = normalized ? path : normalize(path);
|
||||
this.resolved = null;
|
||||
}
|
||||
|
||||
final String getName() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JrtPath newJrtPath(byte[] path) {
|
||||
return new JrtPath(jrtfs, path);
|
||||
public final JrtPath getRoot() {
|
||||
if (this.isAbsolute()) {
|
||||
return jrtfs.getRootPath();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JrtPath newJrtPath(byte[] path, boolean normalized) {
|
||||
return new JrtPath(jrtfs, path, normalized);
|
||||
public final JrtPath getFileName() {
|
||||
if (path.length() == 0)
|
||||
return this;
|
||||
if (path.length() == 1 && path.charAt(0) == '/')
|
||||
return null;
|
||||
int off = path.lastIndexOf('/');
|
||||
if (off == -1)
|
||||
return this;
|
||||
return new JrtPath(jrtfs, path.substring(off + 1), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JrtPath getParent() {
|
||||
initOffsets();
|
||||
int count = offsets.length;
|
||||
if (count == 0) { // no elements so no parent
|
||||
return null;
|
||||
}
|
||||
int off = offsets[count - 1] - 1;
|
||||
if (off <= 0) { // parent is root only (may be null)
|
||||
return getRoot();
|
||||
}
|
||||
return new JrtPath(jrtfs, path.substring(0, off));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getNameCount() {
|
||||
initOffsets();
|
||||
return offsets.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JrtPath getName(int index) {
|
||||
initOffsets();
|
||||
if (index < 0 || index >= offsets.length) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
int begin = offsets[index];
|
||||
int end;
|
||||
if (index == (offsets.length - 1)) {
|
||||
end = path.length();
|
||||
} else {
|
||||
end = offsets[index + 1];
|
||||
}
|
||||
return new JrtPath(jrtfs, path.substring(begin, end));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JrtPath subpath(int beginIndex, int endIndex) {
|
||||
initOffsets();
|
||||
if (beginIndex < 0 || endIndex > offsets.length ||
|
||||
beginIndex >= endIndex) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
// starting/ending offsets
|
||||
int begin = offsets[beginIndex];
|
||||
int end;
|
||||
if (endIndex == offsets.length) {
|
||||
end = path.length();
|
||||
} else {
|
||||
end = offsets[endIndex];
|
||||
}
|
||||
return new JrtPath(jrtfs, path.substring(begin, end));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JrtPath toRealPath(LinkOption... options) throws IOException {
|
||||
return jrtfs.toRealPath(this, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JrtPath toAbsolutePath() {
|
||||
if (isAbsolute())
|
||||
return this;
|
||||
return new JrtPath(jrtfs, "/" + path, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final URI toUri() {
|
||||
try {
|
||||
return new URI("jrt", toAbsolutePath().path, null);
|
||||
} catch (URISyntaxException ex) {
|
||||
throw new AssertionError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean equalsNameAt(JrtPath other, int index) {
|
||||
int mbegin = offsets[index];
|
||||
int mlen;
|
||||
if (index == (offsets.length - 1)) {
|
||||
mlen = path.length() - mbegin;
|
||||
} else {
|
||||
mlen = offsets[index + 1] - mbegin - 1;
|
||||
}
|
||||
int obegin = other.offsets[index];
|
||||
int olen;
|
||||
if (index == (other.offsets.length - 1)) {
|
||||
olen = other.path.length() - obegin;
|
||||
} else {
|
||||
olen = other.offsets[index + 1] - obegin - 1;
|
||||
}
|
||||
if (mlen != olen) {
|
||||
return false;
|
||||
}
|
||||
int n = 0;
|
||||
while (n < mlen) {
|
||||
if (path.charAt(mbegin + n) != other.path.charAt(obegin + n)) {
|
||||
return false;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JrtPath relativize(Path other) {
|
||||
final JrtPath o = checkPath(other);
|
||||
if (o.equals(this)) {
|
||||
return new JrtPath(jrtfs, "", true);
|
||||
}
|
||||
if (path.length() == 0) {
|
||||
return o;
|
||||
}
|
||||
if (jrtfs != o.jrtfs || isAbsolute() != o.isAbsolute()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
final String tp = this.path;
|
||||
final String op = o.path;
|
||||
if (op.startsWith(tp)) { // fast path
|
||||
int off = tp.length();
|
||||
if (op.charAt(off - 1) == '/')
|
||||
return new JrtPath(jrtfs, op.substring(off), true);
|
||||
if (op.charAt(off) == '/')
|
||||
return new JrtPath(jrtfs, op.substring(off + 1), true);
|
||||
}
|
||||
int mc = this.getNameCount();
|
||||
int oc = o.getNameCount();
|
||||
int n = Math.min(mc, oc);
|
||||
int i = 0;
|
||||
while (i < n) {
|
||||
if (!equalsNameAt(o, i)) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
int dotdots = mc - i;
|
||||
int len = dotdots * 3 - 1;
|
||||
if (i < oc) {
|
||||
len += (o.path.length() - o.offsets[i] + 1);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(len);
|
||||
while (dotdots > 0) {
|
||||
sb.append("..");
|
||||
if (sb.length() < len) { // no tailing slash at the end
|
||||
sb.append('/');
|
||||
}
|
||||
dotdots--;
|
||||
}
|
||||
if (i < oc) {
|
||||
sb.append(o.path, o.offsets[i], o.path.length());
|
||||
}
|
||||
return new JrtPath(jrtfs, sb.toString(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JrtFileSystem getFileSystem() {
|
||||
return jrtfs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isAbsolute() {
|
||||
return path.length() > 0 && path.charAt(0) == '/';
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JrtPath resolve(Path other) {
|
||||
final JrtPath o = checkPath(other);
|
||||
if (this.path.length() == 0 || o.isAbsolute()) {
|
||||
return o;
|
||||
}
|
||||
if (o.path.length() == 0) {
|
||||
return this;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(path.length() + o.path.length());
|
||||
sb.append(path);
|
||||
if (path.charAt(path.length() - 1) != '/')
|
||||
sb.append('/');
|
||||
sb.append(o.path);
|
||||
return new JrtPath(jrtfs, sb.toString(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Path resolveSibling(Path other) {
|
||||
Objects.requireNonNull(other, "other");
|
||||
Path parent = getParent();
|
||||
return (parent == null) ? other : parent.resolve(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean startsWith(Path other) {
|
||||
if (!(Objects.requireNonNull(other) instanceof JrtPath))
|
||||
return false;
|
||||
final JrtPath o = (JrtPath)other;
|
||||
final String tp = this.path;
|
||||
final String op = o.path;
|
||||
if (isAbsolute() != o.isAbsolute() || !tp.startsWith(op)) {
|
||||
return false;
|
||||
}
|
||||
int off = op.length();
|
||||
if (off == 0) {
|
||||
return tp.length() == 0;
|
||||
}
|
||||
// check match is on name boundary
|
||||
return tp.length() == off || tp.charAt(off) == '/' ||
|
||||
off == 0 || op.charAt(off - 1) == '/';
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean endsWith(Path other) {
|
||||
if (!(Objects.requireNonNull(other) instanceof JrtPath))
|
||||
return false;
|
||||
final JrtPath o = (JrtPath)other;
|
||||
final JrtPath t = this;
|
||||
int olast = o.path.length() - 1;
|
||||
if (olast > 0 && o.path.charAt(olast) == '/') {
|
||||
olast--;
|
||||
}
|
||||
int last = t.path.length() - 1;
|
||||
if (last > 0 && t.path.charAt(last) == '/') {
|
||||
last--;
|
||||
}
|
||||
if (olast == -1) { // o.path.length == 0
|
||||
return last == -1;
|
||||
}
|
||||
if ((o.isAbsolute() && (!t.isAbsolute() || olast != last))
|
||||
|| last < olast) {
|
||||
return false;
|
||||
}
|
||||
for (; olast >= 0; olast--, last--) {
|
||||
if (o.path.charAt(olast) != t.path.charAt(last)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return o.path.charAt(olast + 1) == '/' ||
|
||||
last == -1 || t.path.charAt(last) == '/';
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JrtPath resolve(String other) {
|
||||
return resolve(getFileSystem().getPath(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Path resolveSibling(String other) {
|
||||
return resolveSibling(getFileSystem().getPath(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean startsWith(String other) {
|
||||
return startsWith(getFileSystem().getPath(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean endsWith(String other) {
|
||||
return endsWith(getFileSystem().getPath(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JrtPath normalize() {
|
||||
String res = getResolved();
|
||||
if (res == path) { // no change
|
||||
return this;
|
||||
}
|
||||
return new JrtPath(jrtfs, res, true);
|
||||
}
|
||||
|
||||
private JrtPath checkPath(Path path) {
|
||||
Objects.requireNonNull(path);
|
||||
if (!(path instanceof JrtPath))
|
||||
throw new ProviderMismatchException();
|
||||
return (JrtPath) path;
|
||||
}
|
||||
|
||||
// create offset list if not already created
|
||||
private void initOffsets() {
|
||||
if (this.offsets == null) {
|
||||
int len = path.length();
|
||||
// count names
|
||||
int count = 0;
|
||||
int off = 0;
|
||||
while (off < len) {
|
||||
char c = path.charAt(off++);
|
||||
if (c != '/') {
|
||||
count++;
|
||||
off = path.indexOf('/', off);
|
||||
if (off == -1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
// populate offsets
|
||||
int[] offsets = new int[count];
|
||||
count = 0;
|
||||
off = 0;
|
||||
while (off < len) {
|
||||
char c = path.charAt(off);
|
||||
if (c == '/') {
|
||||
off++;
|
||||
} else {
|
||||
offsets[count++] = off++;
|
||||
off = path.indexOf('/', off);
|
||||
if (off == -1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.offsets = offsets;
|
||||
}
|
||||
}
|
||||
|
||||
private volatile String resolved;
|
||||
|
||||
final String getResolvedPath() {
|
||||
String r = resolved;
|
||||
if (r == null) {
|
||||
if (isAbsolute()) {
|
||||
r = getResolved();
|
||||
} else {
|
||||
r = toAbsolutePath().getResolvedPath();
|
||||
}
|
||||
resolved = r;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// removes redundant slashs, replace "\" to separator "/"
|
||||
// and check for invalid characters
|
||||
private static String normalize(String path) {
|
||||
int len = path.length();
|
||||
if (len == 0) {
|
||||
return path;
|
||||
}
|
||||
char prevC = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c = path.charAt(i);
|
||||
if (c == '\\' || c == '\u0000') {
|
||||
return normalize(path, i);
|
||||
}
|
||||
if (c == '/' && prevC == '/') {
|
||||
return normalize(path, i - 1);
|
||||
}
|
||||
prevC = c;
|
||||
}
|
||||
if (prevC == '/' && len > 1) {
|
||||
return path.substring(0, len - 1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private static String normalize(String path, int off) {
|
||||
int len = path.length();
|
||||
StringBuilder to = new StringBuilder(len);
|
||||
to.append(path, 0, off);
|
||||
char prevC = 0;
|
||||
while (off < len) {
|
||||
char c = path.charAt(off++);
|
||||
if (c == '\\') {
|
||||
c = '/';
|
||||
}
|
||||
if (c == '/' && prevC == '/') {
|
||||
continue;
|
||||
}
|
||||
if (c == '\u0000') {
|
||||
throw new InvalidPathException(path,
|
||||
"Path: nul character not allowed");
|
||||
}
|
||||
to.append(c);
|
||||
prevC = c;
|
||||
}
|
||||
len = to.length();
|
||||
if (len > 1 && to.charAt(len - 1) == '/') {
|
||||
to.deleteCharAt(len - 1);
|
||||
}
|
||||
return to.toString();
|
||||
}
|
||||
|
||||
// Remove DotSlash(./) and resolve DotDot (..) components
|
||||
private String getResolved() {
|
||||
if (path.length() == 0) {
|
||||
return path;
|
||||
}
|
||||
if (path.indexOf('.') == -1) {
|
||||
return path;
|
||||
}
|
||||
int length = path.length();
|
||||
char[] to = new char[length];
|
||||
int nc = getNameCount();
|
||||
int[] lastM = new int[nc];
|
||||
int lastMOff = -1;
|
||||
int m = 0;
|
||||
for (int i = 0; i < nc; i++) {
|
||||
int n = offsets[i];
|
||||
int len = (i == offsets.length - 1) ? length - n
|
||||
: offsets[i + 1] - n - 1;
|
||||
if (len == 1 && path.charAt(n) == '.') {
|
||||
if (m == 0 && path.charAt(0) == '/') // absolute path
|
||||
to[m++] = '/';
|
||||
continue;
|
||||
}
|
||||
if (len == 2 && path.charAt(n) == '.' && path.charAt(n + 1) == '.') {
|
||||
if (lastMOff >= 0) {
|
||||
m = lastM[lastMOff--]; // retreat
|
||||
continue;
|
||||
}
|
||||
if (path.charAt(0) == '/') { // "/../xyz" skip
|
||||
if (m == 0)
|
||||
to[m++] = '/';
|
||||
} else { // "../xyz" -> "../xyz"
|
||||
if (m != 0 && to[m-1] != '/')
|
||||
to[m++] = '/';
|
||||
while (len-- > 0)
|
||||
to[m++] = path.charAt(n++);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (m == 0 && path.charAt(0) == '/' || // absolute path
|
||||
m != 0 && to[m-1] != '/') { // not the first name
|
||||
to[m++] = '/';
|
||||
}
|
||||
lastM[++lastMOff] = m;
|
||||
while (len-- > 0)
|
||||
to[m++] = path.charAt(n++);
|
||||
}
|
||||
if (m > 1 && to[m - 1] == '/')
|
||||
m--;
|
||||
return (m == to.length) ? new String(to) : new String(to, 0, m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
return path.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object obj) {
|
||||
return obj instanceof JrtPath &&
|
||||
this.path.equals(((JrtPath) obj).path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int compareTo(Path other) {
|
||||
final JrtPath o = checkPath(other);
|
||||
return path.compareTo(o.path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final WatchKey register(
|
||||
WatchService watcher,
|
||||
WatchEvent.Kind<?>[] events,
|
||||
WatchEvent.Modifier... modifiers) {
|
||||
Objects.requireNonNull(watcher, "watcher");
|
||||
Objects.requireNonNull(events, "events");
|
||||
Objects.requireNonNull(modifiers, "modifiers");
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events) {
|
||||
return register(watcher, events, new WatchEvent.Modifier[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final File toFile() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Iterator<Path> iterator() {
|
||||
return new Iterator<Path>() {
|
||||
private int i = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return (i < getNameCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path next() {
|
||||
if (i < getNameCount()) {
|
||||
Path result = getName(i);
|
||||
i++;
|
||||
return result;
|
||||
} else {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new ReadOnlyFileSystemException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Helpers for JrtFileSystemProvider and JrtFileSystem
|
||||
|
||||
final JrtPath readSymbolicLink() throws IOException {
|
||||
if (!jrtfs.isLink(this)) {
|
||||
throw new IOException("not a symbolic link");
|
||||
}
|
||||
return jrtfs.resolveLink(this);
|
||||
}
|
||||
|
||||
final boolean isHidden() {
|
||||
return false;
|
||||
}
|
||||
|
||||
final void createDirectory(FileAttribute<?>... attrs)
|
||||
throws IOException {
|
||||
jrtfs.createDirectory(this, attrs);
|
||||
}
|
||||
|
||||
final InputStream newInputStream(OpenOption... options) throws IOException {
|
||||
if (options.length > 0) {
|
||||
for (OpenOption opt : options) {
|
||||
if (opt != READ) {
|
||||
throw new UnsupportedOperationException("'" + opt + "' not allowed");
|
||||
}
|
||||
}
|
||||
}
|
||||
return jrtfs.newInputStream(this);
|
||||
}
|
||||
|
||||
final DirectoryStream<Path> newDirectoryStream(Filter<? super Path> filter)
|
||||
throws IOException {
|
||||
return new JrtDirectoryStream(this, filter);
|
||||
}
|
||||
|
||||
final void delete() throws IOException {
|
||||
jrtfs.deleteFile(this, true);
|
||||
}
|
||||
|
||||
final void deleteIfExists() throws IOException {
|
||||
jrtfs.deleteFile(this, false);
|
||||
}
|
||||
|
||||
final JrtFileAttributes getAttributes(LinkOption... options) throws IOException {
|
||||
JrtFileAttributes zfas = jrtfs.getFileAttributes(this, options);
|
||||
if (zfas == null) {
|
||||
throw new NoSuchFileException(toString());
|
||||
}
|
||||
return zfas;
|
||||
}
|
||||
|
||||
final void setAttribute(String attribute, Object value, LinkOption... options)
|
||||
throws IOException {
|
||||
JrtFileAttributeView.setAttribute(this, attribute, value);
|
||||
}
|
||||
|
||||
final Map<String, Object> readAttributes(String attributes, LinkOption... options)
|
||||
throws IOException {
|
||||
return JrtFileAttributeView.readAttributes(this, attributes, options);
|
||||
}
|
||||
|
||||
final void setTimes(FileTime mtime, FileTime atime, FileTime ctime)
|
||||
throws IOException {
|
||||
jrtfs.setTimes(this, mtime, atime, ctime);
|
||||
}
|
||||
|
||||
final FileStore getFileStore() throws IOException {
|
||||
// each JrtFileSystem only has one root (as requested for now)
|
||||
if (exists()) {
|
||||
return jrtfs.getFileStore(this);
|
||||
}
|
||||
throw new NoSuchFileException(path);
|
||||
}
|
||||
|
||||
final boolean isSameFile(Path other) throws IOException {
|
||||
if (this == other || this.equals(other)) {
|
||||
return true;
|
||||
}
|
||||
if (other == null || this.getFileSystem() != other.getFileSystem()) {
|
||||
return false;
|
||||
}
|
||||
this.checkAccess();
|
||||
JrtPath o = (JrtPath) other;
|
||||
o.checkAccess();
|
||||
return this.getResolvedPath().equals(o.getResolvedPath()) ||
|
||||
jrtfs.isSameFile(this, o);
|
||||
}
|
||||
|
||||
final SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,
|
||||
FileAttribute<?>... attrs)
|
||||
throws IOException
|
||||
{
|
||||
return jrtfs.newByteChannel(this, options, attrs);
|
||||
}
|
||||
|
||||
final FileChannel newFileChannel(Set<? extends OpenOption> options,
|
||||
FileAttribute<?>... attrs)
|
||||
throws IOException {
|
||||
return jrtfs.newFileChannel(this, options, attrs);
|
||||
}
|
||||
|
||||
final void checkAccess(AccessMode... modes) throws IOException {
|
||||
if (modes.length == 0) { // check if the path exists
|
||||
jrtfs.checkNode(this); // no need to follow link. the "link" node
|
||||
// is built from real node under "/module"
|
||||
} else {
|
||||
boolean w = false;
|
||||
for (AccessMode mode : modes) {
|
||||
switch (mode) {
|
||||
case READ:
|
||||
break;
|
||||
case WRITE:
|
||||
w = true;
|
||||
break;
|
||||
case EXECUTE:
|
||||
throw new AccessDeniedException(toString());
|
||||
default:
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
jrtfs.checkNode(this);
|
||||
if (w && jrtfs.isReadOnly()) {
|
||||
throw new AccessDeniedException(toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final boolean exists() {
|
||||
try {
|
||||
return jrtfs.exists(this);
|
||||
} catch (IOException x) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
final OutputStream newOutputStream(OpenOption... options) throws IOException {
|
||||
if (options.length == 0) {
|
||||
return jrtfs.newOutputStream(this, CREATE_NEW, WRITE);
|
||||
}
|
||||
return jrtfs.newOutputStream(this, options);
|
||||
}
|
||||
|
||||
final void move(JrtPath target, CopyOption... options) throws IOException {
|
||||
if (this.jrtfs == target.jrtfs) {
|
||||
jrtfs.copyFile(true, this, target, options);
|
||||
} else {
|
||||
copyToTarget(target, options);
|
||||
delete();
|
||||
}
|
||||
}
|
||||
|
||||
final void copy(JrtPath target, CopyOption... options) throws IOException {
|
||||
if (this.jrtfs == target.jrtfs) {
|
||||
jrtfs.copyFile(false, this, target, options);
|
||||
} else {
|
||||
copyToTarget(target, options);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyToTarget(JrtPath target, CopyOption... options)
|
||||
throws IOException {
|
||||
boolean replaceExisting = false;
|
||||
boolean copyAttrs = false;
|
||||
for (CopyOption opt : options) {
|
||||
if (opt == REPLACE_EXISTING) {
|
||||
replaceExisting = true;
|
||||
} else if (opt == COPY_ATTRIBUTES) {
|
||||
copyAttrs = true;
|
||||
}
|
||||
}
|
||||
// attributes of source file
|
||||
BasicFileAttributes jrtfas = getAttributes();
|
||||
// check if target exists
|
||||
boolean exists;
|
||||
if (replaceExisting) {
|
||||
try {
|
||||
target.deleteIfExists();
|
||||
exists = false;
|
||||
} catch (DirectoryNotEmptyException x) {
|
||||
exists = true;
|
||||
}
|
||||
} else {
|
||||
exists = target.exists();
|
||||
}
|
||||
if (exists) {
|
||||
throw new FileAlreadyExistsException(target.toString());
|
||||
}
|
||||
if (jrtfas.isDirectory()) {
|
||||
// create directory or file
|
||||
target.createDirectory();
|
||||
} else {
|
||||
try (InputStream is = jrtfs.newInputStream(this);
|
||||
OutputStream os = target.newOutputStream()) {
|
||||
byte[] buf = new byte[8192];
|
||||
int n;
|
||||
while ((n = is.read(buf)) != -1) {
|
||||
os.write(buf, 0, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (copyAttrs) {
|
||||
BasicFileAttributeView view =
|
||||
Files.getFileAttributeView(target, BasicFileAttributeView.class);
|
||||
try {
|
||||
view.setTimes(jrtfas.lastModifiedTime(),
|
||||
jrtfas.lastAccessTime(),
|
||||
jrtfas.creationTime());
|
||||
} catch (IOException x) {
|
||||
try {
|
||||
target.delete(); // rollback?
|
||||
} catch (IOException ignore) {}
|
||||
throw x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,17 +24,22 @@
|
||||
*/
|
||||
package jdk.internal.jrtfs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.FileSystemNotFoundException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.AccessController;
|
||||
import java.security.CodeSource;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
import jdk.internal.jimage.ImageReader;
|
||||
import jdk.internal.jimage.ImageReader.Node;
|
||||
|
||||
/**
|
||||
* @implNote This class needs to maintain JDK 8 source compatibility.
|
||||
*
|
||||
@ -42,20 +47,47 @@ import java.security.PrivilegedAction;
|
||||
* but also compiled and delivered as part of the jrtfs.jar to support access
|
||||
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
|
||||
*/
|
||||
final class SystemImages {
|
||||
private SystemImages() {}
|
||||
abstract class SystemImage {
|
||||
|
||||
abstract Node findNode(String path);
|
||||
abstract byte[] getResource(Node node) throws IOException;
|
||||
abstract void close() throws IOException;
|
||||
|
||||
static SystemImage open() throws IOException {
|
||||
if (modulesImageExists) {
|
||||
// open a .jimage and build directory structure
|
||||
final ImageReader image = ImageReader.open(moduleImageFile);
|
||||
image.getRootDirectory();
|
||||
return new SystemImage() {
|
||||
@Override
|
||||
Node findNode(String path) {
|
||||
return image.findNode(path);
|
||||
}
|
||||
@Override
|
||||
byte[] getResource(Node node) throws IOException {
|
||||
return image.getResource(node);
|
||||
}
|
||||
@Override
|
||||
void close() throws IOException {
|
||||
image.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
if (Files.notExists(explodedModulesDir))
|
||||
throw new FileSystemNotFoundException(explodedModulesDir.toString());
|
||||
return new ExplodedImage(explodedModulesDir);
|
||||
}
|
||||
|
||||
static final String RUNTIME_HOME;
|
||||
// "modules" jimage file Path
|
||||
private static final Path moduleImageFile;
|
||||
final static Path moduleImageFile;
|
||||
// "modules" jimage exists or not?
|
||||
private static final boolean modulesImageExists;
|
||||
final static boolean modulesImageExists;
|
||||
// <JAVA_HOME>/modules directory Path
|
||||
private static final Path explodedModulesDir;
|
||||
static final Path explodedModulesDir;
|
||||
|
||||
static {
|
||||
PrivilegedAction<String> pa = SystemImages::findHome;
|
||||
PrivilegedAction<String> pa = SystemImage::findHome;
|
||||
RUNTIME_HOME = AccessController.doPrivileged(pa);
|
||||
|
||||
FileSystem fs = FileSystems.getDefault();
|
||||
@ -71,25 +103,13 @@ final class SystemImages {
|
||||
});
|
||||
}
|
||||
|
||||
static boolean hasModulesImage() {
|
||||
return modulesImageExists;
|
||||
}
|
||||
|
||||
static Path moduleImageFile() {
|
||||
return moduleImageFile;
|
||||
}
|
||||
|
||||
static Path explodedModulesDir() {
|
||||
return explodedModulesDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate JDK home for this usage of the FileSystemProvider.
|
||||
* When the CodeSource is null (null loader) then jrt:/ is the current runtime,
|
||||
* otherwise the JDK home is located relative to jrt-fs.jar.
|
||||
*/
|
||||
private static String findHome() {
|
||||
CodeSource cs = SystemImages.class.getProtectionDomain().getCodeSource();
|
||||
CodeSource cs = SystemImage.class.getProtectionDomain().getCodeSource();
|
||||
if (cs == null)
|
||||
return System.getProperty("java.home");
|
||||
|
@ -43,15 +43,15 @@ public class PathOps {
|
||||
private Path path;
|
||||
private Exception exc;
|
||||
|
||||
private PathOps(String s) {
|
||||
private PathOps(String first, String... more) {
|
||||
out.println();
|
||||
input = s;
|
||||
input = first;
|
||||
try {
|
||||
path = fs.getPath(s);
|
||||
out.format("%s -> %s", s, path);
|
||||
path = fs.getPath(first, more);
|
||||
out.format("%s -> %s", first, path);
|
||||
} catch (Exception x) {
|
||||
exc = x;
|
||||
out.format("%s -> %s", s, x);
|
||||
out.format("%s -> %s", first, x);
|
||||
}
|
||||
out.println();
|
||||
}
|
||||
@ -175,6 +175,13 @@ public class PathOps {
|
||||
return this;
|
||||
}
|
||||
|
||||
PathOps resolveSibling(String other, String expected) {
|
||||
out.format("test resolveSibling %s\n", other);
|
||||
checkPath();
|
||||
check(path.resolveSibling(other), expected);
|
||||
return this;
|
||||
}
|
||||
|
||||
PathOps relativize(String other, String expected) {
|
||||
out.format("test relativize %s\n", other);
|
||||
checkPath();
|
||||
@ -220,6 +227,10 @@ public class PathOps {
|
||||
return new PathOps(s);
|
||||
}
|
||||
|
||||
static PathOps test(String first, String... more) {
|
||||
return new PathOps(first, more);
|
||||
}
|
||||
|
||||
// -- PathOpss --
|
||||
|
||||
static void header(String s) {
|
||||
@ -231,6 +242,26 @@ public class PathOps {
|
||||
static void doPathOpTests() {
|
||||
header("Path operations");
|
||||
|
||||
// construction
|
||||
test("/")
|
||||
.string("/");
|
||||
test("/", "")
|
||||
.string("/");
|
||||
test("/", "foo")
|
||||
.string("/foo");
|
||||
test("/", "/foo")
|
||||
.string("/foo");
|
||||
test("/", "foo/")
|
||||
.string("/foo");
|
||||
test("foo", "bar", "gus")
|
||||
.string("foo/bar/gus");
|
||||
test("")
|
||||
.string("");
|
||||
test("", "/")
|
||||
.string("/");
|
||||
test("", "foo", "", "bar", "", "/gus")
|
||||
.string("foo/bar/gus");
|
||||
|
||||
// all components
|
||||
test("/a/b/c")
|
||||
.root("/")
|
||||
@ -254,6 +285,10 @@ public class PathOps {
|
||||
.root(null)
|
||||
.parent(null)
|
||||
.name("foo");
|
||||
test("")
|
||||
.root(null)
|
||||
.parent(null)
|
||||
.name("");
|
||||
|
||||
// startsWith
|
||||
test("")
|
||||
@ -261,6 +296,7 @@ public class PathOps {
|
||||
.notStarts("/");
|
||||
test("/")
|
||||
.starts("/")
|
||||
.notStarts("")
|
||||
.notStarts("/foo");
|
||||
test("/foo")
|
||||
.starts("/")
|
||||
@ -278,6 +314,7 @@ public class PathOps {
|
||||
.notStarts("");
|
||||
test("foo")
|
||||
.starts("foo")
|
||||
.notStarts("")
|
||||
.notStarts("f");
|
||||
test("foo/bar")
|
||||
.starts("foo")
|
||||
@ -293,12 +330,14 @@ public class PathOps {
|
||||
.notEnds("/");
|
||||
test("/")
|
||||
.ends("/")
|
||||
.notEnds("")
|
||||
.notEnds("foo")
|
||||
.notEnds("/foo");
|
||||
test("/foo")
|
||||
.ends("foo")
|
||||
.ends("/foo")
|
||||
.notEnds("/");
|
||||
.notEnds("/")
|
||||
.notEnds("fool");
|
||||
test("/foo/bar")
|
||||
.ends("bar")
|
||||
.ends("foo/bar")
|
||||
@ -312,13 +351,31 @@ public class PathOps {
|
||||
.ends("/foo/bar")
|
||||
.notEnds("/bar");
|
||||
test("foo")
|
||||
.ends("foo");
|
||||
.ends("foo")
|
||||
.notEnds("")
|
||||
.notEnds("oo")
|
||||
.notEnds("oola");
|
||||
test("foo/bar")
|
||||
.ends("bar")
|
||||
.ends("bar/")
|
||||
.ends("foo/bar/")
|
||||
.ends("foo/bar");
|
||||
|
||||
.ends("foo/bar")
|
||||
.notEnds("r")
|
||||
.notEnds("barmaid")
|
||||
.notEnds("/bar")
|
||||
.notEnds("ar")
|
||||
.notEnds("barack")
|
||||
.notEnds("/bar")
|
||||
.notEnds("o/bar");
|
||||
test("foo/bar/gus")
|
||||
.ends("gus")
|
||||
.ends("bar/gus")
|
||||
.ends("foo/bar/gus")
|
||||
.notEnds("g")
|
||||
.notEnds("/gus")
|
||||
.notEnds("r/gus")
|
||||
.notEnds("barack/gus")
|
||||
.notEnds("bar/gust");
|
||||
|
||||
// elements
|
||||
test("a/b/c")
|
||||
@ -339,16 +396,54 @@ public class PathOps {
|
||||
// resolve
|
||||
test("/tmp")
|
||||
.resolve("foo", "/tmp/foo")
|
||||
.resolve("/foo", "/foo");
|
||||
.resolve("/foo", "/foo")
|
||||
.resolve("", "/tmp");
|
||||
test("tmp")
|
||||
.resolve("foo", "tmp/foo")
|
||||
.resolve("/foo", "/foo")
|
||||
.resolve("", "tmp");
|
||||
test("")
|
||||
.resolve("", "")
|
||||
.resolve("foo", "foo")
|
||||
.resolve("/foo", "/foo");
|
||||
|
||||
// resolveSibling
|
||||
test("foo")
|
||||
.resolveSibling("bar", "bar")
|
||||
.resolveSibling("/bar", "/bar")
|
||||
.resolveSibling("", "");
|
||||
test("foo/bar")
|
||||
.resolveSibling("gus", "foo/gus")
|
||||
.resolveSibling("/gus", "/gus")
|
||||
.resolveSibling("", "foo");
|
||||
test("/foo")
|
||||
.resolveSibling("gus", "/gus")
|
||||
.resolveSibling("/gus", "/gus")
|
||||
.resolveSibling("", "/");
|
||||
test("/foo/bar")
|
||||
.resolveSibling("gus", "/foo/gus")
|
||||
.resolveSibling("/gus", "/gus")
|
||||
.resolveSibling("", "/foo");
|
||||
test("")
|
||||
.resolveSibling("foo", "foo")
|
||||
.resolveSibling("/foo", "/foo")
|
||||
.resolve("", "");
|
||||
|
||||
// relativize
|
||||
test("/a/b/c")
|
||||
.relativize("/a/b/c", "")
|
||||
.relativize("/a/b/c/d/e", "d/e")
|
||||
.relativize("/a/x", "../../x");
|
||||
.relativize("/a/x", "../../x")
|
||||
.relativize("/x", "../../../x");
|
||||
test("a/b/c")
|
||||
.relativize("a/b/c/d", "d")
|
||||
.relativize("a/x", "../../x")
|
||||
.relativize("x", "../../../x")
|
||||
.relativize("", "../../..");
|
||||
test("")
|
||||
.relativize("a", "a")
|
||||
.relativize("a/b/c", "a/b/c")
|
||||
.relativize("", "");
|
||||
|
||||
// normalize
|
||||
test("/")
|
||||
|
Loading…
Reference in New Issue
Block a user