Removed half-baked java source code parser.
[javainspect.git] / src / main / java / eu / svjatoslav / inspector / java / structure / ClassGraph.java
1 /*
2  * JavaInspect - Utility to visualize java software
3  * Copyright (C) 2013-2020, 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.string.GlobMatcher;
13
14 import java.io.File;
15 import java.io.IOException;
16 import java.io.PrintWriter;
17 import java.util.ArrayList;
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
21
22 import static java.io.File.separator;
23 import static java.lang.System.getProperty;
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<>();
31
32     private final List<String> blacklistClassGlobs = new ArrayList<>();
33
34     private final List<String> whitelistClassGlobs = new ArrayList<>();
35     TargetImageType targetImageType = TargetImageType.SVG;
36     private boolean keepDotFile;
37
38     /**
39      * Default to current directory
40      */
41     private File targetDirectory = new File(getProperty("user.dir") + separator);
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     public void blacklistClassGlob(final String glob) {
67         blacklistClassGlobs.add(glob);
68     }
69
70     public void setTargetImageType(TargetImageType targetImageType) {
71         this.targetImageType = targetImageType;
72     }
73
74     /**
75      * @param resultFileName file name for the generated graph. File extension will be
76      *                       added automatically. Existing file with the same name will be
77      *                       overwritten.
78      */
79
80     public void generateGraph(final String resultFileName) {
81
82         final File dotFile = new File(targetDirectory, resultFileName + ".dot");
83         final File imageFile = new File(targetDirectory, resultFileName + "." + targetImageType.fileExtension);
84
85         try {
86             // write DOT file to disk
87             final PrintWriter out = new PrintWriter(dotFile, "UTF-8");
88             out.write(getDot());
89             out.close();
90
91             // execute GraphViz to visualize graph
92             try {
93                 Runtime.getRuntime()
94                         .exec(new String[]{"dot",
95                                 "-T" + targetImageType.fileExtension,
96                                 dotFile.getAbsolutePath(),
97                                 "-o",
98                                 imageFile.getAbsolutePath()}).waitFor();
99             } catch (final InterruptedException ignored) {
100             }
101
102             if (!keepDotFile)
103                 // delete dot file
104                 if (!dotFile.delete())
105                     throw new RuntimeException("Cannot delete file: " + dotFile.getAbsolutePath());
106
107         } catch (final IOException e) {
108             throw new RuntimeException("Unable to generate graph: " + e.getMessage(), e);
109         }
110
111     }
112
113     private String getDot() {
114         final StringBuilder result = new StringBuilder();
115
116         result.append("digraph Java {\n");
117         result.append("graph [rankdir=LR, overlap = false, concentrate=true];\n");
118
119         for (final Map.Entry<String, ClassDescriptor> entry : fullyQualifiedNameToClassMap
120                 .entrySet())
121             result.append(entry.getValue().getDot());
122
123         result.append("}\n");
124
125         return result.toString();
126     }
127
128     /**
129      * @param clazz class that shall be added to graph
130      * @return {@link ClassDescriptor} corresponding to given {@link Class}
131      */
132     protected ClassDescriptor getOrCreateClassDescriptor(final Class clazz) {
133
134         if (clazz == null)
135             return null;
136
137         final String classFullyQualifiedName = clazz.getName();
138
139         // reuse existing instance if possible
140         if (fullyQualifiedNameToClassMap.containsKey(classFullyQualifiedName))
141             return fullyQualifiedNameToClassMap.get(classFullyQualifiedName);
142
143         // create new class descriptor
144         final ClassDescriptor newClassDescriptor = new ClassDescriptor(this);
145         fullyQualifiedNameToClassMap.put(classFullyQualifiedName,
146                 newClassDescriptor);
147
148         newClassDescriptor.analyzeClass(clazz);
149
150         return newClassDescriptor;
151     }
152
153     /**
154      * Hide orphaned class that have no references
155      *
156      * @return this {@link ClassGraph}
157      */
158     public ClassGraph hideOrphanedClasses() {
159
160         for (final ClassDescriptor classDescriptor : fullyQualifiedNameToClassMap
161                 .values())
162             classDescriptor.hideClassIfNoReferences();
163
164         return this;
165     }
166
167     protected boolean isClassShown(final String className) {
168         for (final String pattern : blacklistClassGlobs)
169             if (GlobMatcher.match(className, pattern))
170                 return false;
171
172         if (!whitelistClassGlobs.isEmpty()) {
173             for (final String pattern : whitelistClassGlobs)
174                 if (GlobMatcher.match(className, pattern))
175                     return true;
176             return false;
177         }
178
179         return true;
180     }
181
182     public ClassGraph setKeepDotFile(final boolean keepDotFile) {
183         this.keepDotFile = keepDotFile;
184
185         return this;
186     }
187
188     public ClassGraph setTargetDirectory(File targetDirectory) {
189         this.targetDirectory = targetDirectory;
190         return this;
191     }
192
193     public ClassGraph whitelistClassGlob(final String glob) {
194         whitelistClassGlobs.add(glob);
195         return this;
196     }
197
198 }