2 * JavaInspect - Utility to visualize java software
3 * Copyright (C) 2013-2020, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
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.
10 package eu.svjatoslav.inspector.java.structure;
12 import eu.svjatoslav.commons.string.GlobMatcher;
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;
22 import static java.io.File.separator;
23 import static java.lang.System.getProperty;
25 public class ClassGraph {
28 * Maps class fully qualified names to class descriptors.
30 private final Map<String, ClassDescriptor> fullyQualifiedNameToClassMap = new HashMap<>();
32 private final List<String> blacklistClassGlobs = new ArrayList<>();
34 private final List<String> whitelistClassGlobs = new ArrayList<>();
35 TargetImageType targetImageType = TargetImageType.SVG;
36 private boolean keepDotFile;
39 * Default to current directory
41 private File targetDirectory = new File(getProperty("user.dir") + separator);
47 * @param objects objects that shall be added to graph
48 * @return this {@link ClassGraph}
50 public ClassGraph add(final Object... objects) {
53 for (final Object object : objects)
59 private void addObject(final Object object) {
60 if (object instanceof Class)
61 getOrCreateClassDescriptor((Class) object);
63 getOrCreateClassDescriptor(object.getClass());
66 public void blacklistClassGlob(final String glob) {
67 blacklistClassGlobs.add(glob);
70 public void setTargetImageType(TargetImageType targetImageType) {
71 this.targetImageType = targetImageType;
75 * @param resultFileName file name for the generated graph. File extension will be
76 * added automatically. Existing file with the same name will be
80 public void generateGraph(final String resultFileName) {
82 final File dotFile = new File(targetDirectory, resultFileName + ".dot");
83 final File imageFile = new File(targetDirectory, resultFileName + "." + targetImageType.fileExtension);
86 // write DOT file to disk
87 final PrintWriter out = new PrintWriter(dotFile, "UTF-8");
91 // execute GraphViz to visualize graph
94 .exec(new String[]{"dot",
95 "-T" + targetImageType.fileExtension,
96 dotFile.getAbsolutePath(),
98 imageFile.getAbsolutePath()}).waitFor();
99 } catch (final InterruptedException ignored) {
104 if (!dotFile.delete())
105 throw new RuntimeException("Cannot delete file: " + dotFile.getAbsolutePath());
107 } catch (final IOException e) {
108 throw new RuntimeException("Unable to generate graph: " + e.getMessage(), e);
113 private String getDot() {
114 final StringBuilder result = new StringBuilder();
116 result.append("digraph Java {\n");
117 result.append("graph [rankdir=LR, overlap = false, concentrate=true];\n");
119 for (final Map.Entry<String, ClassDescriptor> entry : fullyQualifiedNameToClassMap
121 result.append(entry.getValue().getDot());
123 result.append("}\n");
125 return result.toString();
129 * @param clazz class that shall be added to graph
130 * @return {@link ClassDescriptor} corresponding to given {@link Class}
132 protected ClassDescriptor getOrCreateClassDescriptor(final Class clazz) {
137 final String classFullyQualifiedName = clazz.getName();
139 // reuse existing instance if possible
140 if (fullyQualifiedNameToClassMap.containsKey(classFullyQualifiedName))
141 return fullyQualifiedNameToClassMap.get(classFullyQualifiedName);
143 // create new class descriptor
144 final ClassDescriptor newClassDescriptor = new ClassDescriptor(this);
145 fullyQualifiedNameToClassMap.put(classFullyQualifiedName,
148 newClassDescriptor.analyzeClass(clazz);
150 return newClassDescriptor;
154 * Hide orphaned class that have no references
156 * @return this {@link ClassGraph}
158 public ClassGraph hideOrphanedClasses() {
160 for (final ClassDescriptor classDescriptor : fullyQualifiedNameToClassMap
162 classDescriptor.hideClassIfNoReferences();
167 protected boolean isClassShown(final String className) {
168 for (final String pattern : blacklistClassGlobs)
169 if (GlobMatcher.match(className, pattern))
172 if (!whitelistClassGlobs.isEmpty()) {
173 for (final String pattern : whitelistClassGlobs)
174 if (GlobMatcher.match(className, pattern))
182 public ClassGraph setKeepDotFile(final boolean keepDotFile) {
183 this.keepDotFile = keepDotFile;
188 public ClassGraph setTargetDirectory(File targetDirectory) {
189 this.targetDirectory = targetDirectory;
193 public ClassGraph whitelistClassGlob(final String glob) {
194 whitelistClassGlobs.add(glob);