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