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 }