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