2 * JavaInspect - Utility to visualize java software
3 * Copyright (C) 2013-2017, 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;
26 import static java.io.File.separator;
28 public class ClassGraph {
31 * Maps class fully qualified names to class descriptors.
33 private final Map<String, ClassDescriptor> fullyQualifiedNameToClassMap = new HashMap<>();
35 private final List<String> blacklistClassGlobs = new ArrayList<>();
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;
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());
67 * @param path path to recursively scan for java source code could be
68 * relative to current project or absolute
70 public void addProject(final String path) {
71 final ProjectScanner projectScanner = new ProjectScanner(new File(path));
72 for (final Clazz clazz : projectScanner.getAllClasses())
74 System.out.println("Class full name: " + clazz.getFullName());
75 final Class c = Class.forName(clazz.getFullName());
77 } catch (final Exception exception) {
78 System.out.println("cannot add class: "
79 + exception.getMessage());
83 public void blacklistClassGlob(final String glob) {
84 blacklistClassGlobs.add(glob);
87 public void setTargetImageType(TargetImageType targetImageType) {
88 this.targetImageType = targetImageType;
92 * @param resultFileName file name for the generated graph. File extension will be
93 * added automatically. Existing file with the same name will be
97 public void generateGraph(final String resultFileName) {
99 final String dotFilePath = targetDirectoryPath + resultFileName + ".dot";
100 final String imageFilePath = targetDirectoryPath + resultFileName + "." + targetImageType.fileExtension;
103 // write DOT file to disk
104 final PrintWriter out = new PrintWriter(dotFilePath, UTF_8);
108 // execute GraphViz to visualize graph
111 .exec(new String[]{"dot", "-T" + targetImageType.fileExtension, dotFilePath, "-o",
112 imageFilePath}).waitFor();
113 } catch (final InterruptedException ignored) {
118 if (!new File(dotFilePath).delete())
119 throw new RuntimeException("Cannot delete file: " + dotFilePath);
121 } catch (final IOException e) {
122 throw new RuntimeException("Unable to generate graph: " + e.getMessage(), e);
127 private String getDot() {
128 final StringBuilder result = new StringBuilder();
130 result.append("digraph Java {\n");
131 result.append("graph [rankdir=LR, overlap = false, concentrate=true];\n");
133 for (final Map.Entry<String, ClassDescriptor> entry : fullyQualifiedNameToClassMap
135 result.append(entry.getValue().getDot());
137 result.append("}\n");
139 return result.toString();
143 * @param clazz class that shall be added to graph
144 * @return {@link ClassDescriptor} corresponding to given {@link Class}
146 protected ClassDescriptor getOrCreateClassDescriptor(final Class clazz) {
151 final String classFullyQualifiedName = clazz.getName();
153 // reuse existing instance if possible
154 if (fullyQualifiedNameToClassMap.containsKey(classFullyQualifiedName))
155 return fullyQualifiedNameToClassMap.get(classFullyQualifiedName);
157 // create new class descriptor
158 final ClassDescriptor newClassDescriptor = new ClassDescriptor(this);
159 fullyQualifiedNameToClassMap.put(classFullyQualifiedName,
162 newClassDescriptor.analyzeClass(clazz);
164 return newClassDescriptor;
168 * Hide orphaned class that have no references
170 * @return this {@link ClassGraph}
172 public ClassGraph hideOrphanedClasses() {
174 for (final ClassDescriptor classDescriptor : fullyQualifiedNameToClassMap
176 classDescriptor.hideClassIfNoReferences();
181 protected boolean isClassShown(final String className) {
182 for (final String pattern : blacklistClassGlobs)
183 if (WildCardMatcher.match(className, pattern))
186 if (!whitelistClassGlobs.isEmpty()) {
187 for (final String pattern : whitelistClassGlobs)
188 if (WildCardMatcher.match(className, pattern))
196 public ClassGraph setKeepDotFile(final boolean keepDotFile) {
197 this.keepDotFile = keepDotFile;
202 public ClassGraph setTargetDirectoryPath(String directoryPath) {
203 if (!directoryPath.endsWith(separator))
204 directoryPath += separator;
206 targetDirectoryPath = directoryPath;
211 public ClassGraph whitelistClassGlob(final String glob) {
212 whitelistClassGlobs.add(glob);