Code refactoring. Simplified API.
[javainspect.git] / src / main / java / eu / svjatoslav / inspector / java / structure / ClassGraph.java
1 /*
2  * JavaInspect - Utility to visualize java software
3  * Copyright (C) 2013-2015, 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         /**
25          * Maps class fully qualified names to class descriptors.
26          */
27         private final Map<String, ClassDescriptor> fullyQualifiedNameToClassMap = new HashMap<String, ClassDescriptor>();
28
29         private final Filter filter = new Filter();
30
31         public ClassGraph() {
32         }
33
34         /**
35          * @param objects
36          *            objects that shall be added to graph
37          */
38         public ClassGraph add(final Object... objects) {
39
40                 if (objects != null)
41                         for (final Object object : objects)
42                                 addObject(object);
43
44                 return this;
45         }
46
47         private void addObject(final Object object) {
48                 if (object instanceof Class)
49                         getOrCreateClassDescriptor((Class) object);
50                 else
51                         getOrCreateClassDescriptor(object.getClass());
52         }
53
54         /**
55          * @param path
56          *            path to recursively scan for java source code could be
57          *            relative to current project or absolute
58          */
59         public void addProject(final String path) {
60                 final ProjectScanner projectScanner = new ProjectScanner(new File(path));
61                 for (final Clazz clazz : projectScanner.getAllClasses())
62                         try {
63                                 System.out.println("Class full name: " + clazz.getFullName());
64                                 final Class c = this.getClass().forName(clazz.getFullName());
65                                 addObject(c);
66                         } catch (final Exception exception) {
67                                 System.out.println("cannot add class: "
68                                                 + exception.getMessage());
69                         }
70         }
71
72         /**
73          * @param resultFileName
74          *            file name for the generated graph. Existing file with the same
75          *            name will be overwritten.
76          */
77         public void generateGraph(final String resultFileName) {
78                 generateGraph(resultFileName, false);
79         }
80
81         /**
82          * @param resultFileName
83          *            file name for the generated graph. File extension will be
84          *            added automatically. Existing file with the same name will be
85          *            overwritten.
86          *
87          * @param keepDotFile
88          *            if set to <code>true</code> then intermediary GraphViz DOT
89          *            file will be kept.
90          */
91
92         public void generateGraph(final String resultFileName,
93                         final boolean keepDotFile) {
94
95                 final String desktopPath = CommonPathResolver.getDesktopDirectory()
96                                 .getAbsolutePath() + "/";
97
98                 generateGraph(desktopPath, resultFileName, keepDotFile);
99         }
100
101         /**
102          * @param targetDirectory
103          *            target directory name
104          *
105          * @param resultFileName
106          *            file name for the generated graph. File extension will be
107          *            added automatically. Existing file with the same name will be
108          *            overwritten.
109          *
110          * @param keepDotFile
111          *            if set to <code>true</code> then intermediary GraphViz DOT
112          *            file will be kept.
113          */
114
115         public void generateGraph(String targetDirectory,
116                         final String resultFileName, final boolean keepDotFile) {
117
118                 if (!targetDirectory.endsWith("/"))
119                         targetDirectory += "/";
120
121                 final String dotFilePath = targetDirectory + resultFileName + ".dot";
122                 final String imageFilePath = targetDirectory + resultFileName + ".png";
123
124                 System.out.println("Dot file path:" + dotFilePath);
125
126                 try {
127                         // write DOT file to disk
128                         final PrintWriter out = new PrintWriter(dotFilePath);
129                         out.write(getDot());
130                         out.close();
131
132                         // execute GraphViz to visualize graph
133                         try {
134                                 Runtime.getRuntime()
135                                                 .exec(new String[] { "dot", "-Tpng", dotFilePath, "-o",
136                                                                 imageFilePath }).waitFor();
137                         } catch (final InterruptedException e) {
138                         } finally {
139                         }
140
141                         if (!keepDotFile) {
142                                 // delete dot file
143                                 final File dotFile = new File(dotFilePath);
144                                 dotFile.delete();
145                         }
146                 } catch (final IOException e) {
147                         System.err.println(e);
148                 }
149
150         }
151
152         private String getDot() {
153                 final StringBuffer result = new StringBuffer();
154
155                 result.append("digraph Java {\n");
156                 result.append("graph [rankdir=LR, overlap = false, concentrate=true];\n");
157
158                 for (final Map.Entry<String, ClassDescriptor> entry : fullyQualifiedNameToClassMap
159                                 .entrySet())
160                         result.append(entry.getValue().getDot());
161
162                 result.append("}\n");
163
164                 final String resultStr = result.toString();
165                 return resultStr;
166         }
167
168         public Filter getFilter() {
169                 return filter;
170         }
171
172         /**
173          * @param clazz
174          *            class that shall be added to graph
175          */
176         protected ClassDescriptor getOrCreateClassDescriptor(final Class clazz) {
177
178                 if (clazz == null)
179                         return null;
180
181                 final String classFullyQualifiedName = clazz.getName();
182
183                 // reuse existing instance if possible
184                 if (fullyQualifiedNameToClassMap.containsKey(classFullyQualifiedName))
185                         return fullyQualifiedNameToClassMap.get(classFullyQualifiedName);
186
187                 // create new class descriptor
188                 final ClassDescriptor newClassDescriptor = new ClassDescriptor(this);
189                 fullyQualifiedNameToClassMap.put(classFullyQualifiedName,
190                                 newClassDescriptor);
191
192                 newClassDescriptor.analyzeClass(clazz);
193
194                 return newClassDescriptor;
195         }
196
197         /**
198          * Hide orphaned class that have no references
199          */
200         public void hideOrphanedClasses() {
201
202                 for (final ClassDescriptor classDescriptor : fullyQualifiedNameToClassMap
203                                 .values())
204                         classDescriptor.hideClassIfNoReferences();
205
206         }
207
208 }