All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.java.truevfs.access.TFileSystem Maven / Gradle / Ivy

Go to download

This module provides convenient access to the (virtual federated) file system space for TrueVFS client applications. It features simple, uniform, transparent, thread-safe, read/write access to archive files as if they were virtual directories in a file system path. This module also provides Swing GUI classes for viewing file trees and choosing entries in archive files.

There is a newer version: 0.14.0
Show newest version
/*
 * Copyright (C) 2005-2015 Schlichtherle IT Services.
 * All rights reserved. Use is subject to license terms.
 */
package net.java.truevfs.access;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.*;
import java.nio.file.DirectoryStream.Filter;
import java.nio.file.attribute.*;
import java.util.*;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import javax.annotation.concurrent.ThreadSafe;
import net.java.truecommons.cio.Entry;
import net.java.truecommons.cio.Entry.Access;
import static net.java.truecommons.cio.Entry.Access.*;
import static net.java.truecommons.cio.Entry.Size.DATA;
import static net.java.truecommons.cio.Entry.Type.*;
import static net.java.truecommons.cio.Entry.UNKNOWN;
import net.java.truecommons.cio.InputSocket;
import net.java.truecommons.cio.OutputSocket;
import net.java.truecommons.shed.BitField;
import net.java.truecommons.shed.FilteringIterator;
import net.java.truevfs.kernel.spec.FsAccessOption;
import static net.java.truevfs.kernel.spec.FsAccessOption.CACHE;
import static net.java.truevfs.kernel.spec.FsAccessOption.EXCLUSIVE;
import net.java.truevfs.kernel.spec.FsController;
import net.java.truevfs.kernel.spec.FsMountPoint;
import net.java.truevfs.kernel.spec.FsNode;
import net.java.truevfs.kernel.spec.FsNodeName;
import static net.java.truevfs.kernel.spec.FsNodeName.SEPARATOR;
import net.java.truevfs.kernel.spec.FsSyncException;
import net.java.truevfs.kernel.spec.FsSyncOption;
import static net.java.truevfs.kernel.spec.FsSyncOptions.UMOUNT;
import net.java.truevfs.kernel.spec.FsSyncWarningException;

/**
 * A {@link FileSystem} implementation for use with NIO.2.
 *
 * @author Christian Schlichtherle
 */
@ThreadSafe
public final class TFileSystem extends FileSystem {

    private final FsController controller;
    private final TFileSystemProvider provider;

    @SuppressWarnings("deprecation")
    TFileSystem(final TPath path) {
        assert null != path;
        this.controller = TConfig
                .current()
                .getManager()
                .controller(path.getArchiveDetector(), path.getMountPoint());
        this.provider = TFileSystemProvider.get(path.getName());

        assert invariants();
    }

    private boolean invariants() {
        assert null != getController();
        assert null != provider();
        return true;
    }

    private FsController getController() { return controller; }

    FsMountPoint getMountPoint() {
        return getController().getModel().getMountPoint();
    }

    @Override
    public TFileSystemProvider provider() {
        return provider;
    }

