aboutsummaryrefslogtreecommitdiff
path: root/jimfs/src/main/java/com/google/common/jimfs/JimfsFileSystemProvider.java
diff options
context:
space:
mode:
Diffstat (limited to 'jimfs/src/main/java/com/google/common/jimfs/JimfsFileSystemProvider.java')
-rw-r--r--jimfs/src/main/java/com/google/common/jimfs/JimfsFileSystemProvider.java355
1 files changed, 355 insertions, 0 deletions
diff --git a/jimfs/src/main/java/com/google/common/jimfs/JimfsFileSystemProvider.java b/jimfs/src/main/java/com/google/common/jimfs/JimfsFileSystemProvider.java
new file mode 100644
index 0000000..8d487dd
--- /dev/null
+++ b/jimfs/src/main/java/com/google/common/jimfs/JimfsFileSystemProvider.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.common.jimfs;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.jimfs.Feature.FILE_CHANNEL;
+import static com.google.common.jimfs.Jimfs.URI_SCHEME;
+import static java.nio.file.StandardOpenOption.APPEND;
+
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.nio.channels.AsynchronousFileChannel;
+import java.nio.channels.FileChannel;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.AccessMode;
+import java.nio.file.CopyOption;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.LinkOption;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.ProviderMismatchException;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.DosFileAttributes;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.attribute.FileAttributeView;
+import java.nio.file.spi.FileSystemProvider;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import org.checkerframework.checker.nullness.compatqual.NullableDecl;
+
+/**
+ * {@link FileSystemProvider} implementation for Jimfs. This provider implements the actual file
+ * system operations but does not handle creation, caching or lookup of file systems. See {@link
+ * SystemJimfsFileSystemProvider}, which is the {@code META-INF/services/} entry for Jimfs, for
+ * those operations.
+ *
+ * @author Colin Decker
+ */
+final class JimfsFileSystemProvider extends FileSystemProvider {
+
+ private static final JimfsFileSystemProvider INSTANCE = new JimfsFileSystemProvider();
+
+ static {
+ // Register the URL stream handler implementation.
+ try {
+ Handler.register();
+ } catch (Throwable e) {
+ // Couldn't set the system property needed to register the handler. Nothing we can do really.
+ }
+ }
+
+ /** Returns the singleton instance of this provider. */
+ static JimfsFileSystemProvider instance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public String getScheme() {
+ return URI_SCHEME;
+ }
+
+ @Override
+ public FileSystem newFileSystem(URI uri, Map<String, ?> env) throws IOException {
+ throw new UnsupportedOperationException(
+ "This method should not be called directly;"
+ + "use an overload of Jimfs.newFileSystem() to create a FileSystem.");
+ }
+
+ @Override
+ public FileSystem newFileSystem(Path path, Map<String, ?> env) throws IOException {
+ JimfsPath checkedPath = checkPath(path);
+ checkNotNull(env);
+
+ URI pathUri = checkedPath.toUri();
+ URI jarUri = URI.create("jar:" + pathUri);
+
+ try {
+ // pass the new jar:jimfs://... URI to be handled by ZipFileSystemProvider
+ return FileSystems.newFileSystem(jarUri, env);
+ } catch (Exception e) {
+ // if any exception occurred, assume the file wasn't a zip file and that we don't support
+ // viewing it as a file system
+ throw new UnsupportedOperationException(e);
+ }
+ }
+
+ @Override
+ public FileSystem getFileSystem(URI uri) {
+ throw new UnsupportedOperationException(
+ "This method should not be called directly; "
+ + "use FileSystems.getFileSystem(URI) instead.");
+ }
+
+ /** Gets the file system for the given path. */
+ private static JimfsFileSystem getFileSystem(Path path) {
+ return (JimfsFileSystem) checkPath(path).getFileSystem();
+ }
+
+ @Override
+ public Path getPath(URI uri) {
+ throw new UnsupportedOperationException(
+ "This method should not be called directly; " + "use Paths.get(URI) instead.");
+ }
+
+ private static JimfsPath checkPath(Path path) {
+ if (path instanceof JimfsPath) {
+ return (JimfsPath) path;
+ }
+ throw new ProviderMismatchException(
+ "path " + path + " is not associated with a Jimfs file system");
+ }
+
+ /** Returns the default file system view for the given path. */
+ private static FileSystemView getDefaultView(JimfsPath path) {
+ return getFileSystem(path).getDefaultView();
+ }
+
+ @Override
+ public FileChannel newFileChannel(
+ Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
+ JimfsPath checkedPath = checkPath(path);
+ if (!checkedPath.getJimfsFileSystem().getFileStore().supportsFeature(FILE_CHANNEL)) {
+ throw new UnsupportedOperationException();
+ }
+ return newJimfsFileChannel(checkedPath, options, attrs);
+ }
+
+ private JimfsFileChannel newJimfsFileChannel(
+ JimfsPath path, Set<? extends OpenOption> options, FileAttribute<?>... attrs)
+ throws IOException {
+ ImmutableSet<OpenOption> opts = Options.getOptionsForChannel(options);
+ FileSystemView view = getDefaultView(path);
+ RegularFile file = view.getOrCreateRegularFile(path, opts, attrs);
+ return new JimfsFileChannel(file, opts, view.state());
+ }
+
+ @Override
+ public SeekableByteChannel newByteChannel(
+ Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
+ JimfsPath checkedPath = checkPath(path);
+ JimfsFileChannel channel = newJimfsFileChannel(checkedPath, options, attrs);
+ return checkedPath.getJimfsFileSystem().getFileStore().supportsFeature(FILE_CHANNEL)
+ ? channel
+ : new DowngradedSeekableByteChannel(channel);
+ }
+
+ @Override
+ public AsynchronousFileChannel newAsynchronousFileChannel(
+ Path path,
+ Set<? extends OpenOption> options,
+ @NullableDecl ExecutorService executor,
+ FileAttribute<?>... attrs)
+ throws IOException {
+ // call newFileChannel and cast so that FileChannel support is checked there
+ JimfsFileChannel channel = (JimfsFileChannel) newFileChannel(path, options, attrs);
+ if (executor == null) {
+ JimfsFileSystem fileSystem = (JimfsFileSystem) path.getFileSystem();
+ executor = fileSystem.getDefaultThreadPool();
+ }
+ return channel.asAsynchronousFileChannel(executor);
+ }
+
+ @Override
+ public InputStream newInputStream(Path path, OpenOption... options) throws IOException {
+ JimfsPath checkedPath = checkPath(path);
+ ImmutableSet<OpenOption> opts = Options.getOptionsForInputStream(options);
+ FileSystemView view = getDefaultView(checkedPath);
+ RegularFile file = view.getOrCreateRegularFile(checkedPath, opts, NO_ATTRS);
+ return new JimfsInputStream(file, view.state());
+ }
+
+ private static final FileAttribute<?>[] NO_ATTRS = {};
+
+ @Override
+ public OutputStream newOutputStream(Path path, OpenOption... options) throws IOException {
+ JimfsPath checkedPath = checkPath(path);
+ ImmutableSet<OpenOption> opts = Options.getOptionsForOutputStream(options);
+ FileSystemView view = getDefaultView(checkedPath);
+ RegularFile file = view.getOrCreateRegularFile(checkedPath, opts, NO_ATTRS);
+ return new JimfsOutputStream(file, opts.contains(APPEND), view.state());
+ }
+
+ @Override
+ public DirectoryStream<Path> newDirectoryStream(
+ Path dir, DirectoryStream.Filter<? super Path> filter) throws IOException {
+ JimfsPath checkedPath = checkPath(dir);
+ return getDefaultView(checkedPath)
+ .newDirectoryStream(checkedPath, filter, Options.FOLLOW_LINKS, checkedPath);
+ }
+
+ @Override
+ public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
+ JimfsPath checkedPath = checkPath(dir);
+ FileSystemView view = getDefaultView(checkedPath);
+ view.createDirectory(checkedPath, attrs);
+ }
+
+ @Override
+ public void createLink(Path link, Path existing) throws IOException {
+ JimfsPath linkPath = checkPath(link);
+ JimfsPath existingPath = checkPath(existing);
+ checkArgument(
+ linkPath.getFileSystem().equals(existingPath.getFileSystem()),
+ "link and existing paths must belong to the same file system instance");
+ FileSystemView view = getDefaultView(linkPath);
+ view.link(linkPath, getDefaultView(existingPath), existingPath);
+ }
+
+ @Override
+ public void createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs)
+ throws IOException {
+ JimfsPath linkPath = checkPath(link);
+ JimfsPath targetPath = checkPath(target);
+ checkArgument(
+ linkPath.getFileSystem().equals(targetPath.getFileSystem()),
+ "link and target paths must belong to the same file system instance");
+ FileSystemView view = getDefaultView(linkPath);
+ view.createSymbolicLink(linkPath, targetPath, attrs);
+ }
+
+ @Override
+ public Path readSymbolicLink(Path link) throws IOException {
+ JimfsPath checkedPath = checkPath(link);
+ return getDefaultView(checkedPath).readSymbolicLink(checkedPath);
+ }
+
+ @Override
+ public void delete(Path path) throws IOException {
+ JimfsPath checkedPath = checkPath(path);
+ FileSystemView view = getDefaultView(checkedPath);
+ view.deleteFile(checkedPath, FileSystemView.DeleteMode.ANY);
+ }
+
+ @Override
+ public void copy(Path source, Path target, CopyOption... options) throws IOException {
+ copy(source, target, Options.getCopyOptions(options), false);
+ }
+
+ private void copy(Path source, Path target, ImmutableSet<CopyOption> options, boolean move)
+ throws IOException {
+ JimfsPath sourcePath = checkPath(source);
+ JimfsPath targetPath = checkPath(target);
+
+ FileSystemView sourceView = getDefaultView(sourcePath);
+ FileSystemView targetView = getDefaultView(targetPath);
+ sourceView.copy(sourcePath, targetView, targetPath, options, move);
+ }
+
+ @Override
+ public void move(Path source, Path target, CopyOption... options) throws IOException {
+ copy(source, target, Options.getMoveOptions(options), true);
+ }
+
+ @Override
+ public boolean isSameFile(Path path, Path path2) throws IOException {
+ if (path.equals(path2)) {
+ return true;
+ }
+
+ if (!(path instanceof JimfsPath && path2 instanceof JimfsPath)) {
+ return false;
+ }
+
+ JimfsPath checkedPath = (JimfsPath) path;
+ JimfsPath checkedPath2 = (JimfsPath) path2;
+
+ FileSystemView view = getDefaultView(checkedPath);
+ FileSystemView view2 = getDefaultView(checkedPath2);
+
+ return view.isSameFile(checkedPath, view2, checkedPath2);
+ }
+
+ @Override
+ public boolean isHidden(Path path) throws IOException {
+ // TODO(cgdecker): This should probably be configurable, but this seems fine for now
+ /*
+ * If the DOS view is supported, use the Windows isHidden method (check the dos:hidden
+ * attribute). Otherwise, use the Unix isHidden method (just check if the file name starts with
+ * ".").
+ */
+ JimfsPath checkedPath = checkPath(path);
+ FileSystemView view = getDefaultView(checkedPath);
+ if (getFileStore(path).supportsFileAttributeView("dos")) {
+ return view.readAttributes(checkedPath, DosFileAttributes.class, Options.NOFOLLOW_LINKS)
+ .isHidden();
+ }
+ return path.getNameCount() > 0 && path.getFileName().toString().startsWith(".");
+ }
+
+ @Override
+ public FileStore getFileStore(Path path) throws IOException {
+ return getFileSystem(path).getFileStore();
+ }
+
+ @Override
+ public void checkAccess(Path path, AccessMode... modes) throws IOException {
+ JimfsPath checkedPath = checkPath(path);
+ getDefaultView(checkedPath).checkAccess(checkedPath);
+ }
+
+ @NullableDecl
+ @Override
+ public <V extends FileAttributeView> V getFileAttributeView(
+ Path path, Class<V> type, LinkOption... options) {
+ JimfsPath checkedPath = checkPath(path);
+ return getDefaultView(checkedPath)
+ .getFileAttributeView(checkedPath, type, Options.getLinkOptions(options));
+ }
+
+ @Override
+ public <A extends BasicFileAttributes> A readAttributes(
+ Path path, Class<A> type, LinkOption... options) throws IOException {
+ JimfsPath checkedPath = checkPath(path);
+ return getDefaultView(checkedPath)
+ .readAttributes(checkedPath, type, Options.getLinkOptions(options));
+ }
+
+ @Override
+ public Map<String, Object> readAttributes(Path path, String attributes, LinkOption... options)
+ throws IOException {
+ JimfsPath checkedPath = checkPath(path);
+ return getDefaultView(checkedPath)
+ .readAttributes(checkedPath, attributes, Options.getLinkOptions(options));
+ }
+
+ @Override
+ public void setAttribute(Path path, String attribute, Object value, LinkOption... options)
+ throws IOException {
+ JimfsPath checkedPath = checkPath(path);
+ getDefaultView(checkedPath)
+ .setAttribute(checkedPath, attribute, value, Options.getLinkOptions(options));
+ }
+}