001: /*
002: * @(#)ClassDependenceAnalyzer.java 1.17 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package dependenceAnalyzer;
029:
030: import java.io.FilenameFilter;
031: import util.ClassFileFinder;
032: import util.Set;
033: import java.util.Enumeration;
034: import java.util.Dictionary;
035: import java.util.Hashtable;
036: import components.*;
037: import util.ClassFile;
038: import java.io.InputStream;
039:
040: /*
041: * ClassDependenceAnalyzer is a concrete subclass of DependenceAnalyzer
042: * that operates class-by-class. It is not used by JavaFilter or
043: * by JavaCodeCompact. It is for use by other dependence analysis
044: * tools, which might operate on a more course-grained basis.
045: *
046: * Following this class definition is an example main program,
047: * in a comment, that shows a simple driver for this class.
048: */
049:
050: public class ClassDependenceAnalyzer extends DependenceAnalyzer {
051:
052: private FilenameFilter filter;
053: private ClassFileFinder finder;
054: private Dictionary nodeDictionary;
055:
056: public ClassDependenceAnalyzer(ClassFileFinder find,
057: FilenameFilter filt) {
058: super ();
059: filter = filt;
060: finder = find;
061: nodeDictionary = new Hashtable();
062: }
063:
064: /*
065: * This analyzer maintains a name-to-node dictionary of
066: * its graph. This lets you lookup nodes by name. It will
067: * return null if the named entity is not in the graph.
068: */
069: public DependenceNode nodeByName(Object name) {
070: return (DependenceNode) (nodeDictionary.get(name));
071: }
072:
073: /*
074: * To conditionally add a node to the graph. It is not
075: * an error to add something that's already in the graph.
076: * The return value is a DependenceNode. It may be
077: * in any state, but if it was not in the graph previous to
078: * this call, it will certainly be DependenceNode.UNANALYZED.
079: * In other words, this call doesn't cause analysis.
080: */
081: public DependenceNode addNodeByName(Object name) {
082: Object t;
083: if ((t = nodeDictionary.get(name)) != null)
084: return (DependenceNode) t;
085: DependenceNode newNode = new ClassDependenceNode(name);
086: nodeDictionary.put(name, newNode);
087: return newNode;
088: }
089:
090: public Enumeration allNodes() {
091: return nodeDictionary.elements();
092: }
093:
094: public int analyzeDependences(DependenceNode n) {
095:
096: if (n.state() != DependenceNode.UNANALYZED)
097: return n.state();
098: if (filter.accept(null, (String) (n.name()))) {
099: // oops. on our excluded list.
100: // mark as excluded and analyzed.
101: n.flags = ClassDependenceNode.EXCLUDED;
102: n.nodeState = DependenceNode.ANALYZED;
103: return DependenceNode.ANALYZED;
104: }
105: ClassDependenceNode classNode = (ClassDependenceNode) n;
106: String className = (String) (classNode.name());
107:
108: /* Deal with array */
109: if (className.startsWith("[")) {
110: int depth = 1;
111: int len = className.length();
112: while (className.substring(depth, len).startsWith("["))
113: depth++;
114:
115: if ((depth + 1) > (len - 1)) {
116: /* This will be primitive type */
117: classNode.nodeState = DependenceNode.PRIMITIVE;
118: return DependenceNode.PRIMITIVE;
119: }
120: className = className.substring(depth + 1, len - 1);
121: n.nodeState = DependenceNode.ANALYZED;
122: return DependenceNode.ANALYZED;
123: }
124:
125: ClassInfo ci = readClassFile(className);
126: if (ci == null) {
127: // sorry. cannot find.
128: // System.out.println("ci = null and its classname " + className);
129: classNode.nodeState = DependenceNode.ERROR;
130: return DependenceNode.ERROR;
131: }
132: if (classNode.nodeDependsOn == null) {
133: classNode.nodeDependsOn = new Set();
134: }
135: DependenceNode other;
136: // we depend on our super, if any.
137: if (ci.super Class != null) {
138: other = this .addNodeByName(ci.super Class.name.string);
139: classNode.super Class = (ClassDependenceNode) other;
140: }
141:
142: // we might depend on our interfaces.
143: // what do you think?
144:
145: //
146: // find all the constants mentioned in the constant
147: // pool. Call them dependences.
148: //
149: ConstantObject ctable[] = ci.getConstantPool().getConstants();
150: if (ctable != null) {
151: int nconst = ctable.length;
152: for (int i = 0; i < nconst; i++) {
153: ConstantObject co = ctable[i];
154: if (co instanceof ClassConstant) {
155: other = this
156: .addNodeByName(((ClassConstant) co).name.string);
157: newDependence(n, other);
158: }
159: }
160: }
161: n.nodeState = DependenceNode.ANALYZED;
162: return DependenceNode.ANALYZED;
163: }
164:
165: public void printMembers(String classname) {
166: ClassInfo ci = readClassFile(classname.replace('.', '/'));
167:
168: if (ci == null) {
169: System.out.println("Cannot find class " + classname);
170: return;
171: }
172:
173: MethodInfo mi[] = ci.methods;
174: for (int i = 0; i < mi.length; i++)
175: System.out.println(ci.className + "."
176: + mi[i].getMethodName());
177:
178: }
179:
180: private ClassInfo readClassFile(String className) {
181: InputStream classFileStream = finder.findClassFile(className);
182: if (classFileStream == null) {
183: return null;
184: }
185: ClassFile cf = new ClassFile(className, classFileStream, false);
186: if (!cf.readClassFile()) {
187: return null;
188: }
189: return cf.clas;
190: }
191:
192: private void newDependence(DependenceNode here, DependenceNode there) {
193: if (here == there)
194: return; // don't depend on self.
195: DependenceArc a = new DependenceArc(here, there, 0);
196: here.nodeDependsOn.addElement(a);
197: there.nodeDependedOn.addElement(a);
198: }
199:
200: }
201:
202: /*
203: * Following is a simple driver program.
204: * Here is an example of its use:
205: *
206: * java ClassDependenceDemo -classpath <your classes> -path java/lang/Object java/io/OptionalDataException
207: *
208: * And the example output (on Personal Java 1.0), showing two
209: * dependence chains of equal length from java.lang.Object to
210: * java.io.OptionalDataException:
211: *
212: * java/lang/Object => java/lang/Class => java/lang/ClassLoader => java/net/URL => java/io/ObjectInputStream => java/io/OptionalDataException
213: * java/lang/Object => java/lang/Class => java/lang/ClassLoader => java/util/Hashtable => java/io/ObjectInputStream => java/io/OptionalDataException
214: *
215: */
216: // example of class dependence analysis
217: // import util.*;
218: // import java.util.Enumeration;
219: // import dependenceAnalyzer.*;
220: //
221: // class ClassDependenceDemo {
222: // ClassFileFinder finder = new ClassFileFinder();
223: // ClassnameFilter filter = new DummyFilter();
224: // ClassDependenceAnalyzer cda = new ClassDependenceAnalyzer( finder, filter );
225: //
226: // private void addToSearchPath( String pathname ){
227: // finder.addToSearchPath( pathname );
228: // }
229: //
230: // private void showOne( String classname ){
231: // DependenceNode node = cda.addNodeByName( classname );
232: // if ( node == null ){
233: // System.out.println("Cannot find class "+classname);
234: // return;
235: // }
236: // cda.analyzeDependences( node );
237: // printDependences( node );
238: // }
239: //
240: // private void showAll(){
241: // cda.analyzeAllDependences();
242: // Enumeration e = cda.allNodes();
243: // int nClasses = 0;
244: // while ( e.hasMoreElements()){
245: // DependenceNode node = (DependenceNode)(e.nextElement());
246: // printDependences( node );
247: // nClasses++;
248: // }
249: // System.out.println("TOTAL OF "+nClasses+" CLASSES");
250: // }
251: //
252: // private void showPath( String srcclassname, String destclassname ){
253: // ClassDependenceNode src = (ClassDependenceNode)cda.addNodeByName( srcclassname );
254: // ClassDependenceNode dest = (ClassDependenceNode)cda.addNodeByName( destclassname );
255: // Set tipset = new Set();
256: // PathComponent r = new PathComponent( null, src );
257: // tipset.addElement( r );
258: // for ( int i = 0; i < 50; i++ ){
259: // Set newtips = new Set();
260: // Enumeration newGrowth = tipset.elements();
261: // while ( newGrowth.hasMoreElements() ){
262: // PathComponent t = (PathComponent)(newGrowth.nextElement() );
263: // if ( t.link.state() == DependenceNode.UNANALYZED )
264: // cda.analyzeDependences( t.link );
265: // t.grow( newtips );
266: // }
267: // if ( newtips.size() == 0 ){
268: // // exhaused the space without finding it.
269: // System.out.println("No path from "+srcclassname+" to "+destclassname );
270: // return;
271: // }
272: // // now see if we got to our destination.
273: // newGrowth = newtips.elements();
274: // boolean foundDest = false;
275: // while ( newGrowth.hasMoreElements() ){
276: // PathComponent t = (PathComponent)(newGrowth.nextElement() );
277: // if ( t.link == dest ){
278: // t.print( System.out );
279: // System.out.println();
280: // foundDest = true;
281: // }
282: // t.link.flags |= PathComponent.INSET;
283: // }
284: // if ( foundDest ){
285: // return;
286: // }
287: // // round and round.
288: // tipset = newtips;
289: // }
290: // System.out.println("Path from "+srcclassname+" to "+destclassname+" is too long" );
291: // }
292: //
293: // private void process( String args[] ){
294: // for ( int i = 0; i < args.length; i++ ){
295: // String a = args[i];
296: // if ( a.equals("-classpath") ){
297: // addToSearchPath( args[++i] );
298: // continue;
299: // }
300: // if ( a.equals("-path") ){
301: // showPath( args[++i], args[++i] );
302: // continue;
303: // }
304: // if ( a.equals("-show") ){
305: // showOne( args[++i] );
306: // continue;
307: // }
308: // if ( a.equals("-showAll") ){
309: // showAll();
310: // continue;
311: // }
312: // // else must be a class name!
313: // cda.addNodeByName( a );
314: // }
315: // }
316: //
317: // public static void main( String args[] ){
318: // new ClassDependenceDemo().process( args );
319: // }
320: //
321: // static void printDependences( DependenceNode node ){
322: // if ( node.state() != DependenceNode.ANALYZED ){
323: // System.out.println( node.name()+" unanalysed");
324: // return;
325: // }
326: // System.out.println( node.name()+" depends on:");
327: // Enumeration e = node.dependsOn();
328: // while ( e.hasMoreElements() ){
329: // DependenceArc a = (DependenceArc)(e.nextElement());
330: // System.out.println(" "+a.to().name() );
331: // }
332: // }
333: //
334: // }
335: //
336: // class DummyFilter extends util.ClassnameFilter{
337: // public boolean accept( java.io.File dir, String className ){
338: // return false;
339: // }
340: // }
341: //
342: // class PathComponent {
343: // PathComponent root;
344: // ClassDependenceNode link;
345: //
346: // public static int INSET = 8;
347: //
348: // PathComponent( PathComponent r, ClassDependenceNode l ){
349: // root = r;
350: // link = l;
351: // }
352: //
353: // void print( java.io.PrintStream o ){
354: // if ( root != null ){
355: // root.print( o );
356: // o.print(" => ");
357: // }
358: // o.print( link.name() );
359: // }
360: //
361: // void grow( Set tipset ){
362: // Enumeration t = link.dependsOn();
363: // while ( t.hasMoreElements() ){
364: // DependenceArc arc = (DependenceArc)(t.nextElement());
365: // ClassDependenceNode to = (ClassDependenceNode) arc.to();
366: // if ( (to.flags&INSET) != 0 )
367: // continue;
368: // tipset.addElement( new PathComponent( this, to ) );
369: // }
370: // }
371: // }
|