properly render fields of arrays of primitive types
[javainspect.git] / src / main / java / eu / svjatoslav / inspector / java / structure / ClassGraph.java
1 /*
2  * JavaInspect - Utility to visualize java software
3  * Copyright (C) 2013-2014, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of version 3 of the GNU Lesser General Public License
7  * or later as published by the Free Software Foundation.
8  */
9
10 package eu.svjatoslav.inspector.java.structure;
11
12 import java.io.File;
13 import java.io.IOException;
14 import java.io.PrintWriter;
15 import java.util.HashMap;
16 import java.util.Map;
17
18 import eu.svjatoslav.commons.file.CommonPathResolver;
19 import eu.svjatoslav.inspector.java.methods.Clazz;
20 import eu.svjatoslav.inspector.java.methods.ProjectScanner;
21
22 public class ClassGraph {
23
24         public static void render(final String graphName, final Class... classes) {
25                 final ClassGraph classGraph = new ClassGraph(classes);
26
27                 classGraph.generateGraph(graphName);
28         }
29
30         /**
31          * Maps class fully qualified names to class descriptors.
32          */
33         private final Map<String, ClassDescriptor> fullyQualifiedNameToClassMap = new HashMap<String, ClassDescriptor>();
34
35         private Filter filter = new Filter();
36
37         public ClassGraph() {
38         }
39
40         /**
41          * @param classes
42          *            classes that shall be added to graph
43          */
44         public ClassGraph(final Class<? extends Object>... classes) {
45                 for (final Class<? extends Object> clazz : classes)
46                         addClass(clazz);
47         }
48
49         /**
50          * @param objects
51          *            objects that shall be added to graph
52          */
53         public ClassGraph(final Object... objects) {
54                 for (final Object object : objects)
55                         addClass(object.getClass());
56         }
57
58         /**
59          * @param clazz
60          *            class that shall be added to graph
61          */
62         public ClassDescriptor addClass(final Class<? extends Object> clazz) {
63
64                 if (clazz == null)
65                         return null;
66
67                 final String className = clazz.getName();
68
69                 if (fullyQualifiedNameToClassMap.containsKey(className))
70                         return fullyQualifiedNameToClassMap.get(className);
71
72                 return new ClassDescriptor(clazz, this);
73         }
74
75         /**
76          * @param object
77          *            object that shall be added to graph
78          */
79         public ClassDescriptor addObject(final Object object) {
80                 return addClass(object.getClass());
81         }
82
83         /**
84          * @param path
85          *            path to recursively scan for java source code could be
86          *            relative to current project or absolute
87          */
88         public void addProject(final String path) {
89                 final ProjectScanner projectScanner = new ProjectScanner(new File(path));
90                 for (final Clazz clazz : projectScanner.getAllClasses())
91                         try {
92                                 System.out.println("Class full name: " + clazz.getFullName());
93                                 final Class c = this.getClass().forName(clazz.getFullName());
94                                 addClass(c);
95                         } catch (final Exception exception) {
96                                 System.out.println("cannot add class: "
97                                                 + exception.getMessage());
98                         }
99         }
100
101         /**
102          * @param resultFileName
103          *            file name for the generated graph. Existing file with the same
104          *            name will be overwritten.
105          */
106         public void generateGraph(final String resultFileName) {
107                 generateGraph(resultFileName, false);
108         }
109
110         /**
111          * @param resultFileName
112          *            file name for the generated graph. File extension will be
113          *            added automatically. Existing file with the same name will be
114          *            overwritten.
115          *
116          * @param keepDotFile
117          *            if set to <code>true</code> then intermediary GraphViz DOT
118          *            file will be kept.
119          */
120
121         public void generateGraph(final String resultFileName,
122                         final boolean keepDotFile) {
123
124                 final String desktopPath = CommonPathResolver.getDesktopDirectory()
125                                 .getAbsolutePath() + "/";
126
127                 generateGraph(desktopPath, resultFileName, keepDotFile);
128         }
129
130         /**
131          * @param targetDirectory
132          *            target directory name
133          *
134          * @param resultFileName
135          *            file name for the generated graph. File extension will be
136          *            added automatically. Existing file with the same name will be
137          *            overwritten.
138          *
139          * @param keepDotFile
140          *            if set to <code>true</code> then intermediary GraphViz DOT
141          *            file will be kept.
142          */
143
144         public void generateGraph(String targetDirectory,
145                         final String resultFileName, final boolean keepDotFile) {
146
147                 if (!targetDirectory.endsWith("/"))
148                         targetDirectory += "/";
149
150                 final String dotFilePath = targetDirectory + resultFileName + ".dot";
151                 final String imageFilePath = targetDirectory + resultFileName + ".png";
152
153                 System.out.println("Dot file path:" + dotFilePath);
154
155                 try {
156                         // write DOT file to disk
157                         final PrintWriter out = new PrintWriter(dotFilePath);
158                         out.write(getDot());
159                         out.close();
160
161                         // execute GraphViz to visualize graph
162                         try {
163                                 Runtime.getRuntime()
164                                                 .exec(new String[] { "dot", "-Tpng", dotFilePath, "-o",
165                                                                 imageFilePath }).waitFor();
166                         } catch (final InterruptedException e) {
167                         } finally {
168                         }
169
170                         if (!keepDotFile) {
171                                 // delete dot file
172                                 final File dotFile = new File(dotFilePath);
173                                 dotFile.delete();
174                         }
175                 } catch (final IOException e) {
176                         System.err.println(e);
177                 }
178
179         }
180
181         private String getDot() {
182                 final StringBuffer result = new StringBuffer();
183
184                 result.append("digraph Java {\n");
185                 result.append("graph [rankdir=LR, overlap = false, concentrate=true];\n");
186
187                 for (final Map.Entry<String, ClassDescriptor> entry : fullyQualifiedNameToClassMap
188                                 .entrySet())
189                         result.append(entry.getValue().getDot());
190
191                 result.append("}\n");
192
193                 final String resultStr = result.toString();
194                 return resultStr;
195         }
196
197         public Filter getFilter() {
198                 return filter;
199         }
200
201         /**
202          * Hide orphaned class that have no references
203          */
204         public void hideOrphanedClasses() {
205
206                 for (final ClassDescriptor classDescriptor : fullyQualifiedNameToClassMap
207                                 .values())
208                         classDescriptor.hideClassIfNoReferences();
209
210         }
211
212         public void registerClass(final String classFullyQualifiedName,
213                         final ClassDescriptor classDescriptor) {
214                 fullyQualifiedNameToClassMap.put(classFullyQualifiedName,
215                                 classDescriptor);
216         }
217
218         public void setFilter(final Filter filter) {
219                 this.filter = filter;
220         }
221
222 }