2 * JavaInspect - Utility to visualize java software
3 * Copyright (C) 2013-2019, 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.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;
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;
25 import static eu.svjatoslav.inspector.java.methods.JavaFile.UTF_8;
27 public class ClassGraph {
30 * Maps class fully qualified names to class descriptors.
32 private final Map<String, ClassDescriptor> fullyQualifiedNameToClassMap = new HashMap<>();
34 private final List<String> blacklistClassGlobs = new ArrayList<>();
36 private final List<String> whitelistClassGlobs = new ArrayList<>();
37 TargetImageType targetImageType = TargetImageType.SVG;
38 private File targetDirectory = CommonPathResolver.getDesktopDirectory();
39 private boolean keepDotFile;
45 * @param objects objects that shall be added to graph
46 * @return this {@link ClassGraph}
48 public ClassGraph add(final Object... objects) {
51 for (final Object object : objects)
57 private void addObject(final Object object) {
58 if (object instanceof Class)
59 getOrCreateClassDescriptor((Class) object);
61 getOrCreateClassDescriptor(object.getClass());
65 * @param path path to recursively scan for java source code could be
66 * relative to current project or absolute
68 public void addProject(final String path) {
69 final ProjectScanner projectScanner = new ProjectScanner(new File(path));
70 for (final Clazz clazz : projectScanner.getAllClasses())
72 System.out.println("Class full name: " + clazz.getFullName());
73 final Class c = Class.forName(clazz.getFullName());
75 } catch (final Exception exception) {
76 System.out.println("cannot add class: "
77 + exception.getMessage());
81 public void blacklistClassGlob(final String glob) {
82 blacklistClassGlobs.add(glob);
85 public void setTargetImageType(TargetImageType targetImageType) {
86 this.targetImageType = targetImageType;
90 * @param resultFileName file name for the generated graph. File extension will be
91 * added automatically. Existing file with the same name will be
95 public void generateGraph(final String resultFileName) {
97 final File dotFile = new File(targetDirectory, resultFileName + ".dot");
98 final File imageFile = new File(targetDirectory, resultFileName + "." + targetImageType.fileExtension);
101 // write DOT file to disk
102 final PrintWriter out = new PrintWriter(dotFile, UTF_8);
106 // execute GraphViz to visualize graph
109 .exec(new String[]{"dot",
110 "-T" + targetImageType.fileExtension,
111 dotFile.getAbsolutePath(),
113 imageFile.getAbsolutePath()}).waitFor();
114 } catch (final InterruptedException ignored) {
119 if (!dotFile.delete())
120 throw new RuntimeException("Cannot delete file: " + dotFile.getAbsolutePath());
122 } catch (final IOException e) {
123 throw new RuntimeException("Unable to generate graph: " + e.getMessage(), e);
128 private String getDot() {
129 final StringBuilder result = new StringBuilder();
131 result.append("digraph Java {\n");
132 result.append("graph [rankdir=LR, overlap = false, concentrate=true];\n");
134 for (final Map.Entry<String, ClassDescriptor> entry : fullyQualifiedNameToClassMap
136 result.append(entry.getValue().getDot());
138 result.append("}\n");
140 return result.toString();
144 * @param clazz class that shall be added to graph
145 * @return {@link ClassDescriptor} corresponding to given {@link Class}
147 protected ClassDescriptor getOrCreateClassDescriptor(final Class clazz) {
152 final String classFullyQualifiedName = clazz.getName();
154 // reuse existing instance if possible
155 if (fullyQualifiedNameToClassMap.containsKey(classFullyQualifiedName))
156 return fullyQualifiedNameToClassMap.get(classFullyQualifiedName);
158 // create new class descriptor
159 final ClassDescriptor newClassDescriptor = new ClassDescriptor(this);
160 fullyQualifiedNameToClassMap.put(classFullyQualifiedName,
163 newClassDescriptor.analyzeClass(clazz);
165 return newClassDescriptor;
169 * Hide orphaned class that have no references
171 * @return this {@link ClassGraph}
173 public ClassGraph hideOrphanedClasses() {
175 for (final ClassDescriptor classDescriptor : fullyQualifiedNameToClassMap
177 classDescriptor.hideClassIfNoReferences();
182 protected boolean isClassShown(final String className) {
183 for (final String pattern : blacklistClassGlobs)
184 if (WildCardMatcher.match(className, pattern))
187 if (!whitelistClassGlobs.isEmpty()) {
188 for (final String pattern : whitelistClassGlobs)
189 if (WildCardMatcher.match(className, pattern))
197 public ClassGraph setKeepDotFile(final boolean keepDotFile) {
198 this.keepDotFile = keepDotFile;
203 public ClassGraph setTargetDirectory(File targetDirectory) {
204 this.targetDirectory = targetDirectory;
208 public ClassGraph whitelistClassGlob(final String glob) {
209 whitelistClassGlobs.add(glob);