Code cleanup.
[javainspect.git] / src / main / java / eu / svjatoslav / inspector / java / structure / ClassGraph.java
1 /*
2  * JavaInspect - Utility to visualize java software
3  * Copyright (C) 2013-2017, 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 import static java.io.File.separator;
27
28 public class ClassGraph {
29
30     /**
31      * Maps class fully qualified names to class descriptors.
32      */
33     private final Map<String, ClassDescriptor> fullyQualifiedNameToClassMap = new HashMap<>();
34
35     private final List<String> blacklistClassGlobs = new ArrayList<>();
36
37     private final List<String> whitelistClassGlobs = new ArrayList<>();
38     TargetImageType targetImageType = TargetImageType.SVG;
39     private String targetDirectoryPath = CommonPathResolver.getDesktopDirectory()
40             .getAbsolutePath() + separator;
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 = Class.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 blacklistClassGlob(final String glob) {
84         blacklistClassGlobs.add(glob);
85     }
86
87     public void setTargetImageType(TargetImageType targetImageType) {
88         this.targetImageType = targetImageType;
89     }
90
91     /**
92      * @param resultFileName 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
97     public void generateGraph(final String resultFileName) {
98
99         final String dotFilePath = targetDirectoryPath + resultFileName + ".dot";
100         final String imageFilePath = targetDirectoryPath + resultFileName + "." + targetImageType.fileExtension;
101
102         try {
103             // write DOT file to disk
104             final PrintWriter out = new PrintWriter(dotFilePath, UTF_8);
105             out.write(getDot());
106             out.close();
107
108             // execute GraphViz to visualize graph
109             try {
110                 Runtime.getRuntime()
111                         .exec(new String[]{"dot", "-T" + targetImageType.fileExtension, dotFilePath, "-o",
112                                 imageFilePath}).waitFor();
113             } catch (final InterruptedException ignored) {
114             }
115
116             if (!keepDotFile)
117                 // delete dot file
118                 if (!new File(dotFilePath).delete())
119                     throw new RuntimeException("Cannot delete file: " + dotFilePath);
120
121         } catch (final IOException e) {
122             throw new RuntimeException("Unable to generate graph: " + e.getMessage(), e);
123         }
124
125     }
126
127     private String getDot() {
128         final StringBuilder result = new StringBuilder();
129
130         result.append("digraph Java {\n");
131         result.append("graph [rankdir=LR, overlap = false, concentrate=true];\n");
132
133         for (final Map.Entry<String, ClassDescriptor> entry : fullyQualifiedNameToClassMap
134                 .entrySet())
135             result.append(entry.getValue().getDot());
136
137         result.append("}\n");
138
139         return result.toString();
140     }
141
142     /**
143      * @param clazz class that shall be added to graph
144      * @return {@link ClassDescriptor} corresponding to given {@link Class}
145      */
146     protected ClassDescriptor getOrCreateClassDescriptor(final Class clazz) {
147
148         if (clazz == null)
149             return null;
150
151         final String classFullyQualifiedName = clazz.getName();
152
153         // reuse existing instance if possible
154         if (fullyQualifiedNameToClassMap.containsKey(classFullyQualifiedName))
155             return fullyQualifiedNameToClassMap.get(classFullyQualifiedName);
156
157         // create new class descriptor
158         final ClassDescriptor newClassDescriptor = new ClassDescriptor(this);
159         fullyQualifiedNameToClassMap.put(classFullyQualifiedName,
160                 newClassDescriptor);
161
162         newClassDescriptor.analyzeClass(clazz);
163
164         return newClassDescriptor;
165     }
166
167     /**
168      * Hide orphaned class that have no references
169      *
170      * @return this {@link ClassGraph}
171      */
172     public ClassGraph hideOrphanedClasses() {
173
174         for (final ClassDescriptor classDescriptor : fullyQualifiedNameToClassMap
175                 .values())
176             classDescriptor.hideClassIfNoReferences();
177
178         return this;
179     }
180
181     protected boolean isClassShown(final String className) {
182         for (final String pattern : blacklistClassGlobs)
183             if (WildCardMatcher.match(className, pattern))
184                 return false;
185
186         if (!whitelistClassGlobs.isEmpty()) {
187             for (final String pattern : whitelistClassGlobs)
188                 if (WildCardMatcher.match(className, pattern))
189                     return true;
190             return false;
191         }
192
193         return true;
194     }
195
196     public ClassGraph setKeepDotFile(final boolean keepDotFile) {
197         this.keepDotFile = keepDotFile;
198
199         return this;
200     }
201
202     public ClassGraph setTargetDirectoryPath(String directoryPath) {
203         if (!directoryPath.endsWith(separator))
204             directoryPath += separator;
205
206         targetDirectoryPath = directoryPath;
207
208         return this;
209     }
210
211     public ClassGraph whitelistClassGlob(final String glob) {
212         whitelistClassGlobs.add(glob);
213         return this;
214     }
215
216 }