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