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

de.schlichtherle.truezip.nio.file.TFileSystemProvider Maven / Gradle / Ivy

/*
 * Copyright (C) 2005-2015 Schlichtherle IT Services.
 * All rights reserved. Use is subject to license terms.
 */
package de.schlichtherle.truezip.nio.file;

import static de.schlichtherle.truezip.entry.Entry.Type.DIRECTORY;
import static de.schlichtherle.truezip.entry.Entry.Type.FILE;
import static de.schlichtherle.truezip.entry.EntryName.SEPARATOR;
import de.schlichtherle.truezip.file.TArchiveDetector;
import de.schlichtherle.truezip.file.TConfig;
import de.schlichtherle.truezip.file.TVFS;
import static de.schlichtherle.truezip.fs.FsOutputOption.EXCLUSIVE;
import de.schlichtherle.truezip.fs.*;
import de.schlichtherle.truezip.socket.IOSocket;
import de.schlichtherle.truezip.socket.InputSocket;
import de.schlichtherle.truezip.socket.OutputSocket;
import de.schlichtherle.truezip.util.BitField;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.net.URI;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.DirectoryStream.Filter;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
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.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;

/**
 * A {@link FileSystemProvider} implementation
 * based on the TrueZIP Kernel module.
 * 
 * @since  TrueZIP 7.2
 * @author Christian Schlichtherle
 */
@ThreadSafe
public final class TFileSystemProvider extends FileSystemProvider {

    /** The scheme of the provider for the public no-arg constructor. */
    public  static final String DEFAULT_SCHEME = "tpath";

    /** The root mount point of the provider for the public no-arg constructor. */
    public static final String DEFAULT_ROOT_MOUNT_POINT = "file:/";

    private static final URI DEFAULT_ROOT_MOUNT_POINT_URI
            = URI.create(DEFAULT_ROOT_MOUNT_POINT);
    private static final Map
            providers = new WeakHashMap();

    private final String scheme;
    private final FsPath root;

    private Map> fileSystems
            = new WeakHashMap>();

    /**
     * Obtains a file system provider for the given {@link TPath} URI.
     * 
     * @param  name a {@link TPath} URI.
     * @return A file system provider.
     */
    static synchronized TFileSystemProvider get(URI name) {
        if (!TPathScanner.isAbsolute(name))
            return Holder.CURRENT_DIRECTORY_PROVIDER;
        if (!name.isAbsolute())
            name = DEFAULT_ROOT_MOUNT_POINT_URI;
        String scheme = name.getScheme();
        TFileSystemProvider provider = providers.get(scheme);
        if (null != provider)
            return provider;
        provider = new TFileSystemProvider(scheme, name.resolve(SEPARATOR));
        providers.put(scheme, provider);
        return provider;
    }

