make/tools/classanalyzer/src/com/sun/classanalyzer/ClassPaths.java
Print this page
@@ -17,260 +17,472 @@
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
- *
*/
package com.sun.classanalyzer;
+import com.sun.classanalyzer.ClassPaths.*;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
+ * Legacy class path. Each entry can be a directory containing
+ * classes and resources, or a jar file. It supports classpath
+ * wildcard "*" that lists all jar files in the given directory
+ * and also recursive wildcard "**" that lists all jar files
+ * recursively in the given directory and its subdirectories.
*
- * @author mchung
+ * @author Mandy Chung
*/
-public class ClassPath {
+public class ClassPaths {
- public class FileInfo {
+ protected final List<ClassPath> entries = new LinkedList<ClassPath>();
+ private final Set<Klass> classes = new LinkedHashSet<Klass>();
+ private final Set<ResourceFile> resources = new LinkedHashSet<ResourceFile>();
- File file;
- JarFile jarfile;
- int classCount;
- long filesize;
+ private ClassPaths() {
+ }
- FileInfo(File f) throws IOException {
- this.file = f;
- this.classCount = 0;
- if (file.getName().endsWith(".jar")) {
- this.filesize = file.length();
- jarfile = new JarFile(f);
+ public ClassPaths(String... paths) throws IOException {
+ for (String p : paths) {
+ String cp = p;
+ int index = p.indexOf("*");
+ String wildcard = "";
+ if (index >= 0) {
+ cp = p.substring(0, index);
+ wildcard = p.substring(index, p.length());
}
+
+ File f = new File(cp);
+ if (!f.exists()) {
+ throw new RuntimeException("\"" + f + "\" doesn't exist");
}
+ if (wildcard.isEmpty()) {
+ if (f.isDirectory()) {
+ entries.add(new DirClassPath(f));
+ } else if (cp.endsWith(".jar")) {
+ entries.add(new JarFileClassPath(f));
+ } else {
+ entries.add(new ClassPath(f));
+ }
+ } else {
+ if (wildcard.equals("*")) {
+ // add jar files in the specified directory
+ String[] ls = Files.list(f);
+ for (String s : ls) {
+ File sf = new File(f, s);
+ if (s.endsWith(".jar")) {
+ entries.add(new JarFileClassPath(f));
+ }
+ }
+ } else if (wildcard.equals("**")) {
+ // add all jar files in all directories under f
+ addJarFileEntries(f);
+ }
+ }
+ }
+ }
- File getFile() {
- return file;
+ public List<ClassPath> entries() {
+ return Collections.unmodifiableList(entries);
}
- JarFile getJarFile() {
- return jarfile;
+ /**
+ * Returns the modules containing the classes and resources being
+ * processed.
+ */
+ public Set<Module> getModules() {
+ Set<Module> modules = new LinkedHashSet<Module>();
+ for (Klass k : classes) {
+ modules.add(k.getModule().group());
}
+ for (ResourceFile r : resources) {
+ modules.add(r.getModule().group());
+ }
+ return modules;
+ }
- String getName() throws IOException {
- return file.getCanonicalPath();
+ public void parse() throws IOException {
+ parse(null, true, false);
}
+
+ public void parse(boolean deps, boolean apiOnly) throws IOException {
+ parse(null, deps, apiOnly);
}
- private List<FileInfo> fileList = new ArrayList<FileInfo>();
- private static ClassPath instance = new ClassPath();
- static List<FileInfo> getFileInfos() {
- return instance.fileList;
+ public void parse(Filter filter, boolean deps, boolean apiOnly) throws IOException {
+ ClassResourceVisitor crv = new ClassResourceVisitor(classes, resources, deps, apiOnly);
+ ClassPathVisitor cpvisitor = new ClassPathVisitor(crv, filter);
+ visit(cpvisitor, filter, null);
}
- static ClassPath setJDKHome(String jdkhome) throws IOException {
+ public void printStats() {
+ System.out.format("%d classes %d resource files processed%n",
+ classes.size(), resources.size());
+ }
+
+ protected void addJarFileEntries(File f) throws IOException {
+ List<File> ls;
+ if (f.isDirectory()) {
+ ls = Files.walkTree(f, new Files.Filter<File>() {
+ @Override
+ public boolean accept(File f) throws IOException {
+ return f.isDirectory() || f.getName().endsWith(".jar");
+ }
+ });
+ } else {
+ ls = Collections.singletonList(f);
+ }
+ for (File jf : ls) {
+ entries.add(new JarFileClassPath(jf));
+ }
+ }
+ // FIXME - used by open() method
+ static ClassPaths instance = null;
+
+ static ClassPaths newInstance(String cpaths) throws IOException {
+ String[] paths = cpaths.split(File.pathSeparator);
+ instance = new ClassPaths(paths);
+ return instance;
+ }
+
+ static ClassPaths newJDKClassPaths(String jdkhome) throws IOException {
+ instance = new JDKClassPaths(jdkhome);
+ return instance;
+ }
+
+ static class JDKClassPaths extends ClassPaths {
+ JDKClassPaths(String jdkhome) throws IOException {
+ super();
List<File> files = new ArrayList<File>();
File jre = new File(jdkhome, "jre");
File lib = new File(jdkhome, "lib");
+
if (jre.exists() && jre.isDirectory()) {
- listFiles(new File(jre, "lib"), ".jar", files);
- listFiles(lib, ".jar", files);
+ addJarFiles(new File(jre, "lib"));
+ addJarFiles(lib);
} else if (lib.exists() && lib.isDirectory()) {
// either a JRE or a jdk build image
- listFiles(lib, ".jar", files);
-
File classes = new File(jdkhome, "classes");
if (classes.exists() && classes.isDirectory()) {
// jdk build outputdir
- instance.add(classes);
+ this.entries.add(new DirClassPath(classes));
}
+ addJarFiles(lib);
} else {
throw new RuntimeException("\"" + jdkhome + "\" not a JDK home");
}
+ }
- for (File f : files) {
- instance.add(f);
+ // Filter the jigsaw module library, if any
+ final void addJarFiles(File dir) throws IOException {
+ String[] ls = dir.list();
+ for (String fn : ls) {
+ File f = new File(dir, fn);
+ if (f.isDirectory() && !fn.equals("modules")) {
+ addJarFileEntries(f);
+ } else if (f.isFile() && fn.endsWith(".jar")) {
+ addJarFileEntries(f);
}
- return instance;
}
+ }
+ }
- static ClassPath setClassPath(String path) throws IOException {
- if (path.endsWith(".class")) {
- // one class file
- File f = new File(path);
- if (!f.exists()) {
- throw new RuntimeException("Classfile \"" + f + "\" doesn't exist");
+ /**
+ * Visits all entries in the class path.
+ */
+ public <R, P> List<R> visit(final ClassPath.Visitor<R, P> visitor,
+ final Filter filter, P p)
+ throws IOException {
+ List<R> result = new ArrayList<R>();
+ for (ClassPath cp : entries) {
+ if (filter != null && !filter.accept(cp.file)) {
+ continue;
}
+ R r = cp.accept(visitor, p);
+ result.add(r);
+ }
+ return result;
+ }
- instance.add(f);
- } else {
- List<File> jarFiles = new ArrayList<File>();
- String[] locs = path.split(File.pathSeparator);
- for (String p : locs) {
- File f = new File(p);
- if (!f.exists()) {
- throw new RuntimeException("\"" + f + "\" doesn't exist");
+ public static interface FileVisitor {
+ public void visitClass(File f, String cn) throws IOException;
+ public void visitClass(JarFile jf, JarEntry e) throws IOException;
+ public void visitResource(File f, String rn) throws IOException;
+ public void visitResource(JarFile jf, JarEntry e) throws IOException;
}
- if (f.isDirectory()) {
- instance.add(f); // add the directory to look up .class files
- listFiles(f, ".jar", jarFiles);
- } else if (p.endsWith(".jar")) {
- // jar files
- jarFiles.add(f);
- } else {
- throw new RuntimeException("Invalid file \"" + f);
+ public static interface Filter {
+ // any file, jar file, directory
+ public boolean accept(File f) throws IOException;
+ public boolean accept(JarFile jf, JarEntry e) throws IOException;
}
+
+ public static class ClassPath {
+
+ private final File file;
+ private final String name;
+
+ ClassPath(File f) throws IOException {
+ this.file = f;
+ this.name = file.getCanonicalPath();
}
- // add jarFiles if any
- for (File f : jarFiles) {
- instance.add(f);
+
+ File getFile() {
+ return file;
}
+
+ String getName() {
+ return name;
}
- return instance;
+ <R, P> R accept(Visitor<R, P> visitor, P p) throws IOException {
+ return visitor.visitFile(file, this, p);
}
- private void add(File f) throws IOException {
- fileList.add(new FileInfo(f));
+ public interface Visitor<R, P> {
+ public R visitFile(File f, ClassPath cp, P p) throws IOException;
+ public R visitDir(File dir, ClassPath cp, P p) throws IOException;
+ public R visitJarFile(JarFile jf, ClassPath cp, P p) throws IOException;
}
+ }
- public static InputStream open(String pathname) throws IOException {
- for (FileInfo fi : instance.fileList) {
- if (fi.getName().endsWith(".jar")) {
- String path = pathname.replace(File.separatorChar, '/');
- JarEntry e = fi.jarfile.getJarEntry(path);
- if (e != null) {
- return fi.jarfile.getInputStream(e);
+ static class JarFileClassPath extends ClassPath {
+ JarFile jarfile;
+ JarFileClassPath(File f) throws IOException {
+ super(f);
+ this.jarfile = new JarFile(f);
}
- } else if (fi.getFile().isDirectory()) {
- File f = new File(fi.getFile(), pathname);
- if (f.exists()) {
- return new FileInputStream(f);
+
+ @Override
+ <R, P> R accept(Visitor<R, P> visitor, P p) throws IOException {
+ return visitor.visitJarFile(jarfile, this, p);
}
- } else if (fi.file.isFile()) {
- if (fi.getName().endsWith(File.separator + pathname)) {
- return new FileInputStream(fi.file);
}
+
+ class DirClassPath extends ClassPath {
+
+ DirClassPath(File f) throws IOException {
+ super(f);
}
+
+ @Override
+ <R, P> R accept(final Visitor<R, P> visitor, P p) throws IOException {
+ return visitor.visitDir(getFile(), this, p);
}
- return null;
}
- static ClassFileParser parserForClass(String classname) throws IOException {
- String pathname = classname.replace('.', File.separatorChar) + ".class";
+ static class ClassResourceVisitor implements FileVisitor {
- ClassFileParser cfparser = null;
- for (FileInfo fi : instance.fileList) {
- if (fi.getName().endsWith(".class")) {
- if (fi.getName().endsWith(File.separator + pathname)) {
- cfparser = ClassFileParser.newParser(fi.getFile(), true);
- break;
+ private final Set<Klass> classes;
+ private final Set<ResourceFile> resources;
+ private final boolean parseDeps;
+ private final boolean apiOnly;
+
+ ClassResourceVisitor(Set<Klass> classes,
+ Set<ResourceFile> resources,
+ boolean parseDeps,
+ boolean apiOnly) {
+ this.classes = classes;
+ this.resources = resources;
+ this.apiOnly = apiOnly;
+ this.parseDeps = parseDeps;
}
- } else if (fi.getName().endsWith(".jar")) {
- JarEntry e = fi.jarfile.getJarEntry(classname.replace('.', '/') + ".class");
- if (e != null) {
- cfparser = ClassFileParser.newParser(fi.jarfile.getInputStream(e), e.getSize(), true);
- break;
+
+ @Override
+ public void visitClass(File f, String cn) throws IOException {
+ ClassFileParser cfparser = ClassFileParser.newParser(f, true);
+ classes.add(cfparser.this_klass);
+ if (parseDeps) {
+ cfparser.parseDependency(apiOnly);
}
- } else if (fi.getFile().isDirectory()) {
- File f = new File(fi.getFile(), pathname);
- if (f.exists()) {
- cfparser = ClassFileParser.newParser(f, true);
- break;
}
+
+ @Override
+ public void visitClass(JarFile jf, JarEntry e) throws IOException {
+ ClassFileParser cfparser = ClassFileParser.newParser(jf.getInputStream(e), e.getSize(), true);
+ classes.add(cfparser.this_klass);
+ if (parseDeps) {
+ cfparser.parseDependency(apiOnly);
}
}
- return cfparser;
+
+ @Override
+ public void visitResource(File f, String rn) throws IOException {
+ BufferedInputStream in = new BufferedInputStream(new FileInputStream(f));
+ try {
+ ResourceFile res = ResourceFile.addResource(rn, in);
+ resources.add(res);
+ } finally {
+ in.close();
}
+ }
- public static void parseAllClassFiles() throws IOException {
- instance.parseFiles();
+ @Override
+ public void visitResource(JarFile jf, JarEntry e) throws IOException {
+ ResourceFile res = ResourceFile.addResource(e.getName(), jf.getInputStream(e));
+ resources.add(res);
}
+ }
- private void parseFiles() throws IOException {
- Set<Klass> classes = new HashSet<Klass>();
+ static class ClassPathVisitor implements ClassPath.Visitor<Void, Void> {
- int count = 0;
- for (FileInfo fi : fileList) {
- // filter out public generated classes (i.e. not public API)
- // javax.management.remote.rmi._RMIConnectionImpl_Tie
- // javax.management.remote.rmi._RMIServerImpl_Tie
- if (fi.getName().endsWith(".class")) {
- parseClass(fi);
- } else if (fi.getName().endsWith(".jar")) {
- Enumeration<JarEntry> entries = fi.jarfile.entries();
- while (entries.hasMoreElements()) {
- JarEntry e = entries.nextElement();
- if (e.getName().endsWith(".class")) {
- ClassFileParser cfparser = ClassFileParser.newParser(fi.jarfile.getInputStream(e), e.getSize(), true);
- cfparser.parseDependency(false);
- fi.classCount++;
- } else if (!e.isDirectory() && ResourceFile.isResource(e.getName())) {
- ResourceFile.addResource(e.getName(), fi.jarfile.getInputStream(e));
+ private final FileVisitor visitor;
+ private final Filter filter;
+
+ ClassPathVisitor(FileVisitor fv, Filter filter) {
+ this.visitor = fv;
+ this.filter = filter;
}
+
+ @Override
+ public Void visitFile(File f, ClassPath cp, Void v) throws IOException {
+ if (filter != null && !filter.accept(f)) {
+ return null;
}
- } else if (fi.getFile().isDirectory()) {
- List<File> files = new ArrayList<File>();
- listFiles(fi.getFile(), "", files);
- for (File f : files) {
- if (f.getName().endsWith(".class")) {
- parseClass(fi, f);
- } else if (!f.isDirectory() && ResourceFile.isResource(f.getCanonicalPath())) {
+
+ String name = f.getName();
String pathname = f.getCanonicalPath();
- String dir = fi.getName();
- if (!pathname.startsWith(dir)) {
+ String root = cp.getName();
+ if (!name.equals(root) && !pathname.equals(root)) {
+ if (!pathname.startsWith(root)) {
throw new RuntimeException("Incorrect pathname " + pathname);
}
- String name = pathname.substring(dir.length() + 1, pathname.length());
- BufferedInputStream in = new BufferedInputStream(new FileInputStream(f));
- try {
- ResourceFile.addResource(name, in);
- } finally {
- in.close();
+
+ name = pathname.substring(root.length() + 1, pathname.length());
}
+
+ if (name.endsWith(".class")) {
+ visitor.visitClass(f, name);
+ } else if (!f.isDirectory() && ResourceFile.isResource(f.getCanonicalPath())) {
+ visitor.visitResource(f, name);
}
+ return null;
}
+
+ @Override
+ public Void visitDir(final File dir, final ClassPath cp, Void v) throws IOException {
+ List<File> ls = Files.walkTree(dir, null);
+ for (File f : ls) {
+ visitFile(f, (ClassPath) cp, null);
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitJarFile(JarFile jf, ClassPath cp, Void v) throws IOException {
+ if (filter != null && !filter.accept(cp.getFile())) {
+ return null;
+ }
+
+ Enumeration<JarEntry> entries = jf.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry e = entries.nextElement();
+ String name = e.getName();
+ if (filter != null && !filter.accept(jf, e)) {
+ continue;
+ }
+ if (name.endsWith(".class")) {
+ visitor.visitClass(jf, e);
+ } else if (!e.isDirectory() && ResourceFile.isResource(name)) {
+ visitor.visitResource(jf, e);
+ }
+ }
+ return null;
+ }
+ };
+
+ public static InputStream open(String pathname) throws IOException {
+ ClassPath.Visitor<InputStream, String> fv = new ClassPath.Visitor<InputStream, String>() {
+
+ @Override
+ public InputStream visitFile(File f, ClassPath cp, String pathname) throws IOException {
+ if (cp.getName().endsWith(File.separator + pathname)) {
+ return new FileInputStream(f);
} else {
- // should not reach here
- throw new RuntimeException("Unexpected class path: " + fi.getFile());
+ return null;
}
}
+
+ @Override
+ public InputStream visitDir(File dir, ClassPath cp, String pathname) throws IOException {
+ File f = new File(cp.getFile(), pathname);
+ if (f.exists()) {
+ return new FileInputStream(f);
+ } else {
+ return null;
}
+ }
- private void parseClass(FileInfo fi) throws IOException {
- parseClass(fi, fi.getFile());
+ @Override
+ public InputStream visitJarFile(JarFile jf, ClassPath cp, String pathname) throws IOException {
+ String p = pathname.replace(File.separatorChar, '/');
+ JarEntry e = jf.getJarEntry(p);
+ if (e != null) {
+ return jf.getInputStream(e);
+ } else {
+ return null;
}
+ }
+ };
- private void parseClass(FileInfo fi, File f) throws IOException {
- ClassFileParser cfparser = ClassFileParser.newParser(f, true);
- cfparser.parseDependency(false);
- fi.classCount++;
- // need to update the filesize for this directory
- fi.filesize += fi.getFile().length();
+ for (ClassPath cp : instance.entries) {
+ InputStream in = cp.accept(fv, pathname);
+ if (in != null) {
+ return in;
+ }
+ }
+ return null;
+ }
+ public ClassFileParser parserForClass(String classname) throws IOException {
+ String pathname = classname.replace('.', File.separatorChar) + ".class";
+ ClassPath.Visitor<ClassFileParser, String> fv = new ClassPath.Visitor<ClassFileParser, String>() {
+
+ @Override
+ public ClassFileParser visitFile(File f, ClassPath cp, String pathname) throws IOException {
+ if (cp.getName().endsWith(File.separator + pathname)) {
+ return ClassFileParser.newParser(f, true);
+ } else {
+ return null;
}
+ }
- public static void listFiles(File path, String suffix, List<File> result) {
- if (path.isDirectory()) {
- File[] children = path.listFiles();
- for (File c : children) {
- listFiles(c, suffix, result);
+ @Override
+ public ClassFileParser visitDir(File dir, ClassPath cp, String pathname) throws IOException {
+ File f = new File(cp.getFile(), pathname);
+ if (f.exists()) {
+ return ClassFileParser.newParser(f, true);
+ } else {
+ return null;
}
+ }
+ @Override
+ public ClassFileParser visitJarFile(JarFile jf, ClassPath cp, String pathname) throws IOException {
+ String p = pathname.replace(File.separatorChar, '/');
+ JarEntry e = jf.getJarEntry(p);
+ if (e != null) {
+ return ClassFileParser.newParser(jf.getInputStream(e), e.getSize(), true);
} else {
- if (suffix.isEmpty() || path.getName().endsWith(suffix)) {
- result.add(path);
+ return null;
}
}
+ };
+
+ for (ClassPath cp : entries) {
+ ClassFileParser cfparser = cp.accept(fv, pathname);
+ if (cfparser != null) {
+ return cfparser;
}
+ }
+ return null;
+ }
}