/*
* @(#)ClassDependenceAnalyzer.java 1.17 06/10/10
*
* Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*
*/
package dependenceAnalyzer;
import java.io.FilenameFilter;
import util.ClassFileFinder;
import util.Set;
import java.util.Enumeration;
import java.util.Dictionary;
import java.util.Hashtable;
import components.*;
import util.ClassFile;
import java.io.InputStream;
/*
* ClassDependenceAnalyzer is a concrete subclass of DependenceAnalyzer
* that operates class-by-class. It is not used by JavaFilter or
* by JavaCodeCompact. It is for use by other dependence analysis
* tools, which might operate on a more course-grained basis.
*
* Following this class definition is an example main program,
* in a comment, that shows a simple driver for this class.
*/
public class ClassDependenceAnalyzer extends DependenceAnalyzer {
private FilenameFilter filter;
private ClassFileFinder finder;
private Dictionary nodeDictionary;
public ClassDependenceAnalyzer( ClassFileFinder find, FilenameFilter filt ){
super();
filter = filt;
finder = find;
nodeDictionary = new Hashtable();
}
/*
* This analyzer maintains a name-to-node dictionary of
* its graph. This lets you lookup nodes by name. It will
* return null if the named entity is not in the graph.
*/
public DependenceNode nodeByName( Object name ){
return (DependenceNode)(nodeDictionary.get( name ) );
}
/*
* To conditionally add a node to the graph. It is not
* an error to add something that's already in the graph.
* The return value is a DependenceNode. It may be
* in any state, but if it was not in the graph previous to
* this call, it will certainly be DependenceNode.UNANALYZED.
* In other words, this call doesn't cause analysis.
*/
public DependenceNode addNodeByName( Object name ) {
Object t;
if ( (t = nodeDictionary.get( name ) ) != null )
return (DependenceNode)t;
DependenceNode newNode = new ClassDependenceNode( name );
nodeDictionary.put( name, newNode );
return newNode;
}
public Enumeration allNodes() { return nodeDictionary.elements(); }
public int analyzeDependences( DependenceNode n ){
if ( n.state() != DependenceNode.UNANALYZED )
return n.state();
if ( filter.accept( null, (String)(n.name()) ) ){
// oops. on our excluded list.
// mark as excluded and analyzed.
n.flags = ClassDependenceNode.EXCLUDED;
n.nodeState = DependenceNode.ANALYZED;
return DependenceNode.ANALYZED;
}
ClassDependenceNode classNode = (ClassDependenceNode) n;
String className = (String)(classNode.name());
/* Deal with array */
if (className.startsWith("[")) {
int depth = 1;
int len = className.length();
while (className.substring(depth, len).startsWith("["))
depth++;
if ((depth+1) > (len-1)) {
/* This will be primitive type */
classNode.nodeState = DependenceNode.PRIMITIVE;
return DependenceNode.PRIMITIVE;
}
className = className.substring(depth+1, len-1);
n.nodeState = DependenceNode.ANALYZED;
return DependenceNode.ANALYZED;
}
ClassInfo ci = readClassFile( className );
if ( ci == null ){
// sorry. cannot find.
// System.out.println("ci = null and its classname " + className);
classNode.nodeState = DependenceNode.ERROR;
return DependenceNode.ERROR;
}
if ( classNode.nodeDependsOn == null ){
classNode.nodeDependsOn = new Set();
}
DependenceNode other;
// we depend on our super, if any.
if ( ci.superClass != null ){
other = this.addNodeByName( ci.superClass.name.string );
classNode.superClass = (ClassDependenceNode)other;
}
// we might depend on our interfaces.
// what do you think?
//
// find all the constants mentioned in the constant
// pool. Call them dependences.
//
ConstantObject ctable[] = ci.getConstantPool().getConstants();
if ( ctable != null ){
int nconst = ctable.length;
for ( int i = 0; i < nconst ; i++ ){
ConstantObject co = ctable[i];
if ( co instanceof ClassConstant ){
other = this.addNodeByName( ((ClassConstant)co).name.string );
newDependence( n, other );
}
}
}
n.nodeState = DependenceNode.ANALYZED;
return DependenceNode.ANALYZED;
}
public void printMembers( String classname ) {
ClassInfo ci = readClassFile(classname.replace('.', '/'));
if (ci == null) {
System.out.println("Cannot find class " + classname);
return;
}
MethodInfo mi[] = ci.methods;
for (int i = 0; i < mi.length; i++)
System.out.println(ci.className + "." + mi[i].getMethodName());
}
private ClassInfo readClassFile( String className )
{
InputStream classFileStream = finder.findClassFile( className );
if ( classFileStream == null ){
return null;
}
ClassFile cfile = new ClassFile( className, classFileStream, false );
if ( ! cfile.readClassFile() ){
return null;
}
return cfile.cinfo;
}
private void newDependence( DependenceNode here, DependenceNode there )
{
if ( here == there ) return; // don't depend on self.
DependenceArc a = new DependenceArc( here, there, 0 );
here.nodeDependsOn.addElement( a );
there.nodeDependedOn.addElement( a );
}
}
/*
* Following is a simple driver program.
* Here is an example of its use:
*
* java ClassDependenceDemo -classpath <your classes> -path java/lang/Object java/io/OptionalDataException
*
* And the example output (on Personal Java 1.0), showing two
* dependence chains of equal length from java.lang.Object to
* java.io.OptionalDataException:
*
* java/lang/Object => java/lang/Class => java/lang/ClassLoader => java/net/URL => java/io/ObjectInputStream => java/io/OptionalDataException
* java/lang/Object => java/lang/Class => java/lang/ClassLoader => java/util/Hashtable => java/io/ObjectInputStream => java/io/OptionalDataException
*
*/
// example of class dependence analysis
// import util.*;
// import java.util.Enumeration;
// import dependenceAnalyzer.*;
//
// class ClassDependenceDemo {
// ClassFileFinder finder = new ClassFileFinder();
// ClassnameFilter filter = new DummyFilter();
// ClassDependenceAnalyzer cda = new ClassDependenceAnalyzer( finder, filter );
//
// private void addToSearchPath( String pathname ){
// finder.addToSearchPath( pathname );
// }
//
// private void showOne( String classname ){
// DependenceNode node = cda.addNodeByName( classname );
// if ( node == null ){
// System.out.println("Cannot find class "+classname);
// return;
// }
// cda.analyzeDependences( node );
// printDependences( node );
// }
//
// private void showAll(){
// cda.analyzeAllDependences();
// Enumeration e = cda.allNodes();
// int nClasses = 0;
// while ( e.hasMoreElements()){
// DependenceNode node = (DependenceNode)(e.nextElement());
// printDependences( node );
// nClasses++;
// }
// System.out.println("TOTAL OF "+nClasses+" CLASSES");
// }
//
// private void showPath( String srcclassname, String destclassname ){
// ClassDependenceNode src = (ClassDependenceNode)cda.addNodeByName( srcclassname );
// ClassDependenceNode dest = (ClassDependenceNode)cda.addNodeByName( destclassname );
// Set tipset = new Set();
// PathComponent r = new PathComponent( null, src );
// tipset.addElement( r );
// for ( int i = 0; i < 50; i++ ){
// Set newtips = new Set();
// Enumeration newGrowth = tipset.elements();
// while ( newGrowth.hasMoreElements() ){
// PathComponent t = (PathComponent)(newGrowth.nextElement() );
// if ( t.link.state() == DependenceNode.UNANALYZED )
// cda.analyzeDependences( t.link );
// t.grow( newtips );
// }
// if ( newtips.size() == 0 ){
// // exhaused the space without finding it.
// System.out.println("No path from "+srcclassname+" to "+destclassname );
// return;
// }
// // now see if we got to our destination.
// newGrowth = newtips.elements();
// boolean foundDest = false;
// while ( newGrowth.hasMoreElements() ){
// PathComponent t = (PathComponent)(newGrowth.nextElement() );
// if ( t.link == dest ){
// t.print( System.out );
// System.out.println();
// foundDest = true;
// }
// t.link.flags |= PathComponent.INSET;
// }
// if ( foundDest ){
// return;
// }
// // round and round.
// tipset = newtips;
// }
// System.out.println("Path from "+srcclassname+" to "+destclassname+" is too long" );
// }
//
// private void process( String args[] ){
// for ( int i = 0; i < args.length; i++ ){
// String a = args[i];
// if ( a.equals("-classpath") ){
// addToSearchPath( args[++i] );
// continue;
// }
// if ( a.equals("-path") ){
// showPath( args[++i], args[++i] );
// continue;
// }
// if ( a.equals("-show") ){
// showOne( args[++i] );
// continue;
// }
// if ( a.equals("-showAll") ){
// showAll();
// continue;
// }
// // else must be a class name!
// cda.addNodeByName( a );
// }
// }
//
// public static void main( String args[] ){
// new ClassDependenceDemo().process( args );
// }
//
// static void printDependences( DependenceNode node ){
// if ( node.state() != DependenceNode.ANALYZED ){
// System.out.println( node.name()+" unanalysed");
// return;
// }
// System.out.println( node.name()+" depends on:");
// Enumeration e = node.dependsOn();
// while ( e.hasMoreElements() ){
// DependenceArc a = (DependenceArc)(e.nextElement());
// System.out.println(" "+a.to().name() );
// }
// }
//
// }
//
// class DummyFilter extends util.ClassnameFilter{
// public boolean accept( java.io.File dir, String className ){
// return false;
// }
// }
//
// class PathComponent {
// PathComponent root;
// ClassDependenceNode link;
//
// public static int INSET = 8;
//
// PathComponent( PathComponent r, ClassDependenceNode l ){
// root = r;
// link = l;
// }
//
// void print( java.io.PrintStream o ){
// if ( root != null ){
// root.print( o );
// o.print(" => ");
// }
// o.print( link.name() );
// }
//
// void grow( Set tipset ){
// Enumeration t = link.dependsOn();
// while ( t.hasMoreElements() ){
// DependenceArc arc = (DependenceArc)(t.nextElement());
// ClassDependenceNode to = (ClassDependenceNode) arc.to();
// if ( (to.flags&INSET) != 0 )
// continue;
// tipset.addElement( new PathComponent( this, to ) );
// }
// }
// }