    /**
     * Constructs a new TrueZIP file system provider which is identified by the
     * URI {@link #getScheme() scheme} {@value #DEFAULT_SCHEME} for accessing
     * any path within the {@link #getRoot() root mount point} URI
     * {@value #DEFAULT_ROOT_MOUNT_POINT}.
     * 

* Do NOT call this constructor directly - it's solely provided in * order to use this file system provider class with the service location * feature of the NIO.2 API! * * @deprecated Do NOT call this constructor directly! */ @edu.umd.cs.findbugs.annotations.SuppressWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD") @SuppressWarnings("LeakingThisInConstructor") @Deprecated public TFileSystemProvider() { this(DEFAULT_SCHEME, DEFAULT_ROOT_MOUNT_POINT_URI); synchronized (TFileSystemProvider.class) { providers.put(scheme, this); } Logger .getLogger( TFileSystemProvider.class.getName(), TFileSystemProvider.class.getName()) .log(Level.CONFIG, "installed"); } private TFileSystemProvider(final String scheme, final URI root) { assert null != scheme; this.scheme = scheme; this.root = FsPath.create(root); } /** * @deprecated As of TrueZIP 7.5, replaced by {@link TVFS#umount()}. */ @Deprecated public static void umount() throws FsSyncException { TVFS.umount(); } /** * Returns the default scheme of this provider. * * @return the default scheme of this provider. */ @Override public String getScheme() { return scheme; } /** * Returns the root mount point of this provider. * * @return The root mount point of this provider. */ public FsPath getRoot() { return root; } private static TConfig push(Map env) { final TArchiveDetector detector = (TArchiveDetector) env.get( Parameter.ARCHIVE_DETECTOR); final TConfig config = TConfig.push(); if (null != detector) config.setArchiveDetector(detector); return config; } /** * Scans the given {@code path} for prospective archive files using the * given {@code configuration} and returns the file system for the * innermost prospective archive file or throws an * {@link UnsupportedOperationException} if no prospective archive file is * detected. *

* First, the {@code configuration} {@link Parameter}s get enumerated. * If no value is set for a parameter key, the respective value of the * {@link TConfig#get() current configuration} gets used. *

* Next, the {@code path} is scanned for prospective archive files using * the configuration resulting from the first step. * If one or more prospective archive files are found, the file system for * the innermost prospective archive file is returned. * Otherwise, an {@link UnsupportedOperationException} is thrown. * * @param path the path to scan for prospective archive files. * @param configuration may contain a {@link TArchiveDetector} for the key * {@link Parameter#ARCHIVE_DETECTOR}. * @return the file system for the innermost prospective archive file. * @throws UnsupportedOperationException if no prospective archive file has * been detected according to the configuration resulting from * merging the given {@code configuration} with the * {@link TConfig#get() current configuration}. */ @Override public TFileSystem newFileSystem(Path path, Map configuration) { TConfig config = push(configuration); try { TPath p = new TPath(path); if (null == p.getMountPoint().getParent()) throw new UnsupportedOperationException("No prospective archive file detected."); // don't be greedy! return p.getFileSystem(); } finally { config.close(); } } /** * Returns a file system for the given hierarchical {@link TPath} * {@code uri}. *

* First, the {@code configuration} {@link Parameter}s get enumerated. * If no value is set for a parameter key, the respective value of the * {@link TConfig#get() current configuration} gets used. *

* Next, the {@code uri} is scanned for prospective archive files using * the configuration resulting from the first step. * Any trailing separators in {@code uri} get discarded. * If one or more prospective archive files are found, the file system for * the innermost prospective archive file is returned. * Otherwise, the file system for the innermost directory is returned. * * @param uri the {@link TPath} uri to return a file system for. * @param configuration may contain a {@link TArchiveDetector} for the key * {@link Parameter#ARCHIVE_DETECTOR}. * @return the file system for the innermost prospective archive file or * directory. * @throws IllegalArgumentException if the given {@code uri} is opaque. */ @Override public TFileSystem newFileSystem(URI uri, Map configuration) { TConfig config = push(configuration); try { return getFileSystem(uri); } finally { config.close(); } } /** * Returns a file system for the given hierarchical {@link TPath} * {@code uri}. *

* The {@code uri} is scanned for prospective archive files using the * {@link TConfig#get() current configuration}. * Any trailing separators in {@code uri} get discarded. * If one or more prospective archive files are found, the file system for * the innermost prospective archive file is returned. * Otherwise, the file system for the innermost directory is returned. * * @param uri the {@link TPath} uri to return a file system for. * @return the file system for the innermost prospective archive file or * directory. * @throws IllegalArgumentException if the given {@code uri} is opaque. */ @Override public TFileSystem getFileSystem(URI uri) { return getPath(uri).getFileSystem(); } /** * Obtains a file system for the given path. * If a file system doesn't exist yet, it's created. * * @param path a path. * @return A file system. */ TFileSystem getFileSystem(final TPath path) { final FsMountPoint mp = path.getMountPoint(); synchronized (this) { TFileSystem fs = deref(fileSystems.get(mp)); if (null == fs) fileSystems.put(mp, new WeakReference(fs = new TFileSystem(path))); return fs; } } private static T deref(Reference ref) { return null != ref ? ref.get() : null; } /** * Returns a {@code TPath} for the given hierarchical {@code name}. *

* The URI path component is scanned for prospective archive files using * the {@link TConfig#get() current configuration}. * Any trailing separators in {@code name} get discarded. * * @param name the uri to return a {@link TPath} for. * @return the {@link TPath} * @throws IllegalArgumentException if the given {@code uri} is opaque. */ @Override public TPath getPath(URI name) { if (!getScheme().equals(name.getScheme())) throw new IllegalArgumentException(); return new TPath(name); } @Override public SeekableByteChannel newByteChannel( Path path, Set options, FileAttribute... attrs) throws IOException { return promote(path).newByteChannel(options, attrs); } @Override public InputStream newInputStream(Path path, OpenOption... options) throws IOException { return promote(path).newInputStream(options); } @Override public OutputStream newOutputStream(Path path, OpenOption... options) throws IOException { return promote(path).newOutputStream(options); } @Override public DirectoryStream newDirectoryStream(Path dir, Filter filter) throws IOException { return promote(dir).newDirectoryStream(filter); } @Override public void createDirectory(Path dir, FileAttribute... attrs) throws IOException { promote(dir).createDirectory(attrs); } @Override public void delete(Path path) throws IOException { promote(path).delete(); } @Override public void copy(Path source, Path target, CopyOption... options) throws IOException { copy(promote(source), promote(target), options); } private static void copy( final TPath src, final TPath dst, final CopyOption... options) throws IOException { if (isSameFile0(src, dst)) throw new FileSystemException(src.toString(), dst.toString(), "Source and destination are the same file!"); boolean preserve = false; BitField o = dst.getOutputPreferences().set(EXCLUSIVE); for (final CopyOption option : options) { if (!(option instanceof StandardCopyOption)) throw new UnsupportedOperationException(option.toString()); switch ((StandardCopyOption) option) { case REPLACE_EXISTING: o = o.clear(EXCLUSIVE); break; case COPY_ATTRIBUTES: preserve = true; break; case ATOMIC_MOVE: throw new AtomicMoveNotSupportedException(src.toString(), dst.toString(), null); default: throw new UnsupportedOperationException(option.toString()); } } final FsEntry srcEntry = src.getEntry(); final FsEntry dstEntry = dst.getEntry(); if (null == srcEntry) throw new NoSuchFileException(src.toString()); if (!srcEntry.isType(FILE)) throw new FileSystemException(src.toString(), null, "Expected type FILE, but is " + BitField.copyOf(srcEntry.getTypes()) + "!"); if (null != dstEntry) { if (o.get(EXCLUSIVE)) throw new FileAlreadyExistsException(dst.toString()); if (dstEntry.isType(DIRECTORY)) { try { // Try first... dst.delete(); } catch (IOException ex) { // ... before you check! if (!dstEntry.getMembers().isEmpty()) throw (IOException) new DirectoryNotEmptyException(dst.toString()) .initCause(ex); throw ex; } } } final InputSocket input = src.getInputSocket(src.getInputPreferences()); final OutputSocket output = dst.getOutputSocket(o, preserve ? input.getLocalTarget() : null); IOSocket.copy(input, output); } @Override public void move(Path source, Path target, CopyOption... options) throws IOException { move(promote(source), promote(target), options); } private static void move(TPath source, TPath target, CopyOption... options) throws IOException { copy(source, target, StandardCopyOption.COPY_ATTRIBUTES); source.delete(); } @Override public boolean isSameFile(Path a, Path b) throws IOException { return isSameFile0(a, b); } private static boolean isSameFile0(final Path a, final Path b) throws IOException { return a.equals(b) || a.toRealPath().equals(b.toRealPath()); } @Override public boolean isHidden(Path path) throws IOException { return promote(path).getFileName().toString().startsWith("."); } /** @throws UnsupportedOperationException always */ @Override public FileStore getFileStore(Path path) throws IOException { throw new UnsupportedOperationException("Not supported yet."); } @Override public void checkAccess(Path path, AccessMode... modes) throws IOException { promote(path).checkAccess(modes); } @Override @Nullable public V getFileAttributeView( Path path, Class type, LinkOption... options) { return promote(path).getFileAttributeView(type, options); } @Override public A readAttributes( Path path, Class type, LinkOption... options) throws IOException { return promote(path).readAttributes(type, options); } /** @throws UnsupportedOperationException always */ @Override public Map readAttributes(Path path, String attributes, LinkOption... options) throws IOException { throw new UnsupportedOperationException("Not supported yet."); } /** @throws UnsupportedOperationException always */ @Override public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException { throw new UnsupportedOperationException("Not supported yet."); } private static TPath promote(Path path) { try { return (TPath) path; } catch (ClassCastException ex) { throw (ProviderMismatchException) new ProviderMismatchException( ex.toString()) .initCause(ex); } } /** Keys for environment maps. */ @SuppressWarnings("PublicInnerClass") public interface Parameter { /** The key for the {@code archiveDetector} parameter. */ String ARCHIVE_DETECTOR = "archiveDetector"; } private static final class Holder { static final TFileSystemProvider CURRENT_DIRECTORY_PROVIDER = new TFileSystemProvider( "file", new File("").toURI()); private Holder() { } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy