* due to permission issues File.listFiles() can return null

This commit is contained in:
Reinhard Pointner 2014-06-23 15:09:43 +00:00
parent 81f7c32b8c
commit f85c561b78
8 changed files with 114 additions and 164 deletions

View File

@ -48,8 +48,6 @@ import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import net.miginfocom.swing.MigLayout;
import net.sf.ehcache.CacheManager;
import net.filebot.cli.ArgumentBean; import net.filebot.cli.ArgumentBean;
import net.filebot.cli.ArgumentProcessor; import net.filebot.cli.ArgumentProcessor;
import net.filebot.cli.CmdlineOperations; import net.filebot.cli.CmdlineOperations;
@ -63,6 +61,8 @@ import net.filebot.util.ByteBufferInputStream;
import net.filebot.util.PreferencesMap.PreferencesEntry; import net.filebot.util.PreferencesMap.PreferencesEntry;
import net.filebot.util.TeePrintStream; import net.filebot.util.TeePrintStream;
import net.filebot.web.CachedResource; import net.filebot.web.CachedResource;
import net.miginfocom.swing.MigLayout;
import net.sf.ehcache.CacheManager;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
@ -464,7 +464,7 @@ public class Main {
isNewCache = true; isNewCache = true;
// delete all files related to previous cache instances // delete all files related to previous cache instances
for (File it : cache.listFiles()) { for (File it : getChildren(cache)) {
if (!it.equals(lockFile)) { if (!it.equals(lockFile)) {
delete(cache); delete(cache);
} }

View File

@ -165,7 +165,7 @@ public class ArgumentBean {
if (recursive) { if (recursive) {
files.addAll(listFiles(file)); files.addAll(listFiles(file));
} else { } else {
files.addAll(asList(file.listFiles())); files.addAll(getChildren(file));
} }
} else { } else {
files.add(file); files.add(file);

View File

@ -57,11 +57,7 @@ public class ScriptShellMethods {
} }
public static List<File> listFiles(File self, Closure<?> closure) { public static List<File> listFiles(File self, Closure<?> closure) {
File[] files = self.listFiles(); return (List<File>) DefaultGroovyMethods.findAll(FileUtilities.getChildren(self), closure);
if (files == null)
return emptyList();
return (List<File>) DefaultGroovyMethods.findAll(asList(files), closure);
} }
public static boolean isVideo(File self) { public static boolean isVideo(File self) {
@ -101,11 +97,7 @@ public class ScriptShellMethods {
} }
public static boolean hasFile(File self, Closure<?> closure) { public static boolean hasFile(File self, Closure<?> closure) {
File[] files = self.listFiles(); return DefaultGroovyMethods.find(FileUtilities.getChildren(self), closure) != null;
if (files == null)
return false;
return DefaultGroovyMethods.find(asList(files), closure) != null;
} }
public static List<File> getFiles(File self) { public static List<File> getFiles(File self) {
@ -163,7 +155,7 @@ public class ScriptShellMethods {
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
File folder = dir.toFile(); File folder = dir.toFile();
if (FileUtilities.filter(asList(folder.listFiles()), VIDEO_FILES).size() > 0 || MediaDetection.isDiskFolder(folder)) { if (FileUtilities.filter(FileUtilities.getChildren(folder), VIDEO_FILES).size() > 0 || MediaDetection.isDiskFolder(folder)) {
mediaFolders.add(folder); mediaFolders.add(folder);
return FileVisitResult.SKIP_SUBTREE; return FileVisitResult.SKIP_SUBTREE;
} }

View File

@ -1,8 +1,7 @@
package net.filebot.ui.analyze; package net.filebot.ui.analyze;
import static net.filebot.ui.NotificationLogging.*; import static net.filebot.ui.NotificationLogging.*;
import static net.filebot.util.FileUtilities.*;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
@ -14,58 +13,50 @@ import net.filebot.ui.analyze.FileTree.FolderNode;
import net.filebot.ui.transfer.BackgroundFileTransferablePolicy; import net.filebot.ui.transfer.BackgroundFileTransferablePolicy;
import net.filebot.util.ExceptionUtilities; import net.filebot.util.ExceptionUtilities;
import net.filebot.util.FastFile; import net.filebot.util.FastFile;
import net.filebot.util.FileUtilities;
class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy<AbstractTreeNode> { class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy<AbstractTreeNode> {
private final FileTree tree; private final FileTree tree;
public FileTreeTransferablePolicy(FileTree tree) { public FileTreeTransferablePolicy(FileTree tree) {
this.tree = tree; this.tree = tree;
} }
@Override @Override
protected boolean accept(List<File> files) { protected boolean accept(List<File> files) {
return true; return true;
} }
@Override @Override
protected void clear() { protected void clear() {
super.clear(); super.clear();
tree.clear(); tree.clear();
} }
@Override @Override
protected void process(List<AbstractTreeNode> chunks) { protected void process(List<AbstractTreeNode> chunks) {
FolderNode root = tree.getRoot(); FolderNode root = tree.getRoot();
for (AbstractTreeNode node : chunks) { for (AbstractTreeNode node : chunks) {
root.add(node); root.add(node);
} }
tree.getModel().reload(); tree.getModel().reload();
} }
@Override @Override
protected void process(Exception e) { protected void process(Exception e) {
UILogger.log(Level.WARNING, ExceptionUtilities.getRootCauseMessage(e), e); UILogger.log(Level.WARNING, ExceptionUtilities.getRootCauseMessage(e), e);
} }
@Override @Override
protected void load(List<File> files) { protected void load(List<File> files) {
try { try {
for (File file : files) { for (File file : files) {
// use fast file to minimize system calls like length(), isDirectory(), isFile(), ... // use fast file to minimize system calls like length(), isDirectory(), isFile(), ...
AbstractTreeNode node = getTreeNode(new FastFile(file.getPath())); AbstractTreeNode node = getTreeNode(new FastFile(file.getPath()));
// publish on EDT // publish on EDT
publish(node); publish(node);
} }
@ -73,39 +64,37 @@ class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy<Abstra
// supposed to happen if background execution was aborted // supposed to happen if background execution was aborted
} }
} }
private AbstractTreeNode getTreeNode(File file) throws InterruptedException { private AbstractTreeNode getTreeNode(File file) throws InterruptedException {
if (Thread.interrupted()) if (Thread.interrupted())
throw new InterruptedException(); throw new InterruptedException();
File[] files = file.listFiles(); if (file.isDirectory()) {
if (files != null && file.isDirectory()) { List<File> files = getChildren(file);
FolderNode node = new FolderNode(FileUtilities.getFolderName(file), files.length); FolderNode node = new FolderNode(getFolderName(file), files.size());
// add folders first // add folders first
for (File f : files) { for (File f : files) {
if (f.isDirectory()) { if (f.isDirectory()) {
node.add(getTreeNode(f)); node.add(getTreeNode(f));
} }
} }
for (File f : files) { for (File f : files) {
if (f.isFile()) { if (f.isFile()) {
node.add(getTreeNode(f)); node.add(getTreeNode(f));
} }
} }
return node; return node;
} }
return new FileNode(file); return new FileNode(file);
} }
@Override @Override
public String getFileFilterDescription() { public String getFileFilterDescription() {
return "files and folders"; return "files and folders";
} }
} }

View File

@ -1,6 +1,5 @@
package net.filebot.ui.rename; package net.filebot.ui.rename;
import static java.util.Arrays.*;
import static net.filebot.MediaTypes.*; import static net.filebot.MediaTypes.*;
import static net.filebot.ui.transfer.FileTransferable.*; import static net.filebot.ui.transfer.FileTransferable.*;
import static net.filebot.util.FileUtilities.*; import static net.filebot.util.FileUtilities.*;
@ -11,7 +10,6 @@ import java.util.ArrayList;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Scanner; import java.util.Scanner;
import java.util.TreeSet;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -91,10 +89,7 @@ class FilesListTransferablePolicy extends FileTransferablePolicy {
} else if (!recursive || f.isFile() || MediaDetection.isDiskFolder(f)) { } else if (!recursive || f.isFile() || MediaDetection.isDiskFolder(f)) {
entries.add(f); entries.add(f);
} else if (f.isDirectory()) { } else if (f.isDirectory()) {
File[] children = f.listFiles(); queue.addAll(0, sortByUniquePath(getChildren(f))); // FORCE NATURAL FILE ORDER
if (children != null) {
queue.addAll(0, new TreeSet<File>(asList(children))); // FORCE NATURAL FILE ORDER
}
} }
} }

View File

@ -1,7 +1,5 @@
package net.filebot.ui.sfv; package net.filebot.ui.sfv;
import static java.util.Collections.*; import static java.util.Collections.*;
import static net.filebot.hash.VerificationUtilities.*; import static net.filebot.hash.VerificationUtilities.*;
import static net.filebot.ui.NotificationLogging.*; import static net.filebot.ui.NotificationLogging.*;
@ -22,33 +20,28 @@ import net.filebot.hash.VerificationFileReader;
import net.filebot.ui.transfer.BackgroundFileTransferablePolicy; import net.filebot.ui.transfer.BackgroundFileTransferablePolicy;
import net.filebot.util.ExceptionUtilities; import net.filebot.util.ExceptionUtilities;
class ChecksumTableTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumCell> { class ChecksumTableTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumCell> {
private final ChecksumTableModel model; private final ChecksumTableModel model;
private final ChecksumComputationService computationService; private final ChecksumComputationService computationService;
public ChecksumTableTransferablePolicy(ChecksumTableModel model, ChecksumComputationService checksumComputationService) { public ChecksumTableTransferablePolicy(ChecksumTableModel model, ChecksumComputationService checksumComputationService) {
this.model = model; this.model = model;
this.computationService = checksumComputationService; this.computationService = checksumComputationService;
} }
@Override @Override
protected boolean accept(List<File> files) { protected boolean accept(List<File> files) {
return true; return true;
} }
@Override @Override
protected void clear() { protected void clear() {
super.clear(); super.clear();
computationService.reset(); computationService.reset();
model.clear(); model.clear();
} }
@Override @Override
protected void prepare(List<File> files) { protected void prepare(List<File> files) {
@ -56,30 +49,26 @@ class ChecksumTableTransferablePolicy extends BackgroundFileTransferablePolicy<C
model.setHashType(getHashType(files.get(0))); model.setHashType(getHashType(files.get(0)));
} }
} }
@Override @Override
protected void process(List<ChecksumCell> chunks) { protected void process(List<ChecksumCell> chunks) {
model.addAll(chunks); model.addAll(chunks);
} }
@Override @Override
protected void process(Exception e) { protected void process(Exception e) {
UILogger.log(Level.WARNING, ExceptionUtilities.getRootCauseMessage(e), e); UILogger.log(Level.WARNING, ExceptionUtilities.getRootCauseMessage(e), e);
} }
private final ThreadLocal<ExecutorService> executor = new ThreadLocal<ExecutorService>(); private final ThreadLocal<ExecutorService> executor = new ThreadLocal<ExecutorService>();
private final ThreadLocal<VerificationTracker> verificationTracker = new ThreadLocal<VerificationTracker>(); private final ThreadLocal<VerificationTracker> verificationTracker = new ThreadLocal<VerificationTracker>();
@Override @Override
protected void load(List<File> files) throws IOException { protected void load(List<File> files) throws IOException {
// initialize drop parameters // initialize drop parameters
executor.set(computationService.newExecutor()); executor.set(computationService.newExecutor());
verificationTracker.set(new VerificationTracker(3)); verificationTracker.set(new VerificationTracker(3));
try { try {
// handle single verification file drop // handle single verification file drop
if (files.size() == 1 && getHashType(files.get(0)) != null) { if (files.size() == 1 && getHashType(files.get(0)) != null) {
@ -87,7 +76,7 @@ class ChecksumTableTransferablePolicy extends BackgroundFileTransferablePolicy<C
} }
// handle single folder drop // handle single folder drop
else if (files.size() == 1 && files.get(0).isDirectory()) { else if (files.size() == 1 && files.get(0).isDirectory()) {
for (File file : files.get(0).listFiles()) { for (File file : getChildren(files.get(0))) {
load(file, null, files.get(0)); load(file, null, files.get(0));
} }
} }
@ -102,118 +91,111 @@ class ChecksumTableTransferablePolicy extends BackgroundFileTransferablePolicy<C
} finally { } finally {
// shutdown executor after all tasks have been completed // shutdown executor after all tasks have been completed
executor.get().shutdown(); executor.get().shutdown();
// remove drop parameters // remove drop parameters
executor.remove(); executor.remove();
verificationTracker.remove(); verificationTracker.remove();
} }
} }
protected void loadVerificationFile(File file, HashType type) throws IOException, InterruptedException { protected void loadVerificationFile(File file, HashType type) throws IOException, InterruptedException {
VerificationFileReader parser = new VerificationFileReader(createTextReader(file), type.getFormat()); VerificationFileReader parser = new VerificationFileReader(createTextReader(file), type.getFormat());
try { try {
// root for relative file paths in verification file // root for relative file paths in verification file
File baseFolder = file.getParentFile(); File baseFolder = file.getParentFile();
while (parser.hasNext()) { while (parser.hasNext()) {
// make this possibly long-running operation interruptible // make this possibly long-running operation interruptible
if (Thread.interrupted()) if (Thread.interrupted())
throw new InterruptedException(); throw new InterruptedException();
Entry<File, String> entry = parser.next(); Entry<File, String> entry = parser.next();
String name = normalizePathSeparators(entry.getKey().getPath()); String name = normalizePathSeparators(entry.getKey().getPath());
String hash = new String(entry.getValue()); String hash = new String(entry.getValue());
ChecksumCell correct = new ChecksumCell(name, file, singletonMap(type, hash)); ChecksumCell correct = new ChecksumCell(name, file, singletonMap(type, hash));
ChecksumCell current = createComputationCell(name, baseFolder, type); ChecksumCell current = createComputationCell(name, baseFolder, type);
publish(correct, current); publish(correct, current);
} }
} finally { } finally {
parser.close(); parser.close();
} }
} }
protected void load(File absoluteFile, File relativeFile, File root) throws IOException, InterruptedException { protected void load(File absoluteFile, File relativeFile, File root) throws IOException, InterruptedException {
if (Thread.interrupted()) if (Thread.interrupted())
throw new InterruptedException(); throw new InterruptedException();
// ignore hidden files/folders // ignore hidden files/folders
if (absoluteFile.isHidden()) if (absoluteFile.isHidden())
return; return;
// add next name to relative path // add next name to relative path
relativeFile = new File(relativeFile, absoluteFile.getName()); relativeFile = new File(relativeFile, absoluteFile.getName());
if (absoluteFile.isDirectory()) { if (absoluteFile.isDirectory()) {
// load all files in the file tree // load all files in the file tree
for (File child : absoluteFile.listFiles()) { for (File child : getChildren(absoluteFile)) {
load(child, relativeFile, root); load(child, relativeFile, root);
} }
} else { } else {
String name = normalizePathSeparators(relativeFile.getPath()); String name = normalizePathSeparators(relativeFile.getPath());
// publish computation cell first // publish computation cell first
publish(createComputationCell(name, root, model.getHashType())); publish(createComputationCell(name, root, model.getHashType()));
// publish verification cell, if we can // publish verification cell, if we can
Map<File, String> hashByVerificationFile = verificationTracker.get().getHashByVerificationFile(absoluteFile); Map<File, String> hashByVerificationFile = verificationTracker.get().getHashByVerificationFile(absoluteFile);
for (Entry<File, String> entry : hashByVerificationFile.entrySet()) { for (Entry<File, String> entry : hashByVerificationFile.entrySet()) {
HashType hashType = verificationTracker.get().getVerificationFileType(entry.getKey()); HashType hashType = verificationTracker.get().getVerificationFileType(entry.getKey());
publish(new ChecksumCell(name, entry.getKey(), singletonMap(hashType, entry.getValue()))); publish(new ChecksumCell(name, entry.getKey(), singletonMap(hashType, entry.getValue())));
} }
} }
} }
protected ChecksumCell createComputationCell(String name, File root, HashType hash) { protected ChecksumCell createComputationCell(String name, File root, HashType hash) {
ChecksumCell cell = new ChecksumCell(name, root, new ChecksumComputationTask(new File(root, name), hash)); ChecksumCell cell = new ChecksumCell(name, root, new ChecksumComputationTask(new File(root, name), hash));
// start computation task // start computation task
executor.get().execute(cell.getTask()); executor.get().execute(cell.getTask());
return cell; return cell;
} }
@Override @Override
public String getFileFilterDescription() { public String getFileFilterDescription() {
return "files, folders and sfv files"; return "files, folders and sfv files";
} }
private static class VerificationTracker { private static class VerificationTracker {
private final Map<File, Integer> seen = new HashMap<File, Integer>(); private final Map<File, Integer> seen = new HashMap<File, Integer>();
private final Map<File, Map<File, String>> cache = new HashMap<File, Map<File, String>>(); private final Map<File, Map<File, String>> cache = new HashMap<File, Map<File, String>>();
private final Map<File, HashType> types = new HashMap<File, HashType>(); private final Map<File, HashType> types = new HashMap<File, HashType>();
private final int maxDepth; private final int maxDepth;
public VerificationTracker(int maxDepth) { public VerificationTracker(int maxDepth) {
this.maxDepth = maxDepth; this.maxDepth = maxDepth;
} }
public Map<File, String> getHashByVerificationFile(File file) throws IOException { public Map<File, String> getHashByVerificationFile(File file) throws IOException {
// cache all verification files // cache all verification files
File folder = file.getParentFile(); File folder = file.getParentFile();
int depth = 0; int depth = 0;
while (folder != null && depth <= maxDepth) { while (folder != null && depth <= maxDepth) {
Integer seenLevel = seen.get(folder); Integer seenLevel = seen.get(folder);
if (seenLevel != null && seenLevel <= depth) { if (seenLevel != null && seenLevel <= depth) {
// we have completely seen this parent tree before // we have completely seen this parent tree before
break; break;
} }
if (seenLevel == null) { if (seenLevel == null) {
// folder we have never encountered before // folder we have never encountered before
for (File verificationFile : folder.listFiles(MediaTypes.getDefaultFilter("verification"))) { for (File verificationFile : folder.listFiles(MediaTypes.getDefaultFilter("verification"))) {
@ -222,39 +204,37 @@ class ChecksumTableTransferablePolicy extends BackgroundFileTransferablePolicy<C
types.put(verificationFile, hashType); types.put(verificationFile, hashType);
} }
} }
// update // update
seen.put(folder, depth); seen.put(folder, depth);
// step down // step down
folder = folder.getParentFile(); folder = folder.getParentFile();
depth++; depth++;
} }
// just return if we know we won't find anything // just return if we know we won't find anything
if (cache.isEmpty()) { if (cache.isEmpty()) {
return emptyMap(); return emptyMap();
} }
// search all cached verification files // search all cached verification files
Map<File, String> result = new HashMap<File, String>(2); Map<File, String> result = new HashMap<File, String>(2);
for (Entry<File, Map<File, String>> entry : cache.entrySet()) { for (Entry<File, Map<File, String>> entry : cache.entrySet()) {
String hash = entry.getValue().get(file); String hash = entry.getValue().get(file);
if (hash != null) { if (hash != null) {
result.put(entry.getKey(), hash); result.put(entry.getKey(), hash);
} }
} }
return result; return result;
} }
public HashType getVerificationFileType(File verificationFile) { public HashType getVerificationFileType(File verificationFile) {
return types.get(verificationFile); return types.get(verificationFile);
} }
/** /**
* Completely read a verification file and resolve all relative file paths against a given base folder * Completely read a verification file and resolve all relative file paths against a given base folder
@ -262,20 +242,20 @@ class ChecksumTableTransferablePolicy extends BackgroundFileTransferablePolicy<C
private Map<File, String> importVerificationFile(File verificationFile, HashType hashType, File baseFolder) throws IOException { private Map<File, String> importVerificationFile(File verificationFile, HashType hashType, File baseFolder) throws IOException {
VerificationFileReader parser = new VerificationFileReader(createTextReader(verificationFile), hashType.getFormat()); VerificationFileReader parser = new VerificationFileReader(createTextReader(verificationFile), hashType.getFormat());
Map<File, String> result = new HashMap<File, String>(); Map<File, String> result = new HashMap<File, String>();
try { try {
while (parser.hasNext()) { while (parser.hasNext()) {
Entry<File, String> entry = parser.next(); Entry<File, String> entry = parser.next();
// resolve relative path, the hash is probably a substring, so we compact it, for memory reasons // resolve relative path, the hash is probably a substring, so we compact it, for memory reasons
result.put(new File(baseFolder, entry.getKey().getPath()), new String(entry.getValue())); result.put(new File(baseFolder, entry.getKey().getPath()), new String(entry.getValue()));
} }
} finally { } finally {
parser.close(); parser.close();
} }
return result; return result;
} }
} }
} }

View File

@ -380,6 +380,17 @@ public final class FileUtilities {
return f; return f;
} }
public static List<File> getChildren(File file) {
File[] files = file.listFiles();
// children array may be null if folder permissions do not allow listing of files
if (files == null) {
return asList(new File[0]);
}
return asList(files);
}
public static List<File> listFiles(File... folders) { public static List<File> listFiles(File... folders) {
return listFiles(asList(folders)); return listFiles(asList(folders));
} }
@ -417,11 +428,7 @@ public final class FileUtilities {
if (depth > maxDepth) if (depth > maxDepth)
return; return;
File[] children = folder.listFiles(); for (File it : getChildren(folder)) {
if (children == null)
return;
for (File it : children) {
if (!addHidden && it.isHidden()) // ignore hidden files if (!addHidden && it.isHidden()) // ignore hidden files
continue; continue;

View File

@ -1,6 +1,6 @@
package net.filebot.util; package net.filebot.util;
import static net.filebot.util.FileUtilities.*;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -10,47 +10,42 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
public final class TemporaryFolder { public final class TemporaryFolder {
private static final Map<String, TemporaryFolder> folders = new HashMap<String, TemporaryFolder>(); private static final Map<String, TemporaryFolder> folders = new HashMap<String, TemporaryFolder>();
/** /**
* Get a {@link TemporaryFolder} instance for a given name. The actual directory will be * Get a {@link TemporaryFolder} instance for a given name. The actual directory will be created lazily (e.g. when a file is created). The name of the directory will start with the given name (lower-case) and contain a unique id, so multiple application instances may run at the same time without the risk of interference.
* created lazily (e.g. when a file is created). The name of the directory will start with
* the given name (lower-case) and contain a unique id, so multiple application instances
* may run at the same time without the risk of interference.
* *
* @param name case-insensitive name of a temporary folder (e.g. application name) * @param name
* case-insensitive name of a temporary folder (e.g. application name)
* @return temporary folder for this name * @return temporary folder for this name
*/ */
public static TemporaryFolder getFolder(String name) { public static TemporaryFolder getFolder(String name) {
// make name case-insensitive // make name case-insensitive
name = name.toLowerCase(); name = name.toLowerCase();
synchronized (folders) { synchronized (folders) {
TemporaryFolder folder = folders.get(name); TemporaryFolder folder = folders.get(name);
if (folder == null) { if (folder == null) {
File tmpdir = new File(System.getProperty("java.io.tmpdir")); File tmpdir = new File(System.getProperty("java.io.tmpdir"));
String subdir = String.format("%s [%s]", name, UUID.randomUUID()); String subdir = String.format("%s [%s]", name, UUID.randomUUID());
folder = new TemporaryFolder(new File(tmpdir, subdir)); folder = new TemporaryFolder(new File(tmpdir, subdir));
folders.put(name, folder); folders.put(name, folder);
} }
return folder; return folder;
} }
} }
/** /**
* Delete all temporary folders on shutdown * Delete all temporary folders on shutdown
*/ */
static { static {
Runtime.getRuntime().addShutdownHook(new Thread("TemporaryFolder Cleanup") { Runtime.getRuntime().addShutdownHook(new Thread("TemporaryFolder Cleanup") {
@Override @Override
public void run() { public void run() {
synchronized (folders) { synchronized (folders) {
@ -61,57 +56,53 @@ public final class TemporaryFolder {
} }
}); });
} }
private final File root; private final File root;
private TemporaryFolder(File root) { private TemporaryFolder(File root) {
this.root = root; this.root = root;
} }
/** /**
* Create an empty file in this temporary folder. * Create an empty file in this temporary folder.
* *
* @param name name of the file * @param name
* name of the file
* @return newly created file * @return newly created file
* @throws IOException if an I/O error occurred * @throws IOException
* if an I/O error occurred
*/ */
public File createFile(String name) throws IOException { public File createFile(String name) throws IOException {
// if the directory does not exist it will be created // if the directory does not exist it will be created
File file = new File(getFolder(), name); File file = new File(getFolder(), name);
file.createNewFile(); file.createNewFile();
return file; return file;
} }
/** /**
* Creates an empty file in this temporary folder, using the given prefix and suffix to * Creates an empty file in this temporary folder, using the given prefix and suffix to generate its name.
* generate its name.
* *
* @param prefix The prefix string to be used in generating the file's name; must be at * @param prefix
* least three characters long * The prefix string to be used in generating the file's name; must be at least three characters long
* @param suffix The suffix string to be used in generating the file's name; may be null, * @param suffix
* in which case the suffix ".tmp" will be used * The suffix string to be used in generating the file's name; may be null, in which case the suffix ".tmp" will be used
* @return An abstract pathname denoting a newly-created empty file * @return An abstract pathname denoting a newly-created empty file
* @throws IOException If a file could not be created * @throws IOException
* If a file could not be created
* @see File#createTempFile(String, String) * @see File#createTempFile(String, String)
*/ */
public File createFile(String prefix, String suffix) throws IOException { public File createFile(String prefix, String suffix) throws IOException {
return File.createTempFile(prefix, suffix, getFolder()); return File.createTempFile(prefix, suffix, getFolder());
} }
public boolean deleteFile(String name) { public boolean deleteFile(String name) {
return new File(getFolder(), name).delete(); return new File(getFolder(), name).delete();
} }
/** /**
* Retrieve the {@link File} object for this {@link TemporaryFolder}. The actual directory * Retrieve the {@link File} object for this {@link TemporaryFolder}. The actual directory for the {@link TemporaryFolder} instance will be created by this method.
* for the {@link TemporaryFolder} instance will be created by this method.
* *
* @return the {@link File} object for this {@link TemporaryFolder} * @return the {@link File} object for this {@link TemporaryFolder}
*/ */
@ -119,28 +110,25 @@ public final class TemporaryFolder {
if (!root.exists()) { if (!root.exists()) {
root.mkdirs(); root.mkdirs();
} }
return root; return root;
} }
public TemporaryFolder subFolder(String name) { public TemporaryFolder subFolder(String name) {
return new TemporaryFolder(new File(getFolder(), name)); return new TemporaryFolder(new File(getFolder(), name));
} }
public List<File> list(boolean recursive) { public List<File> list(boolean recursive) {
List<File> list = new ArrayList<File>(); List<File> list = new ArrayList<File>();
list(root, list, recursive); list(root, list, recursive);
return list; return list;
} }
private void list(File file, List<File> list, boolean recursive) { private void list(File file, List<File> list, boolean recursive) {
if (file.isDirectory()) { if (file.isDirectory()) {
for (File entry : file.listFiles()) { for (File entry : getChildren(file)) {
if (entry.isDirectory()) { if (entry.isDirectory()) {
if (recursive) { if (recursive) {
list(entry, list, recursive); list(entry, list, recursive);
@ -151,26 +139,25 @@ public final class TemporaryFolder {
} }
} }
} }
public void delete() { public void delete() {
delete(root); delete(root);
} }
/** /**
* Delete files/folders recursively * Delete files/folders recursively
* *
* @param file file/folder that will be deleted * @param file
* file/folder that will be deleted
*/ */
private void delete(File file) { private void delete(File file) {
if (file.isDirectory()) { if (file.isDirectory()) {
for (File entry : file.listFiles()) { for (File entry : getChildren(file)) {
delete(entry); delete(entry);
} }
} }
file.delete(); file.delete();
} }
} }