-
-
Notifications
You must be signed in to change notification settings - Fork 309
GraphViz API
The classgraph-viz library renders a scan result as a GraphViz .dot file, which GraphViz can then lay out and turn into an image. Replace X.Y.Z with the latest release number:
<dependency>
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph-viz</artifactId>
<version>X.Y.Z</version>
</dependency>classgraph-viz depends on classgraph, so this one dependency is all you need -- it brings the scanner with it.
- Generating a graph
- The two kinds of graph
- GraphVizDotFile
- GraphVizDotFileOptions
- What has to be enabled before scanning
import io.github.classgraph.*;
import io.github.classgraph.viz.*;
import java.nio.file.Path;
try (ScanResult scanResult = new ClassGraph().enableNonSystemModules().enableClasspath()
.acceptPackages("com.xyz")
.enableClassInfo().enableFieldInfo().enableMethodInfo().enableAnnotationInfo()
.scan()) {
GraphVizDotFile.write(scanResult, scanResult.getAllClasses(), Path.of("classgraph.dot"));
}Then run GraphViz over the file:
dot -Tsvg classgraph.dot > classgraph.svg
Use GraphVizDotFile.generate(...) instead of write(...) if you want the .dot file contents as a String rather than written to a file. The file is written in UTF-8.
generate / write draw the classes themselves: each class is a box listing its fields, methods and annotations, and the boxes are connected to superclasses, superinterfaces, and the types of fields, method parameters and return values. Class boxes are colored by kind: standard classes yellow, interfaces blue, annotations purple.
This graph cannot show a dependency that only exists inside a method body -- a class referenced by a local variable or an intermediate value. For those, use the second kind of graph.
generateFromInterClassDependencies / writeFromInterClassDependencies draw only the class names, connected by "depends upon" edges. This needs ClassGraph#enableInterClassDependencies() before scanning, and covers every kind of reference, including references inside method bodies. There is only one arrow type: it says that one class depends upon another in some way, without saying how. The exact relationship cannot be recovered without parsing the full bytecode of every method, which ClassGraph does not attempt -- these dependencies are found from the types of fields and methods, and from the class names in each class' constant pool.
Every method is static, and takes the ScanResult the classes came from, and the ClassInfoList to plot (e.g. scanResult.getAllClasses(), or any list you have filtered down yourself).
| Method | Returns |
|---|---|
String generate(ScanResult, ClassInfoList) |
The .dot file contents, with default options. |
String generate(ScanResult, ClassInfoList, GraphVizDotFileOptions) |
The .dot file contents. |
Path write(ScanResult, ClassInfoList, Path) |
Writes the .dot file in UTF-8, and returns the path. Throws IOException. |
Path write(ScanResult, ClassInfoList, Path, GraphVizDotFileOptions) |
The same, with options. |
String generateFromInterClassDependencies(ScanResult, ClassInfoList) |
The inter-class dependency graph, with default options. |
String generateFromInterClassDependencies(ScanResult, ClassInfoList, GraphVizDotFileOptions) |
The inter-class dependency graph. |
Path writeFromInterClassDependencies(ScanResult, ClassInfoList, Path) |
Writes the inter-class dependency graph. Throws IOException. |
Path writeFromInterClassDependencies(ScanResult, ClassInfoList, Path, GraphVizDotFileOptions) |
The same, with options. |
generate and write throw IllegalStateException if ClassGraph#enableClassInfo() was not called before scanning, and the ...FromInterClassDependencies methods throw it if ClassGraph#enableInterClassDependencies() was not called -- in either case there would be nothing to graph.
A freshly constructed GraphVizDotFileOptions holds the defaults. Each method sets one option and returns this, so options can be chained:
GraphVizDotFile.generate(scanResult, scanResult.getAllClasses(),
new GraphVizDotFileOptions().setLayoutSize(12, 8).hideFields().hideMethods());Every option has a method for each of its settings, so an instance you were handed from elsewhere can be set to either value, not only changed away from the default:
| Methods | Effect | Default |
|---|---|---|
setLayoutSize(float sizeX, float sizeY) |
The image output size in inches, for when GraphViz renders the file. | 10.5 by 8 inches |
showFields() / hideFields()
|
Show or do not show fields within class boxes. | shown |
showFieldTypeDependencyEdges() / hideFieldTypeDependencyEdges()
|
Draw or do not draw edges from a class to the types of its fields. | drawn |
showMethods() / hideMethods()
|
Show or do not show methods within class boxes. | shown |
showMethodTypeDependencyEdges() / hideMethodTypeDependencyEdges()
|
Draw or do not draw edges from a class to its methods' return and parameter types. | drawn |
showAnnotations() / hideAnnotations()
|
Show or do not show annotations within class boxes. Covers annotations on the class, and on its fields, its methods and their parameters. | shown |
showAnnotationDependencyEdges() / hideAnnotationDependencyEdges()
|
Draw or do not draw edges from a class to the annotations on it. | drawn |
useSimpleNames() / useFullyQualifiedNames()
|
Show class names in field and method type signatures and in annotations with the package name stripped, or fully qualified. | package name stripped |
includeExternalClasses() / excludeExternalClasses()
|
Show or do not show external (non-accepted) classes in the inter-class dependency graph. includeExternalClasses() only has an effect if ClassGraph#enableExternalClasses() was called before scanning; excludeExternalClasses() hides them even if it was. |
follows the scan's own setting |
The inter-class dependency graph reads only setLayoutSize, includeExternalClasses and excludeExternalClasses -- the options that show or hide the contents of a class box have no effect on it, since it does not draw class contents.
A class box only shows what the scan actually collected, so enable the information you want to see before scanning:
| To show | Call before scanning |
|---|---|
| Classes at all (required) | enableClassInfo() |
| Non-public classes | ignoreClassVisibility() |
| Fields | enableFieldInfo() |
| Non-public fields | ignoreFieldVisibility() |
| Methods | enableMethodInfo() |
| Non-public methods | ignoreMethodVisibility() |
| Annotations | enableAnnotationInfo() |
| Non-public annotation classes as nodes of their own |
ignoreClassVisibility() (an annotation is a class) |
| The inter-class dependency graph | enableInterClassDependencies() |
| External (non-accepted) classes | enableExternalClasses() |
No method in the table is called for you by any other method, so call the method for every row you want. The accept and reject methods do not enable class scanning either. Each ignore...Visibility() method needs the enable...Info() method in the row above it: without it, scan() throws IllegalArgumentException.
Method parameter names only appear in the graph if the code being scanned was compiled with
javac -parameters. In Eclipse this setting is Project Properties > Java Compiler > Store information about method parameters (usable via reflection).