/*
 * FindBugs - Find bugs in Java programs
 * Copyright (C) 2003,2004 University of Maryland
 * 
 * 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 2.1 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.umd.cs.findbugs;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 * Recursively search a directory, its subdirectories, etc.
 * Note that the search algorithm uses a worklist, so its implementation does
 * not use recursive method calls.
 *
 * @author David Hovemeyer
 */
public class RecursiveFileSearch {
	private String baseDir;
	private FileFilter fileFilter;
	private LinkedList<File> directoryWorkList;
	private HashSet<String> directoriesScanned = new HashSet<String>();
	private List<String> directoriesScannedList = new LinkedList<String>();
	private ArrayList<String> resultList;

	/**
	 * Constructor.
	 *
	 * @param baseDir    the base directory for the search
	 * @param fileFilter chooses files to add to the results, and subdirectories
	 *                   to continue the search in
	 */
	public RecursiveFileSearch(String baseDir, FileFilter fileFilter) {
		this.baseDir = baseDir;
		this.fileFilter = fileFilter;
		this.directoryWorkList = new LinkedList<File>();
		this.resultList = new ArrayList<String>();
	}

	static String bestEffortCanonicalPath(File f) {
		try {
			return f.getCanonicalPath();
		} catch (IOException e) {
			return f.getAbsolutePath();
		}
	}
	/**
	 * Perform the search.
	 *
	 * @return this object
	 * @throws InterruptedException if the thread is interrupted before the
	 *                              search completes
	 */
	public RecursiveFileSearch search() throws InterruptedException {
		File baseFile = new File(baseDir);
		String basePath = bestEffortCanonicalPath(baseFile);
		directoryWorkList.add(baseFile);
		directoriesScanned.add(basePath);
		directoriesScannedList.add(basePath);

		while (!directoryWorkList.isEmpty()) {
			File dir = directoryWorkList.removeFirst();
			if (!dir.isDirectory())
				continue;

			File[] contentList = dir.listFiles();
			if (contentList == null) continue;
			for (File aContentList : contentList) {
				if (Thread.interrupted())
					throw new InterruptedException();

				File file = aContentList;

				if (!fileFilter.accept(file)) {
					continue;
				}
				
				if (file.isDirectory()) {
					String myPath = bestEffortCanonicalPath(file);
					if (myPath.startsWith(basePath) && directoriesScanned.add(myPath)) {
						directoriesScannedList.add(myPath);
						directoryWorkList.add(file);
					}
				} else {
					resultList.add(file.getPath());
				}
			}
		}

		return this;
	}

	/**
	 * Get an iterator over the files found by the search.
	 * The full path names of the files are returned.
	 */
	public Iterator<String> fileNameIterator() {
		return resultList.iterator();
	}
	
	/*
	 * Get List of all directories scanned.
	 */
	public List<String> getDirectoriesScanned() {
		return Collections.unmodifiableList(directoriesScannedList);
	}

}

// vim:ts=4
