Refactoring. API simplifications. Documentation updates.
[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                         }
126                 } catch (final IOException e) {
127                         System.err.println(e);
128                 }
129
130         }
131
132         private String getDot() {
133                 final StringBuffer result = new StringBuffer();
134
135                 result.append("digraph Java {\n");
136                 result.append("graph [rankdir=LR, overlap = false, concentrate=true];\n");
137
138                 for (final Map.Entry<String, ClassDescriptor> entry : fullyQualifiedNameToClassMap
139                                 .entrySet())
140                         result.append(entry.getValue().getDot());
141
142                 result.append("}\n");
143
144                 final String resultStr = result.toString();
145                 return resultStr;
146         }
147
148         /**
149          * @param clazz
150          *            class that shall be added to graph
151          */
152         protected ClassDescriptor getOrCreateClassDescriptor(final Class clazz) {
153
154                 if (clazz == null)
155                         return null;
156
157                 final String classFullyQualifiedName = clazz.getName();
158
159                 // reuse existing instance if possible
160                 if (fullyQualifiedNameToClassMap.containsKey(classFullyQualifiedName))
161                         return fullyQualifiedNameToClassMap.get(classFullyQualifiedName);
162
163                 // create new class descriptor
164                 final ClassDescriptor newClassDescriptor = new ClassDescriptor(this);
165                 fullyQualifiedNameToClassMap.put(classFullyQualifiedName,
166                                 newClassDescriptor);
167
168                 newClassDescriptor.analyzeClass(clazz);
169
170                 return newClassDescriptor;
171         }
172
173         /**
174          * Hide orphaned class that have no references
175          */
176         public void hideOrphanedClasses() {
177
178                 for (final ClassDescriptor classDescriptor : fullyQualifiedNameToClassMap
179                                 .values())
180                         classDescriptor.hideClassIfNoReferences();
181
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 }