8003562: Provide a CLI tool to analyze class dependencies
Reviewed-by: jjg, alanb, ulfzibis, erikj
--- a/langtools/make/build.properties Tue Dec 25 17:23:59 2012 -0800
+++ b/langtools/make/build.properties Fri Dec 28 22:25:21 2012 -0800
@@ -153,6 +153,7 @@
javap.includes = \
com/sun/tools/classfile/ \
com/sun/tools/javap/ \
+ com/sun/tools/jdeps/ \
sun/tools/javap/
javap.tests = \
--- a/langtools/makefiles/BuildLangtools.gmk Tue Dec 25 17:23:59 2012 -0800
+++ b/langtools/makefiles/BuildLangtools.gmk Fri Dec 28 22:25:21 2012 -0800
@@ -75,6 +75,7 @@
printf "jdk=$(JDK_VERSION)\nfull=$(FULL_VERSION)\nrelease=$(RELEASE)\n" > $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/javah/resources/version.properties
printf "jdk=$(JDK_VERSION)\nfull=$(FULL_VERSION)\nrelease=$(RELEASE)\n" > $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/javap/resources/version.properties
printf "jdk=$(JDK_VERSION)\nfull=$(FULL_VERSION)\nrelease=$(RELEASE)\n" > $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/javac/resources/version.properties
+ printf "jdk=$(JDK_VERSION)\nfull=$(FULL_VERSION)\nrelease=$(RELEASE)\n" > $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/jdeps/resources/version.properties
echo Compiling $(words $(PROPSOURCES) v1 v2 v3) properties into resource bundles
$(TOOL_COMPILEPROPS_CMD) $(PROPCMDLINE) \
-compile $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/javah/resources/version.properties \
@@ -85,6 +86,9 @@
java.util.ListResourceBundle \
-compile $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/javac/resources/version.properties \
$(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/javac/resources/version.java \
+ java.util.ListResourceBundle \
+ -compile $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/jdeps/resources/version.properties \
+ $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/jdeps/resources/version.java \
java.util.ListResourceBundle
echo PROPS_ARE_CREATED=yes > $@
--- a/langtools/src/share/classes/com/sun/tools/classfile/Dependencies.java Tue Dec 25 17:23:59 2012 -0800
+++ b/langtools/src/share/classes/com/sun/tools/classfile/Dependencies.java Fri Dec 28 22:25:21 2012 -0800
@@ -142,6 +142,15 @@
}
/**
+ * Get a finder to do class dependency analysis.
+ *
+ * @return a Class dependency finder
+ */
+ public static Finder getClassDependencyFinder() {
+ return new ClassDependencyFinder();
+ }
+
+ /**
* Get the finder used to locate the dependencies for a class.
* @return the finder
*/
@@ -246,8 +255,6 @@
return results;
}
-
-
/**
* Find the dependencies of a class, using the current
* {@link Dependencies#getFinder finder} and
@@ -306,38 +313,44 @@
* A location identifying a class.
*/
static class SimpleLocation implements Location {
- public SimpleLocation(String className) {
- this.className = className;
+ public SimpleLocation(String name) {
+ this.name = name;
+ this.className = name.replace('/', '.').replace('$', '.');
}
- /**
- * Get the name of the class being depended on. This name will be used to
- * locate the class file for transitive dependency analysis.
- * @return the name of the class being depended on
- */
+ public String getName() {
+ return name;
+ }
+
public String getClassName() {
return className;
}
+ public String getPackageName() {
+ int i = name.lastIndexOf('/');
+ return (i > 0) ? name.substring(0, i).replace('/', '.') : "";
+ }
+
@Override
public boolean equals(Object other) {
if (this == other)
return true;
if (!(other instanceof SimpleLocation))
return false;
- return (className.equals(((SimpleLocation) other).className));
+ return (name.equals(((SimpleLocation) other).name));
}
@Override
public int hashCode() {
- return className.hashCode();
+ return name.hashCode();
}
@Override
public String toString() {
- return className;
+ return name;
}
+ private String name;
private String className;
}
@@ -431,9 +444,7 @@
}
public boolean accepts(Dependency dependency) {
- String cn = dependency.getTarget().getClassName();
- int lastSep = cn.lastIndexOf("/");
- String pn = (lastSep == -1 ? "" : cn.substring(0, lastSep));
+ String pn = dependency.getTarget().getPackageName();
if (packageNames.contains(pn))
return true;
@@ -451,8 +462,6 @@
private final boolean matchSubpackages;
}
-
-
/**
* This class identifies class names directly or indirectly in the constant pool.
*/
@@ -462,6 +471,26 @@
for (CPInfo cpInfo: classfile.constant_pool.entries()) {
v.scan(cpInfo);
}
+ try {
+ v.addClass(classfile.super_class);
+ v.addClasses(classfile.interfaces);
+ v.scan(classfile.attributes);
+
+ for (Field f : classfile.fields) {
+ v.scan(f.descriptor, f.attributes);
+ }
+ for (Method m : classfile.methods) {
+ v.scan(m.descriptor, m.attributes);
+ Exceptions_attribute e =
+ (Exceptions_attribute)m.attributes.get(Attribute.Exceptions);
+ if (e != null) {
+ v.addClasses(e.exception_index_table);
+ }
+ }
+ } catch (ConstantPoolException e) {
+ throw new ClassFileError(e);
+ }
+
return v.deps;
}
}
@@ -558,9 +587,7 @@
void scan(Descriptor d, Attributes attrs) {
try {
scan(new Signature(d.index).getType(constant_pool));
- Signature_attribute sa = (Signature_attribute) attrs.get(Attribute.Signature);
- if (sa != null)
- scan(new Signature(sa.signature_index).getType(constant_pool));
+ scan(attrs);
} catch (ConstantPoolException e) {
throw new ClassFileError(e);
}
@@ -574,6 +601,43 @@
t.accept(this, null);
}
+ void scan(Attributes attrs) {
+ try {
+ Signature_attribute sa = (Signature_attribute)attrs.get(Attribute.Signature);
+ if (sa != null)
+ scan(sa.getParsedSignature().getType(constant_pool));
+
+ scan((RuntimeVisibleAnnotations_attribute)
+ attrs.get(Attribute.RuntimeVisibleAnnotations));
+ scan((RuntimeVisibleParameterAnnotations_attribute)
+ attrs.get(Attribute.RuntimeVisibleParameterAnnotations));
+ } catch (ConstantPoolException e) {
+ throw new ClassFileError(e);
+ }
+ }
+
+ private void scan(RuntimeAnnotations_attribute attr) throws ConstantPoolException {
+ if (attr == null) {
+ return;
+ }
+ for (int i = 0; i < attr.annotations.length; i++) {
+ int index = attr.annotations[i].type_index;
+ scan(new Signature(index).getType(constant_pool));
+ }
+ }
+
+ private void scan(RuntimeParameterAnnotations_attribute attr) throws ConstantPoolException {
+ if (attr == null) {
+ return;
+ }
+ for (int param = 0; param < attr.parameter_annotations.length; param++) {
+ for (int i = 0; i < attr.parameter_annotations[param].length; i++) {
+ int index = attr.parameter_annotations[param][i].type_index;
+ scan(new Signature(index).getType(constant_pool));
+ }
+ }
+ }
+
void addClass(int index) throws ConstantPoolException {
if (index != 0) {
String name = constant_pool.getClassInfo(index).getBaseName();
@@ -698,6 +762,7 @@
findDependencies(type.paramTypes);
findDependencies(type.returnType);
findDependencies(type.throwsTypes);
+ findDependencies(type.typeParamTypes);
return null;
}
@@ -709,7 +774,7 @@
public Void visitClassType(ClassType type, Void p) {
findDependencies(type.outerType);
- addDependency(type.name);
+ addDependency(type.getBinaryName());
findDependencies(type.typeArgs);
return null;
}
--- a/langtools/src/share/classes/com/sun/tools/classfile/Dependency.java Tue Dec 25 17:23:59 2012 -0800
+++ b/langtools/src/share/classes/com/sun/tools/classfile/Dependency.java Fri Dec 28 22:25:21 2012 -0800
@@ -71,7 +71,19 @@
* dependency analysis.
* @return the name of the class containing the location.
*/
+ String getName();
+
+ /**
+ * Get the fully-qualified name of the class containing the location.
+ * @return the fully-qualified name of the class containing the location.
+ */
String getClassName();
+
+ /**
+ * Get the package name of the class containing the location.
+ * @return the package name of the class containing the location.
+ */
+ String getPackageName();
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/Archive.java Fri Dec 28 22:25:21 2012 -0800
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * 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.tools.jdeps;
+
+import com.sun.tools.classfile.Dependency;
+import com.sun.tools.classfile.Dependency.Location;
+import java.io.File;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+/**
+ * Represents the source of the class files.
+ */
+public class Archive {
+ private static Map<String,Archive> archiveForClass = new HashMap<String,Archive>();
+ public static Archive find(Location loc) {
+ return archiveForClass.get(loc.getName());
+ }
+
+ private final File file;
+ private final String filename;
+ private final DependencyRecorder recorder;
+ private final ClassFileReader reader;
+ public Archive(String name) {
+ this.file = null;
+ this.filename = name;
+ this.recorder = new DependencyRecorder();
+ this.reader = null;
+ }
+
+ public Archive(File f, ClassFileReader reader) {
+ this.file = f;
+ this.filename = f.getName();
+ this.recorder = new DependencyRecorder();
+ this.reader = reader;
+ }
+
+ public ClassFileReader reader() {
+ return reader;
+ }
+
+ public String getFileName() {
+ return filename;
+ }
+
+ public void addClass(String classFileName) {
+ Archive a = archiveForClass.get(classFileName);
+ assert(a == null || a == this); // ## issue warning?
+ if (!archiveForClass.containsKey(classFileName)) {
+ archiveForClass.put(classFileName, this);
+ }
+ }
+
+ public void addDependency(Dependency d) {
+ recorder.addDependency(d);
+ }
+
+ /**
+ * Returns a sorted map of a class to its dependencies.
+ */
+ public SortedMap<Location, SortedSet<Location>> getDependencies() {
+ DependencyRecorder.Filter filter = new DependencyRecorder.Filter() {
+ public boolean accept(Location origin, Location target) {
+ return (archiveForClass.get(origin.getName()) !=
+ archiveForClass.get(target.getName()));
+ }};
+
+ SortedMap<Location, SortedSet<Location>> result =
+ new TreeMap<Location, SortedSet<Location>>(locationComparator);
+ for (Map.Entry<Location, Set<Location>> e : recorder.dependencies().entrySet()) {
+ Location o = e.getKey();
+ for (Location t : e.getValue()) {
+ if (filter.accept(o, t)) {
+ SortedSet<Location> odeps = result.get(o);
+ if (odeps == null) {
+ odeps = new TreeSet<Location>(locationComparator);
+ result.put(o, odeps);
+ }
+ odeps.add(t);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns the set of archives this archive requires.
+ */
+ public Set<Archive> getRequiredArchives() {
+ SortedSet<Archive> deps = new TreeSet<Archive>(new Comparator<Archive>() {
+ public int compare(Archive a1, Archive a2) {
+ return a1.toString().compareTo(a2.toString());
+ }
+ });
+
+ for (Map.Entry<Location, Set<Location>> e : recorder.dependencies().entrySet()) {
+ Location o = e.getKey();
+ Archive origin = Archive.find(o);
+ for (Location t : e.getValue()) {
+ Archive target = Archive.find(t);
+ assert(origin != null && target != null);
+ if (origin != target) {
+ if (!deps.contains(target)) {
+ deps.add(target);
+ }
+ }
+ }
+ }
+ return deps;
+ }
+
+ public String toString() {
+ return file != null ? file.getPath() : filename;
+ }
+
+ private static class DependencyRecorder {
+ static interface Filter {
+ boolean accept(Location origin, Location target);
+ }
+
+ public void addDependency(Dependency d) {
+ Set<Location> odeps = map.get(d.getOrigin());
+ if (odeps == null) {
+ odeps = new HashSet<Location>();
+ map.put(d.getOrigin(), odeps);
+ }
+ odeps.add(d.getTarget());
+ }
+
+ public Map<Location, Set<Location>> dependencies() {
+ return map;
+ }
+
+ private final Map<Location, Set<Location>> map =
+ new HashMap<Location, Set<Location>>();
+ }
+
+ private static Comparator<Location> locationComparator =
+ new Comparator<Location>() {
+ public int compare(Location o1, Location o2) {
+ return o1.toString().compareTo(o2.toString());
+ }
+ };
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/ClassFileReader.java Fri Dec 28 22:25:21 2012 -0800
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * 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.tools.jdeps;
+
+import com.sun.tools.classfile.ClassFile;
+import com.sun.tools.classfile.ConstantPoolException;
+import com.sun.tools.classfile.Dependencies.ClassFileError;
+import java.io.*;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.*;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * ClassFileReader reads ClassFile(s) of a given path that can be
+ * a .class file, a directory, or a JAR file.
+ */
+public class ClassFileReader {
+ /**
+ * Returns a ClassFileReader instance of a given path.
+ */
+ public static ClassFileReader newInstance(File path) throws IOException {
+ if (!path.exists()) {
+ throw new FileNotFoundException(path.getAbsolutePath());
+ }
+
+ if (path.isDirectory()) {
+ return new DirectoryReader(path.toPath());
+ } else if (path.getName().endsWith(".jar")) {
+ return new JarFileReader(path.toPath());
+ } else {
+ return new ClassFileReader(path.toPath());
+ }
+ }
+
+ protected final Path path;
+ protected final String baseFileName;
+ private ClassFileReader(Path path) {
+ this.path = path;
+ this.baseFileName = path.getFileName() != null
+ ? path.getFileName().toString()
+ : path.toString();
+ }
+
+ public String getFileName() {
+ return baseFileName;
+ }
+
+ /**
+ * Returns the ClassFile matching the given binary name
+ * or a fully-qualified class name.
+ */
+ public ClassFile getClassFile(String name) throws IOException {
+ if (name.indexOf('.') > 0) {
+ int i = name.lastIndexOf('.');
+ String pathname = name.replace('.', File.separatorChar) + ".class";
+ if (baseFileName.equals(pathname) ||
+ baseFileName.equals(pathname.substring(0, i) + "$" +
+ pathname.substring(i+1, pathname.length()))) {
+ return readClassFile(path);
+ }
+ } else {
+ if (baseFileName.equals(name.replace('/', File.separatorChar) + ".class")) {
+ return readClassFile(path);
+ }
+ }
+ return null;
+ }
+
+ public Iterable<ClassFile> getClassFiles() throws IOException {
+ return new Iterable<ClassFile>() {
+ public Iterator<ClassFile> iterator() {
+ return new FileIterator();
+ }
+ };
+ }
+
+ protected ClassFile readClassFile(Path p) throws IOException {
+ InputStream is = null;
+ try {
+ is = Files.newInputStream(p);
+ return ClassFile.read(is);
+ } catch (ConstantPoolException e) {
+ throw new ClassFileError(e);
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+
+ class FileIterator implements Iterator<ClassFile> {
+ int count;
+ FileIterator() {
+ this.count = 0;
+ }
+ public boolean hasNext() {
+ return count == 0 && baseFileName.endsWith(".class");
+ }
+
+ public ClassFile next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ try {
+ ClassFile cf = readClassFile(path);
+ count++;
+ return cf;
+ } catch (IOException e) {
+ throw new ClassFileError(e);
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+ }
+
+ public String toString() {
+ return path.toString();
+ }
+
+ private static class DirectoryReader extends ClassFileReader {
+ DirectoryReader(Path path) throws IOException {
+ super(path);
+ }
+
+ public ClassFile getClassFile(String name) throws IOException {
+ if (name.indexOf('.') > 0) {
+ int i = name.lastIndexOf('.');
+ String pathname = name.replace('.', File.separatorChar) + ".class";
+ Path p = path.resolve(pathname);
+ if (!p.toFile().exists()) {
+ p = path.resolve(pathname.substring(0, i) + "$" +
+ pathname.substring(i+1, pathname.length()));
+ }
+ if (p.toFile().exists()) {
+ return readClassFile(p);
+ }
+ } else {
+ Path p = path.resolve(name + ".class");
+ if (p.toFile().exists()) {
+ return readClassFile(p);
+ }
+ }
+ return null;
+ }
+
+ public Iterable<ClassFile> getClassFiles() throws IOException {
+ final Iterator<ClassFile> iter = new DirectoryIterator();
+ return new Iterable<ClassFile>() {
+ public Iterator<ClassFile> iterator() {
+ return iter;
+ }
+ };
+ }
+
+ private List<Path> walkTree(Path dir) throws IOException {
+ final List<Path> files = new ArrayList<Path>();
+ Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+ throws IOException {
+ if (file.toFile().getName().endsWith(".class")) {
+ files.add(file);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ return files;
+ }
+
+ class DirectoryIterator implements Iterator<ClassFile> {
+ private List<Path> entries;
+ private int index = 0;
+ DirectoryIterator() throws IOException {
+ entries = walkTree(path);
+ index = 0;
+ }
+
+ public boolean hasNext() {
+ return index != entries.size();
+ }
+
+ public ClassFile next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ Path path = entries.get(index++);
+ try {
+ return readClassFile(path);
+ } catch (IOException e) {
+ throw new ClassFileError(e);
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+ }
+ }
+
+ private static class JarFileReader extends ClassFileReader {
+ final JarFile jarfile;
+ JarFileReader(Path path) throws IOException {
+ super(path);
+ this.jarfile = new JarFile(path.toFile());
+ }
+
+ public ClassFile getClassFile(String name) throws IOException {
+ if (name.indexOf('.') > 0) {
+ int i = name.lastIndexOf('.');
+ String entryName = name.replace('.', '/') + ".class";
+ JarEntry e = jarfile.getJarEntry(entryName);
+ if (e == null) {
+ e = jarfile.getJarEntry(entryName.substring(0, i) + "$"
+ + entryName.substring(i + 1, entryName.length()));
+ }
+ if (e != null) {
+ return readClassFile(e);
+ }
+ } else {
+ JarEntry e = jarfile.getJarEntry(name + ".class");
+ if (e != null) {
+ return readClassFile(e);
+ }
+ }
+ return null;
+ }
+
+ private ClassFile readClassFile(JarEntry e) throws IOException {
+ InputStream is = null;
+ try {
+ is = jarfile.getInputStream(e);
+ return ClassFile.read(is);
+ } catch (ConstantPoolException ex) {
+ throw new ClassFileError(ex);
+ } finally {
+ if (is != null)
+ is.close();
+ }
+ }
+
+ public Iterable<ClassFile> getClassFiles() throws IOException {
+ final Iterator<ClassFile> iter = new JarFileIterator();
+ return new Iterable<ClassFile>() {
+ public Iterator<ClassFile> iterator() {
+ return iter;
+ }
+ };
+ }
+
+ class JarFileIterator implements Iterator<ClassFile> {
+ private Enumeration<JarEntry> entries;
+ private JarEntry nextEntry;
+ JarFileIterator() {
+ this.entries = jarfile.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry e = entries.nextElement();
+ String name = e.getName();
+ if (name.endsWith(".class")) {
+ this.nextEntry = e;
+ break;
+ }
+ }
+ }
+
+ public boolean hasNext() {
+ return nextEntry != null;
+ }
+
+ public ClassFile next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+
+ ClassFile cf;
+ try {
+ cf = readClassFile(nextEntry);
+ } catch (IOException ex) {
+ throw new ClassFileError(ex);
+ }
+ JarEntry entry = nextEntry;
+ nextEntry = null;
+ while (entries.hasMoreElements()) {
+ JarEntry e = entries.nextElement();
+ String name = e.getName();
+ if (name.endsWith(".class")) {
+ nextEntry = e;
+ break;
+ }
+ }
+ return cf;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/JdepsTask.java Fri Dec 28 22:25:21 2012 -0800
@@ -0,0 +1,650 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * 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.tools.jdeps;
+
+import com.sun.tools.classfile.ClassFile;
+import com.sun.tools.classfile.ConstantPoolException;
+import com.sun.tools.classfile.Dependencies;
+import com.sun.tools.classfile.Dependencies.ClassFileError;
+import com.sun.tools.classfile.Dependency;
+import com.sun.tools.classfile.Dependency.Location;
+import java.io.*;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.regex.Pattern;
+
+/**
+ * Implementation for the jdeps tool for static class dependency analysis.
+ */
+class JdepsTask {
+ class BadArgs extends Exception {
+ static final long serialVersionUID = 8765093759964640721L;
+ BadArgs(String key, Object... args) {
+ super(JdepsTask.this.getMessage(key, args));
+ this.key = key;
+ this.args = args;
+ }
+
+ BadArgs showUsage(boolean b) {
+ showUsage = b;
+ return this;
+ }
+ final String key;
+ final Object[] args;
+ boolean showUsage;
+ }
+
+ static abstract class Option {
+ Option(boolean hasArg, String... aliases) {
+ this.hasArg = hasArg;
+ this.aliases = aliases;
+ }
+
+ boolean isHidden() {
+ return false;
+ }
+
+ boolean matches(String opt) {
+ for (String a : aliases) {
+ if (a.equals(opt)) {
+ return true;
+ } else if (opt.startsWith("--") && hasArg && opt.startsWith(a + "=")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean ignoreRest() {
+ return false;
+ }
+
+ abstract void process(JdepsTask task, String opt, String arg) throws BadArgs;
+ final boolean hasArg;
+ final String[] aliases;
+ }
+
+ static abstract class HiddenOption extends Option {
+ HiddenOption(boolean hasArg, String... aliases) {
+ super(hasArg, aliases);
+ }
+
+ boolean isHidden() {
+ return true;
+ }
+ }
+
+ static Option[] recognizedOptions = {
+ new Option(false, "-h", "-?", "--help") {
+ void process(JdepsTask task, String opt, String arg) {
+ task.options.help = true;
+ }
+ },
+ new Option(false, "-s", "--summary") {
+ void process(JdepsTask task, String opt, String arg) {
+ task.options.showSummary = true;
+ task.options.verbose = Options.Verbose.SUMMARY;
+ }
+ },
+ new Option(false, "-v", "--verbose") {
+ void process(JdepsTask task, String opt, String arg) {
+ task.options.verbose = Options.Verbose.VERBOSE;
+ }
+ },
+ new Option(true, "-V", "--verbose-level") {
+ void process(JdepsTask task, String opt, String arg) throws BadArgs {
+ switch (arg) {
+ case "package":
+ task.options.verbose = Options.Verbose.PACKAGE;
+ break;
+ case "class":
+ task.options.verbose = Options.Verbose.CLASS;
+ break;
+ default:
+ throw task.new BadArgs("err.invalid.arg.for.option", opt);
+ }
+ }
+ },
+ new Option(true, "-c", "--classpath") {
+ void process(JdepsTask task, String opt, String arg) {
+ task.options.classpath = arg;
+ }
+ },
+ new Option(true, "-p", "--package") {
+ void process(JdepsTask task, String opt, String arg) {
+ task.options.packageNames.add(arg);
+ }
+ },
+ new Option(true, "-e", "--regex") {
+ void process(JdepsTask task, String opt, String arg) {
+ task.options.regex = arg;
+ }
+ },
+ new Option(false, "-P", "--profile") {
+ void process(JdepsTask task, String opt, String arg) {
+ task.options.showProfile = true;
+ }
+ },
+ new Option(false, "-R", "--recursive") {
+ void process(JdepsTask task, String opt, String arg) {
+ task.options.depth = 0;
+ }
+ },
+ new HiddenOption(true, "-d", "--depth") {
+ void process(JdepsTask task, String opt, String arg) throws BadArgs {
+ try {
+ task.options.depth = Integer.parseInt(arg);
+ } catch (NumberFormatException e) {
+ throw task.new BadArgs("err.invalid.arg.for.option", opt);
+ }
+ }
+ },
+ new Option(false, "--version") {
+ void process(JdepsTask task, String opt, String arg) {
+ task.options.version = true;
+ }
+ },
+ new HiddenOption(false, "--fullversion") {
+ void process(JdepsTask task, String opt, String arg) {
+ task.options.fullVersion = true;
+ }
+ },
+
+ };
+
+ private static final String PROGNAME = "jdeps";
+ private final Options options = new Options();
+ private final List<String> classes = new ArrayList<String>();
+
+ private PrintWriter log;
+ void setLog(PrintWriter out) {
+ log = out;
+ }
+
+ /**
+ * Result codes.
+ */
+ static final int EXIT_OK = 0, // Completed with no errors.
+ EXIT_ERROR = 1, // Completed but reported errors.
+ EXIT_CMDERR = 2, // Bad command-line arguments
+ EXIT_SYSERR = 3, // System error or resource exhaustion.
+ EXIT_ABNORMAL = 4;// terminated abnormally
+
+ int run(String[] args) {
+ if (log == null) {
+ log = new PrintWriter(System.out);
+ }
+ try {
+ handleOptions(args);
+ if (options.help) {
+ showHelp();
+ }
+ if (options.version || options.fullVersion) {
+ showVersion(options.fullVersion);
+ }
+ if (classes.isEmpty() && !options.wildcard) {
+ if (options.help || options.version || options.fullVersion) {
+ return EXIT_OK;
+ } else {
+ showHelp();
+ return EXIT_CMDERR;
+ }
+ }
+ if (options.regex != null && options.packageNames.size() > 0) {
+ showHelp();
+ return EXIT_CMDERR;
+ }
+ if (options.showSummary && options.verbose != Options.Verbose.SUMMARY) {
+ showHelp();
+ return EXIT_CMDERR;
+ }
+ boolean ok = run();
+ return ok ? EXIT_OK : EXIT_ERROR;
+ } catch (BadArgs e) {
+ reportError(e.key, e.args);
+ if (e.showUsage) {
+ log.println(getMessage("main.usage.summary", PROGNAME));
+ }
+ return EXIT_CMDERR;
+ } catch (IOException e) {
+ return EXIT_ABNORMAL;
+ } finally {
+ log.flush();
+ }
+ }
+
+ private final List<Archive> sourceLocations = new ArrayList<Archive>();
+ private final Archive NOT_FOUND = new Archive(getMessage("artifact.not.found"));
+ private boolean run() throws IOException {
+ findDependencies();
+ switch (options.verbose) {
+ case VERBOSE:
+ case CLASS:
+ printClassDeps(log);
+ break;
+ case PACKAGE:
+ printPackageDeps(log);
+ break;
+ case SUMMARY:
+ for (Archive origin : sourceLocations) {
+ for (Archive target : origin.getRequiredArchives()) {
+ log.format("%-30s -> %s%n", origin, target);
+ }
+ }
+ break;
+ default:
+ throw new InternalError("Should not reach here");
+ }
+ return true;
+ }
+
+ private boolean isValidClassName(String name) {
+ if (!Character.isJavaIdentifierStart(name.charAt(0))) {
+ return false;
+ }
+ for (int i=1; i < name.length(); i++) {
+ char c = name.charAt(i);
+ if (c != '.' && !Character.isJavaIdentifierPart(c)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void findDependencies() throws IOException {
+ Dependency.Finder finder = Dependencies.getClassDependencyFinder();
+ Dependency.Filter filter;
+ if (options.regex != null) {
+ filter = Dependencies.getRegexFilter(Pattern.compile(options.regex));
+ } else if (options.packageNames.size() > 0) {
+ filter = Dependencies.getPackageFilter(options.packageNames, false);
+ } else {
+ filter = new Dependency.Filter() {
+ public boolean accepts(Dependency dependency) {
+ return !dependency.getOrigin().equals(dependency.getTarget());
+ }
+ };
+ }
+
+ List<Archive> archives = new ArrayList<Archive>();
+ Deque<String> roots = new LinkedList<String>();
+ for (String s : classes) {
+ File f = new File(s);
+ if (f.exists()) {
+ archives.add(new Archive(f, ClassFileReader.newInstance(f)));
+ } else {
+ if (isValidClassName(s)) {
+ roots.add(s);
+ } else {
+ warning("warn.invalid.arg", s);
+ }
+ }
+ }
+
+ List<Archive> classpaths = new ArrayList<Archive>(); // for class file lookup
+ if (options.wildcard) {
+ // include all archives from classpath to the initial list
+ archives.addAll(getClassPathArchives(options.classpath));
+ } else {
+ classpaths.addAll(getClassPathArchives(options.classpath));
+ }
+ classpaths.addAll(PlatformClassPath.getArchives());
+
+ // add all archives to the source locations for reporting
+ sourceLocations.addAll(archives);
+ sourceLocations.addAll(classpaths);
+
+ // Work queue of names of classfiles to be searched.
+ // Entries will be unique, and for classes that do not yet have
+ // dependencies in the results map.
+ Deque<String> deque = new LinkedList<String>();
+ Set<String> doneClasses = new HashSet<String>();
+
+ // get the immediate dependencies of the input files
+ for (Archive a : archives) {
+ for (ClassFile cf : a.reader().getClassFiles()) {
+ String classFileName;
+ try {
+ classFileName = cf.getName();
+ } catch (ConstantPoolException e) {
+ throw new ClassFileError(e);
+ }
+ a.addClass(classFileName);
+ if (!doneClasses.contains(classFileName)) {
+ doneClasses.add(classFileName);
+ }
+ for (Dependency d : finder.findDependencies(cf)) {
+ if (filter.accepts(d)) {
+ String cn = d.getTarget().getName();
+ if (!doneClasses.contains(cn) && !deque.contains(cn)) {
+ deque.add(cn);
+ }
+ a.addDependency(d);
+ }
+ }
+ }
+ }
+
+ // add Archive for looking up classes from the classpath
+ // for transitive dependency analysis
+ Deque<String> unresolved = roots;
+ int depth = options.depth > 0 ? options.depth : Integer.MAX_VALUE;
+ do {
+ String name;
+ while ((name = unresolved.poll()) != null) {
+ if (doneClasses.contains(name)) {
+ continue;
+ }
+ ClassFile cf = null;
+ for (Archive a : classpaths) {
+ cf = a.reader().getClassFile(name);
+ if (cf != null) {
+ String classFileName;
+ try {
+ classFileName = cf.getName();
+ } catch (ConstantPoolException e) {
+ throw new ClassFileError(e);
+ }
+ a.addClass(classFileName);
+ if (!doneClasses.contains(classFileName)) {
+ // if name is a fully-qualified class name specified
+ // from command-line, this class might already be parsed
+ doneClasses.add(classFileName);
+ if (depth > 0) {
+ for (Dependency d : finder.findDependencies(cf)) {
+ if (filter.accepts(d)) {
+ String cn = d.getTarget().getName();
+ if (!doneClasses.contains(cn) && !deque.contains(cn)) {
+ deque.add(cn);
+ }
+ a.addDependency(d);
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ if (cf == null) {
+ NOT_FOUND.addClass(name);
+ }
+ }
+ unresolved = deque;
+ deque = new LinkedList<String>();
+ } while (!unresolved.isEmpty() && depth-- > 0);
+ }
+
+ private void printPackageDeps(PrintWriter out) {
+ for (Archive source : sourceLocations) {
+ SortedMap<Location, SortedSet<Location>> deps = source.getDependencies();
+ if (deps.isEmpty())
+ continue;
+
+ for (Archive target : source.getRequiredArchives()) {
+ out.format("%s -> %s%n", source, target);
+ }
+
+ Map<String, Archive> pkgs = new TreeMap<String, Archive>();
+ SortedMap<String, Archive> targets = new TreeMap<String, Archive>();
+ String pkg = "";
+ for (Map.Entry<Location, SortedSet<Location>> e : deps.entrySet()) {
+ String cn = e.getKey().getClassName();
+ String p = packageOf(e.getKey());
+ Archive origin = Archive.find(e.getKey());
+ assert origin != null;
+ if (!pkgs.containsKey(p)) {
+ pkgs.put(p, origin);
+ } else if (pkgs.get(p) != origin) {
+ warning("warn.split.package", p, origin, pkgs.get(p));
+ }
+
+ if (!p.equals(pkg)) {
+ printTargets(out, targets);
+ pkg = p;
+ targets.clear();
+ out.format(" %s (%s)%n", p, origin.getFileName());
+ }
+
+ for (Location t : e.getValue()) {
+ p = packageOf(t);
+ Archive target = Archive.find(t);
+ if (!targets.containsKey(p)) {
+ targets.put(p, target);
+ }
+ }
+ }
+ printTargets(out, targets);
+ out.println();
+ }
+ }
+
+ private void printTargets(PrintWriter out, Map<String, Archive> targets) {
+ for (Map.Entry<String, Archive> t : targets.entrySet()) {
+ String pn = t.getKey();
+ out.format(" -> %-40s %s%n", pn, getPackageInfo(pn, t.getValue()));
+ }
+ }
+
+ private String getPackageInfo(String pn, Archive source) {
+ if (PlatformClassPath.contains(source)) {
+ String name = PlatformClassPath.getProfileName(pn);
+ if (name.isEmpty()) {
+ return "JDK internal API (" + source.getFileName() + ")";
+ }
+ return options.showProfile ? name : "";
+ }
+ return source.getFileName();
+ }
+
+ private static String packageOf(Location loc) {
+ String pkg = loc.getPackageName();
+ return pkg.isEmpty() ? "<unnamed>" : pkg;
+ }
+
+ private void printClassDeps(PrintWriter out) {
+ for (Archive source : sourceLocations) {
+ SortedMap<Location, SortedSet<Location>> deps = source.getDependencies();
+ if (deps.isEmpty())
+ continue;
+
+ for (Archive target : source.getRequiredArchives()) {
+ out.format("%s -> %s%n", source, target);
+ }
+ out.format("%s%n", source);
+ for (Map.Entry<Location, SortedSet<Location>> e : deps.entrySet()) {
+ String cn = e.getKey().getClassName();
+ Archive origin = Archive.find(e.getKey());
+ out.format(" %s (%s)%n", cn, origin.getFileName());
+ for (Location t : e.getValue()) {
+ cn = t.getClassName();
+ Archive target = Archive.find(t);
+ out.format(" -> %-60s %s%n", cn, getPackageInfo(t.getPackageName(), target));
+ }
+ }
+ out.println();
+ }
+ }
+ public void handleOptions(String[] args) throws BadArgs {
+ // process options
+ for (int i=0; i < args.length; i++) {
+ if (args[i].charAt(0) == '-') {
+ String name = args[i];
+ Option option = getOption(name);
+ String param = null;
+ if (option.hasArg) {
+ if (name.startsWith("--") && name.indexOf('=') > 0) {
+ param = name.substring(name.indexOf('=') + 1, name.length());
+ } else if (i + 1 < args.length) {
+ param = args[++i];
+ }
+ if (param == null || param.isEmpty() || param.charAt(0) == '-') {
+ throw new BadArgs("err.missing.arg", name).showUsage(true);
+ }
+ }
+ option.process(this, name, param);
+ if (option.ignoreRest()) {
+ i = args.length;
+ }
+ } else {
+ // process rest of the input arguments
+ for (; i < args.length; i++) {
+ String name = args[i];
+ if (name.charAt(0) == '-') {
+ throw new BadArgs("err.option.after.class", name).showUsage(true);
+ }
+ if (name.equals("*") || name.equals("\"*\"")) {
+ options.wildcard = true;
+ } else {
+ classes.add(name);
+ }
+ }
+ }
+ }
+ }
+
+ private Option getOption(String name) throws BadArgs {
+ for (Option o : recognizedOptions) {
+ if (o.matches(name)) {
+ return o;
+ }
+ }
+ throw new BadArgs("err.unknown.option", name).showUsage(true);
+ }
+
+ private void reportError(String key, Object... args) {
+ log.println(getMessage("error.prefix") + " " + getMessage(key, args));
+ }
+
+ private void warning(String key, Object... args) {
+ log.println(getMessage("warn.prefix") + " " + getMessage(key, args));
+ }
+
+ private void showHelp() {
+ log.println(getMessage("main.usage", PROGNAME));
+ for (Option o : recognizedOptions) {
+ String name = o.aliases[0].substring(1); // there must always be at least one name
+ name = name.charAt(0) == '-' ? name.substring(1) : name;
+ if (o.isHidden() || name.equals("h")) {
+ continue;
+ }
+ log.println(getMessage("main.opt." + name));
+ }
+ }
+
+ private void showVersion(boolean full) {
+ log.println(version(full ? "full" : "release"));
+ }
+
+ private String version(String key) {
+ // key=version: mm.nn.oo[-milestone]
+ // key=full: mm.mm.oo[-milestone]-build
+ if (ResourceBundleHelper.versionRB == null) {
+ return System.getProperty("java.version");
+ }
+ try {
+ return ResourceBundleHelper.versionRB.getString(key);
+ } catch (MissingResourceException e) {
+ return getMessage("version.unknown", System.getProperty("java.version"));
+ }
+ }
+
+ public String getMessage(String key, Object... args) {
+ try {
+ return MessageFormat.format(ResourceBundleHelper.bundle.getString(key), args);
+ } catch (MissingResourceException e) {
+ throw new InternalError("Missing message: " + key);
+ }
+ }
+
+ private static class Options {
+ enum Verbose {
+ CLASS,
+ PACKAGE,
+ SUMMARY,
+ VERBOSE
+ };
+
+ boolean help;
+ boolean version;
+ boolean fullVersion;
+ boolean showFlags;
+ boolean showProfile;
+ boolean showSummary;
+ boolean wildcard;
+ String regex;
+ String classpath = "";
+ int depth = 1;
+ Verbose verbose = Verbose.PACKAGE;
+ Set<String> packageNames = new HashSet<String>();
+ }
+
+ private static class ResourceBundleHelper {
+ static final ResourceBundle versionRB;
+ static final ResourceBundle bundle;
+
+ static {
+ Locale locale = Locale.getDefault();
+ try {
+ bundle = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdeps", locale);
+ } catch (MissingResourceException e) {
+ throw new InternalError("Cannot find jdeps resource bundle for locale " + locale);
+ }
+ try {
+ versionRB = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.version");
+ } catch (MissingResourceException e) {
+ throw new InternalError("version.resource.missing");
+ }
+ }
+ }
+
+ private List<Archive> getArchives(List<String> filenames) throws IOException {
+ List<Archive> result = new ArrayList<Archive>();
+ for (String s : filenames) {
+ File f = new File(s);
+ if (f.exists()) {
+ result.add(new Archive(f, ClassFileReader.newInstance(f)));
+ } else {
+ warning("warn.file.not.exist", s);
+ }
+ }
+ return result;
+ }
+
+ private List<Archive> getClassPathArchives(String paths) throws IOException {
+ List<Archive> result = new ArrayList<Archive>();
+ if (paths.isEmpty()) {
+ return result;
+ }
+ for (String p : paths.split(File.pathSeparator)) {
+ if (p.length() > 0) {
+ File f = new File(p);
+ if (f.exists()) {
+ result.add(new Archive(f, ClassFileReader.newInstance(f)));
+ }
+ }
+ }
+ return result;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/Main.java Fri Dec 28 22:25:21 2012 -0800
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * 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.tools.jdeps;
+
+import java.io.*;
+
+/**
+ *
+ * Usage:
+ * jdeps [options] files ...
+ * where options include:
+ * -p package-name restrict analysis to classes in this package
+ * (may be given multiple times)
+ * -e regex restrict analysis to packages matching pattern
+ * (-p and -e are exclusive)
+ * -v show class-level dependencies
+ * default: package-level dependencies
+ * -r --recursive transitive dependencies analysis
+ * -classpath paths Classpath to locate class files
+ * -all process all class files in the given classpath
+ */
+public class Main {
+ public static void main(String... args) throws Exception {
+ JdepsTask t = new JdepsTask();
+ int rc = t.run(args);
+ System.exit(rc);
+ }
+
+
+ /**
+ * Entry point that does <i>not</i> call System.exit.
+ *
+ * @param args command line arguments
+ * @param out output stream
+ * @return an exit code. 0 means success, non-zero means an error occurred.
+ */
+ public static int run(String[] args, PrintWriter out) {
+ JdepsTask t = new JdepsTask();
+ t.setLog(out);
+ return t.run(args);
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/PlatformClassPath.java Fri Dec 28 22:25:21 2012 -0800
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * 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.tools.jdeps;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.*;
+
+/**
+ * ClassPath for Java SE and JDK
+ */
+class PlatformClassPath {
+ /*
+ * Profiles for Java SE
+ *
+ * This is a temporary workaround until a common API is defined for langtools
+ * to determine which profile a given classname belongs to. The list of
+ * packages and profile names are hardcoded in jdk.properties and
+ * split packages are not supported.
+ */
+ static class Profile {
+ final String name;
+ final Set<String> packages;
+
+ Profile(String name) {
+ this.name = name;
+ this.packages = new HashSet<String>();
+ }
+ }
+
+ private final static String JAVAFX = "javafx";
+ private final static Map<String,Profile> map = getProfilePackages();
+ static String getProfileName(String packageName) {
+ Profile profile = map.get(packageName);
+ if (packageName.startsWith(JAVAFX + ".")) {
+ profile = map.get(JAVAFX);
+ }
+ return profile != null ? profile.name : "";
+ }
+
+ private final static List<Archive> javaHomeArchives = init();
+ static List<Archive> getArchives() {
+ return javaHomeArchives;
+ }
+
+ static boolean contains(Archive archive) {
+ return javaHomeArchives.contains(archive);
+ }
+
+ private static List<Archive> init() {
+ List<Archive> result = new ArrayList<Archive>();
+ String javaHome = System.getProperty("java.home");
+ List<File> files = new ArrayList<File>();
+ File jre = new File(javaHome, "jre");
+ File lib = new File(javaHome, "lib");
+
+ try {
+ if (jre.exists() && jre.isDirectory()) {
+ result.addAll(addJarFiles(new File(jre, "lib")));
+ result.addAll(addJarFiles(lib));
+ } else if (lib.exists() && lib.isDirectory()) {
+ // either a JRE or a jdk build image
+ File classes = new File(javaHome, "classes");
+ if (classes.exists() && classes.isDirectory()) {
+ // jdk build outputdir
+ result.add(new Archive(classes, ClassFileReader.newInstance(classes)));
+ }
+ // add other JAR files
+ result.addAll(addJarFiles(lib));
+ } else {
+ throw new RuntimeException("\"" + javaHome + "\" not a JDK home");
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ // add a JavaFX profile if there is jfxrt.jar
+ for (Archive archive : result) {
+ if (archive.getFileName().equals("jfxrt.jar")) {
+ map.put(JAVAFX, new Profile("jfxrt.jar"));
+ }
+ }
+ return result;
+ }
+
+ private static List<Archive> addJarFiles(File f) throws IOException {
+ final List<Archive> result = new ArrayList<Archive>();
+ final Path root = f.toPath();
+ final Path ext = root.resolve("ext");
+ Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
+ throws IOException
+ {
+ if (dir.equals(root) || dir.equals(ext)) {
+ return FileVisitResult.CONTINUE;
+ } else {
+ // skip other cobundled JAR files
+ return FileVisitResult.SKIP_SUBTREE;
+ }
+ }
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+ throws IOException
+ {
+ File f = file.toFile();
+ String fn = f.getName();
+ if (fn.endsWith(".jar") && !fn.equals("alt-rt.jar")) {
+ result.add(new Archive(f, ClassFileReader.newInstance(f)));
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ return result;
+ }
+
+ private static Map<String,Profile> getProfilePackages() {
+ Map<String,Profile> map = new HashMap<String,Profile>();
+
+ // read the properties as a ResourceBundle as the build compiles
+ // the properties file into Java class. Another alternative is
+ // to load it as Properties and fix the build to exclude this file.
+ ResourceBundle profileBundle =
+ ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdk");
+
+ int i=1;
+ String key;
+ while (profileBundle.containsKey((key = "profile." + i + ".name"))) {
+ Profile profile = new Profile(profileBundle.getString(key));
+ String n = profileBundle.getString("profile." + i + ".packages");
+ String[] pkgs = n.split("\\s+");
+ for (String p : pkgs) {
+ if (p.isEmpty()) continue;
+ assert(map.containsKey(p) == false);
+ profile.packages.add(p);
+ map.put(p, profile);
+ }
+ i++;
+ }
+ return map;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties Fri Dec 28 22:25:21 2012 -0800
@@ -0,0 +1,57 @@
+main.usage.summary=\
+Usage: {0} <options> <classes...>\n\
+use -h, -? or --help for a list of possible options
+
+main.usage=\
+Usage: {0} <options> <classes...>\n\
+where <classes> can be a pathname to a .class file, a directory, a JAR file,\n\
+or a fully-qualified classname or wildcard "*". Possible options include:
+
+error.prefix=Error:
+warn.prefix=Warning:
+
+main.opt.h=\
+\ -h -? --help Print this usage message
+
+main.opt.version=\
+\ --version Version information
+
+main.opt.V=\
+\ -V <level> --verbose-level=<level> Print package-level or class-level dependencies\n\
+\ Valid levels are: "package" and "class"
+
+main.opt.v=\
+\ -v --verbose Print additional information
+
+main.opt.s=\
+\ -s --summary Print dependency summary only
+
+main.opt.p=\
+\ -p <pkg name> --package=<pkg name> Restrict analysis to classes in this package\n\
+\ (may be given multiple times)
+
+main.opt.e=\
+\ -e <regex> --regex=<regex> Restrict analysis to packages matching pattern\n\
+\ (-p and -e are exclusive)
+
+main.opt.P=\
+\ -P --profile Show profile or the file containing a package
+
+main.opt.c=\
+\ -c <path> --classpath=<path> Specify where to find class files
+
+main.opt.R=\
+\ -R --recursive Recursively traverse all dependencies
+
+main.opt.d=\
+\ -d <depth> --depth=<depth> Specify the depth of the transitive dependency analysis
+
+err.unknown.option=unknown option: {0}
+err.missing.arg=no value given for {0}
+err.internal.error=internal error: {0} {1} {2}
+err.invalid.arg.for.option=invalid argument for option: {0}
+err.option.after.class=option must be specified before classes: {0}
+warn.invalid.arg=Invalid classname or pathname not exist: {0}
+warn.split.package=package {0} defined in {1} {2}
+
+artifact.not.found=not found
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/resources/jdk.properties Fri Dec 28 22:25:21 2012 -0800
@@ -0,0 +1,262 @@
+# This properties file does not need localization.
+
+profile.1.name = compact1
+profile.1.packages = \
+ java.io \
+ java.lang \
+ java.lang.annotation \
+ java.lang.invoke \
+ java.lang.ref \
+ java.lang.reflect \
+ java.math \
+ java.net \
+ java.nio \
+ java.nio.channels \
+ java.nio.channels.spi \
+ java.nio.charset \
+ java.nio.charset.spi \
+ java.nio.file \
+ java.nio.file.attribute \
+ java.nio.file.spi \
+ java.security \
+ java.security.cert \
+ java.security.interfaces \
+ java.security.spec \
+ java.text \
+ java.text.spi \
+ java.util \
+ java.util.concurrent \
+ java.util.concurrent.atomic \
+ java.util.concurrent.locks \
+ java.util.jar \
+ java.util.logging \
+ java.util.regex \
+ java.util.spi \
+ java.util.zip \
+ javax.crypto \
+ javax.crypto.interfaces \
+ javax.crypto.spec \
+ javax.security.auth \
+ javax.security.auth.callback \
+ javax.security.auth.login \
+ javax.security.auth.spi \
+ javax.security.auth.x500 \
+ javax.net \
+ javax.net.ssl \
+ javax.security.cert \
+ \
+ com.sun.net.ssl \
+ com.sun.nio.file \
+ com.sun.nio.sctp \
+ com.sun.security.auth \
+ com.sun.security.auth.login
+
+profile.2.name = compact2
+profile.2.packages = \
+ java.sql \
+ javax.sql \
+ javax.xml \
+ javax.xml.datatype \
+ javax.xml.namespace \
+ javax.xml.parsers \
+ javax.xml.stream \
+ javax.xml.stream.events \
+ javax.xml.stream.util \
+ javax.xml.transform \
+ javax.xml.transform.dom \
+ javax.xml.transform.sax \
+ javax.xml.transform.stax \
+ javax.xml.transform.stream \
+ javax.xml.validation \
+ javax.xml.xpath \
+ org.w3c.dom \
+ org.w3c.dom.bootstrap \
+ org.w3c.dom.events \
+ org.w3c.dom.ls \
+ org.xml.sax \
+ org.xml.sax.ext \
+ org.xml.sax.helpers \
+ java.rmi \
+ java.rmi.activation \
+ java.rmi.dgc \
+ java.rmi.registry \
+ java.rmi.server \
+ javax.rmi.ssl \
+ javax.transaction \
+ javax.transaction.xa \
+ \
+ com.sun.net.httpserver \
+ com.sun.net.httpserver.spi
+
+profile.3.name = compact3
+profile.3.packages = \
+ java.lang.instrument \
+ java.lang.management \
+ java.security.acl \
+ java.util.prefs \
+ javax.management \
+ javax.management.loading \
+ javax.management.modelmbean \
+ javax.management.monitor \
+ javax.management.openmbean \
+ javax.management.relation \
+ javax.management.remote \
+ javax.management.remote.rmi \
+ javax.management.timer \
+ javax.naming \
+ javax.naming.directory \
+ javax.naming.event \
+ javax.naming.ldap \
+ javax.naming.spi \
+ javax.sql.rowset \
+ javax.sql.rowset.serial \
+ javax.sql.rowset.spi \
+ javax.security.auth.kerberos \
+ javax.security.sasl \
+ javax.script \
+ javax.smartcardio \
+ javax.xml.crypto \
+ javax.xml.crypto.dom \
+ javax.xml.crypto.dsig \
+ javax.xml.crypto.dsig.dom \
+ javax.xml.crypto.dsig.keyinfo \
+ javax.xml.crypto.dsig.spec \
+ javax.annotation.processing \
+ javax.lang.model \
+ javax.lang.model.element \
+ javax.lang.model.type \
+ javax.lang.model.util \
+ javax.tools \
+ javax.tools.annotation \
+ org.ietf.jgss \
+ \
+ com.sun.management \
+ com.sun.security.auth.callback \
+ com.sun.security.auth.module \
+ com.sun.security.jgss
+
+profile.4.name = Full JRE
+profile.4.packages = \
+ java.applet \
+ java.awt \
+ java.awt.color \
+ java.awt.datatransfer \
+ java.awt.dnd \
+ java.awt.dnd.peer \
+ java.awt.event \
+ java.awt.font \
+ java.awt.geom \
+ java.awt.im \
+ java.awt.im.spi \
+ java.awt.image \
+ java.awt.image.renderable \
+ java.awt.peer \
+ java.awt.print \
+ java.beans \
+ java.beans.beancontext \
+ javax.accessibility \
+ javax.imageio \
+ javax.imageio.event \
+ javax.imageio.metadata \
+ javax.imageio.plugins.bmp \
+ javax.imageio.plugins.jpeg \
+ javax.imageio.spi \
+ javax.imageio.stream \
+ javax.print \
+ javax.print.attribute \
+ javax.print.attribute.standard \
+ javax.print.event \
+ javax.sound.midi \
+ javax.sound.midi.spi \
+ javax.sound.sampled \
+ javax.sound.sampled.spi \
+ javax.swing \
+ javax.swing.border \
+ javax.swing.colorchooser \
+ javax.swing.event \
+ javax.swing.filechooser \
+ javax.swing.plaf \
+ javax.swing.plaf.basic \
+ javax.swing.plaf.metal \
+ javax.swing.plaf.multi \
+ javax.swing.plaf.nimbus \
+ javax.swing.plaf.synth \
+ javax.swing.table \
+ javax.swing.text \
+ javax.swing.text.html \
+ javax.swing.text.html.parser \
+ javax.swing.text.rtf \
+ javax.swing.tree \
+ javax.swing.undo \
+ javax.activation \
+ javax.jws \
+ javax.jws.soap \
+ javax.rmi \
+ javax.rmi.CORBA \
+ javax.xml.bind \
+ javax.xml.bind.annotation \
+ javax.xml.bind.annotation.adapters \
+ javax.xml.bind.attachment \
+ javax.xml.bind.helpers \
+ javax.xml.bind.util \
+ javax.xml.soap \
+ javax.xml.ws \
+ javax.xml.ws.handler \
+ javax.xml.ws.handler.soap \
+ javax.xml.ws.http \
+ javax.xml.ws.soap \
+ javax.xml.ws.spi \
+ javax.xml.ws.spi.http \
+ javax.xml.ws.wsaddressing \
+ javax.annotation \
+ org.omg.CORBA \
+ org.omg.CORBA.DynAnyPackage \
+ org.omg.CORBA.ORBPackage \
+ org.omg.CORBA.TypeCodePackage \
+ org.omg.CORBA.portable \
+ org.omg.CORBA_2_3 \
+ org.omg.CORBA_2_3.portable \
+ org.omg.CosNaming \
+ org.omg.CosNaming.NamingContextExtPackage \
+ org.omg.CosNaming.NamingContextPackage \
+ org.omg.Dynamic \
+ org.omg.DynamicAny \
+ org.omg.DynamicAny.DynAnyFactoryPackage \
+ org.omg.DynamicAny.DynAnyPackage \
+ org.omg.IOP \
+ org.omg.IOP.CodecFactoryPackage \
+ org.omg.IOP.CodecPackage \
+ org.omg.Messaging \
+ org.omg.PortableInterceptor \
+ org.omg.PortableInterceptor.ORBInitInfoPackage \
+ org.omg.PortableServer \
+ org.omg.PortableServer.CurrentPackage \
+ org.omg.PortableServer.POAManagerPackage \
+ org.omg.PortableServer.POAPackage \
+ org.omg.PortableServer.ServantLocatorPackage \
+ org.omg.PortableServer.portable \
+ org.omg.SendingContext \
+ org.omg.stub.java.rmi \
+ org.omg.stub.javax.management.remote.rmi
+
+# Remaining JDK supported API
+profile.5.name = JDK tools
+profile.5.packages = \
+ com.sun.jdi \
+ com.sun.jdi.connect \
+ com.sun.jdi.connect.spi \
+ com.sun.jdi.event \
+ com.sun.jdi.request \
+ com.sun.javadoc \
+ com.sun.tools.doclets \
+ com.sun.tools.doctree \
+ com.sun.source.tree \
+ com.sun.source.util \
+ com.sun.tools.attach \
+ com.sun.tools.attach.spi \
+ com.sun.tools.jconsole \
+ com.sun.tools.javac \
+ com.sun.tools.javah \
+ com.sun.tools.javap \
+ com.sun.tools.javadoc \
+ com.sun.servicetag
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/resources/version.properties-template Fri Dec 28 22:25:21 2012 -0800
@@ -0,0 +1,28 @@
+#
+# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation. Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# 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.
+#
+
+jdk=$(JDK_VERSION)
+full=$(FULL_VERSION)
+release=$(RELEASE)
--- a/langtools/test/Makefile Tue Dec 25 17:23:59 2012 -0800
+++ b/langtools/test/Makefile Fri Dec 28 22:25:21 2012 -0800
@@ -229,7 +229,7 @@
all: $(JPRT_CLEAN) jtreg-tests jck-compiler-tests jck-runtime-tests $(JPRT_ARCHIVE_BUNDLE) all-summary
@echo "Testing completed successfully"
-jtreg apt javac javadoc javah javap: $(JPRT_CLEAN) jtreg-tests $(JPRT_ARCHIVE_BUNDLE) jtreg-summary
+jtreg apt javac javadoc javah javap jdeps: $(JPRT_CLEAN) jtreg-tests $(JPRT_ARCHIVE_BUNDLE) jtreg-summary
@echo "Testing completed successfully"
jck-compiler: $(JPRT_CLEAN) jck-compiler-tests $(JPRT_ARCHIVE_BUNDLE) jck-compiler-summary
@@ -246,6 +246,7 @@
javadoc: JTREG_TESTDIRS = tools/javadoc com/sun/javadoc
javah: JTREG_TESTDIRS = tools/javah
javap: JTREG_TESTDIRS = tools/javap
+jdeps: JTREG_TESTDIRS = tools/jdeps
# Run jtreg tests
#
@@ -426,7 +427,7 @@
# Phony targets (e.g. these are not filenames)
.PHONY: all clean \
- jtreg javac javadoc javah javap jtreg-tests jtreg-summary check-jtreg \
+ jtreg javac javadoc javah javap jdeps jtreg-tests jtreg-summary check-jtreg \
jck-compiler jck-compiler-tests jck-compiler-summary \
jck-runtime jck-runtime-tests jck-runtime-summary check-jck
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/Basic.java Fri Dec 28 22:25:21 2012 -0800
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8003562
+ * @summary Basic tests for jdeps tool
+ * @build Test p.Foo
+ * @run main Basic
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.*;
+import java.util.regex.*;
+
+public class Basic {
+ public static void main(String... args) throws Exception {
+ int errors = 0;
+
+ errors += new Basic().run();
+ if (errors > 0)
+ throw new Exception(errors + " errors found");
+ }
+
+ int run() throws IOException {
+ File testDir = new File(System.getProperty("test.classes", "."));
+ // test a .class file
+ test(new File(testDir, "Test.class"),
+ new String[] {"java.lang", "p"});
+ // test a directory
+ test(new File(testDir, "p"),
+ new String[] {"java.lang", "java.util"});
+ // test class-level dependency output
+ test(new File(testDir, "Test.class"),
+ new String[] {"java.lang.Object", "p.Foo"},
+ new String[] {"-V", "class"});
+ // test -p option
+ test(new File(testDir, "Test.class"),
+ new String[] {"p.Foo"},
+ new String[] {"--verbose-level=class", "-p", "p"});
+ // test -e option
+ test(new File(testDir, "Test.class"),
+ new String[] {"p.Foo"},
+ new String[] {"-V", "class", "-e", "p\\..*"});
+ test(new File(testDir, "Test.class"),
+ new String[] {"java.lang"},
+ new String[] {"-V", "package", "-e", "java\\.lang\\..*"});
+ // test -classpath and -all options
+ test(null,
+ new String[] {"com.sun.tools.jdeps", "java.lang", "java.util",
+ "java.util.regex", "java.io", "p"},
+ new String[] {"--classpath", testDir.getPath(), "*"});
+ return errors;
+ }
+
+ void test(File file, String[] expect) {
+ test(file, expect, new String[0]);
+ }
+
+ void test(File file, String[] expect, String[] options) {
+ String[] args;
+ if (file != null) {
+ args = Arrays.copyOf(options, options.length+1);
+ args[options.length] = file.getPath();
+ } else {
+ args = options;
+ }
+ String[] deps = jdeps(args);
+ checkEqual("dependencies", expect, deps);
+ }
+
+ String[] jdeps(String... args) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ System.err.println("jdeps " + Arrays.toString(args));
+ int rc = com.sun.tools.jdeps.Main.run(args, pw);
+ pw.close();
+ String out = sw.toString();
+ if (!out.isEmpty())
+ System.err.println(out);
+ if (rc != 0)
+ throw new Error("jdeps failed: rc=" + rc);
+ return findDeps(out);
+ }
+
+ // Pattern used to parse lines
+ private static Pattern linePattern = Pattern.compile(".*\r?\n");
+ private static Pattern pattern = Pattern.compile("\\s+ -> (\\S+) +.*");
+
+ // Use the linePattern to break the given String into lines, applying
+ // the pattern to each line to see if we have a match
+ private static String[] findDeps(String out) {
+ List<String> result = new ArrayList<>();
+ Matcher lm = linePattern.matcher(out); // Line matcher
+ Matcher pm = null; // Pattern matcher
+ int lines = 0;
+ while (lm.find()) {
+ lines++;
+ CharSequence cs = lm.group(); // The current line
+ if (pm == null)
+ pm = pattern.matcher(cs);
+ else
+ pm.reset(cs);
+ if (pm.find())
+ result.add(pm.group(1));
+ if (lm.end() == out.length())
+ break;
+ }
+ return result.toArray(new String[0]);
+ }
+
+ void checkEqual(String label, String[] expect, String[] found) {
+ Set<String> s1 = new HashSet<>(Arrays.asList(expect));
+ Set<String> s2 = new HashSet<>(Arrays.asList(found));
+
+ if (!s1.equals(s2))
+ error("Unexpected " + label + " found: '" + s2 + "', expected: '" + s1 + "'");
+ }
+
+ void error(String msg) {
+ System.err.println("Error: " + msg);
+ errors++;
+ }
+
+ int errors;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/Test.java Fri Dec 28 22:25:21 2012 -0800
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * 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.
+ */
+
+public class Test {
+ public void test() {
+ p.Foo f = new p.Foo();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/p/Foo.java Fri Dec 28 22:25:21 2012 -0800
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * 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 p;
+
+import java.util.List;
+import java.util.Collections;
+public class Foo {
+ public static List foo() {
+ return Collections.emptyList();
+ }
+
+ public Foo() {
+ }
+}