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

net.java.truevfs.access.TBIO 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.nio.file.FileAlreadyExistsException;
import java.nio.file.FileSystemException;
import java.nio.file.NoSuchFileException;
import java.nio.file.NotDirectoryException;
import java.util.Arrays;
import javax.annotation.CheckForNull;
import javax.annotation.concurrent.Immutable;
import net.java.truecommons.shed.BitField;
import net.java.truecommons.shed.Paths;
import net.java.truevfs.kernel.spec.FsAccessOption;
import static net.java.truevfs.kernel.spec.FsAccessOption.CREATE_PARENTS;
import net.java.truevfs.kernel.spec.FsNodePath;
import net.java.truecommons.cio.Entry;
import net.java.truecommons.cio.InputSocket;
import net.java.truecommons.cio.IoSockets;
import net.java.truecommons.cio.OutputSocket;

/**
 * Provides fast bulk I/O operations for {@link File}s and {@link TFile}s.
 * 

* Note that in contrast to the {@link TFile} class, the methods in this * class accept plain old {@link File} objects. * However, full advantage is taken if a parameter is a {@link TFile} object. * * @author Christian Schlichtherle */ @Immutable final class TBIO { /** * Moves the source file or directory tree to the destination file or * directory tree by performing a recursive cp-then-delete operation. * Hence, this file system operation works even with archive files or * entries within archive files, but is not atomic. * * @param src the source directory tree or file. * This file system entity needs to exist. * @param dst the destination directory tree or file. * This file systeme entity may or may not exist. * If it does, its contents are overwritten. * @param detector the object used to detect any archive files in the * source and destination paths. * @throws IOException if the source path contains the destination path * or an elementary operation fails for any reason. */ static void mv(final File src, final File dst, final TArchiveDetector detector) throws IOException { checkContains(src, dst); if (dst.exists()) throw new FileAlreadyExistsException(src.getPath(), dst.getPath(), null); mv0(src, dst, detector); } /** Unchecked parameters version. */ private static void mv0(final File src, final File dst, final TArchiveDetector detector) throws IOException { if (src.isDirectory()) { final long srcLastModified = src.lastModified(); final boolean srcIsArchived = src instanceof TFile && null != ((TFile) src).getInnerArchive(); final boolean dstIsArchived = dst instanceof TFile && null != ((TFile) dst).getInnerArchive(); final boolean srcIsGhost = srcIsArchived && 0 >= srcLastModified; if (!srcIsGhost || !dstIsArchived || !TConfig.current().isLenient()) if (!dst.mkdir() && !dst.isDirectory()) throw new NotDirectoryException(dst.getPath()); final String[] members = src.list(); if (null == members) throw new FileSystemException(dst.getPath(), null, "Cannot list directory!"); if (!srcIsArchived && dstIsArchived) { // Create sorted entries if writing a new archive file. // This is courtesy only, so natural order is sufficient. Arrays.sort(members); } for (final String member : members) mv0( new TFile(src, member, detector), new TFile(dst, member, detector), detector); if (!srcIsGhost) if (!dst.setLastModified(srcLastModified)) throw new FileSystemException(dst.getPath(), null, "Cannot set last modification time!"); } else if (src.isFile()) { if (dst.exists() && !dst.isFile()) throw new FileSystemException(dst.getPath(), null, "Not a file!"); cp0(true, src, dst); } else if (src.exists()) { throw new FileSystemException(src.getPath(), null, "Cannot move special file!"); } else { throw new NoSuchFileException(src.getPath()); } if (!src.delete()) throw new FileSystemException(src.getPath(), null, "Cannot delete!"); } /** * Recursively copies the source directory tree or file to the destination * directory tree or file. * * @param preserve If {@code true}, then a best effort approach is used to * copy as much properties of any source files to the destination * files as possible. * Which attributes are actually copied is specific to the * destination file system driver implementation, but the minimum * guarantee is to copy the last modification time. * @param src the source directory tree or file. * This file system entity needs to exist. * @param dst the destination directory tree or file. * This file systeme entity may or may not exist. * If it does, its contents are overwritten. * @param srcDetector the object used to detect any archive files in the * source path. * @param dstDetector the object used to detect any archive files in the * destination path. * @throws IOException if the source path contains the destination path * or any I/O error. */ static void cp_r( final boolean preserve, final File src, final File dst, final TArchiveDetector srcDetector, final TArchiveDetector dstDetector) throws IOException { checkContains(src, dst); cp_r0(preserve, src, dst, srcDetector, dstDetector); } /** Unchecked parameters version. */ private static void cp_r0( final boolean preserve, final File src, final File dst, final TArchiveDetector srcDetector, final TArchiveDetector dstDetector) throws IOException { if (src.isDirectory()) { final long srcLastModified = src.lastModified(); final boolean srcArchived = src instanceof TFile && null != ((TFile) src).getInnerArchive(); final boolean dstArchived = dst instanceof TFile && null != ((TFile) dst).getInnerArchive(); final boolean srcIsGhost = srcArchived && 0 >= srcLastModified; if (!srcIsGhost || !dstArchived || !TConfig.current().isLenient()) if (!dst.mkdir() && !dst.isDirectory()) throw new NotDirectoryException(dst.getPath()); final String[] members = src.list(); if (null == members) throw new FileSystemException(dst.getPath(), null, "Cannot list directory!"); if (!srcArchived && dstArchived) { // Create sorted entries if copying an ordinary directory to a // new archive. // This is a courtesy only, so natural order is sufficient. Arrays.sort(members); } for (final String member : members) cp_r0( preserve, new TFile(src, member, srcDetector), new TFile(dst, member, dstDetector), srcDetector, dstDetector); if (preserve && !srcIsGhost) if (!dst.setLastModified(srcLastModified)) throw new FileSystemException(dst.getPath(), null, "Cannot set last modification time!"); } else if (src.isFile()) { if (dst.exists() && !dst.isFile()) throw new FileSystemException(dst.getPath(), null, "Not a file!"); cp0(preserve, src, dst); } else if (src.exists()) { throw new FileSystemException(src.getPath(), null, "Cannot copy special file!"); } else { throw new NoSuchFileException(src.getPath()); } } /** * Copies a single source file to a destination file. * * @param preserve if an elementary cp operation shall cp as much * properties of the source file to the destination file, too. * Currently, only the last modification time is preserved. * Note that this property set may get extended over time. * @param src the source file. * This file system entity needs to exist. * @param dst the destination file. * This file systeme entity may or may not exist. * If it does, its contents are overwritten. * @throws IOException if the source path contains the destination path * or an elementary operation fails for any reason. */ static void cp(final boolean preserve, final File src, final File dst) throws IOException { checkContains(src, dst); cp0(preserve, src, dst); } /** Unchecked parameters version. */ private static void cp0(final boolean preserve, final File src, final File dst) throws IOException { final BitField preferences = TConfig.current().getAccessPreferences(); final InputSocket input = input(preferences, src); final OutputSocket output = output(preferences, dst, preserve ? input.target() : null); IoSockets.copy(input, output); } /** * Recursively deletes the given file or directory tree. * * @param file the file or directory tree to delete recursively. * @throws IOException if an elementary operation fails for any reason. */ static void rm_r(final File file, final TArchiveDetector detector) throws IOException { if (file.isDirectory()) { final String[] members = file.list(); if (null == members) throw new FileSystemException(file.getPath(), null, "Cannot list directory!"); for (final String member : members) rm_r(new TFile(file, member, detector), detector); } TFile.rm(file); } /** * Throws an {@code IOException} if and only if the path represented by * {@code a} contains the path represented by {@code b}, where a path is * said to contain another path if and only if it is equal or an ancestor * of the other path. *

* Note that this method uses the absolute path of both files as if by * calling {@link File#getAbsolutePath()}. * * @param a a file. * @param b another file. */ private static void checkContains(File a, File b) throws IOException { if (Paths.contains( a.getAbsolutePath(), b.getAbsolutePath(), File.separatorChar)) throw new FileSystemException(a.getPath(), b.getPath(), "First path contains second path!"); } private static TArchiveDetector detector(File file) { return file instanceof TFile ? ((TFile) file).getArchiveDetector() : TConfig.current().getArchiveDetector(); } /** * Returns an input socket for the given file. * * @param file the file to read. * @param options the options for accessing the file. * @return An input socket for the given file. */ static InputSocket input( final BitField options, final File file) { if (file instanceof TFile) { final TFile tfile= (TFile) file; final TFile archive = tfile.getInnerArchive(); if (null != archive) return archive .getController() .input(options, tfile.getNodeName()); } final FsNodePath path = new FsNodePath(file); return TConfig .current() .getManager() .controller(detector(file), path.getMountPoint()) .input(options, path.getNodeName()); } /** * Returns an output socket for the given file. * * @param file the file to write. * @param options the options for accessing the file. * @param template a nullable template from which file attributes shall * get copied. * @return An output socket for the given file. */ static OutputSocket output( final BitField options, final File file, final @CheckForNull Entry template) { if (file instanceof TFile) { final TFile tfile = (TFile) file; final TFile archive = tfile.getInnerArchive(); if (null != archive) return archive .getController() .output( options, tfile.getNodeName(), template); } final FsNodePath path = new FsNodePath(file); return TConfig .current() .getManager() .controller(detector(file), path.getMountPoint()) .output( options.clear(CREATE_PARENTS), path.getNodeName(), template); } private TBIO() { } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy