/** * Provides the set of all concrete classes that subclass/implement a given class/interface. * * @param s The name of a class or interface. * @return The set of all concrete classes that subclass/implement (directly or transitively) the * class/interface named s, if it exists in the class hierarchy, and null otherwise. */ public Set<String> getConcreteSubclasses(final String s) { if (clintToAllConcreteSubs == null) { if (clintToKind == null) build(); missingClints = new HashSet<String>(); Set<String> missingSuperclasses = new HashSet<String>(); Set<String> missingSuperInterfs = new HashSet<String>(); // Map from each concrete class in scope to set containing // itself and its (direct and transitive) superclasses and // its implemented interfaces (not necessarily in scope). Map<String, Set<String>> concreteClassToAllSups = new HashMap<String, Set<String>>(); clintToAllConcreteSubs = new HashMap<String, Set<String>>(); Set<String> emptyRdOnlySet = Collections.emptySet(); for (String c : clintToKind.keySet()) { clintToAllConcreteSubs.put(c, emptyRdOnlySet); if (clintToKind.get(c) != TypeKind.CONCRETE_CLASS) continue; Set<String> clints = new ArraySet<String>(2); clints.add(c); // every concrete class is a concrete successor to itself boolean success1 = true; boolean success2 = true; String d = c; while (true) { success2 &= populateInterfaces(d, clints); String superClass = classToDeclaredSuperclass.get(d); if (superClass == null) { if (!d.equals("java.lang.Object")) { missingClints.add(d); success1 = false; } break; } boolean added = clints.add(superClass); assert (added); d = superClass; } if (success1 && success2) { concreteClassToAllSups.put(c, clints); continue; } if (!success1) missingSuperclasses.add(c); if (!success2) missingSuperInterfs.add(c); } if (!missingClints.isEmpty()) { Messages.log(MISSING_TYPES); for (String c : missingClints) Messages.log("\t" + c); } if (!missingSuperclasses.isEmpty()) { Messages.log(MISSING_SUPERCLASSES); for (String c : missingSuperclasses) Messages.log("\t" + c); } if (!missingSuperInterfs.isEmpty()) { Messages.log(MISSING_SUPERINTERFS); for (String c : missingSuperInterfs) Messages.log("\t" + c); } for (String c : concreteClassToAllSups.keySet()) { Set<String> sups = concreteClassToAllSups.get(c); for (String d : sups) { Set<String> subs = clintToAllConcreteSubs.get(d); if (subs == null || subs == emptyRdOnlySet) { subs = new ArraySet<String>(2); clintToAllConcreteSubs.put(d, subs); } subs.add(c); } } missingClints.clear(); } return clintToAllConcreteSubs.get(s); }
// builds maps clintToKind, classToDeclaredSuperclass, and clintToDeclaredInterfaces private void build() { System.out.println("Starting to build class hierarchy; this may take a while ..."); Set<String> dynLoadedTypes = null; if (Config.CHkind.equals("dynamic")) { List<String> list = Program.g().getDynamicallyLoadedClasses(); dynLoadedTypes = new HashSet<String>(list.size()); dynLoadedTypes.addAll(list); } Classpath cp = new Classpath(); cp.addToClasspath(System.getProperty("sun.boot.class.path")); cp.addExtClasspath(); cp.addToClasspath(Config.userClassPathName); List<ClasspathElement> cpeList = cp.getClasspathElements(); // logging info List<Pair<String, String>> duplicateTypes = new ArrayList<Pair<String, String>>(); List<String> excludedTypes = new ArrayList<String>(); List<String> typesNotDynLoaded = new ArrayList<String>(); clintToKind = new HashMap<String, TypeKind>(); classToDeclaredSuperclass = new HashMap<String, String>(); clintToDeclaredInterfaces = new HashMap<String, Set<String>>(); for (ClasspathElement cpe : cpeList) { for (String fileName : cpe.getEntries()) { if (!fileName.endsWith(".class")) continue; String baseName = fileName.substring(0, fileName.length() - 6); String typeName = baseName.replace('/', '.'); // ignore types excluded from scope if (Config.isExcludedFromScope(typeName)) { excludedTypes.add(typeName); continue; } // ignore duplicate types in classpath if (clintToKind.containsKey(typeName)) { duplicateTypes.add(new Pair<String, String>(typeName, cpe.toString())); continue; } if (dynLoadedTypes != null && !dynLoadedTypes.contains(typeName)) { typesNotDynLoaded.add(typeName); continue; } InputStream is = cpe.getResourceAsStream(fileName); assert (is != null); DataInputStream in = new DataInputStream(is); if (Config.verbose >= 2) Messages.log("Processing class file %s from %s", fileName, cpe); processClassFile(in, typeName); } } if (Config.verbose >= 2) { if (!duplicateTypes.isEmpty()) { Messages.log(IGNORED_DUPLICATE_TYPES); for (Pair<String, String> p : duplicateTypes) Messages.log("\t%s, %s", p.val0, p.val1); } if (!excludedTypes.isEmpty()) { Messages.log(EXCLUDED_TYPES_IN_CHORD); for (String s : excludedTypes) Messages.log("\t" + s); } if (!typesNotDynLoaded.isEmpty()) { Messages.log(EXCLUDED_TYPES_NOT_DYN_LOADED); for (String s : typesNotDynLoaded) Messages.log("\t" + s); } } System.out.println("Finished building class hierarchy."); }