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