/* XXL: The eXtensible and fleXible Library for data processing Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger Head of the Database Research Group Department of Mathematics and Computer Science University of Marburg Germany This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see <http://www.gnu.org/licenses/>. http://code.google.com/p/xxl/ */ package xxl.core.cursors.sources.io; import java.io.File; import java.io.FileFilter; import xxl.core.collections.queues.ArrayQueue; import xxl.core.collections.queues.Queue; import xxl.core.collections.queues.Queues; import xxl.core.cursors.AbstractCursor; /** * This class provides an implementation of the * {@link xxl.core.cursors.Cursor cursor} interface that iterates over the * files contained in the specified path and its subdirectories and returns * their names. The names are given relative to the specified path. * * @see java.util.Iterator * @see xxl.core.cursors.Cursor * @see xxl.core.cursors.AbstractCursor */ public class FileNameCursor extends AbstractCursor<String> { /** * A queue that is used to store the files. */ protected Queue<File> files; /** * The directory that should be searched for files. */ protected File path; /** * A file filter that determines whether a special file name should be * returned or a special subdirectory should be searched. */ protected FileFilter fileFilter; /** * Creates a new file-name cursor that iterates over the files contained in * the specified path. The specified file filter decides, which files * should be returned and which subdirectories should be searched. * * @param path the directory to search for files. * @param fileFilter the file filter that decides which files should be * returned and which subdirectories should be searched. */ public FileNameCursor(File path, FileFilter fileFilter) { files = new ArrayQueue<File>(); this.path = path; this.fileFilter = fileFilter; } /** * Creates a new file-name cursor that iterates over the files contained in * the specified path. The specified file filter decides, which files * should be returned and which subdirectories should be searched. The path * is not allowed to contain wildcards (it must be a valid directory, i.e., * "."). * * @param path the directory to search for files. * @param fileFilter the file filter that decides which files should be * returned and which subdirectories should be searched. */ public FileNameCursor(String path, FileFilter fileFilter) { this(new File(path), fileFilter); } /** * Creates a new file-name cursor that iterates over the files contained in * the specified path. The specified boolean flag decides whether * subdirectories should be searched recursively. * * @param path the path to search for files. * @param recursive decides whether the cursor should search for files in * subdirectories. */ public FileNameCursor(final File path, final boolean recursive) { this( path, new FileFilter() { public boolean accept(File pathname) { return pathname.isFile() || pathname.equals(path) || recursive; } } ); } /** * Creates a new file-name cursor that iterates over the files contained in * the specified path. The specified boolean flag decides whether * subdirectories should be searched recursively. The path is not allowed * to contain wildcards (it must be a valid directory, i.e., "."). * * @param path the path to search for files. * @param recursive decides whether the cursor should search for files in * subdirectories. */ public FileNameCursor(String path, boolean recursive) { this(new File(path), recursive); } /** * Creates a new file-name cursor that iterates over the files contained in * the specified path and its subdirectories. * * @param path the path to search for files. */ public FileNameCursor(File path) { this(path, true); } /** * Creates a new file-name cursor that iterates over the files contained in * the specified path and its subdirectories. The path is not allowed to * contain wildcards (it must be a valid directory, i.e., "."). * * @param path the path to search for files. */ public FileNameCursor(String path) { this(new File(path)); } /** * Opens the file-name cursor, i.e., signals the cursor to reserve * resources, open files, etc. Before a cursor has been opened calls to * methods like <code>next</code> or <code>peek</code> are not guaranteed * to yield proper results. Therefore <code>open</code> must be called * before a cursor's data can be processed. Multiple calls to * <code>open</code> do not have any effect, i.e., if <code>open</code> was * called the cursor remains in the state <i>opened</i> until its * <code>close</code> method is called. * * <p>Note, that a call to the <code>open</code> method of a closed cursor * usually does not open it again because of the fact that its state * generally cannot be restored when resources are released respectively * files are closed.</p> */ public void open() { if (!isOpened) { files.open(); files.enqueue(path); } super.open(); } /** * Closes the file-name cursor, i.e., signals the cursor to clean up * resources, close files, etc. When a cursor has been closed calls to * methods like <code>next</code> or <code>peek</code> are not guaranteed * to yield proper results. Multiple calls to <code>close</code> do not * have any effect, i.e., if <code>close</code> was called the cursor * remains in the state <i>closed</i>. * * <p>Note, that a closed cursor usually cannot be opened again because of * the fact that its state generally cannot be restored when resources are * released respectively files are closed.</p> */ public void close() { if (!isClosed) files.close(); super.close(); } /** * Returns <code>true</code> if the iteration has more elements. (In other * words, returns <code>true</code> if <code>next</code> or * <code>peek</code> would return an element rather than throwing an * exception.) * * @return <code>true</code> if the file-name cursor has more elements. */ protected boolean hasNextObject() { while (!files.isEmpty()) if (files.peek().isDirectory()) Queues.enqueueAll(files, files.dequeue().listFiles(fileFilter)); else return true; return false; } /** * Returns the next element in the iteration. This element will be * accessible by some of the file-name cursor's methods, e.g., * <code>update</code> or <code>remove</code>, until a call to * <code>next</code> or <code>peek</code> occurs. This is calling * <code>next</code> or <code>peek</code> proceeds the iteration and * therefore its previous element will not be accessible any more. * * @return the next element in the iteration. */ protected String nextObject() { return files.dequeue().getPath().substring(path.getPath().length()); } /** * Resets the file-name cursor to its initial state such that the caller is * able to traverse the underlying data structure again without * constructing a new cursor (optional operation). * * <p>Note, that this operation is optional and might not work for all * cursors.</p> * * @throws UnsupportedOperationException if the <code>reset</code> * operation is not supported by the file-name cursor. */ public void reset() throws UnsupportedOperationException{ super.reset(); files.clear(); files.enqueue(path); } /** * Returns <code>true</code> if the <code>reset</code> operation is * supported by the file-name cursor. Otherwise it returns * <code>false</code>. * * @return <code>true</code> if the <code>reset</code> operation is * supported by the file-name cursor, otherwise <code>false</code>. */ public boolean supportsReset() { return true; } }