001 /* Copyright 2000, 2001, Compaq Computer Corporation */ 002 003 package javafe.filespace; 004 005 006 import java.util.Enumeration; 007 import java.io.IOException; 008 009 import javafe.genericfile.*; 010 011 012 /** 013 * A PkgTree is a filtered representation of a filespace {@link Tree} 014 * (cf {@link PathComponent}) where some files and directories that 015 * are clearly not part of the Java namespace have been filtered out; 016 * the remaining nodes can be divided into two categories: (a) 017 * (usually interior) nodes that correspond to potential Java 018 * packages, and (b) exterior nodes that correspond to files that 019 * reside in one of the potential Java packages and that have an 020 * extension (e.g., .java).<p> 021 * 022 * A function, {@link #isPackage(Tree)}, is provided to distinguish 023 * the two categories. A convenience function, {@link 024 * #packages(Tree)}, is provided to enumerate all the potential Java 025 * packages in a PkgTree.<p> 026 * 027 * {@link #isPackage(Tree)} depends only on a node's label; this 028 * ensures that a {@link UnionTree} of several PkgTree's never 029 * combines package and non-package nodes into a single node. (This 030 * is why (b) excludes files without extensions.) Accordingly, 031 * PkgTree's accessors and enumerators can also be used on {@link 032 * UnionTree}s of PkgTrees.<p> 033 * 034 * This module is meant to do a reasonable job of identifying potential 035 * packages. It is not 100% accurate, however, erring on the side of 036 * admitting too many packages. For example, it does not attempt to 037 * disallow package names containing Java keywords.<p> 038 */ 039 040 public class PkgTree extends PreloadedTree { 041 042 /*************************************************** 043 * * 044 * Creation: * 045 * * 046 **************************************************/ 047 048 049 /** The non-null filespace Tree we are filtering */ 050 //@ invariant underlyingTree != null; 051 protected Tree underlyingTree; 052 053 054 /** 055 * Filter a non-null filespace Tree, leaving potential Java 056 * packages and files. 057 */ 058 //@ requires underlyingTree != null; 059 public PkgTree(Tree underlyingTree) { 060 super(underlyingTree.data); 061 this.underlyingTree = underlyingTree; 062 } 063 064 /** 065 * Create a non-root node. underlyingTree must be a non-null 066 * filespace Tree. 067 */ 068 //@ requires underlyingTree != null; 069 //@ requires parent != null && label != null; 070 protected PkgTree(Tree parent, String label, Tree underlyingTree) { 071 super(parent, label, underlyingTree.data); 072 this.underlyingTree = underlyingTree; 073 } 074 075 076 /*************************************************** 077 * * 078 * Deciding what nodes to filter out: * 079 * * 080 **************************************************/ 081 082 /* 083 * Status codes returned by getStatus: 084 */ 085 086 /** ignore the node and its children */ 087 protected static final int IGNORE = 0; 088 089 /** include the node but not its children */ 090 protected static final int INCLUDE_NODE = 1; 091 092 /** include the node and its children */ 093 protected static final int INCLUDE_TREE = 2; 094 095 096 /** 097 * Decide what to do with a node of the underlying filespace, returning 098 * one of the following codes: IGNORE, INCLUDE_NODE, or INCLUDE_TREE. 099 */ 100 protected static int getStatus(/*@ non_null @*/ Tree node) { 101 String label = node.getSimpleName(); 102 String extension = Extension.getExtension(label); 103 104 /* 105 * Ignore all files beginning with "META-" (used by the .jar 106 * format for meta-data): 107 */ 108 if (label.startsWith("META-")) 109 return IGNORE; 110 111 /* 112 * For now, potential packages are directories without 113 * extensions: 114 */ 115 if (((GenericFile)node.data).isDirectory() //@ nowarn Cast,Null; 116 && extension.equals("")) 117 return INCLUDE_TREE; 118 119 /* Directories in jars do not appear as directories */ 120 if ((node.data instanceof ZipGenericFile) 121 && extension.equals("")) 122 return INCLUDE_TREE; 123 124 /* 125 * Non-package files include only those with extensions. 126 * 127 * Note that directories with extensions are treated here as 128 * if they were ordinary files (i.e., ignore their children 129 * and treat them as non-packages). This is necessary to 130 * minic javac's behavior and to allow the checker to complain 131 * about such files. 132 */ 133 if (extension.equals("")) 134 return IGNORE; 135 else 136 return INCLUDE_NODE; 137 } 138 139 /*************************************************** 140 * * 141 * Loading the edges map: * 142 * * 143 **************************************************/ 144 145 /** Load the edges map for use. */ 146 protected void loadEdges() { 147 if (getStatus(underlyingTree) != INCLUDE_TREE) 148 return; // We are ignoring this tree or its children 149 150 for (Enumeration E=underlyingTree.children(); E.hasMoreElements(); ) { 151 Tree child = (Tree)E.nextElement(); 152 if (getStatus(child) != IGNORE) { 153 String label = child.getLabel(); 154 //@ assume label != null; 155 edges.put(label, new PkgTree(this, label, child)); 156 } 157 } 158 } 159 160 161 /*************************************************** 162 * * 163 * Accessors: * 164 * * 165 **************************************************/ 166 167 /** 168 * Is a node of a PkgTree (or a union of PkgTree's) a potential 169 * Java package? 170 */ 171 public static boolean isPackage(/*@ non_null @*/ Tree node) { 172 return Extension.getExtension(node.getSimpleName()).equals(""); 173 } 174 175 /** The name to use for root packages */ 176 public static String rootPackageName = "<the unnamed package>"; 177 178 /** 179 * Return the human-readable name of a package. Uses rootPackageName 180 * as the name of root packages.<p> 181 * 182 * Note: the resulting name will only make sense if node is a 183 * package.<p> 184 */ 185 public static String getPackageName(/*@ non_null @*/ Tree node) { 186 if (node.getParent() == null) 187 return rootPackageName; 188 189 return node.getQualifiedName("."); 190 } 191 192 193 /*************************************************** 194 * * 195 * Enumerators: * 196 * * 197 **************************************************/ 198 199 /** 200 * Enumerate all the potential packages of a PkgTree (or a union of 201 * PkgTree's) in depth-first pre-order using lexical ordering on 202 * siblings (cf. TreeWalker). 203 */ 204 //@ ensures !\result.returnsNull; 205 //@ ensures \result.elementType == \type(Tree); 206 public static /*@ non_null @*/ Enumeration packages(/*@ non_null @*/ Tree node) { 207 Enumeration allNodes = new TreeWalker(node); 208 209 return new FilterEnum(allNodes, new PkgTree_PackagesOnly()); 210 } 211 212 /** 213 * Enumerate all the components of package P with extension E in 214 * sorted order (of labels).<p> 215 * 216 * For a PkgTree (or a union of PkgTrees), if E is "", then all 217 * direct potential subpackages will be selected. Otherwise, only 218 * non-subpackages will be selected.<p> 219 */ 220 //@ ensures !\result.returnsNull; 221 //@ ensures \result.elementType == \type(Tree); 222 public static /*@ non_null @*/ Enumeration components(/*@ non_null @*/ Tree P, 223 /*@ non_null @*/ String E) { 224 Enumeration allComponents = TreeWalker.sortedChildren(P); 225 226 return new FilterEnum(allComponents, 227 new PkgTree_MatchesExtension(E)); 228 } 229 230 231 /*************************************************** 232 * * 233 * Debugging functions: * 234 * * 235 **************************************************/ 236 237 /** Extend printDetails to include our isPackage status */ 238 public void printDetails(String prefix) { 239 super.printDetails(prefix); 240 if (isPackage(this)) 241 System.out.print(" [P]"); 242 } 243 244 /** A simple test driver */ 245 //@ requires (\forall int i; (0 <= i && i < args.length) ==> args[i] != null); 246 public static void main(/*@ non_null @*/ String[] args) throws IOException { 247 if (args.length != 1 && args.length != 3) { 248 System.out.println("PkgTree: usage <path component>" 249 + " [<package name> <extension>]"); 250 return; 251 } 252 253 /* 254 * Convert the path component to a filespace then filter it via 255 * PkgTree. 256 */ 257 Tree T = PathComponent.open(args[0], false); 258 T = new PkgTree(T); 259 260 // If a package name is provided, list all its components with 261 // the given extension: 262 if (args.length==3) { 263 Tree P = T.getQualifiedChild(args[1], '.'); 264 if (P==null) { 265 System.out.println("No such package: " + args[1]); 266 return; 267 }; 268 269 System.out.println(getPackageName(P) + ":"); 270 Enumeration E = components(P, args[2]); 271 while (E.hasMoreElements()) { 272 Tree next = (Tree)E.nextElement(); 273 System.out.println(next.getSimpleName()); 274 } 275 return; 276 }; 277 278 T.print(""); 279 System.out.println(); 280 281 // Enumerate all the potential packages also to test packages(): 282 Enumeration E = packages(T); 283 while (E.hasMoreElements()) { 284 Tree next = (Tree)E.nextElement(); 285 System.out.println(getPackageName(next)); 286 } 287 } 288 } 289 290 291 /** 292 * A filter for accepting only packages: 293 * 294 * This filter is for the use of the PkgTree class only; if inner 295 * classes were available, it would be expressed as an anonymous class. 296 */ 297 class PkgTree_PackagesOnly implements Filter { 298 //@ invariant acceptedType == \type(Tree); 299 300 public PkgTree_PackagesOnly() { 301 //@ set acceptedType = \type(Tree); 302 } 303 304 public boolean accept(Object node) { 305 return PkgTree.isPackage((Tree)node); 306 } 307 } 308 309 310 /** 311 * A filter for accepting only node's with a particular extension: 312 * 313 * This filter is for the use of the PkgTree class only; if inner 314 * classes were available, it would be expressed as an anonymous class. 315 */ 316 class PkgTree_MatchesExtension implements Filter { 317 318 //@ invariant acceptedType == \type(Tree); 319 320 //@ invariant targetExtension != null; 321 String targetExtension; 322 323 //@ requires targetExtension != null; 324 PkgTree_MatchesExtension(String targetExtension) { 325 this.targetExtension = targetExtension; 326 //@ set acceptedType = \type(Tree); 327 } 328 329 public boolean accept(Object node) { 330 return Extension.hasExtension( 331 ((Tree)node).getSimpleName(), targetExtension); 332 } 333 334 }