You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 18e4000
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: AGENTS.md
+67-5Lines changed: 67 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -17,6 +17,64 @@
17
17
-**Anonymous/synthetic classes are first-class graph members.** Java `Outer$N`/`Outer$` (anonymous/synthetic inner classes) and the Kotlin literal `"<anonymous>"` FQN are **not** sieved out by `GraphDependencyCollector`; they genuinely participate in cycles and can harbour antipatterns, so they are vertices in the class graph and rendered with `$` as the enclosing-class separator. `GraphDependencyCollector` keeps only the `from == to` self-edge guard, plus a degenerate-package guard so a packageless `"<anonymous>"` source never creates an `""` package-graph vertex. **Sink-only** anonymous/synthetic vertices (those with no outgoing edges) are suppressed only at render time in `HtmlReport.isSinkAnonymousOrSyntheticVertex` to keep the Class/Cycle Map DOT graph readable; active ones still render.
18
18
- **Anonymous DOT node ids are source-file derived.** OpenRewrite attributes a Kotlin anonymous object / function-literal type with {@code "<anonymous>"} as the trailing simple-name segment of its FQN: standalone ({@code "<anonymous>"}) or, in real graphs (e.g. FXGL), prefixed by the enclosing class/package ({@code "dev.DeveloperWASDControl.<anonymous>"}). {@code HtmlReport.isAnonymousFqn(vertex)} detects a vertex when its trailing segment starts with {@code <}. {@code HtmlReport.renderSafeNodeId(vertex, codebaseGraphDTO)} then derives the enclosing owner from the vertex's mapped source-file path in {@code CodebaseGraphDTO.classToSourceFilePathMapping} (file base name without extension, e.g. {@code DeveloperWASDControl.kt} -> {@code DeveloperWASDControl}). The DOT node id renders as {@code DeveloperWASDControl_anonymous} and the human-readable label as {@code DeveloperWASDControl\$anonymous} ({@code $} escaped as {@code \$} for DOT). When no source path is mapped (or DTO is null) it degrades to the reversible {@code lt_}/{@code _gt} {@code <}/{@code >} encoding. The renderer is responsible for DOT/HTML-safe encoding of the literal {@code "<anonymous>"} FQN ({@code <}/{@code >} are illegal in Graphviz node ids; {@code <}/{@code >} escaping in HTML table labels).
19
19
20
+
## Java 25 analysis (JEP 238 multi-release jar)
21
+
22
+
`rewrite-java-25` is a **required** (BOM-managed, no explicit version) compile
23
+
dependency of `codebase-graph-builder` and ships in every distributed artifact
24
+
(Maven plugin, CLI fat jar). Its class files are Java 25 (class-file 69.0) but
25
+
are inert on Java 17/21 classpaths: the jar declares no `META-INF/services`
26
+
entries, and no base-level class in the module references it.
27
+
28
+
Activation uses a **JEP 238 multi-release jar**, not reflection or version
29
+
detection. `codebase-graph-builder` declares `Multi-Release: true` and ships
30
+
two variants of `org.hjug.graphbuilder.graphbuilder.Java25ParserFactory`:
31
+
32
+
- the base variant (`src/main/java`, `--release 17`) returns
33
+
`Optional.empty()`;
34
+
- the Java 25 variant (`src/main/java25`, compiled `--release 25` into
35
+
`META-INF/versions/25` by the auto-activated `jdk25-multi-release` profile)
36
+
calls `Java25Parser.builder().build()` directly, wrapped in a
37
+
`catch (Throwable)` graceful-degradation guard.
38
+
39
+
On JDK 25+ runtimes the JVM's versioned jar lookup shadows the base variant —
40
+
parser selection is fully runtime-driven with no configuration surface.
41
+
`JavaSourceFileGraphBuilder.createJavaParser(config)` is just
**codebase-graph-builder** — The most complex module. Uses OpenRewrite to parse Java source across versions (11/17/21), builds class and package dependency graphs with JGraphT, detects cycle-breaking candidates using two graph algorithms, and returns everything in `CodebaseGraphDTO`. Entry point: `JavaGraphBuilder.getCodebaseGraphDTO()`.
52
+
**codebase-graph-builder** — The most complex module. Uses OpenRewrite to parse Java source across versions (11/17/21, and 25 when running on a Java 25+ runtime — see "Java 25 analysis" below), builds class and package dependency graphs with JGraphT, detects cycle-breaking candidates using two graph algorithms, and returns everything in `CodebaseGraphDTO`. Entry point: `JavaGraphBuilder.getCodebaseGraphDTO()`.
53
53
54
54
**graph-algorithms** — Two cycle-decomposition algorithms used by `JavaGraphBuilder`:
55
55
-*Directed Feedback Vertex Set* (`org.hjug.feedback.vertex.kernelized`) — kernelized algorithm; identifies the minimum vertex set to remove to break all cycles.
@@ -92,6 +92,10 @@ Mutation testing via PIT (`pitest-maven`) is configured but not part of the defa
- Source/target: Java 17 minimum; OpenRewrite parser supports 11, 17, 21 — plus 25 when running on a Java 25+ runtime.
96
96
- Logging: SLF4J; use `log.debug()` for verbose per-class output, `log.info()` sparingly.
97
97
- Spotless enforces Palantir Java format — run `mvn spotless:apply` before committing.
98
+
99
+
## Java 25 analysis
100
+
101
+
`rewrite-java-25` is a **required** compile dependency of `codebase-graph-builder` and ships in every distribution (Maven plugin, CLI fat jar). Its class files are compiled for class-file version 69 but are inert on Java 17/21 classpaths (no `META-INF/services` entries, no base-level references). Activation uses a **JEP 238 multi-release jar** rather than reflection or runtime-version detection: `codebase-graph-builder` declares `Multi-Release: true` and carries two variants of `org.hjug.graphbuilder.graphbuilder.Java25ParserFactory` — the base variant (release 17, returns `Optional.empty()`) and a Java 25 variant compiled from `src/main/java25` into `META-INF/versions/25` (directly constructs `Java25Parser`, with a `catch (Throwable)` graceful-degradation guard). On JDK 25+ runtimes the JVM's versioned jar lookup shadows the base variant, so `JavaSourceFileGraphBuilder.createJavaParser(config)` is just `Java25ParserFactory.createJava25Parser().orElseGet(JavaParser::fromJavaVersion...)`. OpenRewrite's `fromJavaVersion()` additionally elevates to `Java25Parser` on its own when the runtime is 25+ and `rewrite-java-25` is on the classpath (covering exploded-directory classpaths where jar shadowing does not apply). The former `forceJava25Parser` escape hatch has been removed everywhere; there is no configuration surface for parser selection. Build constraints: never reference class-file-69 code from base sources (only `src/main/java25`); the versioned class must keep the base's exact public API; release artifacts must be built with JDK 25 (`release.yml` asserts the versioned entries exist); JaCoCo excludes `META-INF/versions/**`; the CLI shade config re-adds `Multi-Release: true` to the fat jar manifest; `refactor-first-maven-plugin` requires `maven-plugin-plugin`/`maven-plugin-annotations` 3.16.0 (ASM 9.10.1) so descriptor generation can scan class-file-69 entries. See `plans/jep-238-plan.md` for the design record.
Copy file name to clipboardExpand all lines: README.md
+5-2Lines changed: 5 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,7 +12,7 @@ Code map viewers are powered by [3D Force Graph](https://vasturiano.github.io/3d
12
12
<br>If there are more than 4000 classes + relationships, a simplified 3D viewer will be available to avoid page load slowdowns. Features will be toggleable in the 3D UI in a future release.
13
13
14
14
## How to Use RefactorFirst Quickly
15
-
Run the command below in your Java project's top-level directory. You'll need Git, Java 11 (or newer) and Maven 3 installed. This command will analyze Maven and non-Maven projects:
15
+
Run the command below in your Java project's top-level directory. You'll need Git, Java 17 (or newer) and Maven 3 installed. This command will analyze Maven and non-Maven projects:
@@ -43,7 +43,8 @@ The graphs generated in the report will look similar to this one:
43
43

44
44
45
45
## Please Note: Java 17 (or newer) required to run RefactorFirst
46
-
**Java 25 codebase analysis is supported!**
46
+
**Java 25 codebase analysis is supported!** Run RefactorFirst on a Java 25+ runtime and the Java 25 parser is used automatically; on older runtimes analysis falls back to the parser matching your runtime.
47
+
The Maven and Gradle plugins themselves still only require a Java 17 runtime.
47
48
Please use a recent JDK release of the Java version you are using.
48
49
If you use an old JDK release of your chosen Java version, you may encounter issues during analysis.
49
50
@@ -118,6 +119,8 @@ Specify with -D if running on the command line. e.g. ```-DbackEdgeAnalysisCount
118
119
|projectVersion|The version of your project to be displayed on the report| Your Maven project version |
119
120
|outputDirectory|The location the project report will be written| ```${projectDir}/target/site/refactor-first-report.html```
120
121
122
+
The Java 25 parser activates automatically when running on a JDK 25+ runtime (JEP 238 multi-release jar); there is no configuration flag for it.
123
+
121
124
122
125
## But I'm using Gradle / my project layout isn't typical!
123
126
I plan to create a Gradle plugin and (possibly) support non-conventional project structures in the future, but in the meantime you can create a dummy POM file in the same directory as your .git directory to show your project's name in the report:
0 commit comments