/* * @(#)MemberDependenceAnalyzer.java 1.21 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 util.*; import java.io.FilenameFilter; import java.util.Enumeration; import java.util.Dictionary; import java.util.Hashtable; /* * This is the main class for member-level dependence analysis * of Java code. The most well-developed example of this is, of course, * JavaFilter. See also the class util.DepgenUtil for some * higher-level functions that operate on this class, without * constituting a main program. */ public class MemberDependenceAnalyzer extends DependenceAnalyzer { public boolean signatureFlag = false; public MemberDependenceAnalyzer( ClassFileFinder find, FilenameFilter filt ){ super( ); cdict = new ClassDictionary( find, filt ); } public void useSignatureDependence(boolean f) { signatureFlag = f; } /* * 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. * Actually, each class maintains such. Use them. */ public DependenceNode nodeByName( Object name ){ MemberName mname = (MemberName)name; return mname.classEntry.lookupMember( mname ); } /* * 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 ){ MemberName mname = (MemberName)name; return mname.classEntry.lookupAddMember( mname, 0 ); } public Enumeration allNodes() { return new MemberEnumeration( allClasses() ); } /* * To cause the state of a node to move from UNANALYZED, * one hopes to ANALYZED, but perhaps to ERROR. * Return value is the new state of the node. */ public int analyzeDependences( DependenceNode n ){ if ( n.state() != DependenceNode.UNANALYZED ) return n.state(); ClassEntry c; if ( n instanceof ClassEntry ){ c = (ClassEntry)n; } else { c = ((MemberName)(n.nodeName)).classEntry; } if ( c.state() == ClassEntry.UNANALYZED ){ c.analyzeClass( cdict, signatureFlag ); } switch ( c.state() ){ case ClassEntry.ERROR: return ( n.nodeState = DependenceNode.ERROR ); case ClassEntry.ANALYZED: return n.nodeState; } // this cannot happen! throw new RuntimeException( Localizer.getString("memberdependenceanalyzer.class_remains_unanalyzed", c.name()) ); } public Enumeration allClasses(){ if ( cdict == null ) return EmptyEnumeration.instance; return cdict.elements(); } public ClassEntry classByName( String cname ){ return cdict.lookupAdd( cname ); } public boolean makeInterface( ClassEntry c ){ return c.makeInterface( cdict ); } /* * Protection loophole. * This exists so that the calling program can add edges * to the dependence graph, in addition to the ones discovered * by the processing in class ClassEntry. JavaFilter, for * example, allows the user to describe the dependences of * native code, which cannot be discovered automatically. * This is because native code might have a dependence on * a data member that is not referenced by any Java code! */ public void addDependenceArc( DependenceNode fromNode, DependenceNode toNode, int arcType ) { DependenceArc a = new DependenceArc( fromNode, toNode, arcType ); fromNode.nodeDependsOn.add( a ); toNode.nodeDependedOn.add( a ); } /* * State. Not public. */ // ClassDictionary cdict; // encapsulation of all class naming and lookup. } class MemberEnumeration implements Enumeration { private Enumeration curEnumeration; private Enumeration classes; MemberEnumeration( Enumeration c ){ if ( c == null ){ curEnumeration = EmptyEnumeration.instance; } else { classes = c; nextEnumeration(); } } public boolean hasMoreElements(){ if ( curEnumeration.hasMoreElements() ) return true; nextEnumeration(); return curEnumeration.hasMoreElements(); } public Object nextElement(){ while (true){ try { Object o = curEnumeration.nextElement(); // got one. Return it. return o; } catch ( java.util.NoSuchElementException e ){ // user called nextElement without calling // hasMoreElements first. This is unusual, // but not strictly illegal. If this is the // empty enumeration, then there is no more // so we just re-throw the exception. Otherwise // we move on to the next class. if ( curEnumeration == EmptyEnumeration.instance ) throw e; nextEnumeration(); } } } private void nextEnumeration() { while ( classes.hasMoreElements() ){ ClassEntry v = (ClassEntry)(classes.nextElement()); Enumeration e = v.members(); if ( (e != null) && e.hasMoreElements() ){ curEnumeration = e; return; } } curEnumeration = EmptyEnumeration.instance; // bad news. } }