1 /* 2 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 /** Encapsulates a notion of a directory tree. Designed to allow fast 26 querying of full paths for unique filenames in the hierarchy. */ 27 28 import java.io.*; 29 import java.util.*; 30 31 public class DirectoryTree { 32 33 /** The root of the read directoryTree */ 34 private Node rootNode; 35 36 /** Subdirs to ignore; Vector of Strings */ 37 private Vector subdirsToIgnore; 38 39 /** This maps file names to Lists of nodes. */ 40 private Hashtable nameToNodeListTable; 41 42 /** Output "."'s as directories are read. Defaults to false. */ 43 private boolean verbose; 44 45 public DirectoryTree() { 46 subdirsToIgnore = new Vector(); 47 verbose = false; 48 } 49 50 public void addSubdirToIgnore(String subdir) { 51 subdirsToIgnore.add(subdir); 52 } 53 54 private class FileIterator implements Iterator { 55 private Vector nodes = new Vector(); 56 57 public FileIterator(Node rootNode) { 58 nodes.add(rootNode); 59 prune(); 60 } 61 public boolean hasNext() { 62 return nodes.size() > 0; 63 } 64 public Object next() { 65 Node last = (Node)nodes.remove(nodes.size() - 1); 66 prune(); 67 return new File(last.getName()); 68 } 69 70 public void remove() { 71 throw new RuntimeException(); 72 } 73 74 private void prune() { 75 while (nodes.size() > 0) { 76 Node last = (Node)nodes.get(nodes.size() - 1); 77 78 if (last.isDirectory()) { 79 nodes.remove(nodes.size() - 1); 80 nodes.addAll(last.children); 81 } else { 82 // Is at file 83 return; 84 } 85 } 86 } 87 } 88 89 public Iterator getFileIterator() { 90 return new FileIterator(rootNode); 91 } 92 93 /** Output "."'s to System.out as directories are read. Defaults 94 to false. */ 95 public void setVerbose(boolean newValue) { 96 verbose = newValue; 97 } 98 99 public boolean getVerbose() { 100 return verbose; 101 } 102 103 public String getRootNodeName() { 104 return rootNode.getName(); 105 } 106 107 /** Takes an absolute path to the root directory of this 108 DirectoryTree. Throws IllegalArgumentException if the given 109 string represents a plain file or nonexistent directory. */ 110 111 public void readDirectory(String baseDirectory) 112 throws IllegalArgumentException { 113 File root = new File(Util.normalize(baseDirectory)); 114 if (!root.isDirectory()) { 115 throw new IllegalArgumentException("baseDirectory \"" + 116 baseDirectory + 117 "\" does not exist or " + 118 "is not a directory"); 119 } 120 try { 121 root = root.getCanonicalFile(); 122 } 123 catch (IOException e) { 124 throw new RuntimeException(e.toString()); 125 } 126 rootNode = new Node(root); 127 readDirectory(rootNode, root); 128 } 129 130 /** Queries the DirectoryTree for a file or directory name. Takes 131 only the name of the file or directory itself (i.e., no parent 132 directory information should be in the passed name). Returns a 133 List of DirectoryTreeNodes specifying the full paths of all of 134 the files or directories of this name in the DirectoryTree. 135 Returns null if the directory tree has not been read from disk 136 yet or if the file was not found in the tree. */ 137 public List findFile(String name) { 138 if (rootNode == null) { 139 return null; 140 } 141 142 if (nameToNodeListTable == null) { 143 nameToNodeListTable = new Hashtable(); 144 try { 145 buildNameToNodeListTable(rootNode); 146 } catch (IOException e) { 147 e.printStackTrace(); 148 return null; 149 } 150 } 151 152 return (List) nameToNodeListTable.get(name); 153 } 154 155 private void buildNameToNodeListTable(Node curNode) 156 throws IOException { 157 String fullName = curNode.getName(); 158 String parent = curNode.getParent(); 159 String separator = System.getProperty("file.separator"); 160 161 if (parent != null) { 162 if (!fullName.startsWith(parent)) { 163 throw new RuntimeException( 164 "Internal error: parent of file name \"" + fullName + 165 "\" does not match file name \"" + parent + "\"" 166 ); 167 } 168 169 int len = parent.length(); 170 if (!parent.endsWith(separator)) { 171 len += separator.length(); 172 } 173 174 String fileName = fullName.substring(len); 175 176 if (fileName == null) { 177 throw new RuntimeException( 178 "Internal error: file name was empty" 179 ); 180 } 181 182 List nodeList = (List) nameToNodeListTable.get(fileName); 183 if (nodeList == null) { 184 nodeList = new Vector(); 185 nameToNodeListTable.put(fileName, nodeList); 186 } 187 188 nodeList.add(curNode); 189 } else { 190 if (curNode != rootNode) { 191 throw new RuntimeException( 192 "Internal error: parent of file + \"" + fullName + "\"" + 193 " was null" 194 ); 195 } 196 } 197 198 if (curNode.isDirectory()) { 199 Iterator iter = curNode.getChildren(); 200 if (iter != null) { 201 while (iter.hasNext()) { 202 buildNameToNodeListTable((Node) iter.next()); 203 } 204 } 205 } 206 } 207 208 /** Reads all of the files in the given directory and adds them as 209 children of the directory tree node. Requires that the passed 210 node represents a directory. */ 211 212 private void readDirectory(Node parentNode, File parentDir) { 213 File[] children = parentDir.listFiles(); 214 if (children == null) 215 return; 216 if (verbose) { 217 System.out.print("."); 218 System.out.flush(); 219 } 220 for (int i = 0; i < children.length; i++) { 221 File child = children[i]; 222 children[i] = null; 223 boolean isDir = child.isDirectory(); 224 boolean mustSkip = false; 225 if (isDir) { 226 for (Iterator iter = subdirsToIgnore.iterator(); 227 iter.hasNext(); ) { 228 if (child.getName().equals((String) iter.next())) { 229 mustSkip = true; 230 break; 231 } 232 } 233 } 234 if (!mustSkip) { 235 Node childNode = new Node(child); 236 parentNode.addChild(childNode); 237 if (isDir) { 238 readDirectory(childNode, child); 239 } 240 } 241 } 242 } 243 244 private class Node implements DirectoryTreeNode { 245 private File file; 246 private Vector children; 247 248 /** file must be a canonical file */ 249 Node(File file) { 250 this.file = file; 251 children = new Vector(); 252 } 253 254 public boolean isFile() { 255 return file.isFile(); 256 } 257 258 public boolean isDirectory() { 259 return file.isDirectory(); 260 } 261 262 public String getName() { 263 return file.getPath(); 264 } 265 266 public String getParent() { 267 return file.getParent(); 268 } 269 270 public void addChild(Node n) { 271 children.add(n); 272 } 273 274 public Iterator getChildren() throws IllegalArgumentException { 275 return children.iterator(); 276 } 277 278 public int getNumChildren() throws IllegalArgumentException { 279 return children.size(); 280 } 281 282 public DirectoryTreeNode getChild(int i) 283 throws IllegalArgumentException, ArrayIndexOutOfBoundsException { 284 return (DirectoryTreeNode) children.get(i); 285 } 286 } 287 }