    /**
     * Commits all pending changes for this (federated) file system and all its
     * federated child file systems to their respective parent file system,
     * closes their associated target (archive) file in order to allow access
     * by third parties (e.g. other processes), cleans up any temporary
     * allocated resources (e.g. temporary files) and purges any cached
     * data.
     * 

* Calling this method is equivalent to * {@link #sync(BitField) sync(FsSyncOptions.UMOUNT)}. * * @throws FsSyncWarningException if only warning conditions * apply. * This implies that the respective parent file system has been * synchronized with constraints, e.g. if an unclosed archive entry * stream gets forcibly closed. * @throws FsSyncException if any error conditions apply. * This implies some loss of data! * @see #sync(BitField) */ @Override public void close() throws FsSyncWarningException, FsSyncException { sync(UMOUNT); } /** * Commits all pending changes for this (federated) file system and all its * federated child file systems to their respective parent file system with * respect to the given options. * * @param options a bit field of options for the synchronization operation. * @throws IllegalArgumentException if the combination of synchronization * options is illegal, e.g. if * {@code FsSyncOption.FORCE_CLOSE_INPUT} is cleared and * {@code FsSyncOption.FORCE_CLOSE_OUTPUT} is set or if * {@code FsSyncOption.ABORT_CHANGES} is set. * @throws FsSyncWarningException if only warning conditions * apply. * This implies that the respective parent file system has been * synchronized with constraints, e.g. if * {@code FsSyncOption.FORCE_CLOSE_INPUT} or * {@code FsSyncOption.FORCE_CLOSE_OUTPUT} is set and an unclosed * archive entry stream gets forcibly closed. * @throws FsSyncException if any error conditions apply. * This implies some loss of data! */ @SuppressWarnings("deprecation") public void sync(BitField options) throws FsSyncWarningException, FsSyncException { TVFS.sync(getMountPoint(), options); } /** * Returns {@code true}. * * @return {@code true}. */ @Override public boolean isOpen() { return true; } /** * Returns {@code false}. * * @return {@code false}. */ @Override public boolean isReadOnly() { return false; } /** * Returns {@link File#separator}. * * @return {@link File#separator}. */ @Override public String getSeparator() { return File.separator; } @Override public Iterable getRootDirectories() { return Collections.singleton((Path) new TPath(getMountPoint().toHierarchicalUri().resolve(SEPARATOR))); } /** @throws UnsupportedOperationException always */ @Override public Iterable getFileStores() { throw new UnsupportedOperationException("Not supported yet."); } @Override public Set supportedFileAttributeViews() { return Collections.singleton("basic"); } /** * Constructs a new path from the given sub path strings. *

* This method scans the {@link TPath#toString() path name} resulting * from the segment parameters to detect prospective archive files using * the current archive detector * {@code TConfig.current().getArchiveDetector()}. *

* The supported path name separators are "{@link File#separator}" and * "{@code /}". * Any leading and trailing separators in the resulting path name current * discarded. * * @param first the first sub path string. * @param more optional sub path strings. */ @Override public TPath getPath(String first, String... more) { return new TPath(this, first, more); } /** @throws UnsupportedOperationException always */ @Override public PathMatcher getPathMatcher(String syntaxAndPattern) { throw new UnsupportedOperationException("Not supported yet."); } /** @throws UnsupportedOperationException always */ @Override public UserPrincipalLookupService getUserPrincipalLookupService() { throw new UnsupportedOperationException("Not supported yet."); } /** @throws UnsupportedOperationException always */ @Override public WatchService newWatchService() throws IOException { throw new UnsupportedOperationException("Not supported yet."); } SeekableByteChannel newByteChannel( final TPath path, final Set options, final FileAttribute... attrs) throws IOException { final FsNodeName name = path.getNodeName(); final FsController controller = getController(); if (options.isEmpty() || options.contains(StandardOpenOption.READ)) { final BitField o = path.inputOptions(options).set(CACHE); return controller .input(o, name) .channel(null); } else { final BitField o = path.outputOptions(options).set(CACHE); try { return controller .output(o, name, null) .channel(null); } catch (final IOException ex) { // TODO: Filter FileAlreadyExistsException. if (o.get(EXCLUSIVE) && null != controller.node(o, name)) throw (IOException) new FileAlreadyExistsException(path.toString()) .initCause(ex); throw ex; } } } InputStream newInputStream(TPath path, OpenOption... options) throws IOException { return getController() .input(path.inputOptions(options), path.getNodeName()) .stream(null); } OutputStream newOutputStream(TPath path, OpenOption... options) throws IOException { return getController() .output(path.outputOptions(options), path.getNodeName(), null) .stream(null); } DirectoryStream newDirectoryStream( final TPath path, final Filter filter) throws IOException { final FsNode entry = stat(path); final Set set; if (null == entry || null == (set = entry.getMembers())) throw new NotDirectoryException(path.toString()); @NotThreadSafe class Adapter implements Iterator { final Iterator it = set.iterator(); @Override public boolean hasNext() { return it.hasNext(); } @Override public Path next() { return path.resolve(it.next()); } @Override public void remove() { throw new UnsupportedOperationException("Not supported yet."); } } // Adapter @NotThreadSafe class FilterIterator extends FilteringIterator { FilterIterator() { super(new Adapter()); } @Override protected boolean accept(Path element) { try { return filter.accept(element); } catch (IOException ex) { throw new DirectoryIteratorException(ex); } } } // FilterIterator return new Stream(new FilterIterator()); } @NotThreadSafe private static final class Stream implements DirectoryStream { final Iterator it; boolean consumed; Stream(final Iterator it) { this.it = it; } @Override public Iterator iterator() { if (consumed) throw new IllegalStateException(); consumed = true; return it; } @Override public void close() { consumed = true; } } // Stream void createDirectory(final TPath path, final FileAttribute... attrs) throws IOException { if (0 < attrs.length) throw new UnsupportedOperationException(); final FsController controller = getController(); final FsNodeName name = path.getNodeName(); final BitField options = path.getAccessPreferences(); try { controller.make( options, name, DIRECTORY, null); } catch (IOException ex) { if (null != controller.node(options, name)) throw (IOException) new FileAlreadyExistsException(path.toString()) .initCause(ex); throw ex; } } void delete(TPath path) throws IOException { getController().unlink(path.getAccessPreferences(), path.getNodeName()); } FsNode stat(TPath path) throws IOException { return getController().node(path.getAccessPreferences(), path.getNodeName()); } InputSocket input( TPath path, BitField options) { return getController().input(options, path.getNodeName()); } OutputSocket output( TPath path, BitField options, @CheckForNull Entry template) { return getController().output(options, path.getNodeName(), template); } void checkAccess(final TPath path, final AccessMode... modes) throws IOException { final FsNodeName name = path.getNodeName(); final BitField options = path.getAccessPreferences(); final BitField types = types(modes); getController().checkAccess(options, name, types); } private static BitField types(final AccessMode... modes) { final EnumSet access = EnumSet.noneOf(Access.class); for (final AccessMode mode : modes) { switch (mode) { case READ: access.add(READ); break; case WRITE: access.add(WRITE); break; case EXECUTE: access.add(EXECUTE); break; } } return BitField.copyOf(access); } @Nullable V getFileAttributeView( TPath path, Class type, LinkOption... options) { if (type.isAssignableFrom(BasicFileAttributeView.class)) return type.cast(new FsNodeAttributeView(path)); return null; } A readAttributes( TPath path, Class type, LinkOption... options) throws IOException { if (type.isAssignableFrom(BasicFileAttributes.class)) return type.cast(new FsNodeAttributes(path)); throw new UnsupportedOperationException(); } private final class FsNodeAttributeView implements BasicFileAttributeView { private final TPath path; FsNodeAttributeView(final TPath path) { this.path = path; } @Override public String name() { return "basic"; } @Override public BasicFileAttributes readAttributes() throws IOException { return new FsNodeAttributes(path); } @Override public void setTimes( final FileTime lastModifiedTime, final FileTime lastAccessTime, final FileTime createTime) throws IOException { final FsController controller = getController(); final Map times = new EnumMap<>(Access.class); if (null != lastModifiedTime) times.put(WRITE, lastModifiedTime.toMillis()); if (null != lastAccessTime) times.put(READ, lastAccessTime.toMillis()); if (null != createTime) times.put(CREATE, createTime.toMillis()); controller.setTime( path.getAccessPreferences(), path.getNodeName(), times); } } // FsNodeAttributeView private final class FsNodeAttributes implements BasicFileAttributes { private final FsNode entry; FsNodeAttributes(final TPath path) throws IOException { if (null == (entry = getController() .node(path.getAccessPreferences(), path.getNodeName()))) throw new NoSuchFileException(path.toString()); } @Override public FileTime lastModifiedTime() { return FileTime.fromMillis(entry.getTime(WRITE)); } @Override public FileTime lastAccessTime() { return FileTime.fromMillis(entry.getTime(READ)); } @Override public FileTime creationTime() { return FileTime.fromMillis(entry.getTime(CREATE)); } @Override public boolean isRegularFile() { return entry.isType(FILE); } @Override public boolean isDirectory() { return entry.isType(DIRECTORY); } @Override public boolean isSymbolicLink() { return entry.isType(SYMLINK); } @Override public boolean isOther() { return entry.isType(SPECIAL); } @Override public long size() { final long size = entry.getSize(DATA); return UNKNOWN == size ? 0 : size; } /** @throws UnsupportedOperationException always */ @Override public Object fileKey() { throw new UnsupportedOperationException("Not supported yet."); } } // FsNodeAttributes }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy