duke@435: /* duke@435: * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. duke@435: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@435: * duke@435: * This code is free software; you can redistribute it and/or modify it duke@435: * under the terms of the GNU General Public License version 2 only, as duke@435: * published by the Free Software Foundation. duke@435: * duke@435: * This code is distributed in the hope that it will be useful, but WITHOUT duke@435: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@435: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@435: * version 2 for more details (a copy is included in the LICENSE file that duke@435: * accompanied this code). duke@435: * duke@435: * You should have received a copy of the GNU General Public License version duke@435: * 2 along with this work; if not, write to the Free Software Foundation, duke@435: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@435: * duke@435: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, duke@435: * CA 95054 USA or visit www.sun.com if you need additional information or duke@435: * have any questions. duke@435: * duke@435: */ duke@435: duke@435: /** Encapsulates a notion of a directory tree. Designed to allow fast duke@435: querying of full paths for unique filenames in the hierarchy. */ duke@435: duke@435: import java.io.*; duke@435: import java.util.*; duke@435: duke@435: public class DirectoryTree { duke@435: duke@435: /** The root of the read directoryTree */ duke@435: private Node rootNode; duke@435: duke@435: /** Subdirs to ignore; Vector of Strings */ duke@435: private Vector subdirsToIgnore; duke@435: duke@435: /** This maps file names to Lists of nodes. */ duke@435: private Hashtable nameToNodeListTable; duke@435: duke@435: /** Output "."'s as directories are read. Defaults to false. */ duke@435: private boolean verbose; duke@435: duke@435: public DirectoryTree() { duke@435: subdirsToIgnore = new Vector(); duke@435: verbose = false; duke@435: } duke@435: duke@435: /** Takes an absolute path to the root directory of this duke@435: DirectoryTree. Throws IllegalArgumentException if the given duke@435: string represents a plain file or nonexistent directory. */ duke@435: duke@435: public DirectoryTree(String baseDirectory) { duke@435: this(); duke@435: readDirectory(baseDirectory); duke@435: } duke@435: duke@435: public void addSubdirToIgnore(String subdir) { duke@435: subdirsToIgnore.add(subdir); duke@435: } duke@435: duke@435: /** Output "."'s to System.out as directories are read. Defaults duke@435: to false. */ duke@435: public void setVerbose(boolean newValue) { duke@435: verbose = newValue; duke@435: } duke@435: duke@435: public boolean getVerbose() { duke@435: return verbose; duke@435: } duke@435: duke@435: public String getRootNodeName() { duke@435: return rootNode.getName(); duke@435: } duke@435: duke@435: /** Takes an absolute path to the root directory of this duke@435: DirectoryTree. Throws IllegalArgumentException if the given duke@435: string represents a plain file or nonexistent directory. */ duke@435: duke@435: public void readDirectory(String baseDirectory) duke@435: throws IllegalArgumentException { duke@435: File root = new File(baseDirectory); duke@435: if (!root.isDirectory()) { duke@435: throw new IllegalArgumentException("baseDirectory \"" + duke@435: baseDirectory + duke@435: "\" does not exist or " + duke@435: "is not a directory"); duke@435: } duke@435: try { duke@435: root = root.getCanonicalFile(); duke@435: } duke@435: catch (IOException e) { duke@435: throw new RuntimeException(e.toString()); duke@435: } duke@435: rootNode = new Node(root); duke@435: readDirectory(rootNode, root); duke@435: } duke@435: duke@435: /** Queries the DirectoryTree for a file or directory name. Takes duke@435: only the name of the file or directory itself (i.e., no parent duke@435: directory information should be in the passed name). Returns a duke@435: List of DirectoryTreeNodes specifying the full paths of all of duke@435: the files or directories of this name in the DirectoryTree. duke@435: Returns null if the directory tree has not been read from disk duke@435: yet or if the file was not found in the tree. */ duke@435: public List findFile(String name) { duke@435: if (rootNode == null) { duke@435: return null; duke@435: } duke@435: duke@435: if (nameToNodeListTable == null) { duke@435: nameToNodeListTable = new Hashtable(); duke@435: try { duke@435: buildNameToNodeListTable(rootNode); duke@435: } catch (IOException e) { duke@435: e.printStackTrace(); duke@435: return null; duke@435: } duke@435: } duke@435: duke@435: return (List) nameToNodeListTable.get(name); duke@435: } duke@435: duke@435: private void buildNameToNodeListTable(Node curNode) duke@435: throws IOException { duke@435: String fullName = curNode.getName(); duke@435: String parent = curNode.getParent(); duke@435: String separator = System.getProperty("file.separator"); duke@435: duke@435: if (parent != null) { duke@435: if (!fullName.startsWith(parent)) { duke@435: throw new RuntimeException( duke@435: "Internal error: parent of file name \"" + fullName + duke@435: "\" does not match file name \"" + parent + "\"" duke@435: ); duke@435: } duke@435: duke@435: int len = parent.length(); duke@435: if (!parent.endsWith(separator)) { duke@435: len += separator.length(); duke@435: } duke@435: duke@435: String fileName = fullName.substring(len); duke@435: duke@435: if (fileName == null) { duke@435: throw new RuntimeException( duke@435: "Internal error: file name was empty" duke@435: ); duke@435: } duke@435: duke@435: List nodeList = (List) nameToNodeListTable.get(fileName); duke@435: if (nodeList == null) { duke@435: nodeList = new Vector(); duke@435: nameToNodeListTable.put(fileName, nodeList); duke@435: } duke@435: duke@435: nodeList.add(curNode); duke@435: } else { duke@435: if (curNode != rootNode) { duke@435: throw new RuntimeException( duke@435: "Internal error: parent of file + \"" + fullName + "\"" + duke@435: " was null" duke@435: ); duke@435: } duke@435: } duke@435: duke@435: if (curNode.isDirectory()) { duke@435: Iterator iter = curNode.getChildren(); duke@435: if (iter != null) { duke@435: while (iter.hasNext()) { duke@435: buildNameToNodeListTable((Node) iter.next()); duke@435: } duke@435: } duke@435: } duke@435: } duke@435: duke@435: /** Reads all of the files in the given directory and adds them as duke@435: children of the directory tree node. Requires that the passed duke@435: node represents a directory. */ duke@435: duke@435: private void readDirectory(Node parentNode, File parentDir) { duke@435: File[] children = parentDir.listFiles(); duke@435: if (children == null) duke@435: return; duke@435: if (verbose) { duke@435: System.out.print("."); duke@435: System.out.flush(); duke@435: } duke@435: for (int i = 0; i < children.length; i++) { duke@435: File child = children[i]; duke@435: children[i] = null; duke@435: boolean isDir = child.isDirectory(); duke@435: boolean mustSkip = false; duke@435: if (isDir) { duke@435: for (Iterator iter = subdirsToIgnore.iterator(); duke@435: iter.hasNext(); ) { duke@435: if (child.getName().equals((String) iter.next())) { duke@435: mustSkip = true; duke@435: break; duke@435: } duke@435: } duke@435: } duke@435: if (!mustSkip) { duke@435: Node childNode = new Node(child); duke@435: parentNode.addChild(childNode); duke@435: if (isDir) { duke@435: readDirectory(childNode, child); duke@435: } duke@435: } duke@435: } duke@435: } duke@435: duke@435: private class Node implements DirectoryTreeNode { duke@435: private File file; duke@435: private Vector children; duke@435: duke@435: /** file must be a canonical file */ duke@435: Node(File file) { duke@435: this.file = file; duke@435: children = new Vector(); duke@435: } duke@435: duke@435: public boolean isFile() { duke@435: return file.isFile(); duke@435: } duke@435: duke@435: public boolean isDirectory() { duke@435: return file.isDirectory(); duke@435: } duke@435: duke@435: public String getName() { duke@435: return file.getPath(); duke@435: } duke@435: duke@435: public String getParent() { duke@435: return file.getParent(); duke@435: } duke@435: duke@435: public void addChild(Node n) { duke@435: children.add(n); duke@435: } duke@435: duke@435: public Iterator getChildren() throws IllegalArgumentException { duke@435: return children.iterator(); duke@435: } duke@435: duke@435: public int getNumChildren() throws IllegalArgumentException { duke@435: return children.size(); duke@435: } duke@435: duke@435: public DirectoryTreeNode getChild(int i) duke@435: throws IllegalArgumentException, ArrayIndexOutOfBoundsException { duke@435: return (DirectoryTreeNode) children.get(i); duke@435: } duke@435: } duke@435: }