You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@river.apache.org by jc...@apache.org on 2009/04/17 12:32:02 UTC
svn commit: r765938 -
/incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/
Author: jcosters
Date: Fri Apr 17 10:32:02 2009
New Revision: 765938
URL: http://svn.apache.org/viewvc?rev=765938&view=rev
Log:
Changes for RIVER-272.
Added:
incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/
incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/AbstractDependencyVisitor.java (with props)
incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/AbstractVisitor.java (with props)
incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClassDepend.java (with props)
incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClassDependParameters.java (with props)
incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClassDependencyTreeNodes.java (with props)
incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClasspathPackages.java (with props)
incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/PackageClasses.java (with props)
incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ReferencedClasses.java (with props)
Added: incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/AbstractDependencyVisitor.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/AbstractDependencyVisitor.java?rev=765938&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/AbstractDependencyVisitor.java (added)
+++ incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/AbstractDependencyVisitor.java Fri Apr 17 10:32:02 2009
@@ -0,0 +1,242 @@
+package com.sun.jini.tool.classdepend;
+
+/***
+ * ASM examples: examples showing how ASM can be used
+ * Copyright (c) 2000-2005 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.signature.SignatureReader;
+import org.objectweb.asm.signature.SignatureVisitor;
+
+/**
+ *
+ *
+ */
+abstract class AbstractDependencyVisitor extends AbstractVisitor {
+
+ AbstractDependencyVisitor() { }
+
+ abstract protected void addName(String name);
+
+ /* -- ClassVisitor -- */
+
+ public void visit(int version, int access, String name, String signature,
+ String superName, String[] interfaces)
+ {
+ if (signature == null) {
+ addNameInternal(superName);
+ addNames(interfaces);
+ } else {
+ addSignature(signature);
+ }
+ }
+
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ addDesc(desc);
+ return this;
+ }
+
+ public FieldVisitor visitField(int access, String name, String desc,
+ String signature, Object value)
+ {
+ if (signature == null) {
+ addDesc(desc);
+ } else {
+ addTypeSignature(signature);
+ }
+ if (value instanceof Type) {
+ addType((Type) value);
+ }
+ return this;
+ }
+
+ public MethodVisitor visitMethod(int access, String name, String desc,
+ String signature, String[] exceptions)
+ {
+ if (signature == null) {
+ addMethodDesc(desc);
+ } else {
+ addSignature(signature);
+ }
+ addNames(exceptions);
+ return this;
+ }
+
+ public void visitInnerClass(String name, String outerName,
+ String innerName, int access)
+ {
+ /* XXX: Do we need to consider inner classes?
+ * Yes the old ClassDep tool includes them */
+ addNameInternal(outerName);
+ addNameInternal(name);
+ }
+
+ /* -- MethodVisitor -- */
+
+ public AnnotationVisitor visitParameterAnnotation(int parameter,
+ String desc,
+ boolean visible)
+ {
+ addDesc(desc);
+ return this;
+ }
+
+ public void visitTypeInsn(int opcode, String desc) {
+ if (desc.charAt(0) == '[') {
+ addDesc(desc);
+ } else {
+ addNameInternal(desc);
+ }
+ }
+
+ public void visitFieldInsn(int opcode, String owner, String name,
+ String desc)
+ {
+ addNameInternal(owner);
+ addDesc(desc);
+ }
+
+ public void visitMethodInsn(int opcode, String owner, String name,
+ String desc)
+ {
+ addNameInternal(owner);
+ addMethodDesc(desc);
+ }
+
+ public void visitLdcInsn(Object cst) {
+ if (cst instanceof Type) {
+ addType((Type) cst);
+ }
+ }
+
+ public void visitMultiANewArrayInsn(String desc, int dims) {
+ addDesc(desc);
+ }
+
+ public void visitLocalVariable(String name, String desc, String signature,
+ Label start, Label end, int index)
+ {
+ if (signature != null) {
+ addTypeSignature(signature);
+ }
+ }
+
+ public void visitTryCatchBlock(Label start, Label end, Label handler,
+ String type)
+ {
+ addNameInternal(type);
+ }
+
+ /* -- AnnotationVisitor -- */
+
+ public void visit(String name, Object value) {
+ if (value instanceof Type) {
+ addType((Type) value);
+ }
+ }
+
+ public void visitEnum(String name, String desc, String value) {
+ addDesc(desc);
+ }
+
+ public AnnotationVisitor visitAnnotation(String name, String desc) {
+ addDesc(desc);
+ return this;
+ }
+
+ /* -- SignatureVisitor -- */
+
+ public void visitTypeVariable(String name) {
+ /* XXX: Need to do something? */
+ System.out.println(name);
+ }
+
+ public void visitClassType(String name) {
+ addNameInternal(name);
+ }
+
+ public void visitInnerClassType(String name) {
+ addNameInternal(name);
+ }
+
+ /* -- Utilities -- */
+
+ private void addNameInternal(String name) {
+ if (name != null) {
+ addName(name.replace('/', '.'));
+ }
+ }
+
+ private void addNames(String[] names) {
+ if (names != null) {
+ int l = names.length;
+ for (int i = 0; i < l; i++) {
+ String name = names[i];
+ addNameInternal(name);
+ }
+ }
+ }
+
+ private void addDesc(String desc) {
+ addType(Type.getType(desc));
+ }
+
+ private void addMethodDesc(String desc) {
+ addType(Type.getReturnType(desc));
+ Type [] type = Type.getArgumentTypes(desc);
+ int l = type.length;
+ for (int i = 0; i < l; i++) {
+ addType(type[i]);
+ }
+ }
+
+ private void addType(Type t) {
+ switch (t.getSort()) {
+ case Type.ARRAY:
+ addType(t.getElementType());
+ break;
+ case Type.OBJECT:
+ addNameInternal(t.getClassName());
+ break;
+ }
+ }
+
+ private void addSignature(String signature) {
+ new SignatureReader(signature).accept(this);
+ }
+
+ private void addTypeSignature(String signature) {
+ new SignatureReader(signature).acceptType(this);
+ }
+}
Propchange: incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/AbstractDependencyVisitor.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/AbstractVisitor.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/AbstractVisitor.java?rev=765938&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/AbstractVisitor.java (added)
+++ incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/AbstractVisitor.java Fri Apr 17 10:32:02 2009
@@ -0,0 +1,42 @@
+package com.sun.jini.tool.classdepend;
+
+import org.objectweb.asm.commons.EmptyVisitor;
+import org.objectweb.asm.signature.SignatureVisitor;
+
+/**
+ * An abstract implementation of the ASM visitor interfaces, including {@link
+ * SignatureVisitor}.
+ */
+abstract class AbstractVisitor extends EmptyVisitor
+ implements SignatureVisitor
+{
+ public void visitFormalTypeParameter(String name) { }
+
+ public SignatureVisitor visitClassBound() { return this; }
+
+ public SignatureVisitor visitInterfaceBound() { return this; }
+
+ public SignatureVisitor visitSuperclass() { return this; }
+
+ public SignatureVisitor visitInterface() { return this; }
+
+ public SignatureVisitor visitParameterType() { return this; }
+
+ public SignatureVisitor visitReturnType() { return this; }
+
+ public SignatureVisitor visitExceptionType() { return this; }
+
+ public void visitBaseType(char descriptor) { }
+
+ public void visitTypeVariable(String name) { }
+
+ public SignatureVisitor visitArrayType() { return this; }
+
+ public void visitClassType(String name) { }
+
+ public void visitInnerClassType(String name) { }
+
+ public void visitTypeArgument() { }
+
+ public SignatureVisitor visitTypeArgument(char wildcard) { return this; }
+}
Propchange: incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/AbstractVisitor.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClassDepend.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClassDepend.java?rev=765938&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClassDepend.java (added)
+++ incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClassDepend.java Fri Apr 17 10:32:02 2009
@@ -0,0 +1,440 @@
+package com.sun.jini.tool.classdepend;
+
+import com.sun.jini.tool.classdepend.ClassDependParameters.CDPBuilder;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+
+/**
+ * Provides a utility for computing which classes are depended on by a set of
+ * classes. This class is not thread safe.
+ */
+public class ClassDepend {
+
+ /** The system classpath. */
+ private static final String systemClasspath =
+ System.getProperty("java.class.path");
+
+ /**
+ * The class loader used to load classes being checked for dependencies.
+ */
+ private final ClassLoader loader;
+
+ /**
+ * The class loader for classes that should be excluded because they are
+ * considered part of the platform.
+ */
+ private final ClassLoader platformLoader;
+
+ /**
+ * Used to compute the classes available in the classpath for a package.
+ */
+ private final PackageClasses packageClasses;
+
+ private volatile boolean printClassesWithFileSeparator = false;
+
+ /**
+ * Public Factory method for creating a new instance of ClassDepend.
+ *
+ * The <code>classpath</code> argument
+ * specifies the classpath that will be used to look up the class bytecode
+ * for classes whose dependencies are being computed. If the value
+ * specified is <code>null</code>, then the system class loader will be
+ * used. Otherwise, a {@link URLClassLoader} will be constructed using the
+ * URLs specified by <code>classpath</code> and with a parent class loader
+ * that is the parent of the system class loader. The
+ * <code>platform</code> argument specifies the classpath that will be used
+ * to find classes that should be considered part of the platform and
+ * should be excluded from consideration when computing dependencies. If
+ * the value specified is <code>null</code>, then the parent of the system
+ * class loader will be used. Otherwise, a <code>URLClassLoader</code>
+ * will be constructed using the URLs specified by <code>platform</code>
+ * and with a parent class loader that is the parent of the system class
+ * loader.
+ *
+ * @param classpath the classpath for finding classes, or
+ * <code>null</code>
+ * @param platform the classpath for finding platform classes, or
+ * <code>null</code>
+ * @param warn print warnings instead of throwing an exception when a Class
+ * can't be found or when ClassLoading fails.
+ * @return ClassDepend
+ * @throws java.net.MalformedURLException
+ * @throws java.io.IOException
+ */
+ public static ClassDepend newInstance(String classpath, String platform, boolean warn)
+ throws MalformedURLException, IOException{
+ /* Explanation for us mere mortals.
+ * Ternary conditional operator:
+ * Object ob = expression ? this if true : else this;
+ * ClassDepend classdepend = if warn not true, then new ClassDepend(), else
+ * new anonymous class that extends ClassDepend
+ * with noteClassNotFound and
+ * noteClassLoadingFailed method overrides.
+ *
+ * This prevents exceptions from being thrown and prints warnings
+ * instead on the System err
+ *
+ * Using a Factory method to return a new instance allows
+ * us to return different versions of ClassDepend, as we have
+ * here by overriding the default methods for debugging.
+ */
+ ClassDepend classDepend = !warn
+ ? new ClassDepend(classpath, platform)
+ : new ClassDepend(classpath, platform) {
+ protected void noteClassNotFound(String name) {
+ System.err.println("Warning: Class not found: " + name);
+ }
+ protected void noteClassLoadingFailed(String name, IOException e) {
+ System.err.println("Warning: Problem loading class "
+ + name + ": " + e.getMessage());
+ }
+ };
+ return classDepend;
+ }
+
+ public static void main(String[] args) {
+ try {
+ CDPBuilder cdpb = new CDPBuilder();
+ String classpath = null;
+ String platform = null;
+ boolean warn = false; //supress exceptions, print to error, warn instead
+ boolean files = false; //print class with file path separator
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
+ if (arg.equals("-cp")) {
+ classpath = args[++i];
+ } else if (arg.equals("-platform")) {
+ platform = args[++i];
+ } else if (arg.equals("-exclude")) {
+ cdpb.addOutsidePackageOrClass(args[++i]);
+ } else if (arg.equals("-norecurse")) {
+ cdpb.recurse(false);
+ } else if (arg.equals("-warn")) {
+ warn = true;
+ } else if (arg.equals("-files")) {
+ files = true;
+ } else if (arg.startsWith("-")) {
+ throw new IllegalArgumentException("Bad option: " + arg);
+ } else {
+ cdpb.addRootClass(arg);
+ }
+ }
+ ClassDependParameters cdp = cdpb.build();
+ ClassDepend classDepend = ClassDepend.newInstance(classpath, platform, warn);
+ String[] dependencies = (String[]) classDepend.compute(cdp).toArray(new String[0]);
+ Arrays.sort(dependencies);
+ int l = dependencies.length;
+ for ( int i = 0 ; i < l ; i++) {
+ String cl = dependencies[i];
+ if (files) {
+ cl = cl.replace('.', File.separatorChar).concat(".class");
+ }
+ System.out.println(cl);
+ }
+ } catch (Throwable e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ /**
+ * Creates an instance of this class. The <code>classpath</code> argument
+ * specifies the classpath that will be used to look up the class bytecode
+ * for classes whose dependencies are being computed. If the value
+ * specified is <code>null</code>, then the system class loader will be
+ * used. Otherwise, a {@link URLClassLoader} will be constructed using the
+ * URLs specified by <code>classpath</code> and with a parent class loader
+ * that is the parent of the system class loader. The
+ * <code>platform</code> argument specifies the classpath that will be used
+ * to find classes that should be considered part of the platform and
+ * should be excluded from consideration when computing dependencies. If
+ * the value specified is <code>null</code>, then the parent of the system
+ * class loader will be used. Otherwise, a <code>URLClassLoader</code>
+ * will be constructed using the URLs specified by <code>platform</code>
+ * and with a parent class loader that is the parent of the system class
+ * loader.
+ *
+ * @param classpath the classpath for finding classes, or
+ * <code>null</code>
+ * @param platform the classpath for finding platform classes, or
+ * <code>null</code>
+ * @throws MalformedURLException if the URLs specified in the arguments
+ * are malformed
+ * @throws IOException if an I/O error occurs while accessing files in the
+ * classpath
+ */
+ ClassDepend(String classpath, String platform)
+ throws MalformedURLException, IOException {
+ if (classpath == null) {
+ classpath = systemClasspath;
+ }
+ ClassLoader system = ClassLoader.getSystemClassLoader();
+ ClassLoader parent = system.getParent();
+ loader = (systemClasspath.equals(classpath))
+ ? system
+ : new URLClassLoader(getClasspathURLs(classpath), parent);
+ packageClasses = new PackageClasses(classpath);
+ platformLoader = (platform == null)
+ ? parent
+ : new URLClassLoader(getClasspathURLs(platform), parent);
+ //System.out.println(platformLoader.toString());
+ }
+
+ /**
+ * Computes information about class dependencies. The computation of
+ * dependencies starts with the classes that match the names in
+ * <code>roots</code>. Classes are found in a URL class loader by the
+ * <code>classpath</code> specified in the constructor. Classes that can
+ * be found in the class loader specified by the <code>platform</code>
+ * argument to the constructor are excluded from the computation. Classes
+ * that match names in <code>excludes</code> are excluded from the
+ * computation. Dependencies will be computed recursively if
+ * <code>recurse</code> is <code>true</code>; otherwise, only classes
+ * directly referenced by classes named in <code>roots</code> will be
+ * included.
+ *
+ * @param cdp The immutable parameter class.
+ * @see ClassDependParameters, CDPBuilder
+ * @return a set of the dependent classes
+ * @throws ClassNotFoundException if a class is not found and this
+ * exception is thrown by {@link #noteClassNotFound
+ * noteClassNotFound}
+ * @throws IOException if an I/O error occurs while reading class
+ * bytecodes and this exception is thrown by {@link
+ * #noteClassLoadingFailed noteClassLoadingFailed}
+ */
+ public Set compute(ClassDependParameters cdp)
+ throws ClassNotFoundException, IOException
+ {
+ // Collection roots, Collection excludes, boolean recurse
+ Set compute = computeClasses(cdp.rootClasses()); // Discovers all root classes including those in packages
+ Pattern excludePattern = createPattern(cdp.outsidePackagesOrClasses());
+ Pattern includePattern = createPattern(cdp.insidePackages());
+ Pattern hidePattern = createPattern(cdp.hidePackages());
+ Pattern showPattern = createPattern(cdp.showPackages());
+ Set result = new HashSet();
+ Set seen = new HashSet();
+
+ while (!compute.isEmpty()) {
+ Set computeNext = new HashSet(); //built from
+ Iterator computeIterator = compute.iterator();
+ while (computeIterator.hasNext()) {
+ String name = (String) computeIterator.next();
+ // filter out the crap
+ if (!seen.contains(name) &&
+ ( !cdp.excludePlatformClasses() || !classPresent(name, platformLoader) ) &&
+ !matches(name, excludePattern) &&
+ ( cdp.insidePackages().size() == 0 || matches(name, includePattern)))
+ {
+ /* If we have shows or hides, we don't want to add these to
+ * the result, however we don't want to drop out of the
+ * dependency search either
+ */
+ seen.add(name);
+ if(( cdp.hidePackages().size() == 0 || !matches(name,hidePattern) )
+ && ( cdp.showPackages().size() == 0 || matches(name, showPattern)))
+ {
+ result.add(name); //Add the result after filtering
+ }
+ String resource = getResourceName(name);
+ if (cdp.recurse()) {
+ InputStream in = loader.getResourceAsStream(resource);
+ if (in == null) {
+ noteClassNotFound(name);
+ } else {
+ try {
+ // Discover the referenced classes by loading classfile and inspecting
+ computeNext.addAll(
+ ReferencedClasses.compute(
+ new BufferedInputStream(in)));
+ } catch (IOException e) {
+ noteClassLoadingFailed(name, e);
+ } finally {
+ try {
+ in.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ } else if (loader.getResource(resource) == null) {
+ noteClassNotFound(name);
+ }
+ }
+ }
+ compute = computeNext;
+ }
+ return result;
+ }
+
+ /**
+ * Called when the specified class is not found. <p>
+ *
+ * This implementation throws a <code>ClassNotFoundException</code>.
+ *
+ * @param name the class name
+ * @throws ClassNotFoundException to signal that processing should
+ * terminate and the exception should be thrown to the caller
+ */
+ protected void noteClassNotFound(String name)
+ throws ClassNotFoundException
+ {
+ throw new ClassNotFoundException("Class not found: " + name);
+ }
+
+ /**
+ * Called when attempts to load the bytecodes for the specified class fail.
+ *
+ * @param name the class name
+ * @param e the exception caused by the failure
+ * @throws IOException to signal that processing should terminate and the
+ * exception should be thrown to the caller
+ */
+ protected void noteClassLoadingFailed(String name, IOException e)
+ throws IOException
+ {
+ throw e;
+ }
+
+ /**
+ * Returns the classes in the classpath that match the specified names by
+ * expanding package wildcards.
+ */
+ private Set computeClasses(Collection names)
+ throws IOException
+ {
+ Set result = new HashSet();
+ Iterator namesIterator = names.iterator();
+ while (namesIterator.hasNext()) {
+ String name = (String) namesIterator.next();
+ if (name.endsWith(".*")) {
+ name = name.substring(0, name.length() - 2);
+ result.addAll(packageClasses.compute(false, name));
+ } else if (name.endsWith(".**")) {
+ name = name.substring(0, name.length() - 3);
+ result.addAll(packageClasses.compute(true, name));
+ } else {
+ result.add(name);
+ }
+ }
+ return result;
+ }
+
+ /** Returns the URLs associated with a classpath. */
+ private URL[] getClasspathURLs(String classpath)
+ throws MalformedURLException
+ {
+ StringTokenizer tokens =
+ new StringTokenizer(classpath, File.pathSeparator);
+ URL[] urls = new URL[tokens.countTokens()];
+ for (int i = 0; tokens.hasMoreTokens(); i++) {
+ String file = tokens.nextToken();
+ try {
+ urls[i] = new File(file).toURL();
+ } catch (MalformedURLException e) {
+ urls[i] = new URL(file);
+ }
+ }
+ return urls;
+ }
+
+ /** Checks if the class is present in the given loader. */
+ private boolean classPresent(String name, ClassLoader loader) {
+ return loader.getResource(getResourceName(name)) != null;
+ }
+
+ /** Returns the name of the resource associated with a class name. */
+ private String getResourceName(String classname) {
+ return classname.replace('.', '/').concat(".class");
+ }
+
+ /**
+ * Creates a pattern that matches class names for any of the names in the
+ * argument. Returns null if the argument is empty. xNames that end in
+ * '.*' match classes in the package, names that end in '.**' match classes
+ * in the package and it's subpackage. Other names match the class.
+ */
+ private Pattern createPattern(Collection names) {
+ if (names.isEmpty()) {
+ return null;
+ }
+ StringBuffer sb = new StringBuffer();
+ boolean first = true;
+ Iterator namesItr = names.iterator();
+ while (namesItr.hasNext()) {
+ String name = (String) namesItr.next();
+ if (!first) {
+ sb.append('|');
+ } else {
+ first = false;
+ }
+ if (name.endsWith(".*")) {
+ sb.append(
+ quote( name.substring(0, name.length() - 1)) +
+ "[^.]+");
+ } else if (name.endsWith(".**")) {
+ sb.append(
+ quote(name.substring(0, name.length() - 2)) +
+ ".+");
+ } else {
+ sb.append(quote(name));
+ }
+ }
+ return Pattern.compile(sb.toString());
+ }
+
+ /**
+ * Checks if the string matches the pattern, returning false if the pattern
+ * is null.
+ */
+ private boolean matches(String string, Pattern pattern) {
+ return pattern != null && pattern.matcher(string).matches();
+ }
+
+ /**
+ * Returns a literal pattern String for the specified String.
+ * Added to backport Java 1.5 sources to 1.4. adds the functionality
+ * of java.util.regex.Patter.quote() method missing from Java 1.4 version
+ *
+ * This method produces a String that can be used to create a
+ * Pattern that would match the string s as if it were a literal pattern.
+ * Metacharacters or escape sequences in the input sequence
+ * will be given no special meaning.
+ * @param s - The String to be literalised
+ * @return A literal string replacement
+ */
+
+ private String quote(String s) {
+ StringBuffer sb = new StringBuffer(s.length() * 2).append("\\Q");
+ int previousEndQuotationIndex = 0;
+ int endQuotationIndex = 0;
+ while ((endQuotationIndex = s.indexOf("\\E", previousEndQuotationIndex)) >= 0) {
+ sb.append(s.substring(previousEndQuotationIndex, endQuotationIndex));
+ sb.append("\\E\\\\E\\Q");
+ previousEndQuotationIndex = endQuotationIndex + 2;
+ }
+ sb.append(s.substring(previousEndQuotationIndex));
+ sb.append("\\E");
+ String literalPattern = sb.toString();
+ return literalPattern;
+ }
+
+ public boolean printClassesWithFileSeparator() {
+ return printClassesWithFileSeparator;
+ }
+
+ public void setPrintClassesWithFileSeparator(boolean printClassesWithFileSeparator) {
+ this.printClassesWithFileSeparator = printClassesWithFileSeparator;
+ }
+}
Propchange: incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClassDepend.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClassDependParameters.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClassDependParameters.java?rev=765938&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClassDependParameters.java (added)
+++ incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClassDependParameters.java Fri Apr 17 10:32:02 2009
@@ -0,0 +1,366 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.sun.jini.tool.classdepend;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Effectively Immutable parameter class for ClassDepend.
+ * When River transitions to Java 5, this will
+ * allow easy concurrent programming using the new concurrent utils packages.
+ *
+ * This class cannot be instantiated directly, you must use a CDPBuilder to
+ * return a ClassDependParamters object instance.
+ *
+ * @author Peter Firmstone
+ * @see ClassDepend, CDPBuilder
+ */
+public class ClassDependParameters {
+ /* outsidePackagesOrClasses excluded from search ,excludes the names of classes,
+ * or package patterns, that should be excluded from the computation */
+
+ private final String[] outsidePackagesOrClasses;
+ private final String[] rootClasses; // classes were interested in finding dependencies for.
+ private final String[] insidePackages; // package scope to search for dependencies in.
+ private final String[] showPackages; //Show only the dependencies found in these Packages.
+ private final String[] hidePackages; //Hide these packages from output, the dependencies are still calculated.
+ /*
+ * Specifies the fully qualified name of a class for which dependency
+ * information is desired. This option causes the tool to display
+ * information about every class in the dependency graph that references
+ * the specified class. This information is sent to the error stream of
+ * the tool, not to the normal output stream. This option can be specified
+ * zero or more times. If this option is used, all other output options
+ * are ignored, and the normal class output is not produced.
+ * This option is useful for debugging.
+ * Originally concieved in the original ClassDep as the -tells option.
+ */
+ private final String[] printErrStreamInfoOfClassesDependantOn;
+ private final boolean ignoreOuterParentClass; // For internal classes
+ private final boolean recurse;
+ private final boolean excludePlatformClasses;
+
+ private ClassDependParameters(CDPBuilder builder) {
+
+ outsidePackagesOrClasses = (String[]) builder.outsidePackagesOrClasses.toArray(
+ new String[builder.outsidePackagesOrClasses.size()]);
+ rootClasses = (String[]) builder.rootClasses.toArray(
+ new String[builder.rootClasses.size()]);
+ insidePackages = (String[]) builder.insidePackages.toArray(
+ new String[builder.insidePackages.size()]);
+ showPackages = (String[]) builder.showPackages.toArray(
+ new String[builder.showPackages.size()]);
+ hidePackages = (String[]) builder.hidePackages.toArray(
+ new String[builder.hidePackages.size()]);
+ ignoreOuterParentClass = builder.ignoreOuterParentClass;
+ recurse = builder.recurse;
+ excludePlatformClasses = builder.excludePlatformClasses;
+ printErrStreamInfoOfClassesDependantOn = (String[])
+ builder.printErrStreamInfoOfClassesDependantOn.toArray(
+ new String[builder.printErrStreamInfoOfClassesDependantOn.size()]);
+
+ }
+
+ private List cloneArraytoList(String[] array) {
+ /* We can get away with cloning the Array since Strings are immutable.
+ * the copy, a cloned array, has identical object references to String
+ * objects contained in the original. The retrieved ArrayList can be modified
+ * without affecting the original array.
+ */
+ String[] ac = (String[]) array.clone();
+ return Arrays.asList(ac);
+
+ }
+
+ /**
+ * outsidePackagesOrClasses - excluded from search ,excludes the names
+ * of classes, or package patterns, that should be excluded from the
+ * dependency computation
+ * @see ClassDepend
+ * @return outsidePackagesOrClasses
+ */
+ public List outsidePackagesOrClasses() {
+ return cloneArraytoList(outsidePackagesOrClasses);
+ }
+
+ public List rootClasses() {
+ return cloneArraytoList(rootClasses);
+ }
+
+ public List insidePackages() {
+ return cloneArraytoList(insidePackages);
+ }
+
+ public List showPackages() {
+ return cloneArraytoList(showPackages);
+ }
+
+ public List hidePackages() {
+ return cloneArraytoList(hidePackages);
+ }
+
+ public List printErrStreamInfoOfClassesDependantOn() {
+ return cloneArraytoList(printErrStreamInfoOfClassesDependantOn);
+ }
+
+ public boolean ignoreOuterParentClass() {
+ return ignoreOuterParentClass;
+ }
+ /* recurse if <code>true</code>, compute dependencies recursively;
+ * if <code>false</code>, only include classes directly referenced
+ * by classes in <code>roots</code>
+ */
+
+ public boolean recurse() {
+ return recurse;
+ }
+
+ public boolean excludePlatformClasses() {
+ return excludePlatformClasses;
+ }
+
+ public String[] getPrintErrStreamInfoOfClassesDependantOn() {
+ return printErrStreamInfoOfClassesDependantOn;
+ }
+
+ /**
+ * CDPBuilder - to build an immutable ClassDependParameters object, much
+ * like the StringBuilder and String class relationship.
+ *
+ * CDP Builder is not threadsafe.
+ *
+ * Optional Parameters are set by methods that can be chained on the
+ * Builder object, which has a no argument constructor.
+ * The <code>build()</code> method returns the new ClassDependParameters
+ * object, the builder can be used to build as many ClassDependParameter
+ * objects as desired.
+ */
+ public static class CDPBuilder {
+ /* Lists are good for building, they're dynamically resizable
+ * this builder is not threadsafe.
+ */
+
+ private List outsidePackagesOrClasses = new ArrayList();
+ private List rootClasses = new ArrayList();
+ private List insidePackages = new ArrayList();
+ private List showPackages = new ArrayList();
+ private List hidePackages = new ArrayList();
+ private List printErrStreamInfoOfClassesDependantOn = new ArrayList();
+ private boolean ignoreOuterParentClass = false;
+ private boolean recurse = true;
+ private boolean excludePlatformClasses = false;
+
+ public CDPBuilder() {
+ }
+
+ /**
+ * The package patterns or class names to be excluded from the dependency
+ * search results.
+ * @param outsidePackageOrClass Package pattern or Class to be excluded from
+ * dependency checking.
+ * A package pattern ending in .* excludes the packages in the package
+ * root directory, to decend recursively into and exclude subpackages,
+ * the package pattern should end in .**
+ *
+ *
+ * @see ClassDepend, ClassDependParameters
+ * @return CDPBuilder so named optional parameters can be chained
+ */
+ public CDPBuilder addOutsidePackageOrClass(String outsidePackageOrClass) {
+ outsidePackagesOrClasses.add(outsidePackageOrClass);
+ return this;
+ }
+
+ public CDPBuilder addOutsidePackagesOrClasses(String[] outsidePackagesOrClasses) {
+ int l = outsidePackagesOrClasses.length;
+ for (int i = 0; i < l; i++) {
+ this.outsidePackagesOrClasses.add(outsidePackagesOrClasses[i]);
+ }
+ return this;
+ }
+
+ public CDPBuilder addOutsidePackagesOrClasses(List excludes) {
+ outsidePackagesOrClasses.addAll(excludes);
+ return this;
+ }
+
+ public CDPBuilder addRootClass(String rootClass) {
+ rootClasses.add(rootClass);
+ return this;
+ }
+
+ public CDPBuilder addRootClasses(String[] rootClasses) {
+ int l = rootClasses.length;
+ for (int i = 0; i < l; i++) {
+ this.rootClasses.add(rootClasses[i]);
+ }
+ return this;
+ }
+
+ public CDPBuilder addRootClasses(List classes) {
+ rootClasses.addAll(classes);
+ return this;
+ }
+
+ /**
+ * Inside packages limit the scope of the dependency search to
+ * Classes within these packages.
+ * @param insidePackage A String pattern including the fully qualified
+ * package name, followed by .* to capture classes
+ * in the packages root directory or by .** to
+ * include subpackages recursively as well.
+ * @return CDPBuilder - enables optional parameter method chaining.
+ */
+ public CDPBuilder addInsidePackage(String insidePackage) {
+ insidePackages.add(insidePackage);
+ return this;
+ }
+
+ /**
+ * Inside packages limit the scope of the dependency search to
+ * Classes within these packages.
+ * @param insidePackages
+ * @return CDPBuilder - enables optional parameter method chaining.
+ */
+ public CDPBuilder addInsidePackages(String[] insidePackages) {
+ for (int i = 0, l = insidePackages.length; i < l; i++) {
+ this.insidePackages.add(insidePackages[i]);
+ }
+ return this;
+ }
+
+ public CDPBuilder addInsidePackages(List inside) {
+ insidePackages.addAll(inside);
+ return this;
+ }
+
+ public CDPBuilder addShowPackages(String [] showPackages){
+ for (int i = 0, l = showPackages.length; i < l; i++){
+ this.showPackages.add(showPackages[i]);
+ }
+ return this;
+ }
+
+ public CDPBuilder addShowPackages(List showPackages){
+ this.showPackages.addAll(showPackages);
+ return this;
+ }
+
+ public CDPBuilder addShowPackage(String showPackage){
+ this.showPackages.add(showPackage);
+ return this;
+ }
+
+ public CDPBuilder addHidePackages(String [] hidePackages){
+ for (int i = 0, l = hidePackages.length; i < l; i++){
+ this.hidePackages.add(hidePackages[i]);
+ }
+ return this;
+ }
+
+ public CDPBuilder addHidePackages(List hidePackages){
+ this.hidePackages.addAll(hidePackages);
+ return this;
+ }
+
+ public CDPBuilder addHidePackage(String hidePackage){
+ this.hidePackages.add(hidePackage);
+ return this;
+ }
+
+ /**
+ * Specifies the fully qualified name of a class for which dependency
+ * information is desired. This option causes the tool to display
+ * information about every class in the dependency graph that references
+ * the specified class. This information is sent to the error stream of
+ * the tool, not to the normal output stream. This option can be specified
+ * zero or more times. If this option is used, all other output options
+ * are ignored, and the normal class output is not produced.
+ * This option is useful for debugging.
+ * Originally concieved in the original ClassDep as the -tells option.
+ *
+ * @param clas A fully qualified class name for which dependent classes
+ * will print information to the error stream.
+ * @return this
+ */
+ public CDPBuilder printErrStreamInfoOfClassesDependantOn(String clas) {
+ printErrStreamInfoOfClassesDependantOn.add(clas);
+ return this;
+ }
+ /**
+ * Print information to the error stream for all classes
+ * that depend on those in <code>classes</code>, the normal class
+ * output is not produced.
+ * @param classes
+ * @return
+ */
+ public CDPBuilder printErrStreamInfoOfClassesDependantOn(String [] classes){
+ int l = classes.length;
+ for (int i = 0; i < l; i++){
+ this.printErrStreamInfoOfClassesDependantOn.add(classes[i]);
+ }
+ return this;
+ }
+ /**
+ * Print information to the error stream for all classes
+ * that depend on those in <code>classes</code>, the normal class
+ * output is not produced.
+ * @param classes
+ * @return
+ */
+ public CDPBuilder printErrStreamInfoOfClassesDependantOn(List classes) {
+ printErrStreamInfoOfClassesDependantOn.addAll(classes);
+ return this;
+ }
+
+
+
+ public CDPBuilder ignoreOuterParentClass(boolean b) {
+ ignoreOuterParentClass = b;
+ return this;
+ }
+
+ /**
+ * This option causes ClassDepend to inspect class files for dependencies,
+ * if true. If false, ClassDepend doesn't inspect class files, it simply
+ * gathers the names of class files from within Package directories and
+ * JAR files.
+ *
+ * @param b
+ * @return
+ */
+ public CDPBuilder recurse(boolean b) {
+ recurse = b;
+ return this;
+ }
+
+ /**
+ * This optional parameter if true, excludes Java platform classes
+ * from the dependency search.
+ * If false the platform classes returned will depend on the Java
+ * platform and version the test is executing on.
+ * @see ClassDepend, ClassDependParameters
+ * @param b
+ * @return CDPBuilder - enables optional parameter method chaining.
+ */
+ public CDPBuilder excludePlatformClasses(boolean b) {
+ excludePlatformClasses = b;
+ return this;
+ }
+
+ /**
+ * Builds ClassDependParameters immutable object from optional
+ * parameters, execute this method last, after setting all optional
+ * parameters.
+ * @see ClassDependParameters
+ * @return ClassDependParameter object
+ */
+ public ClassDependParameters build() {
+ return new ClassDependParameters(this);
+ }
+ }
+}
Propchange: incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClassDependParameters.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClassDependencyTreeNodes.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClassDependencyTreeNodes.java?rev=765938&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClassDependencyTreeNodes.java (added)
+++ incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClassDependencyTreeNodes.java Fri Apr 17 10:32:02 2009
@@ -0,0 +1,143 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package com.sun.jini.tool.classdepend;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *
+ * @author Peter Firmstone
+ */
+public class ClassDependencyTreeNodes {
+ /* This will get changed to a concurrentHashMap when River transitions to
+ * JDK 1.5
+ */
+ private final Map callers = Collections.synchronizedMap( new HashMap());
+
+ public ClassDependencyTreeNodes(){
+ }
+
+ public void addNode (String callingClass, String dependencyClass, String method){
+ synchronized (callers){
+ if (callers.containsKey(callingClass)){
+ ClassDependencyBranch cdpb = (ClassDependencyBranch) callers.get(method);
+ cdpb.addMethod(dependencyClass, method);
+ }else {
+ ClassDependencyBranch cdpb = new ClassDependencyBranch(callingClass);
+ cdpb.addMethod(dependencyClass, method);
+ callers.put(callingClass, cdpb);
+ }
+ }
+ }
+
+ public List getDependencies ( String callingClass){
+ List deps;
+ synchronized (callers) {
+ deps = ( (ClassDependencyBranch) callers.get(callingClass)).getClassesCalled();
+ }
+ return deps;
+ }
+
+ public List getAllDependencies (){
+ List deps = new ArrayList();
+ synchronized (callers) {
+ Iterator itr = callers.values().iterator();
+ while (itr.hasNext()){
+ deps.addAll( ( (ClassDependencyBranch) itr.next()).getClassesCalled());
+ }
+ }
+ return deps;
+ }
+
+ /*
+ public List getDependencyDetails( String callingClass){
+
+ }
+ */
+
+ static class ClassDependencyBranch {
+ /* The keys are the fully qualified names of methods called, the values,
+ * the String values of the class's fully qualified name containing the
+ * method called. The calling class is stored as a string.
+ */
+ private final Map methodsCalled = Collections.synchronizedMap(new HashMap());
+ private final String callingClass;
+
+ public ClassDependencyBranch (String callingClass){
+ this.callingClass = callingClass;
+ }
+
+ public void addMethod(String classCalled, String method){
+ synchronized (methodsCalled) {
+ if (methodsCalled.containsKey(classCalled)) {
+ Set methods = (Set) methodsCalled.get(classCalled);
+ methods.add(method);
+ }else {
+ /* this set is only ever accessed through the enclosing
+ * synchronized HashMap so it will never be accessed by
+ * concurrent threads, unless its reference escapes of course.
+ */
+ Set methods = new HashSet();
+ methods.add(method); //Only if not already present.
+ methodsCalled.put(classCalled, methods);
+ }
+ }
+
+ }
+
+ public String getCallingClass() {
+ return callingClass;
+ }
+
+ public List calls() {
+ List calls = new ArrayList();
+ synchronized (methodsCalled) {
+ Iterator itr = methodsCalled.keySet().iterator();
+ while (itr.hasNext()) {
+ String key = (String) itr.next();
+ Iterator itrMethods = ( (Set) methodsCalled.get(key)).iterator();
+ while (itrMethods.hasNext()){
+ String method = (String) itrMethods.next();
+ String call = callingClass + " called class: " + key + " via method: " + method;
+ calls.add(call);
+ }
+ }
+ }
+ return calls;
+ }
+
+ public List calls(String classCalled){
+ List calls = new ArrayList();
+ synchronized (methodsCalled) {
+ Iterator itr = ( (Set) methodsCalled.get(classCalled)).iterator();
+ while (itr.hasNext()){
+ calls.add(itr.next());
+ }
+ }
+ return calls;
+ }
+
+ public List getClassesCalled () {
+ List calls = new ArrayList();
+ synchronized (methodsCalled) {
+ Iterator itr = methodsCalled.keySet().iterator();
+ while (itr.hasNext()){
+ calls.add(itr.next());
+ }
+ }
+ return calls;
+ }
+ }
+
+
+}
Propchange: incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClassDependencyTreeNodes.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClasspathPackages.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClasspathPackages.java?rev=765938&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClasspathPackages.java (added)
+++ incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClasspathPackages.java Fri Apr 17 10:32:02 2009
@@ -0,0 +1,152 @@
+package com.sun.jini.tool.classdepend;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/** Utility class for finding the names of packages in a class path. */
+public class ClasspathPackages {
+
+ /**
+ * Prints the packages in the class path to standard output using the
+ * default character encoding. If an argument is specified, it is used as
+ * the class path, otherwise the system class path is used.
+ *
+ * @param args the arguments
+ */
+ public static void main(String[] args) {
+ String classpath;
+ if (args.length == 0) {
+ classpath = System.getProperty("java.class.path");
+ } else if (args.length == 1) {
+ classpath = args[0];
+ } else {
+ throw new IllegalArgumentException(
+ "Usage: java " + ClasspathPackages.class.getName() +
+ " [classpath]");
+ }
+ SortedSet packages = new TreeSet(compute(classpath));
+ Iterator pkgIter = packages.iterator();
+
+ while (pkgIter.hasNext()) {
+ String pkg = (String) pkgIter.next();
+ System.out.println(pkg.length() == 0 ? "[empty package]" : pkg);
+ }
+ }
+
+ /**
+ * Computes the packages in the specified class path. The class path is
+ * interpreted as a list of file names, separated by the {@link
+ * File#pathSeparator File.pathSeparator} character. Empty names are
+ * treated as the current directory, names ending in the {@link
+ * File#separator File.separator} character are treated as directories, and
+ * other names are treated as JAR files. Directories or JAR files that
+ * have errors when they are accessed will be ignored.
+ *
+ * @param classpath the class path
+ * @return the package names
+ */
+ public static Set compute(String classpath) {
+ if (classpath == null) {
+ throw new NullPointerException("The classpath cannot be null");
+ }
+ Set packages = new HashSet();
+ StringTokenizer tokens =
+ new StringTokenizer(classpath, File.pathSeparator);
+ while (tokens.hasMoreTokens()) {
+ String token = tokens.nextToken();
+ if (token.equals("")) {
+ token = ".";
+ }
+ File file = new File(token);
+ if (!file.exists()) {
+ continue;
+ } else if (file.isDirectory()) {
+ String dir = file.getPath();
+ if (!dir.endsWith(File.separator)) {
+ dir += File.separator;
+ }
+ addPackages(dir, dir, packages);
+ } else {
+ JarFile jarFile;
+ try {
+ jarFile = new JarFile(file);
+ } catch (IOException e) {
+ continue;
+ }
+ try {
+ for (Enumeration entries = jarFile.entries();
+ entries.hasMoreElements(); )
+ {
+ JarEntry entry = (JarEntry) entries.nextElement();
+ addPackage( entry, packages);
+ }
+ } finally {
+ try {
+ jarFile.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+ return packages;
+ }
+
+ /**
+ * Adds packages of classes recursively located in the directory, using the
+ * top argument to specify the top level directory containing the package
+ * hierarchy.
+ */
+ private static void addPackages(
+ final String top, String dir, final Set packages)
+ {
+ File file = new File(dir);
+ if (file.exists()) {
+ /* Collect the package names as a side effect */
+ file.listFiles(new FileFilter() {
+ public boolean accept(File child) {
+ String path = child.getPath();
+ String name = path.substring(top.length());
+ if (name.endsWith(".class") && child.isFile()) {
+ int sep = name.lastIndexOf(File.separatorChar);
+ if (sep <= 0) {
+ packages.add("");
+ } else {
+ packages.add(
+ name.substring(0, sep).replace(
+ File.separatorChar, '.'));
+ }
+ } else if (child.isDirectory()) {
+ addPackages(top, path, packages);
+ }
+ return false;
+ }
+ });
+ }
+ }
+
+ /** Adds the package of the class named by the JAR entry, if any */
+ private static void addPackage(JarEntry entry, Set packages) {
+ String name = entry.getName();
+ if (name.endsWith(".class")) {
+ name = name.substring(0, name.length() - 6);
+ int slash = name.lastIndexOf('/');
+ if (slash <= 0) {
+ name = "";
+ } else {
+ name = name.substring(0, slash);
+ name = name.replace('/', '.');
+ }
+ packages.add(name);
+ }
+ }
+}
Propchange: incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ClasspathPackages.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/PackageClasses.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/PackageClasses.java?rev=765938&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/PackageClasses.java (added)
+++ incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/PackageClasses.java Fri Apr 17 10:32:02 2009
@@ -0,0 +1,266 @@
+package com.sun.jini.tool.classdepend;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/** Utility class for finding the names of the classes in a set of packages. */
+public class PackageClasses {
+
+ /** The names of directories in the classpath. */
+ private final Set directories = new HashSet();
+
+ /** The names of files in JAR files in the classpath. */
+ private final Set jarContents = new HashSet();
+
+ /**
+ * Prints the classes in a package in the class path to standard output
+ * using the default character encoding. If second argument is specified,
+ * it is used as the class path, otherwise the system class path is used.
+ *
+ * @param args the arguments
+ * @throws IllegalArgumentException if less than one or more than two
+ * arguments are provided
+ * @throws IOException if an I/O error occurs
+ */
+ public static void main(String[] args) throws IOException {
+ String classpath;
+ if (args.length == 1) {
+ classpath = System.getProperty("java.class.path");
+ } else if (args.length == 2) {
+ classpath = args[1];
+ } else {
+ throw new IllegalArgumentException(
+ "Usage: java " + PackageClasses.class.getName() +
+ " package [classpath]");
+ }
+ PackageClasses pc = new PackageClasses(classpath);
+ SortedSet classes = new TreeSet(pc.compute(args[0]));
+ Iterator classesIter = classes.iterator();
+ while (classesIter.hasNext()) {
+ System.out.println(classesIter.next());
+ }
+ }
+
+ /**
+ * Creates an instance with the specified class path. The class path is
+ * interpreted as a list of file names, separated by the {@link
+ * File#pathSeparator File.pathSeparator} character. Empty names are
+ * treated as the current directory, names ending in the {@link
+ * File#separator File.separator} character are treated as directories, and
+ * other names are treated as JAR files.
+ *
+ * @param classpath the class path
+ * @throws IOException if a problem occurs accessing files in the class
+ * path
+ */
+ public PackageClasses(String classpath) throws IOException {
+ if (classpath == null) {
+ throw new NullPointerException("The classpath cannot be null");
+ }
+ StringTokenizer tokens =
+ new StringTokenizer(classpath, File.pathSeparator);
+ while (tokens.hasMoreTokens()) {
+ String token = tokens.nextToken();
+ if (token.equals("")) {
+ token = ".";
+ }
+ File file = new File(token);
+ if (!file.exists()) {
+ throw new FileNotFoundException(
+ "File or directory not found: " + file);
+ } else if (file.isDirectory()) {
+ String path = file.getPath();
+ if (!path.endsWith(File.separator)) {
+ path += File.separator;
+ }
+ directories.add(path);
+ } else {
+ JarFile jarFile;
+ try {
+ jarFile = new JarFile(file);
+ } catch (IOException e) {
+ IOException e2 = new IOException(
+ "Problem accessing file or directory: " + file);
+ e2.initCause(e);
+ throw e2;
+ }
+ try {
+ for (Enumeration entries = jarFile.entries();
+ entries.hasMoreElements(); )
+ {
+ JarEntry entry = (JarEntry) entries.nextElement();
+ jarContents.add(entry.getName());
+ }
+ } finally {
+ try {
+ jarFile.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a set of the fully qualified names of classes in the specified
+ * packages, not including classes in subpackages of those packages.
+ *
+ * @param packages the packages
+ * @return the class names
+ * @throws IOException if a problem occurs accessing files in the class
+ * path
+ */
+ public Set compute(String[] packages)
+ throws IOException
+ {
+ return compute(false, packages);
+ }
+
+ public Set compute(String packAge)
+ throws IOException
+ {
+ String [] packages = {packAge};
+ return compute(false, packages);
+ }
+
+ /**
+ * Returns a set of the fully qualified names of classes in the specified
+ * packages, optionally including classes in subpackages of those packages.
+ *
+ * @param recurse if <code>true</code>, find classes in subpackages of
+ * the specified packages
+ * @param packages the packages
+ * @return the class names
+ * @throws IOException if a problem occurs accessing files in the class
+ * path
+ */
+ public Set compute(boolean recurse, String[] packages)
+ throws IOException
+ {
+ if (packages == null) {
+ throw new NullPointerException(
+ "The packages argument cannot be null");
+ }
+ Set classes = new HashSet();
+ int l = packages.length;
+ for (int i = 0 ; i < l ; i++) {
+ String pkg = packages[i];
+ if (pkg == null) {
+ throw new NullPointerException(
+ "Elements of the packages argument cannot be null");
+ }
+ String pkgFileName = pkg.replace('.', File.separatorChar);
+ if (!"".equals(pkgFileName)) {
+ pkgFileName += File.separatorChar;
+ }
+ Iterator dirIter = directories.iterator();
+ while (dirIter.hasNext()) {
+ String dir = (String) dirIter.next();
+ File file = new File(dir, pkgFileName);
+ if (file.exists()) {
+ collectClasses(file, recurse, pkg, classes);
+ }
+ }
+ /* JAR files always use forward slashes */
+ if (File.separatorChar != '/') {
+ pkgFileName = pkgFileName.replace(File.separatorChar, '/');
+ }
+ Iterator jarContentIter = jarContents.iterator();
+ while (jarContentIter.hasNext()) {
+ String file = (String) jarContentIter.next();
+ if (file.startsWith(pkgFileName) && file.endsWith(".class")) {
+ file = removeDotClass(file);
+ if (recurse ||
+ file.indexOf('/', pkgFileName.length() + 1) < 0)
+ {
+ /*
+ * Include the file if it is in a subdirectory only if
+ * recurse is true. Otherwise, check that the class
+ * file matches the package name exactly.
+ * -tjb@sun.com (06/07/2006)
+ */
+ classes.add(file.replace('/', '.'));
+ }
+ }
+ }
+ }
+ return classes;
+ }
+
+ /**
+ * Returns a set of the fully qualified names of classes in the specified
+ * packages, optionally including classes in subpackages of those packages.
+ *
+ * @param recurse if <code>true</code>, find classes in subpackages of
+ * the specified package
+ * @param packAge the package
+ * @return the class names
+ * @throws IOException if a problem occurs accessing files in the class
+ * path
+ */
+ public Set compute(boolean recurse, String packAge)
+ throws IOException
+ {
+ String [] packages = {packAge};
+ return compute(recurse, packages);
+ }
+
+ /**
+ * Adds the names of classes in the directory to the set of classes,
+ * recursiving into subdirectories if requested.
+ */
+ private static void collectClasses(File directory,
+ final boolean recurse,
+ final String pkg,
+ final Set classes)
+ throws IOException
+ {
+ final IOException[] failed = { null };
+ File[] result = directory.listFiles(new FileFilter() {
+ public boolean accept(File child) {
+ String name = child.getName();
+ if (name != null) {
+ if (name.endsWith(".class") && child.isFile()) {
+ String classname = removeDotClass(name);
+ if (!"".equals(pkg)) {
+ classname = pkg + '.' + classname;
+ }
+ classes.add(classname);
+ } else if (recurse && child.isDirectory()) {
+ String subpackage =
+ "".equals(pkg) ? name : pkg + '.' + name;
+ try {
+ collectClasses(
+ child, recurse, subpackage, classes);
+ } catch (IOException e) {
+ failed[0] = e;
+ }
+ }
+ }
+ return false;
+ }
+ });
+ if (failed[0] != null) {
+ throw failed[0];
+ } else if (result == null) {
+ throw new IOException(
+ "A problem occurred accessing directory: " + directory);
+ }
+ }
+
+ /** Strips the .class suffix from a string. */
+ private static String removeDotClass(String s) {
+ return s.substring(0, s.length() - 6);
+ }
+}
Propchange: incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/PackageClasses.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ReferencedClasses.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ReferencedClasses.java?rev=765938&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ReferencedClasses.java (added)
+++ incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ReferencedClasses.java Fri Apr 17 10:32:02 2009
@@ -0,0 +1,48 @@
+package com.sun.jini.tool.classdepend;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashSet;
+import java.util.Set;
+import org.objectweb.asm.ClassReader;
+
+/**
+ * A utility class for computing the classes referred to by another class.
+ * This class cannot be instantiated.
+ */
+public class ReferencedClasses {
+
+ /** This class cannot be instantiated. */
+ private ReferencedClasses() {
+ throw new AssertionError();
+ }
+
+ /**
+ * Computes the classes referred to by another class. The argument should
+ * be a input stream containing the bytecodes for the class. The return
+ * value is a set containing the names of the classes referred to by the
+ * class bytecodes in the input. The input stream is left open when this
+ * method returns.
+ *
+ * @param in the input stream containing the class bytecodes
+ * @return a set of the names of the classes referred to by the class
+ * bytecodes
+ * @throws IOException if a I/O failure occurs while reading the class
+ * bytecodes
+ */
+ public static Set compute(InputStream in) throws IOException {
+ if (in == null) {
+ throw new NullPointerException(
+ "The in argument must not be null");
+ }
+ final Set dependencies = new HashSet();
+ new ClassReader(in).accept(
+ new AbstractDependencyVisitor() {
+ protected void addName(String name) {
+ dependencies.add(name);
+ }
+ },
+ ClassReader.SKIP_DEBUG);
+ return dependencies;
+ }
+}
Propchange: incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/ReferencedClasses.java
------------------------------------------------------------------------------
svn:eol-style = native
Re: svn commit: r765938 - /incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/
Posted by Jukka Zitting <ju...@gmail.com>.
Hi,
On Fri, Apr 17, 2009 at 12:32 PM, <jc...@apache.org> wrote:
> Log:
> Changes for RIVER-272.
When submitting code written by others, it's a good idea to mention
the author in the commit message. I would personally have used a
commit message like:
RIVER-272: ClassDep.java relies on Sun specific Internal JDK API
Added com.sun.jini.tool.classdepend classes contributed by Peter Firmstone.
> Added:
> incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/
Again, why not commit this in trunk?
> ==============================================================================
> --- incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/AbstractVisitor.java (added)
> +++ incubator/river/jtsk/skunk/jcosters/src/com/sun/jini/tool/classdepend/AbstractVisitor.java Fri Apr 17 10:32:02 2009
> @@ -0,0 +1,42 @@
> +package com.sun.jini.tool.classdepend;
> +[...]
All source files should start with the standard Apache license header
from http://www.apache.org/legal/src-headers.html:
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
BR,
Jukka Zitting