package net.sf.openrocket.file.iterator;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sf.openrocket.util.JarUtil;
import net.sf.openrocket.util.Pair;
/**
* A DirectoryIterator that scans for files within a directory in the file system
* matching a FileFilter. The scan is optionally recursive.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class DirectoryIterator extends FileIterator {
private static final Logger logger = LoggerFactory.getLogger(DirectoryIterator.class);
private final FileFilter filter;
private final File[] files;
private final boolean recursive;
private int position = 0;
private DirectoryIterator subIterator = null;
/**
* Sole constructor.
*
* @param directory the directory to read.
* @param filter the filter for selecting files.
* @throws IOException if the directory cannot be read.
*/
public DirectoryIterator(File directory, FileFilter filter, boolean recursive)
throws IOException {
this.filter = filter;
this.recursive = recursive;
this.files = directory.listFiles(new DirSelectionFileFilter(filter, recursive));
if (this.files == null) {
throw new IOException("not a directory or IOException occurred when listing files " +
"from " + directory);
}
}
@Override
protected Pair<String, InputStream> findNext() {
// Check if we're recursing
if (subIterator != null) {
if (subIterator.hasNext()) {
return subIterator.next();
} else {
subIterator.close();
subIterator = null;
}
}
// Scan through file entries
while (position < files.length) {
File file = files[position];
position++;
try {
if (recursive && file.isDirectory()) {
subIterator = new DirectoryIterator(file, filter, recursive);
if (subIterator.hasNext()) {
return subIterator.next();
} else {
subIterator.close();
subIterator = null;
continue;
}
}
InputStream is = new BufferedInputStream(new FileInputStream(file));
return new Pair<String, InputStream>(file.getName(), is);
} catch (IOException e) {
logger.warn("Error opening file/directory " + file, e);
}
}
return null;
}
/**
* Return a DirectoryIterator for a directory that can be located either
* within the containing JAR file, in the classpath or in the current directory
* (searched in this order). The first place that contains matching files
* will be iterated through.
*
* @param directory the directory to search for.
* @param filter the filter for matching files in the directory.
* @return a DirectoryIterator for iterating through the files in the
* directory, or <code>null</code> if no directory containing
* matching files can be found.
*/
public static FileIterator findDirectory(String directory, FileFilter filter) {
FileIterator iterator = null;
// Try to load from containing JAR file
File jarFile = JarUtil.getCurrentJarFile();
if (jarFile != null) {
try {
iterator = new ZipDirectoryIterator(jarFile, directory, filter);
if (iterator.hasNext()) {
return iterator;
}
iterator.close();
} catch (IOException e) {
logger.error("Error opening containing JAR file " + jarFile, e);
}
}
// Try to find directory as a system resource
URL url = ClassLoader.getSystemResource(directory);
if (url != null) {
try {
File dir = JarUtil.urlToFile(url);
iterator = new DirectoryIterator(dir, filter, true);
if (iterator.hasNext()) {
return iterator;
}
iterator.close();
} catch (Exception e1) {
logger.error("Error opening directory from URL " + url);
}
}
// Try to open directory as such
try {
iterator = new DirectoryIterator(new File(directory), filter, true);
if (iterator.hasNext()) {
return iterator;
}
iterator.close();
} catch (IOException e) {
logger.error("Error opening directory " + directory);
}
return null;
}
/**
* A FileFilter wrapper that accepts or discards directories.
*/
private class DirSelectionFileFilter implements FileFilter {
private final boolean acceptDirs;
private final FileFilter parentFilter;
public DirSelectionFileFilter(FileFilter filter, boolean acceptDirs) {
this.acceptDirs = acceptDirs;
this.parentFilter = filter;
}
@Override
public boolean accept(File pathname) {
if (pathname.getName().startsWith(".")) {
return false;
}
if (pathname.isDirectory()) {
return acceptDirs;
}
return parentFilter.accept(pathname);
}
}
}