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

de.schlichtherle.truezip.file.TBIO Maven / Gradle / Ivy

Go to download

This module provides the TFile* classes for 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: 7.7.10
Show newest version
/*
 * Copyright (C) 2011 Schlichtherle IT Services
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package de.schlichtherle.truezip.file;

import java.io.File;
import de.schlichtherle.truezip.io.Paths;
import de.schlichtherle.truezip.fs.FsPath;
import de.schlichtherle.truezip.entry.Entry;
import de.schlichtherle.truezip.fs.FsInputOption;
import de.schlichtherle.truezip.socket.InputSocket;
import de.schlichtherle.truezip.socket.IOSocket;
import de.schlichtherle.truezip.fs.FsOutputOption;
import de.schlichtherle.truezip.socket.OutputSocket;
import de.schlichtherle.truezip.util.BitField;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.DefaultAnnotation;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.IOException;
import java.util.Arrays;
import net.jcip.annotations.Immutable;

import static de.schlichtherle.truezip.fs.FsOutputOption.*;

/**
 * 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 * @version $Id$ */ @DefaultAnnotation(NonNull.class) @Immutable final class TBIO { /** You cannot instantiate this class. */ private 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. */ public static void mv(final File src, final File dst, final TArchiveDetector detector) throws IOException { if (dst.exists()) throw new IOException(dst + " (destination exists already)"); checkContains(src, dst); 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.get().isLenient()) if (!dst.mkdir() && !dst.isDirectory()) throw new IOException(dst + " (not a directory)"); final String[] members = src.list(); 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 IOException(dst + " (cannot set last modification time)"); } else if (src.isFile()) { if (dst.exists() && !dst.isFile()) throw new IOException(dst + " (not a file)"); cp0(true, src, dst); } else if (src.exists()) { throw new IOException(src + " (cannot move special file)"); } else { throw new IOException(src + " (missing file)"); } if (!src.delete()) throw new IOException(src + " (cannot delete)"); } /** * Recursively copies the source directory tree or file to the destination * directory tree or 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 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 an elementary operation fails for any reason. */ public 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.get().isLenient()) if (!dst.mkdir() && !dst.isDirectory()) throw new IOException(dst + " (not a directory)"); final String[] members = src.list(); if (!srcArchived && dstArchived) { // Create sorted entries if writing 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 IOException(dst + " (cannot set last modification time)"); } else if (src.isFile()) { if (dst.exists() && !dst.isFile()) throw new IOException(dst + " (not a file)"); cp0(preserve, src, dst); } else if (src.exists()) { throw new IOException(src + " (cannot copy special file)"); } else { throw new IOException(src + " (missing file)"); } } /** * Copies a single source file to a destination file. * The name of this method is inspired by the Unix command line utility * {@code cp}. * * @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. */ public 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 TConfig config = TConfig.get(); final InputSocket input = getInputSocket(src, config.getInputPreferences()); final OutputSocket output = getOutputSocket(dst, config.getOutputPreferences(), preserve ? input.getLocalTarget() : null); IOSocket.copy(input, output); } /** * Recursively deletes the given file or directory tree. * * @param node the file or directory tree to delete recursively. * @throws IOException if an elementary operation fails for any reason. */ public static void rm_r(final File node, final TArchiveDetector detector) throws IOException { if (node.isDirectory()) for (final String member : node.list()) rm_r(new TFile(node, member, detector), detector); if (!node.delete()) throw new IOException(node + " (cannot delete)"); } /** * 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 IOException(b + " (contained in " + a + ")"); } /** * Returns an input socket for the given file. * * @param src the file to read. * @param options the options for accessing the file. * @return An input socket for the given file. */ static InputSocket getInputSocket(final File src, final BitField options) { if (src instanceof TFile) { final TFile file = (TFile) src; final TFile archive = file.getInnerArchive(); if (null != archive) return archive.getController().getInputSocket( file.getInnerFsEntryName(), options); } final FsPath path = new FsPath(src); return TConfig .get() .getManager() .getController(path.getMountPoint(), getDetector(src)) .getInputSocket(path.getEntryName(), options); } /** * Returns an output socket for the given file. * * @param dst 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 getOutputSocket(final File dst, final BitField options, final @CheckForNull Entry template) { if (dst instanceof TFile) { final TFile file = (TFile) dst; final TFile archive = file.getInnerArchive(); if (null != archive) return archive.getController().getOutputSocket( file.getInnerFsEntryName(), options, template); } final FsPath path = new FsPath(dst); return TConfig .get() .getManager() .getController(path.getMountPoint(), getDetector(dst)) .getOutputSocket( path.getEntryName(), options.clear(CREATE_PARENTS), template); } private static TArchiveDetector getDetector(File file) { return file instanceof TFile ? ((TFile) file).getArchiveDetector() : TFile.getDefaultArchiveDetector(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy