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/DependGraphAction.java


1   package com.port80.eclipse.jdt.graph;
2   
3   import java.awt.Color;
4   import java.io.File;
5   import java.io.FileNotFoundException;
6   import java.io.FileOutputStream;
7   import java.io.PrintWriter;
8   import java.lang.reflect.InvocationTargetException;
9   import java.util.HashMap;
10  import java.util.HashSet;
11  import java.util.Iterator;
12  import java.util.Map;
13  import java.util.Set;
14  import java.util.TreeSet;
15  
16  import org.eclipse.core.runtime.IProgressMonitor;
17  import org.eclipse.core.runtime.Path;
18  import org.eclipse.core.runtime.SubProgressMonitor;
19  import org.eclipse.jdt.core.ISourceRange;
20  import org.eclipse.jdt.core.dom.CompilationUnit;
21  import org.eclipse.jdt.core.dom.IMethodBinding;
22  import org.eclipse.jdt.core.dom.ITypeBinding;
23  import org.eclipse.jdt.core.dom.Modifier;
24  import org.eclipse.jdt.internal.corext.SourceRange;
25  import org.eclipse.jdt.internal.ui.packageview.PackageExplorerPart;
26  import org.eclipse.jface.action.IAction;
27  import org.eclipse.jface.dialogs.ProgressMonitorDialog;
28  import org.eclipse.jface.operation.IRunnableWithProgress;
29  import org.eclipse.jface.viewers.ISelection;
30  import org.eclipse.jface.viewers.ISelectionProvider;
31  import org.eclipse.jface.viewers.IStructuredSelection;
32  import org.eclipse.swt.SWT;
33  import org.eclipse.swt.widgets.FileDialog;
34  import org.eclipse.swt.widgets.Shell;
35  import org.eclipse.ui.IWorkbenchWindow;
36  import org.eclipse.ui.IWorkbenchWindowActionDelegate;
37  import org.eclipse.ui.PartInitException;
38  
39  import com.port80.eclipse.jdt.JdtPlugin;
40  import com.port80.eclipse.jdt.util.JavaUtil;
41  import com.port80.eclipse.util.GraphEditorInput;
42  import com.port80.graph.GraphException;
43  import com.port80.graph.IEdge;
44  import com.port80.graph.IGraph;
45  import com.port80.graph.IVertex;
46  import com.port80.graph.dot.impl.Dot;
47  import com.port80.graph.impl.DirectedGraph;
48  import com.port80.util.Msg;
49  import com.port80.util.attr.ColorFactory;
50  import com.port80.util.attr.IAttrTable;
51  
52  /**
53   * Extract an method call graph in UML like format for a given set of classes (<b>'selected'</b> objects).
54   * 
55   * Each vertex represent either a caller or a callee. 
56   * Caller is a method or a class labelled with method that calls external methods.
57   * Callee is method that is called by a caller.
58   * Edges of type '-hasCalls' represent calls from the caller vertex to the callee vertex.
59   * Edges of type '-typehier' represent type herierhical from superclass to derived classes.
60   * Edges of type '-implemented' represent that the head vertex (method) implements tail vertex (interface).
61   * 
62   * @see IWorkbenchWindowActionDelegate
63   */
64  public class DependGraphAction implements IWorkbenchWindowActionDelegate {
65  
66    ////////////////////////////////////////////////////////////////////////
67  
68    private static final String NAME = "DependGraphAction";
69    private static final boolean DEBUG = false;
70    private static final boolean VERBOSE = true;
71  
72    // Vertex color
73    private static final Color COLOR_TOP = ColorFactory.create("0xffe040"); // "ff9090";
74    private static final Color COLOR_UNCONNECTED = ColorFactory.create("0xc0c0c0");
75    private static final Color COLOR_NESTED_CLASS = ColorFactory.create("0xffe020");
76    private static final Color COLOR_CLASS = ColorFactory.create("0xffff20");
77    private static final Color COLOR_INTERFACE = ColorFactory.create("0xffffe0");
78    // "0xb0e0ff"; // "0xffd0a0"; // "0xa8d8ff"; // azure1"; // "0xfff0e8";
79    private static final Color COLOR_METHOD = ColorFactory.create("0xb0f040"); // "greenyellow";
80    private static final Color COLOR_CONSTRUCTOR = ColorFactory.create("0xc8e8c8"); // "greenyellow";
81    private static final Color COLOR_CALLEE_CLASS = ColorFactory.create("0xe8e890");
82    private static final Color COLOR_CALLEE_INTERFACE = ColorFactory.create("0xf0f0d0");
83    private static final Color COLOR_CALLEE_METHOD = ColorFactory.create("0xd8f8d8");
84    // Edge color
85    private static final Color COLOR_SUPERCLASS = ColorFactory.create("0xff0000");
86    private static final Color COLOR_SUPERINTERFACE = ColorFactory.create("0xff9090");
87    private static final Color COLOR_OVERRIDEN = ColorFactory.create("0xff0000");
88    private static final Color COLOR_IMPLEMENTED = ColorFactory.create("0xff9090");
89    //
90    private static final String STYLE_UNCONNECTED = "dotted";
91    private static final String STYLE_SUPERCLASS = "solid,3.0";
92    private static final String STYLE_SUPERINTERFACE = "solid,3.0";
93    private static final String STYLE_OVERRIDEN = "dotted,3.0";
94    private static final String STYLE_IMPLEMENTED = "dotted,3.0";
95    //
96    private static final String STYLE = "solid";
97    private static final String STYLE_CALLEE = "none";
98    private static final String STYLE_TOP = "solid,3.0";
99    //private static final String ARROW_SUPERCLASS = "dot,8";
100   //private static final String ARROW_SUPERINTERFACE = "odot,8";
101   //
102   private static final int MAX_LABELWIDTH = 40;
103   private static final String COLOR_CALLEE = "0xc02020";
104   private static final String COLOR_CALLEE_PARAMS = "0x606060";
105   private static final String COLOR_PUBLIC = "0xe00000";
106   private static final String COLOR_PROTECTED = "0x2020ff"; // "0x0000c0";
107   private static final String COLOR_PACKAGE = "seagreen"; // "0x006000";
108   private static final String COLOR_PRIVATE = "0x000000";
109 
110   static class Options {
111 
112     public static final int DEPEND_METHOD = 0;
113     public static final int DEPEND_REFERENCE = 1;
114     public static final int SCOPE_SELECTED = 0;
115     public static final int SCOPE_CONNECTED = 1;
116     public static final int DETAIL_CLASS = 0;
117     public static final int DETAIL_METHOD = 1;
118     public static final int DETAIL_CLASSWITHMETHOD = 2;
119 
120     public static final String[] MSG_DEPENDS = new String[] { "Method Call", "Type Reference" };
121     public static final String[] MSG_SCOPES = new String[] { "Selected", "Selected + connected" };
122     public static final String[] MSG_DETAILS =
123       new String[] { "Classes", "Methods", "Classes with called methods" };
124 
125     int depend = DEPEND_METHOD;
126     int scope = SCOPE_SELECTED;
127     int detail = DETAIL_CLASS;
128     boolean show = true;
129     String seed;
130     String passes;
131     public int getSeed() {
132       try {
133         return Integer.parseInt(seed);
134       } catch (Exception e) {
135         return -7;
136       }
137     }
138     public int getPasses() {
139       try {
140         return Integer.parseInt(passes);
141       } catch (Exception e) {
142         return -7;
143       }
144     }
145   }
146 
147   ////////////////////////////////////////////////////////////////////////
148 
149   IGraph fGraph;
150   Object[] fSelected;
151   Options fOptions;
152   ISourceRange fParsingErrorRange;
153   String fOutDir;
154 
155   ////////////////////////////////////////////////////////////////////////
156 
157   public DependGraphAction() {
158     fOptions = new Options();
159   }
160 
161   /**
162    * The action has been activated. The argument of the
163    * method represents the 'real' action sitting
164    * in the workbench UI.
165    * @see IWorkbenchWindowActionDelegate#run
166    */
167   public void run(IAction action) {
168     PackageExplorerPart view = JavaUtil.getPackageView();
169     if (view == null)
170       return;
171     ISelectionProvider provider = (ISelectionProvider) view.getSite().getSelectionProvider();
172     fSelected = ((IStructuredSelection) provider.getSelection()).toArray();
173     if (fSelected.length == 0)
174       return;
175     Shell shell=JdtPlugin.getActiveWorkbenchShell();
176     if (!DependGraphOptionsDialog
177       .getOptions(fOptions, fSelected, shell))
178       return;
179     //
180     try {
181       new ProgressMonitorDialog(shell).run(true, true, new DependGraphRunnable());
182     } catch (InvocationTargetException e) {
183       Msg.err(e);
184     } catch (InterruptedException e) {
185       Msg.warn(NAME + ",run(IAction): Cancelled");
186     }
187     if (fGraph == null)
188       return;
189     if (VERBOSE)
190       System.err.println(fGraph.sprintGraph());
191     if (DEBUG)
192       System.err.println(fGraph.sprintGraph());
193     saveGraph(fGraph);
194   }
195 
196   private void saveGraph(IGraph graph) {
197     FileDialog dialog = new FileDialog(JdtPlugin.getActiveWorkbenchShell(), SWT.SAVE);
198     if (fOutDir == null)
199       fOutDir = JdtPlugin.getWorkspace().getRoot().getLocation().toString();
200     dialog.setFilterPath(fOutDir);
201     String fname = dialog.open();
202     if (fname != null && fname.length() > 0) {
203       System.err.println(NAME + ".saveGraph(): filename=" + fname);
204       fOutDir = new Path(fname).removeLastSegments(1).toString();
205       if (!fname.endsWith(".dot"))
206         fname += ".dot";
207       File ifile = Msg.createFile(fname);
208       if (ifile != null) {
209         try {
210           PrintWriter writer = new PrintWriter(new FileOutputStream(ifile));
211           writer.println(fGraph.sprintGraph());
212           writer.close();
213         } catch (FileNotFoundException e) {
214           System.err.println(
215             NAME + ".saveGraph(): Can't open output .dot file: " + fname);
216           return;
217         }
218       }
219       if (fOptions.show) {
220         Dot.dot(fGraph, fOptions.getSeed(), fOptions.getPasses());
221         try {
222           JdtPlugin.getActiveWorkbenchWindow().getActivePage().openEditor(
223             new GraphEditorInput(fname, fGraph),
224             "com.port80.eclipse.jdt.graph.views.GraphViewer");
225         } catch (PartInitException e) {
226           Msg.err(e);
227         }
228       }
229     }
230   }
231 
232   private class DependGraphRunnable implements IRunnableWithProgress {
233     public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
234       if (fOptions.depend == Options.DEPEND_REFERENCE) {
235         fGraph = referenceGraph(fSelected, monitor);
236       } else {
237         switch (fOptions.detail) {
238           case Options.DETAIL_METHOD :
239             fGraph = methodCallGraph(fSelected, monitor);
240             break;
241           case Options.DETAIL_CLASS :
242           case Options.DETAIL_CLASSWITHMETHOD :
243           default :
244             fGraph = classCallGraph(fSelected, monitor);
245             break;
246         }
247       }
248       if (monitor.isCanceled()) {
249         if (VERBOSE)
250           Msg.warn(NAME + ".DependGraphRunnable(): Canceled");
251         fGraph = null;
252       }
253     }
254   }
255 
256   /**
257    * Selection in the workbench has been changed. We 
258    * can change the state of the 'real' action here
259    * if we want, but this can only happen after 
260    * the delegate has been created.
261    * @see IWorkbenchWindowActionDelegate#selectionChanged
262    */
263   public void selectionChanged(IAction action, ISelection selection) {
264   }
265 
266   /**
267    * We can use this method to dispose of any system
268    * resources we previously allocated.
269    * @see IWorkbenchWindowActionDelegate#dispose
270    */
271   public void dispose() {
272   }
273 
274   /**
275    * We will cache window object in order to
276    * be able to provide parent shell for the message dialog.
277    * @see IWorkbenchWindowActionDelegate#init
278    */
279   public void init(IWorkbenchWindow window) {
280   }
281 
282   ////////////////////////////////////////////////////////////////////////
283 
284   /** 
285    * Construct class dependency graph between classes in given objects base on type references.
286    * 
287    * @return The graph with type as vertices and reference relationship as edges.
288    * @param selected The JavaElement operate on.
289    */
290   IGraph referenceGraph(Object[] selected, IProgressMonitor monitor) {
291     final String PREFIX = NAME + ".dependGraph(): ";
292     // Fully qualified typename->filename mapping for source/binary types among the selected objects.
293     Map filemap = new HashMap();
294     IGraph graph = new DirectedGraph("TypeReferenceGraph", null, null);
295     //graph.setAttr("ranksep", 1.1);
296     //graph.setAttr("nodesep", 1.1);
297     graph.setAttr("criticaluseinbus", true);
298     graph.setAttr("ptp_percent", 100);
299     IAttrTable vtable = graph.getVertexAttrTable();
300     vtable.setAttr("fontsize", 11.0);
301     vtable.setAttr("inthreshold", 6);
302     vtable.setAttr("outthreshold", 6);
303     //
304     // Create vertices.
305     //
306     monitor.beginTask("Reference Graph", 100);
307     int style = SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK;
308     GraphUtil.resolveReferences(
309       selected,
310       graph,
311       filemap,
312       new ReferenceGraphBuilder(),
313       new SubProgressMonitor(monitor, 90, style));
314     //
315     // Create edges.
316     //
317     Set vset = graph.getVertexSet();
318     SubProgressMonitor submon = new SubProgressMonitor(monitor, 10, style);
319     submon.beginTask("Creating graph", vset.size());
320     String typename;
321     for (Iterator uit = vset.iterator(); uit.hasNext();) {
322       submon.worked(1);
323       IVertex src = (IVertex) uit.next();
324       connectInheritance(src, graph);
325       if (fOptions.detail == Options.DETAIL_CLASS)
326         continue;
327       Set references = (Set) src.getAttr("-references");
328       if (references == null)
329         continue;
330       IEdge e;
331       IVertex dest;
332       for (Iterator it = references.iterator(); it.hasNext();) {
333         typename = (String) it.next();
334         dest = graph.getVertex(typename);
335         if (dest == null)
336           continue;
337         if (dest.equals(src))
338           continue;
339         if (src.findEdgesTo(dest, "-hasReference", null) != null)
340           continue;
341         try {
342           e = graph.newEdge(src, dest, null, null);
343           e.setAttr("-hasReference", true);
344         } catch (GraphException ex) {
345           Msg.err(PREFIX + "new edge: src=" + src + ", dest=" + dest, ex);
346         }
347       }
348     }
349     //
350     Set vertices = graph.allVertices();
351     for (Iterator it = vertices.iterator(); it.hasNext();) {
352       IVertex v = (IVertex) it.next();
353       removeInheritedDepends(v, graph, filemap);
354     }
355     for (Iterator it = vertices.iterator(); it.hasNext();) {
356       IVertex v = (IVertex) it.next();
357       if (DEBUG)
358         System.err.println(PREFIX + "v=" + v + ", inSize=" + v.inSize());
359       //if(v.inSize()==0) v.setAttrFromString("style","filled,3.0");
360       if (v.inSize() == 0 && v.outSize() != 0) {
361         //v.setAttrFromString("fillcolor", COLOR_TOP); //style","solid,3.0");
362         v.setAttr("label", "<b>" + v.getAttr("label") + "</b>");
363       }
364       if (!filemap.containsKey(v.getName())) {
365         setCalleeStyle(v);
366       }
367       v.removeUnregisteredAttrs();
368     }
369     submon.done();
370     monitor.done();
371     return graph;
372   }
373 
374   /** 
375    * Construct class dependency graph between classes in given objects base on method calls.
376    * 
377    * @return The graph with type as vertices and reference relationship as edges.
378    * @param selected The JavaElement operate on.
379    */
380   IGraph classCallGraph(Object[] selected, IProgressMonitor monitor) {
381     final String PREFIX = NAME + ".classGraph(): ";
382     // Fully qualified typename->filename mapping for source/binary types among the selected objects.
383     Map filemap = new HashMap();
384     IGraph graph = new DirectedGraph("ClassCallGraph", null, null);
385     //graph.setAttr("ranksep", 1.1);
386     //graph.setAttr("nodesep", 1.1);
387     graph.setAttr("criticaluseinbus", true);
388     graph.setAttr("ptp_percent", 100);
389     IAttrTable vtable = graph.getVertexAttrTable();
390     vtable.setAttr("fontsize", 11.0);
391     vtable.setAttr("style", STYLE);
392     vtable.setAttr("inthreshold", 6);
393     vtable.setAttr("outthreshold", 6);
394     //
395     // Create vertices.
396     //
397     monitor.beginTask("Class CallGraph", 100);
398     int style = SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK;
399     GraphUtil.resolveReferences(
400       selected,
401       graph,
402       filemap,
403       new ClassCallGraphBuilder(),
404       new SubProgressMonitor(monitor, 90, style));
405     //
406     // Create edges.
407     //
408     Set vset = graph.allVertices();
409     IProgressMonitor submon = new SubProgressMonitor(monitor, 10);
410     submon.beginTask("Creating graph", vset.size());
411     for (Iterator uit = vset.iterator(); uit.hasNext();) {
412       submon.worked(1);
413       String typename;
414       IEdge e;
415       IVertex src = (IVertex) uit.next();
416       connectInheritance(src, graph);
417       Set invokes = (Set) src.getAttr("-invokes");
418       if (invokes == null)
419         continue;
420       for (Iterator it = invokes.iterator(); it.hasNext();) {
421         MethodWrapper method = (MethodWrapper) it.next();
422         typename = method.getFullDeclaringTypeName();
423         IVertex dest = graph.getVertex(typename);
424         if (dest == null) {
425           if (fOptions.scope == Options.SCOPE_CONNECTED) {
426             Msg.err(
427               NAME
428                 + ".classGraph(): dest vertex not exists: typename="
429                 + typename);
430           }
431           continue;
432         }
433         if (fOptions.detail == Options.DETAIL_CLASSWITHMETHOD) {
434           Set callees = (Set) dest.getAttr("-callees");
435           if (callees == null) {
436             callees = new TreeSet();
437             dest.setAttr("-callees", callees);
438           }
439           callees.add(method);
440         }
441         if (dest.equals(src))
442           continue;
443         if (src.findEdgesTo(dest, "-hasCalls", null) != null)
444           continue;
445         try {
446           e = graph.newEdge(src, dest, null, null);
447           e.setAttr("-hasCalls", true);
448         } catch (GraphException ex) {
449           Msg.err(PREFIX + "new edge: src=" + src + ", dest=" + dest, ex);
450         }
451       }
452     }
453     for (Iterator uit = vset.iterator(); uit.hasNext();) {
454       IVertex v = (IVertex) uit.next();
455       StringBuffer label = new StringBuffer(v.getAttrString("label"));
456       //if(v.inSize()==0) v.setAttr("style","filled,3.0");
457       if (v.inSize() == 0) {
458         //v.setAttr("fillcolor", COLOR_TOP); //style","solid,3.0");
459         label.insert(0, "<b>");
460         label.append("</b>");
461       }
462       // Signatures of methods get called by others.
463       Set callees = (Set) v.getAttr("-callees");
464       if (fOptions.detail == Options.DETAIL_CLASS || callees == null) {
465         v.setAttr("label", label.toString());
466       } else {
467         addCallees(callees, label, v);
468       }
469     }
470     //
471     for (Iterator it = vset.iterator(); it.hasNext();) {
472       IVertex v = (IVertex) it.next();
473       if (!filemap.containsKey(v.getName())) {
474         setCalleeStyle(v);
475       }
476       v.removeUnregisteredAttrs();
477     }
478     submon.done();
479     monitor.done();
480     return graph;
481   }
482 
483   IGraph methodCallGraph(Object[] selected, IProgressMonitor monitor) {
484     final String PREFIX = NAME + ".methodGraph(): ";
485     // Fully qualified typename->filename mapping for source/binary types among the selected objects.
486     Map filemap = new HashMap();
487     IGraph graph = new DirectedGraph("MethodCallGraph", null, null);
488     //graph.setAttr("ranksep", 1.1);
489     //graph.setAttr("nodesep", 1.1);
490     graph.setAttr("criticaluseinbus", true);
491     graph.setAttr("ptp_percent", 100);
492     IAttrTable vtable = graph.getVertexAttrTable();
493     vtable.setAttr("fontsize", 11.0);
494     vtable.setAttr("style", STYLE);
495     vtable.setAttr("inthreshold", 6);
496     vtable.setAttr("outthreshold", 6);
497     //
498     // Create vertices.
499     //
500     monitor.beginTask("Method CallGraph", 100);
501     int style = SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK;
502     GraphUtil.resolveReferences(
503       selected,
504       graph,
505       filemap,
506       new MethodCallGraphBuilder(),
507       new SubProgressMonitor(monitor, 90, style));
508     //
509     // Create edges.
510     //
511     IVertex src, dest;
512     IEdge e;
513     Set vset = graph.getVertexSet();
514     IProgressMonitor submon = new SubProgressMonitor(monitor, 10);
515     submon.beginTask("Creating graph", vset.size());
516     for (Iterator uit = vset.iterator(); uit.hasNext();) {
517       submon.worked(1);
518       String typename;
519       src = (IVertex) uit.next();
520       if (src.getAttrBool("-isType"))
521         connectInheritance(src, graph);
522       Set implemented = (Set) src.getAttr("-implemented");
523       if (implemented != null)
524         connectImplemented(implemented, src, graph);
525       Set invokes = (Set) src.getAttr("-invokes");
526       if (invokes == null)
527         continue;
528       for (Iterator it = invokes.iterator(); it.hasNext();) {
529         MethodWrapper method = (MethodWrapper) it.next();
530         dest = graph.getVertex(method.getMethodSignature());
531         if (dest == null) {
532           // Try if a type vertex present.
533           typename = method.getFullDeclaringTypeName();
534           if (filemap.get(typename) != null) {
535             // Selected methods should use type vertex only if method is a constructor.
536             if (!method.isConstructor()) {
537               Msg.err(
538                 PREFIX
539                   + "selected method vertex not exists: typename="
540                   + typename
541                   + ", method="
542                   + method.getFullName());
543               continue;
544             }
545           }
546           dest = graph.getVertex(typename);
547           if (dest == null)
548             continue;
549         }
550         if (dest.equals(src))
551           continue;
552         if (src.findEdgesTo(dest, "-hasCalls", null) != null)
553           continue;
554         try {
555           e = graph.newEdge(src, dest, null, null);
556           e.setAttr("-hasCalls", true);
557         } catch (GraphException ex) {
558           Msg.err(PREFIX + "new edge: src=" + src + ", dest=" + dest, ex);
559         }
560       }
561     }
562     for (Iterator uit = vset.iterator(); uit.hasNext();) {
563       IVertex v = (IVertex) uit.next();
564       StringBuffer label = new StringBuffer(v.getAttrString("label"));
565       if (v.inSize() == 0) {
566         if (v.outSize() == 0) {
567           v.setAttr("style", STYLE_UNCONNECTED);
568         } else {
569           v.setAttr("style", STYLE_TOP);
570         }
571       }
572       // Signatures of methods get called by others.
573       Set callees = (Set) v.getAttr("-callees");
574       if (callees == null) {
575         v.setAttr("label", label.toString());
576       } else {
577         addCallees(callees, label, v);
578       }
579       //
580       if (!filemap.containsKey(v.getName())) {
581         // Referenced type.
582         setCalleeStyle(v);
583       }
584       v.removeUnregisteredAttrs();
585     }
586     submon.done();
587     monitor.done();
588     return graph;
589   }
590 
591   ////////////////////////////////////////////////////////////////////////
592 
593   /**
594    * Find superclass and super interfaces for the given type and added them to the given vertex.
595    * v.getAttr("-superClasses")=Set of full typename of all super classes.
596    * v.getAttr("-superInterfaces")=Set of full typename of all super interfaces.
597    * 
598    * @return Set of all superclasses and superinterface ITypeBinding.
599    */
600   Set findSupers(TypeWrapper type, IVertex v) {
601     ITypeBinding parent;
602     String typename;
603     Set ret = new HashSet();
604     parent = type.getBinding().getSuperclass();
605     if (parent != null) {
606       Set dest = (Set) v.getAttr("-superClasses");
607       if (dest == null) {
608         dest = new HashSet();
609         v.setAttr("-superClasses", dest);
610       }
611       typename = GraphUtil.getFullTypeName(parent);
612       dest.add(typename);
613       ret.add(parent);
614     }
615     ITypeBinding[] a = type.getBinding().getInterfaces();
616     if (a != null && a.length > 0) {
617       Set dest = (Set) v.getAttr("-superInterfaces");
618       if (dest == null) {
619         dest = new HashSet();
620         v.setAttr("-superInterfaces", dest);
621       }
622       for (int i = 0; i < a.length; ++i) {
623         typename = GraphUtil.getFullTypeName(a[i]);
624         dest.add(typename);
625         ret.add(a[i]);
626       }
627     }
628     return ret;
629   }
630 
631   /**
632    * Find all interfaces that the method implement.
633    * 
634    * @enter Vertices for the superclass and superinterfaces of the type 
635    * that declared the given method should have been created.
636    * 
637    * @param method The method.
638    * @param vertex The vertex that represent the method.
639    * @param graph The top level graph.
640    */
641   void findImplemented(MethodWrapper method, IVertex vertex, IGraph graph) {
642     IMethodBinding binding = method.getBinding();
643     String signature = GraphUtil.getSimpleMethodName(binding) + GraphUtil.getMethodParamSignature(binding);
644     ITypeBinding parent = method.getDeclaringTypeBinding();
645     Set implemented = (Set) vertex.getAttr("-implemented");
646     if (implemented == null) {
647       implemented = new HashSet();
648       vertex.setAttr("-implemented", implemented);
649     }
650     String typename;
651     IVertex v;
652     Set methods;
653     ITypeBinding type = parent.getSuperclass();
654     if ((type != null) && (typename = filterType(GraphUtil.getFullTypeName(type))) != null) {
655       // Overriden superclasses
656       v = graph.getVertex(typename);
657       if (v == null) {
658         if (fOptions.scope == Options.SCOPE_CONNECTED)
659           Msg.err(
660             NAME
661               + ".findImplemented(): superclass vertex not found: typename="
662               + typename);
663       } else {
664         // Cache the method names for repeating search on the same type later.
665         methods = (Set) v.getAttr("-methods");
666         if (methods == null) {
667           methods = getDeclaredMethodSignatures(type, null);
668           v.setAttr("-methods", methods);
669         }
670         if (methods.contains(signature))
671           implemented.add(v);
672       }
673     }
674     // Implemented interfaces
675     ITypeBinding[] interfaces = parent.getInterfaces();
676     for (int i = 0; i < interfaces.length; ++i) {
677       type = interfaces[i];
678       typename = GraphUtil.getFullTypeName(type);
679       // Find vertex that represent the interface.
680       v = graph.getVertex(typename);
681       if (v == null) {
682         if (fOptions.scope == Options.SCOPE_CONNECTED)
683           Msg.err(
684             NAME
685               + ".findImplemented(): interface vertex not found: typename="
686               + typename);
687         continue;
688       }
689       methods = (Set) v.getAttr("-methods");
690       if (methods == null) {
691         methods = getDeclaredMethodSignatures(type, null);
692         v.setAttr("-methods", methods);
693       }
694       if (methods.contains(signature))
695         implemented.add(v);
696     }
697     if (DEBUG) {
698       System.err.println(NAME + ".findImplemented(): signature=" + signature);
699       for (Iterator it = implemented.iterator(); it.hasNext();) {
700         System.err.println("\n\t" + it.next());
701       }
702     }
703   }
704 
705   /**
706    * Create vertices for each superclass and superinterface.
707    */
708   void createSuperVertices(Set types, IGraph graph) {
709     TypeWrapper type;
710     for (Iterator it = types.iterator(); it.hasNext();) {
711       type = new TypeWrapper((ITypeBinding) it.next());
712       if (type.getFullName().equals("java.lang.Object"))
713         continue;
714       createVertex(type, graph);
715     }
716   }
717 
718   /**
719    * Reference to method of selected classes.
720    * . Any such methods should be added when processing the declared methods
721    *   so should be ignored where referenced except for methods that are synthetic.
722    *   Synthetic methods are not declared and so we need to create the method vertex
723    *   for them when they are being referenced.
724    * 
725    * @enter Vertex for the 'declaring' type of the method should have been created.
726    * @param filemap FullTypeName/FullMethodSignature->IVertex mapping of selected objects.
727    */
728   void addSyntheticMethod(MethodWrapper method, IGraph graph, Map filemap) {
729     String methodname = method.getMethodSignature();
730     if (graph.getVertex(methodname) != null)
731       // Method vertex already exists.
732       return;
733     String typename = method.getFullDeclaringTypeName();
734     IMethodBinding binding = method.getBinding();
735     if (!binding.isSynthetic() && !binding.isConstructor()) {
736       // NOTE: isSynthetic() returned false for synthesised default constructor!
737       Msg.err(
738         NAME
739           + ".addSyntheticMethod(): method is not synthetic/constructor: method="
740           + methodname);
741     }
742     if (VERBOSE)
743       System.err.println(NAME + ".addSyntheticMethod(): added: " + methodname);
744     IVertex v = graph.getVertex(typename);
745     if (v == null) {
746       Msg.err(
747         NAME
748           + ".addSyntheticMethod(): declaring type vertex not exist: typename="
749           + typename
750           + "\n\tmethod="
751           + method.getFullName());
752       return;
753     }
754     Set callees = (Set) v.getAttr("-callees");
755     if (callees == null) {
756       callees = new TreeSet();
757       v.setAttr("-callees", callees);
758     }
759     callees.add(method);
760   }
761 
762   /**
763    * Create a vertex for the caller (type/method) if not exists, otherwise return existing vertex.
764    * @return IVertex created or existing vertex.
765    */
766   IVertex createVertex(Object wrapper, IGraph graph) {
767     String name = null;
768     IVertex ret = null;
769     try {
770       if (wrapper instanceof TypeWrapper) {
771         TypeWrapper type = (TypeWrapper) wrapper;
772         name = type.getFullName();
773         ret = graph.getVertex(name);
774         if (ret == null) {
775           ret = graph.newVertex(name, null);
776           ret.setAttr("label", type.getSimpleName());
777           ret.setAttr("-isType", true);
778           ret.setAttr("-modifiers", new Integer(type.getModifiers()));
779           if (type.isInterface()) {
780             ret.setAttr("-isInterface", true);
781             ret.setAttr("fillcolor", COLOR_INTERFACE);
782           } else if (name.indexOf('$') > 0 || name.indexOf('{') > 0)
783             ret.setAttr("fillcolor", COLOR_NESTED_CLASS);
784           else
785             ret.setAttr("fillcolor", COLOR_CLASS);
786         }
787       } else if (wrapper instanceof MethodWrapper) {
788         MethodWrapper method = (MethodWrapper) wrapper;
789         IMethodBinding binding = method.getBinding();
790         name = method.getMethodSignature();
791         ret = graph.getVertex(name);
792         if (ret == null) {
793           ret = graph.newVertex(name, null);
794           name = GraphUtil.chop(method.getSimpleName(), MAX_LABELWIDTH);
795           String typename =
796             GraphUtil.chop(method.getSimpleDeclaringTypeName(), MAX_LABELWIDTH);
797           String params = wrapParam(method.getSimpleParamSignature(), MAX_LABELWIDTH);
798           int modifiers = method.getModifiers();
799           if (Modifier.isStatic(modifiers))
800             name = "<b>" + name + "</b>";
801           if (Modifier.isPublic(modifiers))
802             name = "<color \"" + COLOR_PUBLIC + "\">" + name + "</color>";
803           else if (Modifier.isProtected(modifiers))
804             name = "<color \"" + COLOR_PROTECTED + "\">" + name + "</color>";
805           else if (method.isPrivate())
806             name = "<color \"" + COLOR_PRIVATE + "\">" + name + "</color>";
807           else
808             name = "<color \"" + COLOR_PACKAGE + "\">" + name + "</color>";
809           boolean hasparams = !params.equals("()");
810           params = "<color \"" + COLOR_CALLEE_PARAMS + "\">" + params + "</color>";
811           ret.setAttr(
812             "label",
813             "<color \""
814               + COLOR_CALLEE_PARAMS
815               + "\">"
816               + typename
817               + "</color><hr>\n"
818               + name
819               + "</color>"
820               + (hasparams ? ("\n" + params) : params));
821           ret.setAttr("-modifiers", new Integer(modifiers));
822           if (binding.isConstructor())
823             ret.setAttr("fillcolor", COLOR_CONSTRUCTOR);
824           else
825             ret.setAttr("fillcolor", COLOR_METHOD);
826         }
827       }
828     } catch (GraphException e) {
829       Msg.err(NAME + ".newMethodVertex(): Error creating vertex: " + name);
830       return null;
831     }
832     return ret;
833   }
834 
835   /**
836    * Filter out references to predefined packages.
837    * @param typename Full type name of type to be filtered.
838    */
839   String filterType(String typename) {
840     if (typename.startsWith("java.lang.") || typename.startsWith("java.util."))
841       return null;
842     return typename;
843   }
844 
845   ////////////////////////////////////////////////////////////////////////
846 
847   /**
848    *  Find all vertices that can be reached by the super classes of the given vertices.
849    * 
850    *  @param ret Return the set of vertices reachable through the super classes of the given vertex.
851    */
852   private void findSuperConnected(IVertex vertex, IGraph graph, Set ret) {
853     Set supers = (Set) vertex.getAttr("-superClasses");
854     if (supers == null)
855       return;
856     IVertex v;
857     for (Iterator it = supers.iterator(); it.hasNext();) {
858       String typename = (String) it.next();
859       v = graph.getVertex(typename);
860       if (v == null)
861         continue;
862       if (v.equals(vertex)) {
863         Msg.err(NAME + ".findSuperConnected(): v=" + v.getName());
864         continue;
865       }
866       IEdge[] outs = v.outs();
867       for (int i = 0; i < outs.length; ++i) {
868         ret.add(outs[i].getHead());
869       }
870       findSuperConnected(v, graph, ret);
871     }
872   }
873 
874   /**
875    * @return The Set of all method signatures of methods that declared in the given type.
876    */
877   private Set getDeclaredMethodSignatures(ITypeBinding type, Set ret) {
878     if (ret == null)
879       ret = new HashSet();
880     IMethodBinding[] methods = type.getDeclaredMethods();
881     for (int i = 0; i < methods.length; ++i) {
882       ret.add(
883         GraphUtil.getSimpleMethodName(methods[i])
884           + GraphUtil.getMethodParamSignature(methods[i]));
885     }
886     ITypeBinding parent = type.getSuperclass();
887     if (parent != null) {
888       getDeclaredMethodSignatures(parent, ret);
889     }
890     ITypeBinding[] a = type.getInterfaces();
891     if (a == null) {
892       if (DEBUG) {
893         System.err.println(
894           NAME + ".getMethodSignatures(): type=" + GraphUtil.getFullTypeName(type));
895         for (Iterator it = ret.iterator(); it.hasNext();) {
896           System.err.println("\t" + it.next());
897         }
898       }
899     } else {
900       for (int i = 0; i < a.length; ++i) {
901         getDeclaredMethodSignatures(a[i], ret);
902       }
903     }
904     return ret;
905   }
906 
907   private void setCalleeStyle(IVertex v) {
908     v.setAttr("style", STYLE_CALLEE);
909     if (v.getAttrBool("-isType")) {
910       if (v.getAttrBool("-isInterface"))
911         v.setAttr("fillcolor", COLOR_CALLEE_INTERFACE);
912       else
913         v.setAttr("fillcolor", COLOR_CALLEE_CLASS);
914     } else
915       v.setAttr("fillcolor", COLOR_CALLEE_METHOD);
916   }
917 
918   /**
919          * Breakup param string to fit in given line width.
920          * @todo Unit test.
921          */
922   private String wrapParam(String params, int max) {
923     if (params.length() <= max)
924       return params;
925     StringBuffer buf = new StringBuffer();
926     int space = max;
927     int index;
928     while (params.length() > 0) {
929       index = params.indexOf(',');
930       if (index < 0)
931         index = params.length();
932       else
933         ++index;
934       if (index < space) {
935         buf.append(params.substring(0, index));
936         params = params.substring(index);
937         space -= index;
938       } else {
939         if (space != max) {
940           buf.append("\n");
941           space = max;
942         }
943         if (index > max) {
944           buf.append(params.substring(0, max - 3) + "...");
945           space = 0;
946         } else {
947           buf.append(params.substring(0, index));
948           space -= index;
949         }
950         params = params.substring(index);
951       }
952     }
953     return buf.toString();
954   }
955 
956   /**
957    *  Remove self edge and dependency that are already present in super classes.
958    *  Color the edges that involve inheritance.
959    */
960   private void removeInheritedDepends(IVertex vertex, IGraph graph, Map filemap) {
961     Set ret = new HashSet();
962     findSuperConnected(vertex, graph, ret);
963     IVertex v;
964     IEdge e;
965     int n = 0;
966     IEdge[] outs = vertex.outs();
967     for (int i = 0; i < outs.length; ++i) {
968       e = outs[i];
969       if (!e.getAttrBool("-hasReference"))
970         continue;
971       v = e.getHead();
972       if (v.equals(vertex) || ret.contains(v)) {
973         v.removeInsFrom(vertex);
974         n += vertex.removeOutsTo(v);
975         if (DEBUG)
976           System.err.println("\tremoved= " + v.getName());
977       }
978     }
979   }
980 
981   /**
982    * Create edge to superclass and superinterfaces.
983    **/
984   private void connectInheritance(IVertex src, IGraph graph) {
985     String typename;
986     IEdge e;
987     IVertex v;
988     //
989     Set supers = (Set) src.getAttr("-superClasses");
990     if (supers == null)
991       supers = new HashSet();
992     Set superif = (Set) src.getAttr("-superInterfaces");
993     if (superif != null)
994       supers.addAll(superif);
995     for (Iterator it = supers.iterator(); it.hasNext();) {
996       typename = (String) it.next();
997       IVertex dest = graph.getVertex(typename);
998       if (dest == null)
999         continue;
1000      if (src.findEdgesTo(dest, "-typehier", null) != null) {
1001        continue;
1002      }
1003      try {
1004        e = graph.newEdge(src, dest, null, null);
1005        e.setAttr("-typehier", true);
1006        e.setAttr("critical", true);
1007        v = e.getHead();
1008        if (v.getAttrBool("-isInterface")) {
1009          e.setAttr("style", STYLE_SUPERINTERFACE);
1010          e.setAttr("color", COLOR_SUPERINTERFACE);
1011        } else {
1012          e.setAttr("style", STYLE_SUPERCLASS);
1013          e.setAttr("color", COLOR_SUPERCLASS);
1014        }
1015      } catch (GraphException ex) {
1016        Msg.err(NAME + ".buildInheritance(): new edge: src=" + src + ", dest=" + dest, ex);
1017      }
1018    }
1019  }
1020
1021  /**
1022   * Connect method to interfaces that it implements.
1023   */
1024  private void connectImplemented(Set implemented, IVertex src, IGraph graph) {
1025    IVertex dest;
1026    IEdge e;
1027    for (Iterator it = implemented.iterator(); it.hasNext();) {
1028      dest = (IVertex) it.next();
1029      if (dest.equals(src))
1030        continue;
1031      if (src.findEdgesFrom(dest, "-implemented", null) != null)
1032        continue;
1033      try {
1034        e = graph.newEdge(dest, src, null, null);
1035        e.setAttr("-implemented", true);
1036        e.setAttr("critical", true);
1037        if (e.getHead().getAttrBool("-isInterface")) {
1038          e.setAttr("style", STYLE_IMPLEMENTED);
1039          e.setAttr("color", COLOR_IMPLEMENTED);
1040        } else {
1041          e.setAttr("style", STYLE_OVERRIDEN);
1042          e.setAttr("color", COLOR_OVERRIDEN);
1043        }
1044      } catch (GraphException ex) {
1045        Msg.err(NAME + ".connectImplemented(): new edge: src=" + src + ", dest=" + dest, ex);
1046      }
1047    }
1048  }
1049
1050  private void addCallees(Set callees, StringBuffer label, IVertex v) {
1051    String name, params;
1052    boolean first = true;
1053    for (Iterator it = callees.iterator(); it.hasNext();) {
1054      MethodWrapper method = (MethodWrapper) it.next();
1055      if (first) {
1056        label.append("<hr>");
1057        first = false;
1058      }
1059      name = method.getSimpleName();
1060      int len = name.length();
1061      if (len > MAX_LABELWIDTH - 4) {
1062        name = GraphUtil.chop(name, MAX_LABELWIDTH - 4);
1063        params = "(...";
1064      } else {
1065        params = GraphUtil.chop(method.getSimpleParamSignature(), MAX_LABELWIDTH - len);
1066      }
1067      label.append(
1068        "\n<left><color \""
1069          + COLOR_CALLEE
1070          + "\">"
1071          + name
1072          + "</color><color \""
1073          + COLOR_CALLEE_PARAMS
1074          + "\"> "
1075          + params
1076          + "</color>");
1077    }
1078    v.setAttr("label", label.toString());
1079  }
1080
1081  ////////////////////////////////////////////////////////////////////////
1082
1083  class ReferenceGraphBuilder implements IGraphAction {
1084    private final String NAME = "ReferenceGraphBuilder";
1085
1086    /** 
1087     * Find type references in a compiled AST.
1088     * 
1089     * @return Number of references found.  A vertex is created in the graph for the srcpath and
1090     * vertex attributes -references, -superClasses, -superInterfaces holds the reference information.
1091     * @param srcpath The full path of the source unit, full type name (without .java) for binary class file.
1092     * @param ast The compiled ast for the source unit.
1093     * @param graph The return graph updated with the references.
1094     * @param filemap Return superclass map (srcpath->typename).
1095     */
1096    public int addRef(String srcpath, CompilationUnit ast, IGraph graph, Map filemap) {
1097      int n = 0;
1098      MethodReferenceASTVisitor visitor = null;
1099      try {
1100        visitor = MethodReferenceASTVisitor.startAt(ast);
1101      } catch (ASTError e) {
1102        fParsingErrorRange =
1103          new SourceRange(e.errorNode.getStartPosition(), e.errorNode.getLength());
1104        if (DEBUG)
1105          System.err.println(NAME + ".addRef(): parse error: " + e.errorNode.toString());
1106      }
1107      if (visitor == null) {
1108        Msg.err(NAME + ".addRef(): parse error: visitor==null: srcpath=" + srcpath);
1109        return 0;
1110      }
1111
1112      MethodReferenceASTVisitor.Result ret = visitor.getResult();
1113      //
1114      IVertex v;
1115      Set references;
1116      Map map;
1117      TypeWrapper wrapper;
1118      Set declares = ret.getDeclares();
1119      for (Iterator it = declares.iterator(); it.hasNext();) {
1120        wrapper = (TypeWrapper) it.next();
1121        v = createVertex(wrapper, graph);
1122        v.setAttr("alt1", srcpath);
1123        filemap.put(v.getName(), v);
1124        Set supers = findSupers(wrapper, v);
1125        if (fOptions.scope == Options.SCOPE_CONNECTED)
1126          createSuperVertices(supers, graph);
1127      }
1128      //
1129      Set destset;