Source code: com/port80/eclipse/jdt/graph/GraphUtil.java
1 package com.port80.eclipse.jdt.graph;
2
3 import java.io.File;
4 import java.io.FileNotFoundException;
5 import java.io.FileOutputStream;
6 import java.io.PrintWriter;
7 import java.util.HashSet;
8 import java.util.Iterator;
9 import java.util.Map;
10 import java.util.Set;
11
12 import org.eclipse.core.runtime.IProgressMonitor;
13 import org.eclipse.core.runtime.Path;
14 import org.eclipse.core.runtime.SubProgressMonitor;
15 import org.eclipse.jdt.core.IClassFile;
16 import org.eclipse.jdt.core.ICompilationUnit;
17 import org.eclipse.jdt.core.IJavaProject;
18 import org.eclipse.jdt.core.IPackageFragment;
19 import org.eclipse.jdt.core.IPackageFragmentRoot;
20 import org.eclipse.jdt.core.IType;
21 import org.eclipse.jdt.core.JavaModelException;
22 import org.eclipse.jdt.core.dom.AST;
23 import org.eclipse.jdt.core.dom.CompilationUnit;
24 import org.eclipse.jdt.core.dom.IBinding;
25 import org.eclipse.jdt.core.dom.IMethodBinding;
26 import org.eclipse.jdt.core.dom.IPackageBinding;
27 import org.eclipse.jdt.core.dom.ITypeBinding;
28 import org.eclipse.swt.SWT;
29 import org.eclipse.swt.widgets.FileDialog;
30
31 import com.port80.eclipse.jdt.JdtPlugin;
32 import com.port80.graph.GraphException;
33 import com.port80.graph.IGraph;
34 import com.port80.graph.IVertex;
35 import com.port80.util.Msg;
36
37 /**
38 * @author chrisl
39 *
40 * To change this generated comment edit the template variable "typecomment":
41 * Window>Preferences>Java>Templates.
42 * To enable and disable the creation of type comments go to
43 * Window>Preferences>Java>Code Generation.
44 */
45 public class GraphUtil {
46
47 ////////////////////////////////////////////////////////////////////////
48
49 private static final String NAME = "GraphUtil";
50 private static final boolean DEBUG = false;
51 private static final boolean VERBOSE = false;
52
53 ////////////////////////////////////////////////////////////////////////
54
55 private static String fOutDir = null;
56
57 ////////////////////////////////////////////////////////////////////////
58
59 public static boolean saveGraph(IGraph graph) {
60 boolean ret = false;
61 FileDialog dialog = new FileDialog(JdtPlugin.getActiveWorkbenchShell(), SWT.SAVE);
62 if (fOutDir == null)
63 fOutDir = JdtPlugin.getWorkspace().getRoot().getLocation().toString();
64 dialog.setFilterPath(fOutDir);
65 String fname = dialog.open();
66 if (fname != null && fname.length() > 0) {
67 Msg.println(NAME + ".saveGraph(): filename=" + fname);
68 fOutDir = new Path(fname).removeLastSegments(1).toString();
69 ret = graph.saveImage(fname, 1f);
70 if (!fname.endsWith(".dot"))
71 fname += ".dot";
72 File ifile = Msg.createFile(fname);
73 if (ifile != null) {
74 try {
75 PrintWriter writer = new PrintWriter(new FileOutputStream(ifile));
76 writer.println(graph.sprintGraph());
77 writer.close();
78 } catch (FileNotFoundException e) {
79 Msg.println(NAME + ".saveGraph(): Can't open output .dot file: " + fname);
80 }
81 }
82 }
83 return ret;
84 }
85
86 public static void resolveReferences(
87 Object[] selected,
88 IGraph graph,
89 Map filemap,
90 IGraphAction action,
91 IProgressMonitor monitor) {
92 monitor.beginTask("Parsing", selected.length);
93 for (int i = 0; i < selected.length; ++i) {
94 try {
95 if (selected[i] instanceof IJavaProject) {
96 IJavaProject project = (IJavaProject) selected[i];
97 addUnitsFromFragments(
98 project.getPackageFragments(),
99 graph,
100 filemap,
101 action,
102 monitor);
103 } else if (selected[i] instanceof IPackageFragmentRoot) {
104 IPackageFragmentRoot root = (IPackageFragmentRoot) selected[i];
105 addUnitsFromFragments(root.getChildren(), graph, filemap, action, monitor);
106 } else if (selected[i] instanceof IPackageFragment) {
107 addUnitsFromFragment(
108 (IPackageFragment) selected[i],
109 graph,
110 filemap,
111 action,
112 monitor);
113 } else if (selected[i] instanceof ICompilationUnit) {
114 addCompilateUnit((ICompilationUnit) selected[i], graph, filemap, action);
115 monitor.worked(1);
116 } else if (selected[i] instanceof IClassFile) {
117 IClassFile classfile = (IClassFile) selected[i];
118 addClassFile(classfile, classfile.getJavaProject(), graph, filemap, action);
119 monitor.worked(1);
120 } else {
121 System.err.println(
122 NAME
123 + ".resolveReferences(): unknown selected: type="
124 + selected[i].getClass().getName()
125 + ": "
126 + selected[i]);
127 monitor.worked(1);
128 }
129 } catch (JavaModelException e) {}
130 }
131 monitor.done();
132 }
133
134 /**
135 * Find the vertices for the given typenames if exists.
136 * @return Set of vertices found, empty set if none exists or typenames==null.
137 */
138 public static Set findVerticesFromTypenames(Set typenames, IGraph graph, Map filemap) {
139 Set ret = new HashSet();
140 if (typenames == null)
141 return ret;
142 IVertex v;
143 for (Iterator it = typenames.iterator(); it.hasNext();) {
144 String typename = (String) it.next();
145 String destpath = (String) filemap.get(typename);
146 if (destpath == null)
147 continue;
148 v = graph.getVertex(destpath);
149 if (v != null)
150 ret.add(v);
151 }
152 return ret;
153 }
154
155 /**
156 * Extract a proper label string from the given type name.
157 */
158 public static String getLabel(String typename) {
159 int index = typename.indexOf(".java");
160 if (index > 0) {
161 typename = typename.substring(0, index);
162 }
163 index = typename.lastIndexOf('/');
164 if (index >= 0) {
165 typename = typename.substring(index + 1);
166 } else {
167 index = typename.lastIndexOf('.');
168 if (index >= 0)
169 typename = typename.substring(index + 1);
170 }
171 return typename;
172 }
173
174 /**
175 * Create a new vertex in the given graph with the given name.
176 * If vertex of given name already exists, return the existing vertex.
177 * @return Existing vertex or new created vertex, null if create error.
178 */
179 public static IVertex newVertex(String name, IGraph graph) {
180 IVertex ret = graph.getVertex(name);
181 if (ret == null) {
182 try {
183 ret = graph.newVertex(name, null);
184 ret.setAttr("label", getLabel(name));
185 } catch (GraphException e) {
186 Msg.err(NAME + ".newVertex(): can't create vertex: name=" + name, e);
187 ret = null;
188 }
189 }
190 return ret;
191 }
192
193 /**
194 * Add mapping FullTypeName-> ICompilationUnit full path for all types
195 * defined in the given ICompilationUnit.
196 *
197 * @return The full path of the given ICompilationUnit.
198 */
199 public static String addToFileMap(ICompilationUnit unit, Map filemap) {
200 // Save typename->filename mapping.
201 String unitname = unit.getPath().toString();
202 try {
203 IType[] types = unit.getTypes();
204 for (int k = 0; k < types.length; ++k)
205 filemap.put(types[k].getFullyQualifiedName(), unitname);
206 } catch (JavaModelException e) {}
207 return unitname;
208 }
209
210 ////////////////////////////////////////////////////////////////////////
211
212 private static void addUnitsFromFragments(
213 Object[] fragments,
214 IGraph graph,
215 Map filemap,
216 IGraphAction action,
217 IProgressMonitor monitor) {
218 SubProgressMonitor submon = new SubProgressMonitor(monitor, 1);
219 submon.beginTask("Fragments", fragments.length);
220 for (int i = 0; i < fragments.length; ++i) {
221 if (!(fragments[i] instanceof IPackageFragment)) {
222 if (DEBUG)
223 System.err.println(
224 NAME
225 + ".addUnitsFromFragments(): not a fragment: type="
226 + fragments[i].getClass().getName()
227 + ": "
228 + fragments[i]);
229 submon.worked(1);
230 continue;
231 }
232 addUnitsFromFragment((IPackageFragment) fragments[i], graph, filemap, action, submon);
233 }
234 }
235
236 public static void addUnitsFromFragment(
237 IPackageFragment fragment,
238 IGraph graph,
239 Map filemap,
240 IGraphAction action,
241 IProgressMonitor monitor) {
242
243 ICompilationUnit[] sources = null;
244 IClassFile[] binaries = null;
245 IJavaProject project = fragment.getJavaProject();
246 try {
247 sources = fragment.getCompilationUnits();
248 binaries = fragment.getClassFiles();
249 } catch (JavaModelException ex) {
250 Msg.err(NAME + ".addUnitsFromFragment(): fragment=" + fragment, ex);
251 }
252 //
253 // Add source
254 //
255 if (sources != null) {
256 SubProgressMonitor submon = new SubProgressMonitor(monitor, 1);
257 submon.beginTask("sources", sources.length);
258 for (int i = 0; i < sources.length; ++i) {
259 //String unitname = updateFileMap(sources[i], filemap);
260 String unitname = sources[i].getPath().toString();
261 CompilationUnit ast = AST.parseCompilationUnit(sources[i], true);
262 action.addRef(unitname, ast, graph, filemap);
263 submon.worked(1);
264 if (submon.isCanceled())
265 break;
266 }
267 submon.done();
268 }
269 //
270 // Add binaries
271 //
272 if (binaries != null) {
273 SubProgressMonitor submon = new SubProgressMonitor(monitor, 1);
274 submon.beginTask("binaries", binaries.length);
275 for (int i = 0; i < binaries.length; ++i) {
276 addClassFile(binaries[i], project, graph, filemap, action);
277 submon.worked(1);
278 if (submon.isCanceled())
279 break;
280 }
281 submon.done();
282 }
283 }
284
285 public static void addClassFile(
286 IClassFile binary,
287 IJavaProject project,
288 IGraph graph,
289 Map filemap,
290 IGraphAction action) {
291 String source = null;
292 IType type = null;
293 try {
294 source = binary.getSource();
295 type = binary.getType();
296 } catch (JavaModelException ex) {
297 Msg.err(NAME + ".addClassFile(): binary=" + binary, ex);
298 return;
299 }
300 String typename = type.getFullyQualifiedName();
301 String name = typename;
302 int index = name.indexOf('$');
303 if (index > 0)
304 name = name.substring(0, index);
305 name = name.replace('.', '/');
306 name = name + ".java";
307 CompilationUnit ast = AST.parseCompilationUnit(source.toCharArray(), name, project);
308 action.addRef(typename, ast, graph, filemap);
309 //filemap.put(typename, typename);
310 if (DEBUG)
311 System
312 .err
313 .println(
314 NAME
315 + ".addClassFile(): name="
316 + name
317 + ", typename="
318 + typename
319 + ", project="
320 + project.getElementName()
321 + ", source.length="
322 + source.length() //+", source="
323 //+source
324 );
325 }
326
327 public static void addCompilateUnit(ICompilationUnit unit, IGraph graph, Map filemap, IGraphAction action) {
328 CompilationUnit ast = AST.parseCompilationUnit(unit, true);
329 //String unitpath = updateFileMap(unit, fFileMap);
330 String unitpath = unit.getPath().toString();
331 action.addRef(unitpath, ast, graph, filemap);
332 }
333
334 ////////////////////////////////////////////////////////////////////////
335
336 public static String getFullTypeName(IBinding binding) {
337 if (binding == null)
338 return null;
339 if (!(binding instanceof ITypeBinding))
340 return null;
341 ITypeBinding type = (ITypeBinding) binding;
342 IPackageBinding p = type.getPackage();
343 String packagename = null;
344 if (p != null)
345 packagename = p.getName();
346 //FIXME: How to get the type id ($n) of an anonymous class.
347 String typename = getSimpleTypeName(type);
348 if (packagename != null)
349 return packagename + "." + typename;
350 else
351 return typename;
352 }
353
354 /**
355 * Simple typename without package qualifiers.
356 */
357 public static String getSimpleTypeName(ITypeBinding type) {
358 String typename;
359 if (type.isAnonymous()) {
360 ITypeBinding parent = type.getSuperclass();
361 if (parent == null || parent.getName().equals("Object")) {
362 ITypeBinding[] a = type.getInterfaces();
363 if (a != null && a.length > 0)
364 parent = a[0];
365 }
366 if (parent != null)
367 typename = "{" + parent.getName() + "}";
368 else
369 typename = "{Object}";
370 } else
371 typename = type.getName();
372 if ((type = type.getDeclaringClass()) != null) {
373 typename = getSimpleTypeName(type) + "$" + typename;
374 }
375 return typename;
376 }
377
378 public static String getFullMethodName(IMethodBinding method) {
379 if (method == null)
380 return null;
381 ITypeBinding type = method.getDeclaringClass();
382 String typename = GraphUtil.getFullTypeName(type);
383 String methodname = getSimpleMethodName(method);
384 if (typename != null)
385 return typename + "." + methodname;
386 else
387 return methodname;
388 }
389
390 /**
391 * Method name without type qualifier and parameter signatures.
392 */
393 public static String getSimpleMethodName(IMethodBinding method) {
394 if (method == null)
395 return null;
396 String methodname = method.getName();
397 if (methodname == null || methodname.length() == 0) {
398 // Anonymous class constructor invokation returns "()" without a valid method name!
399 ITypeBinding type = method.getDeclaringClass();
400 methodname = getSimpleTypeName(type);
401 }
402 return methodname;
403 }
404
405 /**
406 * Method signature with full typenames.
407 */
408 public static String getMethodSignature(IMethodBinding method) {
409 if (method == null)
410 return null;
411 return getFullMethodName(method) + getMethodParamSignature(method);
412 }
413
414 /**
415 * Method signature with simple name instead of full typenames.
416 */
417 public static String getMethodParamSignature(IMethodBinding method) {
418 if (method == null)
419 return null;
420 StringBuffer signature = new StringBuffer();
421 signature.append('(');
422 ITypeBinding[] params = method.getParameterTypes();
423 for (int i = 0; i < params.length; ++i) {
424 if (i != 0)
425 signature.append(',');
426 signature.append(getFullTypeName(params[i]));
427 }
428 signature.append(')');
429 return signature.toString();
430 }
431
432 /**
433 * Method signature with simple name instead of full typenames.
434 */
435 public static String getSimpleMethodParamSignature(IMethodBinding method) {
436 if (method == null)
437 return null;
438 StringBuffer signature = new StringBuffer();
439 signature.append('(');
440 ITypeBinding[] params = method.getParameterTypes();
441 for (int i = 0; i < params.length; ++i) {
442 if (i != 0)
443 signature.append(',');
444 signature.append(params[i].getName());
445 }
446 signature.append(')');
447 return signature.toString();
448 }
449
450 /**
451 * Truncate string.
452 */
453 public static String chop(String str, int max) {
454 if (str.length() <= max)
455 return str;
456 return (str.substring(0, max - 3) + "...");
457 }
458
459 ////////////////////////////////////////////////////////////////////////
460 }