Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/port80/eclipse/jdt/graph/FileDependGraphAction.java


1   package com.port80.eclipse.jdt.graph;
2   
3   import java.lang.reflect.InvocationTargetException;
4   import java.util.HashMap;
5   import java.util.HashSet;
6   import java.util.Iterator;
7   import java.util.Map;
8   import java.util.Set;
9   
10  import org.eclipse.core.runtime.IProgressMonitor;
11  import org.eclipse.jdt.core.ISourceRange;
12  import org.eclipse.jdt.core.dom.CompilationUnit;
13  import org.eclipse.jdt.internal.corext.SourceRange;
14  import org.eclipse.jdt.internal.ui.packageview.PackageExplorerPart;
15  import org.eclipse.jface.action.IAction;
16  import org.eclipse.jface.dialogs.ProgressMonitorDialog;
17  import org.eclipse.jface.operation.IRunnableWithProgress;
18  import org.eclipse.jface.viewers.ISelection;
19  import org.eclipse.jface.viewers.ISelectionProvider;
20  import org.eclipse.jface.viewers.IStructuredSelection;
21  import org.eclipse.ui.IWorkbenchWindow;
22  import org.eclipse.ui.IWorkbenchWindowActionDelegate;
23  
24  import com.port80.eclipse.jdt.graph.TypeReferenceASTVisitor.Result;
25  import com.port80.eclipse.jdt.util.JavaUtil;
26  import com.port80.graph.GraphException;
27  import com.port80.graph.IEdge;
28  import com.port80.graph.IGraph;
29  import com.port80.graph.IVertex;
30  import com.port80.graph.dot.impl.Dot;
31  import com.port80.graph.impl.DirectedGraph;
32  import com.port80.util.Msg;
33  import com.port80.util.attr.IAttrTable;
34  
35  /**
36   * Depend Graph depicts the source file and class file type reference relationships.
37   * For source file, all types in the source file are grouped into one node.
38   * For binary class file, nested class are grouped to the node for their enclosing class.
39   * 
40   * Reference is depicted by an edge to the referenced type/file.
41   * To make the graph more readable, edge from an inherited type is removed if
42   * its super class/interface exists in the graph and already has an edge to the same 
43   * destination.
44   * 
45   * Type are represented by vertex. The vertex name is the absolute file path for
46   * source type and fully qualified typename + ".java" for binary type.  Each vertex
47   * also have the following attributes:
48   *
49   * . -fullTypeName (String) Fully qualified type name.
50   * . -isInterface (Boolean) True if type is an interface.
51   * . -superClasses (Set) of fully qualified type name of the superclass.
52   * . -superInterfaceName (Set) of fully qualified type name of the super interfaces.
53   * . -references (Set) of fully qualified type name of referenced types.
54   * 
55   * @deprecated Superceded by DependGraphAction.
56   * @see DependGraphAction
57   * @see IWorkbenchWindowActionDelegate
58   */
59  public class FileDependGraphAction implements IWorkbenchWindowActionDelegate,IGraphAction {
60  
61    ////////////////////////////////////////////////////////////////////////
62  
63    private static final String NAME = "DependGraphAction";
64    private static final boolean DEBUG = true;
65    private static final boolean VERBOSE = true;
66    // A hack to print the class heirarchy graph before extracting the shared methods to a utility class.
67    private static final boolean CLASSGRAPH = true;
68  
69    private static final String COLOR_TOP = "0xffe040"; // "ff9090";
70    private static final String COLOR_CLASS = "0xffff20";
71    private static final String COLOR_INTERFACE = "0xffffe0";
72    private static final String COLOR_SUPERCLASS = "0xff0000";
73    private static final String COLOR_SUPERINTERFACE = "0xff9090";
74    private static final String STYLE_SUPERCLASS = "solid,3.0";
75    private static final String STYLE_SUPERINTERFACE = "dotted,3.0";
76  
77    private ISourceRange fParsingErrorRange;
78    private IWorkbenchWindow fWindow;
79    private Object[] fSelected;
80    private IGraph fGraph;
81    
82    ////////////////////////////////////////////////////////////////////////
83  
84    /**
85     * The constructor.
86     */
87    public FileDependGraphAction() {}
88  
89    /**
90     * Find depend graph for selected package.
91     * 
92     * //FIXME: For now, only look at source files.
93     */
94    public void run(IAction action) {
95      PackageExplorerPart view = JavaUtil.getPackageView();
96      if (view == null)
97        return;
98      ISelectionProvider provider = (ISelectionProvider) view.getSite().getSelectionProvider();
99      fSelected = ((IStructuredSelection) provider.getSelection()).toArray();
100     if (fSelected.length == 0)
101       return;
102     try {
103       new ProgressMonitorDialog(fWindow.getShell()).run(true, true, new IRunnableWithProgress() {
104         public void run(IProgressMonitor monitor) {
105           dependGraph(monitor);
106         }
107       });
108     } catch (InvocationTargetException e) {
109       Msg.err(e);
110     } catch (InterruptedException e) {
111       Msg.warn(NAME + ",run(IAction): Cancelled");
112     }
113     if(fGraph==null) return;
114     if (VERBOSE)
115       Msg.println(fGraph.sprintGraph());
116     Dot.dot(fGraph, -5, 1);
117     if (VERBOSE)
118       Msg.println(fGraph.sprintGraph());
119     GraphUtil.saveGraph(fGraph);
120   }
121 
122   /**
123    * Selection in the workbench has been changed. We 
124    * can change the state of the 'real' action here
125    * if we want, but this can only happen after 
126    * the delegate has been created.
127    * @see IWorkbenchWindowActionDelegate#selectionChanged
128    */
129   public void selectionChanged(IAction action, ISelection selection) {}
130 
131   /**
132    * We can use this method to dispose of any system
133    * resources we previously allocated.
134    * @see IWorkbenchWindowActionDelegate#dispose
135    */
136   public void dispose() {}
137 
138   /**
139    * We will cache window object in order to
140    * be able to provide parent shell for the message dialog.
141    * NOTE: This is not called if action is activated from a context menu (eg. from JDT package view).
142    * @see IWorkbenchWindowActionDelegate#init
143    */
144   public void init(IWorkbenchWindow window) {
145     fWindow=window;
146   }
147 
148   ////////////////////////////////////////////////////////////////////////
149 
150   /** 
151    * Find type references in a compiled AST.
152    * 
153    * @return Number of references found.  A vertex is created in the graph for the srcpath and
154    * vertex attributes -references, -superClasses, -superInterfaces holds the reference information.
155    * @param srcpath The full path of the source unit, full type name (without .java) for binary class file.
156    * @param ast The compiled ast for the source unit.
157    * @param graph The return graph updated with the references.
158    * @param filemap Return superclass map (srcpath->typename).
159    */
160   public int addRef(String srcpath, CompilationUnit ast, IGraph graph, Map filemap) {
161     Set keys;
162     int n = 0;
163     IVertex v = GraphUtil.newVertex(srcpath, graph);
164     if (v == null) {
165       Msg.err(NAME + ".addRef(): v==null: srcpath=" + srcpath);
166       return 0;
167     }
168     TypeReferenceASTVisitor visitor = null;
169     try {
170       visitor = TypeReferenceASTVisitor.startAt(ast);
171     } catch (ASTError e) {
172       fParsingErrorRange = new SourceRange(e.errorNode.getStartPosition(), e.errorNode.getLength());
173       if (DEBUG)
174         System.err.println(NAME + ".addRef(): parse error: " + e.errorNode.toString());
175     }
176     if (visitor == null) {
177       Msg.err(NAME + ".addRef(): parse error: visitor==null: srcpath=" + srcpath);
178       return 0;
179     }
180 
181     TypeReferenceASTVisitor.Result ret = visitor.getResult();
182     //
183     Map declares = ret.getDeclares();
184     keys = declares.keySet();
185     for (Iterator it = keys.iterator(); it.hasNext();) {
186       String typename = (String) it.next();
187       filemap.put(typename, srcpath);
188     }
189     //
190     Map references;
191     Set dest;
192     if (CLASSGRAPH) {
193       dest = (Set) v.getAttr("-references");
194       if (dest == null) {
195         dest = new HashSet();
196         v.setAttr("-references", dest);
197       }
198       references = ret.getSuperClasses();
199       keys = references.keySet();
200       dest.addAll(keys);
201       n += keys.size();
202       references = ret.getSuperInterfaces();
203       keys = references.keySet();
204       dest.addAll(keys);
205       n += keys.size();
206     } else {
207       int kind = ret.getKind();
208       if (kind == Result.INTERFACE) {
209         v.setAttr("-isInterface", true);
210         references = ret.getSuperInterfaces();
211       } else {
212         references = ret.getReferences();
213       }
214       dest = (Set) v.getAttr("-references");
215       if (dest == null) {
216         dest = new HashSet();
217         v.setAttr("-references", dest);
218       }
219       keys = references.keySet();
220       dest.addAll(keys);
221       n += keys.size();
222     }
223     //
224     dest = (Set) v.getAttr("-superClasses");
225     if (dest == null) {
226       dest = new HashSet();
227       v.setAttr("-superClasses", dest);
228     }
229     Map supers = ret.getSuperClasses();
230     if (supers.size() > 0) {
231       dest.addAll(supers.keySet());
232       n += supers.size();
233     }
234     //
235     dest = (Set) v.getAttr("-superInterfaces");
236     if (dest == null) {
237       dest = new HashSet();
238       v.setAttr("-superInterfaces", dest);
239     }
240     supers = ret.getSuperInterfaces();
241     if (supers.size() > 0) {
242       dest.addAll(supers.keySet());
243     }
244     if (DEBUG)
245       System.err.println(NAME + ".addRef(): srcpath=" + srcpath + ", added=" + n);
246     return n;
247   }
248 
249   ////////////////////////////////////////////////////////////////////////
250 
251   void dependGraph(IProgressMonitor monitor) {
252     fGraph=dependGraph(fSelected,monitor);
253   }
254       
255   /** 
256    * Construct class dependency graph between classes in given objects base on type references.
257    * 
258    * @return The graph with type as vertices and reference relationship as edges.
259    * @param selected The JavaElement operate on.
260    */
261   IGraph dependGraph(Object[] selected, IProgressMonitor monitor) {
262     final String PREFIX = NAME + ".dependGraph(): ";
263     // Fully qualified typename->filename mapping for source/binary types among the selected objects.
264     Map filemap = new HashMap();
265     IGraph graph = new DirectedGraph("DependGraph", null, null);
266     //graph.setAttr("ranksep", 1.1);
267     //graph.setAttr("nodesep", 1.1);
268     graph.setAttr("criticaluseinbus", true);
269     IAttrTable vtable = graph.getVertexAttrTable();
270     vtable.setAttr("fontsize", 11.0);
271     vtable.setAttr("inthreshold", 6);
272     vtable.setAttr("outthreshold", 6);
273     //
274     // Create vertices.
275     //
276     GraphUtil.resolveReferences(selected,graph,filemap,this, monitor);
277     if(monitor.isCanceled()) {
278       if(VERBOSE) Msg.warn(NAME+".dependGraph(): Canceled");
279       return null;
280     }
281     //
282     // Create edges.
283     //
284     Set vset = graph.getVertexSet();
285     for (Iterator uit = vset.iterator(); uit.hasNext();) {
286       IVertex src = (IVertex) uit.next();
287       Set references = (Set) src.getAttr("-references");
288       if (references == null)
289         continue;
290       for (Iterator it = references.iterator(); it.hasNext();) {
291         String desttype = (String) it.next();
292         String destpath = (String) filemap.get(desttype);
293         if (destpath == null)
294           continue;
295         IVertex dest = graph.getVertex(destpath);
296         if (dest == null)
297           continue;
298         if (dest.equals(src))
299           continue;
300         if (src.findEdgesTo(dest,null) != null)
301           continue;
302         try {
303           graph.newEdge(src, dest, null, null);
304         } catch (GraphException e) {
305           Msg.err(PREFIX + "new edge: src=" + src + ", dest=" + dest, e);
306         }
307       }
308     }
309     //
310     Set vertices = graph.allVertices();
311     for (Iterator it = vertices.iterator(); it.hasNext();) {
312       IVertex v = (IVertex) it.next();
313       removeInheritedDepends(graph, v, filemap);
314     }
315     for (Iterator it = vertices.iterator(); it.hasNext();) {
316       IVertex v = (IVertex) it.next();
317       if (DEBUG)
318         Msg.println(PREFIX + "v=" + v + ", inSize=" + v.inSize());
319       //if(v.inSize()==0) v.setAttrFromString("style","filled,3.0");
320       if (v.inSize() == 0 && v.outSize()!=0) {
321         //v.setAttrFromString("fillcolor", COLOR_TOP); //style","solid,3.0");
322         v.setAttr("label", "<b>" + v.getAttr("label") + "</b>");
323       }
324       if (v.getAttr("-isInterface") == null)
325         v.setAttrFromString("fillcolor", COLOR_CLASS);
326       else
327         v.setAttrFromString("fillcolor", COLOR_INTERFACE);
328     }
329     return graph;
330   }
331 
332   /**
333    *  Remove self edge and dependency that are already present in super classes.
334    *  Color the edges that involve inheritance.
335    */
336   private void removeInheritedDepends(IGraph graph, IVertex vertex, Map filemap) {
337     Set supers = (Set) vertex.getAttr("-superClasses");
338     Set ret = new HashSet();
339     if (supers == null)
340       supers = new HashSet();
341     else
342       findSuperConnected(vertex, supers, graph, filemap, ret);
343     //
344     // Make sure edge to super class and super interfaces stay.
345     //
346     Set superif = (Set) vertex.getAttr("-superInterfaces");
347     if (superif != null) {
348       supers.addAll(superif);
349     }
350     Set keeps = GraphUtil.findVerticesFromTypenames(supers, graph, filemap);
351     //
352     IVertex v;
353     IEdge e;
354     int n = 0;
355     IEdge[] outs = vertex.outs();
356     if (DEBUG) {
357       Msg.println(NAME + ".removeInheritedDepends(): vertex=" + vertex.getName());
358       for (Iterator it = supers.iterator(); it.hasNext();) {
359         Msg.println("\tsuper= " + (String) it.next());
360       }
361     }
362     for (int i = 0; i < outs.length; ++i) {
363       e = outs[i];
364       v = e.getHead();
365       if (keeps.contains(v)) {
366         e.setAttr("critical", true);
367         if (v.getAttr("-isInterface") != null) {
368           e.setAttrFromString("style", STYLE_SUPERINTERFACE);
369           e.setAttrFromString("color", COLOR_SUPERINTERFACE);
370         } else {
371           e.setAttrFromString("style", STYLE_SUPERCLASS);
372           e.setAttrFromString("color", COLOR_SUPERCLASS);
373         }
374         e.setAttr("weight", v.getAttrInt("weight", 1) * 4);
375         continue;
376       }
377       if (!ret.contains(v) && !v.equals(vertex))
378         continue;
379       v.removeInsFrom(vertex);
380       n += vertex.removeOutsTo(v);
381       if (DEBUG)
382         Msg.println("\tremoved= " + v.getName());
383     }
384   }
385 
386   /**
387    *  Find all vertices that can be reached by the given set of vertices.
388    *  @param ret Return the set of reachable vertices.
389    */
390   private void findSuperConnected(IVertex vertex, Set supers, IGraph graph, Map filemap, Set ret) {
391     if (supers == null)
392       return;
393     IVertex v;
394     for (Iterator it = supers.iterator(); it.hasNext();) {
395       String typename = (String) it.next();
396       String fullpath = (String) filemap.get(typename);
397       if (fullpath == null)
398         continue;
399       v = graph.getVertex(fullpath);
400       if (v == null)
401         continue;
402       if (v.equals(vertex))
403         // It is possible that a nested class may have its super in the same
404         // file as itself, in such case, don't recurse.
405         continue;
406       IEdge[] outs = v.outs();
407       for (int i = 0; i < outs.length; ++i) {
408         ret.add(outs[i].getHead());
409       }
410       supers = (Set) v.getAttr("-superClasses");
411       if (supers == null || supers.size() == 0)
412         continue;
413       findSuperConnected(v, supers, graph, filemap, ret);
414     }
415   }
416 
417   ////////////////////////////////////////////////////////////////////////
418 }