Java tutorial
/* * Copyright 2013 Lyor Goldstein * * 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 org.apache.commons.io; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.StreamCorruptedException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.apache.commons.collections15.AbstractExtendedTransformer; import org.apache.commons.collections15.ExtendedCollectionUtils; import org.apache.commons.collections15.ExtendedTransformer; import org.apache.commons.collections15.Predicate; import org.apache.commons.collections15.Transformer; import org.apache.commons.io.filefilter.CanExecuteFileFilter; import org.apache.commons.io.filefilter.CanReadFileFilter; import org.apache.commons.io.filefilter.CanWriteFileFilter; import org.apache.commons.io.filefilter.DirectoryFileFilter; import org.apache.commons.io.filefilter.ExistFileFilter; import org.apache.commons.io.filefilter.HiddenFileFilter; import org.apache.commons.io.filefilter.IOFileFilter; import org.apache.commons.io.filefilter.NotFileFilter; import org.apache.commons.io.filefilter.OrFileFilter; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ExtendedArrayUtils; import org.apache.commons.lang3.ExtendedStringUtils; import org.apache.commons.lang3.ExtendedValidate; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.exception.ExtendedExceptionUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Triplet; import org.apache.commons.net.util.URLUtils; /** * @author lgoldstein * */ public class ExtendedFileUtils extends FileUtils { public ExtendedFileUtils() { super(); } public static final String JAR_FILE_SUFFIX = ".jar"; public static final File[] EMPTY_FILES = {}; /** * URL/URI scheme that refers to a JAR */ public static final String JAR_URL_SCHEME = "jar"; /** * Prefix used in URL(s) that reference a resource inside a JAR */ public static final String JAR_URL_PREFIX = JAR_URL_SCHEME + ":"; /** * URL/URI scheme that refers to a file */ public static final String FILE_URL_SCHEME = "file"; /** * Prefix used in URL(s) that reference a file resource */ public static final String FILE_URL_PREFIX = FILE_URL_SCHEME + ":"; public static final char READ_ACCESS_CHAR = 'r', WRITE_ACCESS_CHAR = 'w', EXECUTE_ACCESS_CHAR = 'x', FOLDER_ACCESS_CHAR = 'd', NO_ACCESS_CHAR = '-'; /** * @param file The {@link File} to be read * @return The {@code char[]} with the file's contents * @throws IOException If failed to read from the file */ public static final char[] readFileToCharArray(File file) throws IOException { long size = file.length(); Validate.isTrue((size >= 0L) && (size < Integer.MAX_VALUE), "Bad file size for %s: %d", file, Long.valueOf(size)); char[] chars = new char[(int) size]; Reader rdr = new FileReader(file); try { IOUtils.readFully(rdr, chars); } finally { rdr.close(); } return chars; } /** * Copies 2 {@link File}-s using an efficient internal Java mechanism * (better than read-write cycle) * @param srcFile source file object * @param dstFile destination file object - if destination folder does not * exist it is created using {@link File#mkdirs()} * @return number of copied bytes - Note: if un-successful, some partial * content may have been copied. * @throws IOException if read/write error */ public static final long quickCopyFile(File srcFile, File dstFile) throws IOException { return quickCopyFile(srcFile, dstFile, (-1L)); } /** * Copies 2 {@link File}-s using an efficient internal Java mechanism * (better than read-write cycle) * @param srcFile source file object * @param dstFile destination file object - if destination folder does not * exist it is created using {@link File#mkdirs()} * @param cpySize Max. number of bytes to copy - if negative then till EOF * @return number of copied bytes - Note: if un-successful, some partial * content may have been copied. * @throws IOException if read/write error */ @SuppressWarnings("resource") public static final long quickCopyFile(File srcFile, File dstFile, long cpySize) throws IOException { Validate.notNull(srcFile, "No source file", ArrayUtils.EMPTY_OBJECT_ARRAY); ExtendedValidate.isTrue(srcFile.isFile(), "Not a source file: %s", srcFile); Validate.notNull(dstFile, "No destination file", ArrayUtils.EMPTY_OBJECT_ARRAY); ExtendedValidate.isTrue((!dstFile.exists()) || dstFile.isFile(), "Not a destination file: %s", dstFile); File dstFolder = dstFile.getParentFile(); if ((!dstFolder.exists()) && (!dstFolder.mkdirs())) throw new IOException("Failed to created destination folder(s): " + dstFolder); FileChannel srcChannel = null, dstChannel = null; try { srcChannel = new FileInputStream(srcFile).getChannel(); dstChannel = new FileOutputStream(dstFile).getChannel(); // Copy file contents from source to destination long srcLen = srcFile.length(), copyLen = dstChannel.transferFrom(srcChannel, 0, (cpySize < 0L) ? srcLen : cpySize); if ((cpySize < 0L) && (copyLen != srcLen)) { // make sure full copy throw new StreamCorruptedException( "Mismatched copy length: expected=" + srcLen + ", actual=" + copyLen); } return copyLen; } finally { ExtendedIOUtils.closeAll(srcChannel, dstChannel); } } /** * A {@link IOFileFilter} that <code>accept</code>s all non-<code>null</code> * {@link File}s that are JAR(s) * @see #isJarFileName(File) */ public static final IOFileFilter JAR_FILE_FILTER = new IOFileFilter() { @Override public boolean accept(File dir, String name) { if (!isJarFileName(name)) { return false; // debug breakpoint } else { return true; } } @Override public boolean accept(File f) { if (!isJarFileName(f)) { return false; // debug breakpoint } else { return true; } } @Override public String toString() { return "JAR_FILE_FILTER"; } }; public static final boolean isJarFileName(File f) { return (f != null) && isJarFileName(f.getName()); } public static final boolean isJarFileName(String name) { if (StringUtils.isEmpty(name) || (name.length() <= JAR_FILE_SUFFIX.length())) { return false; } else { return name.endsWith(JAR_FILE_SUFFIX); } } public static final URL toJarURL(File file, String name) throws MalformedURLException { if ((file == null) || StringUtils.isEmpty(name)) { throw new MalformedURLException("toJarURL(" + file + ")[" + name + "] incomplete specification"); } URL baseURL = toURL(file); String urlPrefix = baseURL.toExternalForm(); String resourceURL = new StringBuilder(JAR_URL_PREFIX.length() + urlPrefix.length() + name.length() + 2) .append(JAR_URL_PREFIX).append(urlPrefix).append(URLUtils.RESOURCE_SUBPATH_SEPARATOR) .append(URLUtils.RESOURCE_PATH_SEPARATOR).append(name).toString(); return new URL(resourceURL); } /** * Converts a URL to a file * @param url The {@link URL} - ignored if <code>null</code> * @return The matching {@link File} * @throws MalformedURLException If URL does not refer to a file location * @see #asFile(URL) */ public static File asFile(URL url) throws MalformedURLException { if (url == null) { return null; } try { return asFile(url.toURI()); } catch (URISyntaxException e) { throw new MalformedURLException("asFile(" + URLUtils.toString(url) + ")" + " cannot (" + e.getClass().getSimpleName() + ")" + " convert to URI: " + e.getMessage()); } } /** * Converts a URI to a file * @param uri The {@link URI} - ignored if <code>null</code> * @return The matching {@link File} * @throws MalformedURLException If URI does not refer to a file location * @see #FILE_URL_SCHEME */ public static File asFile(URI uri) throws MalformedURLException { if (uri == null) { return null; } if (!FILE_URL_SCHEME.equals(uri.getScheme())) { throw new MalformedURLException("asFile(" + uri + ") not a '" + FILE_URL_SCHEME + "' scheme"); } // TODO consider using decodeUrl(...) return new File(uri); } public static final List<URL> toURL(File... files) throws MalformedURLException { return toURL(ExtendedArrayUtils.asList(files)); } public static final List<URL> toURL(Collection<? extends File> files) throws MalformedURLException { try { return ExtendedCollectionUtils.collectToList(files, FILE_TO_URL_TRANSFORMER); } catch (RuntimeException e) { Throwable t = ExtendedExceptionUtils.getSafeCause(e); if (t instanceof MalformedURLException) { throw (MalformedURLException) t; } else { throw e; } } } /** * Transforms a {@link File} to a {@link URL} via the {@link #toURL(File)} * call. If a {@link MalformedURLException} is thrown then it is caught and * re-thrown as a {@link RuntimeException} whose cause is the original * {@link MalformedURLException} */ public static final ExtendedTransformer<File, URL> FILE_TO_URL_TRANSFORMER = new AbstractExtendedTransformer<File, URL>( File.class, URL.class) { @Override public URL transform(File input) { try { return toURL(input); } catch (MalformedURLException e) { throw new RuntimeException(e); } } }; public static URL toURL(File f) throws MalformedURLException { if (f == null) { return null; } else { return f.toURI().toURL(); } } /** * Compares 2 {@link File}s using case <U>insensitive</U> comparison * of their {@link File#getAbsolutePath()} value(s). <B>Note:</B> this * choice was made since in Windows paths are case insensitive. Furthermore, * it is bad practice to use paths that differ only in their case */ public static final Comparator<File> BY_ABSOLUTE_PATH_COMPARATOR = new Comparator<File>() { @Override public int compare(File o1, File o2) { if (o1 == o2) { return 0; } String p1 = (o1 == null) ? null : o1.getAbsolutePath(); String p2 = (o2 == null) ? null : o2.getAbsolutePath(); return ExtendedStringUtils.safeCompare(p1, p2, false); } }; /** * A <code>null</code>-safe way to retrieve a {@link String} representation * of a {@link File} instance. <B>Caveat emptor:</B> this method <B><U>should * not be used instead of {@link File#getAbsolutePath()}</U></B> where the * absolute path is required as its implementation may change in the future * @param f The {@link File} instance * @return The {@link File#getAbsolutePath()} - <code>null</code> if * <code>null</code> file instance */ public static final String toString(File f) { if (f == null) { return null; } else { return f.getAbsolutePath(); } } private static final class LazyTempFolderHolder { private static final File tmpDir = FileUtils.getTempDirectory(); } // a more efficient replacement for getTempDirectory @SuppressWarnings("synthetic-access") public static final File retrieveTempDirectory() { return LazyTempFolderHolder.tmpDir; } /** * Converts a group of {@link URL}s to their {@link File} "sources" * equivalents * @param urls The initial {@link Collection} of {@link URL}s - may be * <code>null</code>/empty * @return A {@link List} of the converted {@link File} equivalents * @see #toFileSource(URL) * @throws MalformedURLException if failed to convert a {@link URL} to a {@link File} */ public static final List<File> toFileSource(final Collection<? extends URL> urls) throws MalformedURLException { try { return ExtendedCollectionUtils.collectToList(urls, URL2SRCFILE_TRANSFORMER); } catch (RuntimeException e) { Throwable t = ExtendedExceptionUtils.getSafeCause(e); if (t instanceof MalformedURLException) { throw (MalformedURLException) t; } else { throw e; } } } public static final ExtendedTransformer<URL, File> URL2SRCFILE_TRANSFORMER = new AbstractExtendedTransformer<URL, File>( URL.class, File.class) { @Override public File transform(URL input) { try { return toFileSource(input); } catch (MalformedURLException e) { throw new RuntimeException(e); } } }; /** * Converts a {@link URL} that may refer to an internal resource to * a {@link File} representing is "source" container (e.g., * if it is a resource in a JAR, then the result is the JAR's path) * @param url The {@link URL} - ignored if <code>null</code> * @return The matching {@link File} * @throws MalformedURLException If source URL does not refer to a * file location * @see #toFileSource(URI) */ public static File toFileSource(URL url) throws MalformedURLException { if (url == null) { return null; } try { return toFileSource(url.toURI()); } catch (URISyntaxException e) { throw new MalformedURLException("toFileSource(" + URLUtils.toString(url) + ")" + " cannot (" + e.getClass().getSimpleName() + ")" + " convert to URI: " + e.getMessage()); } } /** * Converts a {@link URI} that may refer to an internal resource to * a {@link File} representing is "source" container (e.g., * if it is a resource in a JAR, then the result is the JAR's path) * @param uri The {@link URI} - ignored if <code>null</code> * @return The matching {@link File} * @throws MalformedURLException If source URI does not refer to a * file location * @see URLUtils#getURLSource(URI) */ public static File toFileSource(URI uri) throws MalformedURLException { String src = URLUtils.getURLSource(uri); if (StringUtils.isEmpty(src)) { return null; } if (!src.startsWith(FILE_URL_PREFIX)) { throw new MalformedURLException("toFileSource(" + src + ") not a '" + FILE_URL_SCHEME + "' scheme"); } try { return new File(new URI(src)); } catch (URISyntaxException e) { throw new MalformedURLException("toFileSource(" + src + ")" + " cannot (" + e.getClass().getSimpleName() + ")" + " convert to URI: " + e.getMessage()); } } /* public static final File getURLSourceFile(URL url) throws MalformedURLException { return getURLSourceFile((url == null) ? null : url.toExternalForm()); } public static final File getURLSourceFile(URI uri) throws MalformedURLException { return getURLSourceFile((uri == null) ? null : uri.toString()); } public static final File getURLSourceFile(String url) throws MalformedURLException { String src=getURLSource(url); if (StringUtils.isEmpty(src)) { return null; } else { try { return asFile(new URI(src)); } catch(URISyntaxException e) { throw new MalformedURLException("getURLSourceFile(" + url + ")" + " failed (" + e.getClass().getSimpleName() + ")" + " to create URI=" + src + ": " + e.getMessage()); } } } */ /** * An {@link ExtendedTransformer} implementation that converts a {@link File} * to a {@link String} using the {@link #getURLSource(File)} method */ public static final ExtendedTransformer<File, String> FILE2SOURCE_TRANSFORMER = new AbstractExtendedTransformer<File, String>( File.class, String.class) { @Override public String transform(File src) { return ExtendedFileUtils.getURLSource(src); } }; /** * @param file The {@link File} value - ignored if <code>null</code> * @return The file source path where {@link #JAR_URL_PREFIX} and * any sub-resource are stripped * @see URLUtils#getURLSource(URI) */ public static final String getURLSource(File file) { return URLUtils.getURLSource((file == null) ? null : file.toURI()); } /** * A {@link IOFileFilter} that uses O/S specific knowledge to determine if * a {@link File} is executable - e.g., for Windows it checks the file * extension * @see CanExecuteFileFilter#CAN_EXECUTE * @see ExtendedFilenameUtils#isWindowsExecutableExtension(String) */ public static final IOFileFilter OS_EXECUTABLE_FILTER = new IOFileFilter() { @Override public boolean accept(File dir, String name) { if (FilenameUtils.isSystemWindows()) { return ExtendedFilenameUtils.isWindowsExecutableExtension(name); } else { return CanExecuteFileFilter.CAN_EXECUTE.accept(dir, name); } } @Override public boolean accept(File pathname) { if (FilenameUtils.isSystemWindows()) { return ExtendedFilenameUtils .isWindowsExecutableExtension(FilenameUtils.getExtension(pathname.getName())); } else { return CanExecuteFileFilter.CAN_EXECUTE.accept(pathname); } } @Override public String toString() { return "OS_EXECUTABLE_FILTER"; } }; /** * A {@link IOFileFilter} that accepts files with hidden names * @see ExtendedFilenameUtils#isHiddenName(CharSequence) */ public static final IOFileFilter HIDDEN_NAME_FILTER = new IOFileFilter() { @Override public boolean accept(File dir, String name) { return ExtendedFilenameUtils.isHiddenName(name); } @Override public boolean accept(File pathname) { if (pathname == null) { return false; } else { return ExtendedFilenameUtils.isHiddenName(pathname.getName()); } } @Override public String toString() { return "HIDDEN_NAME_FILTER"; } }; public static final IOFileFilter NON_HIDDEN_NAME_FILTER = new NotFileFilter(HIDDEN_NAME_FILTER); /** * A {@link IOFileFilter} that accepts files that are accepted either by * the {@link HiddenFileFilter#HIDDEN} or the {@link #HIDDEN_NAME_FILTER}. * In other words, either files declared by the file system as hidden * or ones that follow the dot prefix naming convention */ public static final IOFileFilter COMPOUND_HIDDEN_FILE_FILTER = new OrFileFilter(HiddenFileFilter.HIDDEN, HIDDEN_NAME_FILTER); public static final IOFileFilter COMPOUND_NON_HIDDEN_FILE_FILTER = new NotFileFilter( COMPOUND_HIDDEN_FILE_FILTER); /** * Creates a {@link File} instance referencing the path created from the * given sub-components. This is a more efficient version of {@link FileUtils#getFile(String...)} * @param pathComponents The path components in the <U>order</U> that they * should be used to build the path - ignored if <code>null</code>/empty * @return Result {@link File} - <code>null</code> if no components provided * @throws IllegalArgumentException If one of the components is <code>null</code> * or empty * @see #buildFile(Collection) */ public static File buildFile(CharSequence... pathComponents) throws IllegalArgumentException { return buildFile(ExtendedArrayUtils.asList(pathComponents)); } /** * Creates a {@link File} instance referencing the path created from the * given sub-components * @param pathComponents The path components in the <U>order</U> that they * should be used to build the path - ignored if <code>null</code>/empty * @return Result {@link File} - <code>null</code> if no components provided * @throws IllegalArgumentException If one of the components is <code>null</code> * or empty * @see ExtendedFilenameUtils#buildFilePath(Collection) */ public static final File buildFile(Collection<? extends CharSequence> pathComponents) throws IllegalArgumentException { String path = ExtendedFilenameUtils.buildFilePath(pathComponents); if (StringUtils.isEmpty(path)) { return null; } else { return new File(path); } } /** * Creates a new {@link File} from a root folder and some path components. * This is a more efficient version of {@link FileUtils#getFile(File, String...)} * @param rootFolder The root folder * @param pathComponents The sub-path components - can be <code>null</code>/empty, * in which case the root folder is returned as the result * @return The {@link File} instance representing the <U>relative</U> location * from the root folder with the sub-path appended to it (if exists) * @throws IllegalArgumentException If no root folder specified * @see #buildRelativeFile(File, Collection) */ public static File buildRelativeFile(File rootFolder, CharSequence... pathComponents) throws IllegalArgumentException { return buildRelativeFile(rootFolder, ExtendedArrayUtils.asList(pathComponents)); } /** * Creates a new {@link File} from a root folder and some path components * @param rootFolder The root folder * @param pathComponents The sub-path components - can be <code>null</code>/empty, * in which case the root folder is returned as the result * @return The {@link File} instance representing the <U>relative</U> location * from the root folder with the sub-path appended to it (if exists) * @throws IllegalArgumentException If no root folder specified * @see ExtendedFilenameUtils#buildFilePath(Collection) */ public static File buildRelativeFile(File rootFolder, Collection<? extends CharSequence> pathComponents) throws IllegalArgumentException { if (rootFolder == null) { throw new IllegalArgumentException("No root folder provided"); } String subPath = ExtendedFilenameUtils.buildFilePath(pathComponents); if (StringUtils.isEmpty(subPath)) { return rootFolder; } else { return new File(rootFolder, subPath); } } /** * @param file The input {@link File} - ignored if <code>null</code> * @return A {@link List} of the name components comprising the full * path - in the same order as they appear in the hierarchy */ public static final List<String> breakDownFilePath(File file) { if (file == null) { return Collections.emptyList(); } List<String> pathComponents = new ArrayList<String>(); for (File curFile = file; curFile != null; curFile = curFile.getParentFile()) { String name = curFile.getName(); if (StringUtils.isEmpty(name)) { // this happens for Windows root drive name = curFile.getAbsolutePath(); } pathComponents.add(0, name); } return pathComponents; } /** * @param f The {@link File} to be examined - ignored if <code>null</code> * @return A {@link String} representing the file's access according to Linux format * @see #appendFileAccess(Appendable, File) */ public static final String getFileAccess(File f) { if ((f == null) || (!f.exists())) { return ""; } try { return appendFileAccess(new StringBuilder(3), f).toString(); } catch (IOException e) { throw new RuntimeException( "getFileAccess(" + toString(f) + ") " + e.getClass().getSimpleName() + ": " + e.getMessage(), e); } } public static final ExtendedTransformer<File, Character> FOLDER_FILE_ACCESS_CHAR = new AccessCharTransformer( DirectoryFileFilter.DIRECTORY, FOLDER_ACCESS_CHAR); public static final ExtendedTransformer<File, Character> READABLE_FILE_ACCESS_CHAR = new AccessCharTransformer( CanReadFileFilter.CAN_READ, READ_ACCESS_CHAR); public static final ExtendedTransformer<File, Character> WRITABLE_FILE_ACCESS_CHAR = new AccessCharTransformer( CanWriteFileFilter.CAN_WRITE, WRITE_ACCESS_CHAR); public static final ExtendedTransformer<File, Character> EXECUTABLE_FILE_ACCESS_CHAR = new AccessCharTransformer( OS_EXECUTABLE_FILTER, EXECUTE_ACCESS_CHAR); public static final List<ExtendedTransformer<File, Character>> FILE_ACCESS_CHARS = Collections .unmodifiableList(Arrays.asList(FOLDER_FILE_ACCESS_CHAR, READABLE_FILE_ACCESS_CHAR, WRITABLE_FILE_ACCESS_CHAR, EXECUTABLE_FILE_ACCESS_CHAR)); /** * Appends the Linux-format access data of the examined file * @param sb The {@link Appendable} instance to use * @param f The {@link File} to be examined - ignored if <code>null</code> * @return The updated {@link Appendable} instance * @throws IOException If failed to append the data * @see #FOLDER_ACCESS_CHAR * @see #READ_ACCESS_CHAR * @see #WRITABLE_FILE_ACCESS_CHAR * @see #EXECUTABLE_FILE_ACCESS_CHAR * @see #OS_EXECUTABLE_FILTER */ public static final <A extends Appendable> A appendFileAccess(final A sb, final File f) throws IOException { if ((f == null) || (!f.exists())) { return sb; } for (Transformer<File, Character> xformer : FILE_ACCESS_CHARS) { sb.append(xformer.transform(f).charValue()); } return sb; } public static final void copyDirectory(File srcDir, File destDir, Predicate<Pair<File, File>> predicate) throws IOException { copyDirectory(srcDir, destDir, predicate, true); } public static final void copyDirectory(File srcDir, File destDir, Predicate<Pair<File, File>> predicate, boolean preserveFileDate) throws IOException { copyDirectory(srcDir, destDir, null, predicate, preserveFileDate); } public static final void copyDirectory(File srcDir, File destDir, FileFilter filter, Predicate<Pair<File, File>> predicate) throws IOException { copyDirectory(srcDir, destDir, filter, predicate, true); } public static final void copyDirectory(File srcDir, File destDir, FileFilter filter, Predicate<Pair<File, File>> predicate, boolean preserveFileDate) throws IOException { if (srcDir == null) { throw new NullPointerException("Source must not be null"); } if (destDir == null) { throw new NullPointerException("Destination must not be null"); } if (!srcDir.exists()) { throw new FileNotFoundException("Source '" + srcDir + "' directory does not exist"); } if (!srcDir.isDirectory()) { throw new IOException("Source '" + srcDir + "' exists but is not a directory"); } String srcPath = srcDir.getCanonicalPath(), dstPath = destDir.getCanonicalPath(); if (FilenameUtils.isSystemWindows()) { if (srcPath.equalsIgnoreCase(dstPath)) { throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same"); } } else { if (srcPath.equals(dstPath)) { throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same"); } } // Cater for destination being directory within the source directory (see IO-141) List<String> exclusionList = null; if (dstPath.startsWith(srcPath)) { File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter); if (srcFiles != null && srcFiles.length > 0) { exclusionList = new ArrayList<String>(srcFiles.length); for (int i = 0; i < srcFiles.length; i++) { File copiedFile = new File(destDir, srcFiles[i].getName()); exclusionList.add(copiedFile.getCanonicalPath()); } } } doCopyDirectory(srcDir, destDir, filter, predicate, preserveFileDate, exclusionList); } private static void doCopyDirectory(File srcDir, File destDir, FileFilter filter, Predicate<Pair<File, File>> predicate, boolean preserveFileDate, Collection<String> exclusionList) throws IOException { if (destDir.exists()) { if (!destDir.isDirectory()) { throw new IOException("Destination '" + destDir + "' exists but is not a directory"); } } else { if (!destDir.mkdirs()) { throw new IOException("Destination '" + destDir + "' directory cannot be created"); } if (preserveFileDate) { destDir.setLastModified(srcDir.lastModified()); } } if (!destDir.canWrite()) { throw new IOException("Destination '" + destDir + "' cannot be written to"); } // recurse File[] files = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter); if (files == null) { // null if security restricted throw new IOException("Failed to list contents of " + srcDir); } for (File srcFile : files) { File copiedFile = new File(destDir, srcFile.getName()); if ((exclusionList != null) && exclusionList.contains(srcFile.getCanonicalPath())) { continue; } if ((predicate != null) && (!predicate.evaluate(Pair.of(srcFile, copiedFile)))) { continue; } if (srcFile.isDirectory()) { doCopyDirectory(srcFile, copiedFile, filter, predicate, preserveFileDate, exclusionList); } else { copyFile(srcFile, copiedFile, preserveFileDate); } } } public static final void copyFile(File srcFile, File destFile, Predicate<Pair<File, File>> predicate) throws IOException { copyFile(srcFile, destFile, predicate, true); } public static final void copyFile(File srcFile, File destFile, Predicate<Pair<File, File>> predicate, boolean preserveFileDate) throws IOException { if ((predicate == null) || predicate.evaluate(Pair.of(srcFile, destFile))) { copyFile(srcFile, destFile, preserveFileDate); } } public static final void moveFile(File srcFile, File destFile, Predicate<Pair<File, File>> predicate) throws IOException { if ((predicate == null) || predicate.evaluate(Pair.of(srcFile, destFile))) { if (destFile.exists() && destFile.isDirectory()) { moveFileToDirectory(srcFile, destFile, false); } else { moveFile(srcFile, destFile); } } } public static final void deleteFile(File file, Predicate<File> predicate) throws IOException { if (!file.exists()) { return; // nothing to do } if ((predicate != null) && (!predicate.evaluate(file))) { return; } if (file.isDirectory()) { File[] files = file.listFiles(); if (ExtendedArrayUtils.length(files) > 0) { for (File f : files) { deleteFile(f, predicate); } } // directory might not be empty due to the predicate if (predicate != null) { files = file.listFiles(); if (ExtendedArrayUtils.length(files) > 0) { return; } } } if (!file.delete()) { throw new IOException("Failed to delete " + toString(file)); } } /** * Compares the contents of the {@link File}-s * @param srcFile First file * @param dstFile Second file * @param maxRead Max. number of bytes to compare - if negative then * <U>all</U> bytes are compared * @param readSize work buffer size to be used to read data from the files * @return A {@link Triplet} containing the difference offset and the * different {@link Byte} value(s) - <code>null</code> if no difference * @throws IOException If failed to access the file(s) */ public static final Triplet<Long, Byte, Byte> findDifference(File srcFile, File dstFile, long maxRead, int readSize) throws IOException { Validate.notNull(srcFile, "No 1st file", ArrayUtils.EMPTY_OBJECT_ARRAY); ExtendedValidate.isTrue(srcFile.isFile(), "1st file not a file: %s", srcFile); Validate.notNull(dstFile, "No 2nd file", ArrayUtils.EMPTY_OBJECT_ARRAY); ExtendedValidate.isTrue(dstFile.isFile(), "2nd file not a file: %s", dstFile); InputStream src = null, dst = null; try { src = new FileInputStream(srcFile); dst = new FileInputStream(dstFile); return ExtendedIOUtils.findDifference(src, dst, maxRead, readSize); } finally { ExtendedIOUtils.closeAll(src, dst); } } /** * Compares the contents of the {@link File}-s * @param srcFile First file * @param dstFile Second file * @param maxRead Max. number of bytes to compare - if negative then * <U>all</U> bytes are compared * @return A {@link Triplet} containing the difference offset and the * different {@link Byte} value(s) - <code>null</code> if no difference * @throws IOException If failed to access the file(s) */ public static final Triplet<Long, Byte, Byte> findDifference(File srcFile, File dstFile, long maxRead) throws IOException { return findDifference(srcFile, dstFile, maxRead, ExtendedIOUtils.DEFAULT_BUFFER_SIZE_VALUE); } /** * Compares the contents of the {@link File}-s * @param srcFile Source file * @param dstFile Destination file * @return A {@link Triplet} containing the difference offset and the * different {@link Byte} value(s) - <code>null</code> if no difference * @throws IOException If failed to access the file(s) */ public static final Triplet<Long, Byte, Byte> findDifference(File srcFile, File dstFile) throws IOException { return findDifference(srcFile, dstFile, (-1L), ExtendedIOUtils.DEFAULT_BUFFER_SIZE_VALUE); } private static final class AccessCharTransformer extends AbstractExtendedTransformer<File, Character> { private static final Character NO_ACCESS = Character.valueOf(NO_ACCESS_CHAR); private final FileFilter filter; private final Character successChar; protected AccessCharTransformer(FileFilter accessFilter, char accessChar) { super(File.class, Character.class); if ((filter = accessFilter) == null) { throw new IllegalStateException("No file access filter provided"); } successChar = Character.valueOf(accessChar); } @Override public Character transform(File f) { if (!ExistFileFilter.EXIST.accept(f)) { return NO_ACCESS; } if (filter.accept(f)) { return successChar; } else { return NO_ACCESS; } } } }