---
title: Generating an aerial view of your project with OpenRewrite
tags: java, jreleaser, openrewrite, graph
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t1ybqf9x4vz6tgj47kkn.webp
canonical_url: https://jtama.github.io/posts/en/generating-an-aerial-view-of-your-project-with-openrewrite/
series: Openrewrite
published: true
---
> Read it in [french here](https://jtama.github.io/posts/g-n-rer-une-vue-a-rienne-de-votre-projet-avec-openrewrite/)
In the previous article, we discussed (well, _I wrote, you read_) [Scanning Recipes](https://jtama.github.io/posts/technique-avanc-e-d-openrewrite-orchestrer-des-refactorings-complexes-avec-les-scanningrecipe/) when you need complete information to make a decision, or to generate code _ex nihilo_.
I promised you a little toy at the end... The time has come for the reveal: the **Project Graph Generator**.

## The Need: Mapping Your Code
It’s sometimes difficult to get a global view of the dependencies within your own code. Is this package too tightly coupled to others? What is the central class of my domain? Has my wonderful initial design withstood the ravages of time?
Since [OpenRewrite](https://docs.openrewrite.org/) already knows how to build the complete **LST** of your codebase, it’s not _exactly_ complicated to traverse it to deduce relationships.
This is exactly the goal of the [project-graph-generator](https://github.com/jtama/project-graph-generator) project: scanning your sources to deduce a dependency graph and produce a simple HTML page using [D3.js](https://d3js.org/) to display it.
## TL;DR;
> Oh thou, who doesn’t want to know more, but only wants to play with the graph, go no further than this chapter, you might gain some knowledge!
The project is easily usable via the `rewrite-maven-plugin` [or any other way to trigger an OpenRewrite recipe](https://docs.openrewrite.org/running-recipes). Here is the command to launch a complete analysis of your project:
```console
mvn -U org.openrewrite.maven:rewrite-maven-plugin:run \
-Drewrite.recipeArtifactCoordinates=io.github.jtama:project-graph-generator:RELEASE \ ①
-Drewrite.activeRecipes=io.github.jtama.openrewrite.ProjectAerialViewGenerator \
-Drewrite.exportDatatables=true
```
1. `RELEASE` = `latest`
Once the analysis is complete, the plugin generates a standalone HTML file containing the data AND the visualization. Simply open the `class-diagram.html` file at the root of your project in your browser to explore the web of your architecture.
You can also pass additional options to filter the nodes according to your needs:
* **`maxNodes=20`**\
Filters for classes with the highest number of incoming connections.
* **`basePackages=com.mycompany`**\
Forces the target base package
* **`includeTests=true`**\
Also include test classes.
To learn more 👉 [Here is the project repository](https://github.com/jtama/project-graph-generator). Go ahead, it’s open source, use it, fork it, make issues and pull requests!

## The Mechanics: Analysis Without Modification
Under the hood, the tool is based on the **ScanningRecipe** concept we saw previously. The major difference here is that the **generation** phase generates ***HTML****, and the *modification** phase (`visit`) does nothing, nada, zilch. The ~~entire goal~~ of the recipe is concentrated in the first pass: scanning the **LST**. Well, no, not the goal, rather the intelligence (not the A.I. kind).
It all starts with an accumulator - our famous graph - which will store the classes (Nodes) and their relationships (Links) as the scan progresses:
```java
public static class GraphScanAccumulator {
public List<Node> nodes = new ArrayList<>(); // ①
public List<Link> links = new ArrayList<>();
// ... search methods findNode() and findLink()
}
```
1. Did you seriously think I was going to explain this line?
To fill this graph, we will traverse the **LST** using a `JavaIsoVisitor`.
First, we identify each component of our architecture by overriding the `visitClassDeclaration` method. Each new class encountered becomes a "Node":
```java
@Override
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
if (includeTests() || not(isTestClass()).test(getCursor())) { // ①
if (classDecl.getType() != null) {
String fqn = classDecl.getType().getFullyQualifiedName();
graph.findNode(fqn).orElseGet(() -> { // ②
Node newNode = new Node(fqn, classDecl.getType().getPackageName());
graph.nodes.add(newNode);
return newNode;
});
}
return super.visitClassDeclaration(classDecl, ctx); // ③
}
return classDecl;
}
```
1. We only look at test classes if explicitly requested.
2. We use our `graph` accumulator to register the current class if it hasn’t been seen already.
3. We don’t forget to call `super` so that the visitor continues to descend into the tree. **_If and only if_** the class is of interest to us, otherwise, we don’t waste our time.
Next, we must weave the web. How do we know that class `A` depends on class `B`? We simply capture type usages (method calls, fields access, instantiations). For example, here’s how we proceed for method invocations:
```java
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
var mi = super.visitMethodInvocation(method, ctx); // ①
JavaType.FullyQualified targetType = mi.getMethodType() != null ? mi.getMethodType().getDeclaringType() : null; // ②
if (targetType != null) {
addLink(targetType); // ③
}
return mi;
}
```
1. Let the visitor visit.
2. Extract the type information (`JavaType`) carried by the invoked method to know which class it belongs to. The type can be `null`, particularly if OpenRewrite failed to determine it.
3. Invoke the `addLink` method.
The `addLink` method contains all the logic allowing us to determine if the `targetType` interests us, that is to say if it is part of the target packages. If so, we create a new link or strengthen an existing one.
The same logic is obviously applied [to member references, class fields, constructor invocations, etc](https://github.com/jtama/project-graph-generator/blob/main/src/main/java/io/github/jtama/openrewrite/ProjectAerialViewGenerator.java#L118).
This is the beauty of OpenRewrite’s model: the LST is already perfectly typed by the compiler during _parsing_. We don’t need to guess who an invoked method belongs to, the type information tells us with certainty.
## Going Further: Raw Export with Datatables
Generating an HTML page is nice. But what if you want to cross-reference this data, render it yourself in a tool like [Gephi](https://gephi.org/), or even provide it as context to an LLM to audit your architecture?
This is where OpenRewrite’s [Datatables](https://docs.openrewrite.org/authoring-recipes/data-tables) come in. In addition to the view, `project-graph-generator` can export metadata in the form of easily usable CSV files:
* *`target/rewrite/datatables/io.github.jtama.openrewrite.model.NodesReport.csv` *\
Contains all the classes found with their package and their number of incoming/outgoing connections.
* *`target/rewrite/datatables/io.github.jtama.openrewrite.model.LinksReport.csv` *\
Exhaustive list of links between classes with an associated weight.
* *`target/rewrite/datatables/io.github.jtama.openrewrite.model.JavaSourceFileExcludedReport.csv` *\
All classes that were excluded from the final result (often due to package filtering).
Now, it’s your turn. Run the scanner on your _legacy_, and contemplate (or be frightened by) the extent of the web!
---
**P.S.**: As you may have noticed, this plugin is distributed on Maven Central (`io.github.jtama:project-graph-generator`). If you’re wondering how to easily automate the entire publication chain without tearing your hair out, I recommend you take a look at [my JReleaser tutorial](https://jtama.github.io/posts/en/release-everything-with-jreleaser/).