--- a/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java Fri Apr 15 10:14:57 2016 -0700
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java Fri Apr 15 13:05:52 2016 -0700
@@ -136,7 +136,7 @@
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);
}
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/AbstractJrtFileAttributes.java Fri Apr 15 10:14:57 2016 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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();
- }
-}
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/AbstractJrtFileSystem.java Fri Apr 15 10:14:57 2016 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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;
-}
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/AbstractJrtPath.java Fri Apr 15 10:14:57 2016 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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;
- }
- }
- }
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java Fri Apr 15 13:05:52 2016 -0700
@@ -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);
+ }
+}
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtDirectoryStream.java Fri Apr 15 10:14:57 2016 -0700
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtDirectoryStream.java Fri Apr 15 13:05:52 2016 -0700
@@ -30,6 +30,7 @@
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 @@
*/
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
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtExplodedFileAttributes.java Fri Apr 15 10:14:57 2016 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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();
- }
-}
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtExplodedFileSystem.java Fri Apr 15 10:14:57 2016 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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);
- }
-}
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtExplodedPath.java Fri Apr 15 10:14:57 2016 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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;
- }
-}
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributeView.java Fri Apr 15 10:14:57 2016 -0700
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributeView.java Fri Apr 15 13:05:52 2016 -0700
@@ -29,6 +29,7 @@
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 @@
final class JrtFileAttributeView implements BasicFileAttributeView {
private static enum AttrID {
-
size,
creationTime,
lastAccessTime,
@@ -56,21 +56,19 @@
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 @@
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 @@
}
@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 {
- try {
- if (AttrID.valueOf(attribute) == AttrID.lastModifiedTime) {
- setTimes((FileTime) value, null, null);
+ 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");
}
- if (AttrID.valueOf(attribute) == AttrID.lastAccessTime) {
- setTimes(null, (FileTime) value, null);
- }
- if (AttrID.valueOf(attribute) == AttrID.creationTime) {
- setTimes(null, null, (FileTime) value);
+ attribute = attribute.substring(colonPos);
+ }
+ try {
+ 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();
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java Fri Apr 15 10:14:57 2016 -0700
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java Fri Apr 15 13:05:52 2016 -0700
@@ -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 @@
* 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 @@
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();
+ }
}
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileStore.java Fri Apr 15 10:14:57 2016 -0700
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileStore.java Fri Apr 15 13:05:52 2016 -0700
@@ -30,6 +30,7 @@
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 @@
protected final FileSystem jrtfs;
- JrtFileStore(AbstractJrtPath jrtPath) {
+ JrtFileStore(JrtPath jrtPath) {
this.jrtfs = jrtPath.getFileSystem();
}
@@ -71,9 +72,7 @@
@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 @@
@Override
public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
- return (type == BasicFileAttributeView.class
- || type == JrtFileAttributeView.class);
+ return type == BasicFileAttributeView.class ||
+ type == JrtFileAttributeView.class;
}
}
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java Fri Apr 15 10:14:57 2016 -0700
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java Fri Apr 15 13:05:52 2016 -0700
@@ -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 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;
-import jdk.internal.jimage.ImageReader;
-import jdk.internal.jimage.ImageReader.Node;
-
/**
* jrt file system implementation built on System jimage files.
@@ -51,33 +75,21 @@
* 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 @@
@Override
public void close() throws IOException {
+ if (!isClosable)
+ throw new UnsupportedOperationException();
cleanup();
}
@@ -95,237 +109,397 @@
protected void finalize() throws Throwable {
try {
cleanup();
- } catch (IOException ignored) {
- }
- super.finalize();
+ } catch (IOException ignored) {}
}
- // AbstractJrtFileSystem method implementations
@Override
- JrtPath getRootPath() {
- return rootPath;
+ public FileSystemProvider provider() {
+ return provider;
+ }
+
+ @Override
+ public Iterable<Path> getRootDirectories() {
+ ArrayList<Path> dirs = new ArrayList<>();
+ dirs.add(getRootPath());
+ return dirs;
}
@Override
- boolean isSameFile(AbstractJrtPath p1, AbstractJrtPath p2) throws IOException {
- ensureOpen();
- Node node1 = findNode(p1);
- Node node2 = findNode(p2);
- return node1.equals(node2);
+ public JrtPath getPath(String first, String... more) {
+ if (more.length == 0) {
+ return new JrtPath(this, first);
+ }
+ 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
+ public final boolean isReadOnly() {
+ return true;
+ }
+
+ @Override
+ public final UserPrincipalLookupService getUserPrincipalLookupService() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public final WatchService newWatchService() {
+ throw new UnsupportedOperationException();
}
@Override
- boolean isLink(AbstractJrtPath jrtPath) throws IOException {
- return checkNode(jrtPath).isLink();
+ 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
- AbstractJrtPath resolveLink(AbstractJrtPath jrtPath) throws IOException {
- Node node = checkNode(jrtPath);
+ 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(String path) {
+ try {
+ return image.findNode(path);
+ } catch (RuntimeException re) {
+ throw new InvalidPathException(path, re.toString());
}
}
- private Node lookup(byte[] path) {
- Node node = null;
- try {
- node = bootImage.findNode(getString(path));
- } catch (RuntimeException re) {
- throw new InvalidPathException(getString(path), re.toString());
+ 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 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);
- }
- }
- }
-
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);
- }
}
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystemProvider.java Fri Apr 15 10:14:57 2016 -0700
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystemProvider.java Fri Apr 15 13:05:52 2016 -0700
@@ -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 @@
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 @@
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 @@
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 @@
JrtFsLoader(URL[] urls) {
super(urls);
}
-
@Override
protected Class<?> loadClass(String cn, boolean resolve)
throws ClassNotFoundException
@@ -208,21 +204,7 @@
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 @@
}
// 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 @@
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 @@
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 @@
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 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);
}
}
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtPath.java Fri Apr 15 10:14:57 2016 -0700
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtPath.java Fri Apr 15 13:05:52 2016 -0700
@@ -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 @@
* 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 {
+
+ 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(JrtFileSystem jrtfs, String path, boolean normalized) {
+ this.jrtfs = jrtfs;
+ this.path = normalized ? path : normalize(path);
+ this.resolved = null;
+ }
+
+ final String getName() {
+ return path;
+ }
+
+ @Override
+ public final JrtPath getRoot() {
+ if (this.isAbsolute()) {
+ return jrtfs.getRootPath();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ 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;
+ }
- JrtPath(AbstractJrtFileSystem jrtfs, byte[] path) {
- this(jrtfs, path, false);
+ @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;
}
- JrtPath(AbstractJrtFileSystem jrtfs, byte[] path, boolean normalized) {
- super(jrtfs, path, normalized);
+ @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
- protected JrtPath newJrtPath(byte[] path) {
- return new JrtPath(jrtfs, path);
+ 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
- protected JrtPath newJrtPath(byte[] path, boolean normalized) {
- return new JrtPath(jrtfs, path, normalized);
+ 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;
+ }
+ }
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/SystemImage.java Fri Apr 15 13:05:52 2016 -0700
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2014, 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.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.
+ *
+ * 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 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
+ final static Path moduleImageFile;
+ // "modules" jimage exists or not?
+ final static boolean modulesImageExists;
+ // <JAVA_HOME>/modules directory Path
+ static final Path explodedModulesDir;
+
+ static {
+ PrivilegedAction<String> pa = SystemImage::findHome;
+ RUNTIME_HOME = AccessController.doPrivileged(pa);
+
+ FileSystem fs = FileSystems.getDefault();
+ moduleImageFile = fs.getPath(RUNTIME_HOME, "lib", "modules");
+ explodedModulesDir = fs.getPath(RUNTIME_HOME, "modules");
+
+ modulesImageExists = AccessController.doPrivileged(
+ new PrivilegedAction<Boolean>() {
+ @Override
+ public Boolean run() {
+ return Files.isRegularFile(moduleImageFile);
+ }
+ });
+ }
+
+ /**
+ * 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 = SystemImage.class.getProtectionDomain().getCodeSource();
+ if (cs == null)
+ return System.getProperty("java.home");
+
+ // assume loaded from $TARGETJDK/jrt-fs.jar
+ URL url = cs.getLocation();
+ if (!url.getProtocol().equalsIgnoreCase("file"))
+ throw new RuntimeException(url + " loaded in unexpected way");
+ try {
+ return Paths.get(url.toURI()).getParent().toString();
+ } catch (URISyntaxException e) {
+ throw new InternalError(e);
+ }
+ }
+}
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/SystemImages.java Fri Apr 15 10:14:57 2016 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-/*
- * Copyright (c) 2014, 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.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.Path;
-import java.nio.file.Paths;
-import java.security.AccessController;
-import java.security.CodeSource;
-import java.security.PrivilegedAction;
-
-/**
- * @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 SystemImages {
- private SystemImages() {}
-
-
- static final String RUNTIME_HOME;
- // "modules" jimage file Path
- private static final Path moduleImageFile;
- // "modules" jimage exists or not?
- private static final boolean modulesImageExists;
- // <JAVA_HOME>/modules directory Path
- private static final Path explodedModulesDir;
-
- static {
- PrivilegedAction<String> pa = SystemImages::findHome;
- RUNTIME_HOME = AccessController.doPrivileged(pa);
-
- FileSystem fs = FileSystems.getDefault();
- moduleImageFile = fs.getPath(RUNTIME_HOME, "lib", "modules");
- explodedModulesDir = fs.getPath(RUNTIME_HOME, "modules");
-
- modulesImageExists = AccessController.doPrivileged(
- new PrivilegedAction<Boolean>() {
- @Override
- public Boolean run() {
- return Files.isRegularFile(moduleImageFile);
- }
- });
- }
-
- 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();
- if (cs == null)
- return System.getProperty("java.home");
-
- // assume loaded from $TARGETJDK/jrt-fs.jar
- URL url = cs.getLocation();
- if (!url.getProtocol().equalsIgnoreCase("file"))
- throw new RuntimeException(url + " loaded in unexpected way");
- try {
- return Paths.get(url.toURI()).getParent().toString();
- } catch (URISyntaxException e) {
- throw new InternalError(e);
- }
- }
-}
--- a/jdk/test/jdk/internal/jrtfs/PathOps.java Fri Apr 15 10:14:57 2016 -0700
+++ b/jdk/test/jdk/internal/jrtfs/PathOps.java Fri Apr 15 13:05:52 2016 -0700
@@ -43,15 +43,15 @@
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 @@
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 @@
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 @@
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 @@
.root(null)
.parent(null)
.name("foo");
+ test("")
+ .root(null)
+ .parent(null)
+ .name("");
// startsWith
test("")
@@ -261,6 +296,7 @@
.notStarts("/");
test("/")
.starts("/")
+ .notStarts("")
.notStarts("/foo");
test("/foo")
.starts("/")
@@ -278,6 +314,7 @@
.notStarts("");
test("foo")
.starts("foo")
+ .notStarts("")
.notStarts("f");
test("foo/bar")
.starts("foo")
@@ -293,12 +330,14 @@
.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 @@
.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 @@
// 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("/")