55
66import java .io .BufferedReader ;
77import java .io .BufferedWriter ;
8+ import java .io .File ;
89import java .io .InputStreamReader ;
910import java .nio .file .Files ;
1011import java .nio .file .Path ;
11- import java .util .ArrayList ;
12- import java .util .List ;
13- import java .util .stream . Collectors ;
12+ import java .util .* ;
13+ import java .util .regex . Matcher ;
14+ import java .util .regex . Pattern ;
1415import java .util .stream .Stream ;
1516
1617public class DelombokPass implements Pass {
1718
19+ private static final Pattern PACKAGE_PATTERN = Pattern .compile ("^\\ s*package\\ s+([\\ w.]+)\\ s*;" );
20+
1821 @ Override
1922 public void execute (Java2GraphConfig config , GraphContext context ) throws Exception {
2023 if (!config .isEnableLombok ()) {
2124 return ;
2225 }
2326
24- System .out .println ("Processing Lombok annotations via isolated process (filtering builds/hidden )..." );
27+ System .out .println ("Processing Lombok annotations via isolated process (with classpath resolution )..." );
2528
2629 Path delombokDir = Files .createTempDirectory ("delombok" );
2730 String javaHome = System .getProperty ("java.home" );
2831 String javaBin = javaHome + "/bin/java" ;
29- String classpath = System .getProperty ("java.class.path" );
32+ String mainClasspath = System .getProperty ("java.class.path" );
33+
34+ // Resolve the project's dependencies for delombok attribution
35+ String projectClasspath = resolveProjectClasspath (config .getJarPaths ());
3036
31- // Identify source directories while excluding build/hidden folders
32- List <String > srcPaths = findSourceDirs (config .getSrcDir ());
33- if (srcPaths .isEmpty ()) {
37+ // Identify unique source roots (package roots) to preserve structure while avoiding build artifacts
38+ Set <String > srcRoots = findSourceRoots (config .getSrcDir ());
39+ if (srcRoots .isEmpty ()) {
3440 System .out .println ("No Java source files found in " + config .getSrcDir ());
3541 return ;
3642 }
3743
38- // Use a response file for delombok to avoid command-line length limits and manage inputs
44+ // Use a response file for delombok to avoid command-line length limits
3945 Path argFile = Files .createTempFile ("delombok_args" , ".args" );
4046 try (BufferedWriter writer = Files .newBufferedWriter (argFile )) {
41- for (String path : srcPaths ) {
47+ for (String path : srcRoots ) {
4248 writer .write ("\" " + path .replace ("\\ " , "\\ \\ " ) + "\" " );
4349 writer .newLine ();
4450 }
@@ -62,11 +68,17 @@ public void execute(Java2GraphConfig config, GraphContext context) throws Except
6268 command .add ("--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED" );
6369
6470 command .add ("-cp" );
65- command .add (classpath );
71+ command .add (mainClasspath );
6672 command .add ("lombok.launch.Main" );
6773 command .add ("delombok" );
6874 command .add ("--nocopy" );
6975 command .add ("--quiet" );
76+
77+ if (!projectClasspath .isEmpty ()) {
78+ command .add ("--classpath" );
79+ command .add (projectClasspath );
80+ }
81+
7082 command .add ("@" + argFile .toAbsolutePath ());
7183 command .add ("-d" );
7284 command .add (delombokDir .toAbsolutePath ().toString ());
@@ -79,14 +91,13 @@ public void execute(Java2GraphConfig config, GraphContext context) throws Except
7991 try (BufferedReader reader = new BufferedReader (new InputStreamReader (process .getInputStream ()))) {
8092 String line ;
8193 while ((line = reader .readLine ()) != null ) {
82- // Suppress verbose hidden directory messages from Lombok
8394 if (line .contains ("hidden directory" )) continue ;
8495 output .append (line ).append ("\n " );
8596 }
8697 }
8798
8899 int exitCode = process .waitFor ();
89- Files .deleteIfExists (argFile ); // Clean up the temp args file
100+ Files .deleteIfExists (argFile );
90101
91102 if (exitCode != 0 ) {
92103 System .err .println ("Delombok failed with exit code " + exitCode );
@@ -96,20 +107,74 @@ public void execute(Java2GraphConfig config, GraphContext context) throws Except
96107 throw new RuntimeException ("Delombok process failed. See logs for details." );
97108 }
98109
99- // Update the src directory for subsequent passes
110+ // IMPORTANT: Update the src directory for subsequent passes
100111 config .setSrcDir (delombokDir );
101112
102113 System .out .println ("Lombok processing complete. Delomboked to: " + delombokDir );
103114 }
104115
105- private List <String > findSourceDirs (Path root ) throws Exception {
106- try (Stream <Path > walk = Files .walk (root )) {
107- return walk .filter (Files ::isDirectory )
108- .filter (path -> !isExcluded (path ))
109- .filter (this ::hasJavaFiles )
110- .map (path -> path .toAbsolutePath ().toString ())
111- .collect (Collectors .toList ());
116+ private String resolveProjectClasspath (List <Path > jarPaths ) {
117+ if (jarPaths == null || jarPaths .isEmpty ()) {
118+ return "" ;
119+ }
120+ List <String > classpathElements = new ArrayList <>();
121+ for (Path path : jarPaths ) {
122+ if (Files .isRegularFile (path ) && path .toString ().endsWith (".jar" )) {
123+ classpathElements .add (path .toAbsolutePath ().toString ());
124+ } else if (Files .isDirectory (path )) {
125+ try (Stream <Path > walk = Files .walk (path )) {
126+ walk .filter (p -> Files .isRegularFile (p ) && p .toString ().endsWith (".jar" ))
127+ .map (p -> p .toAbsolutePath ().toString ())
128+ .forEach (classpathElements ::add );
129+ } catch (Exception e ) {
130+ // Ignore and continue
131+ }
132+ }
133+ }
134+ return String .join (File .pathSeparator , classpathElements );
135+ }
136+
137+ private Set <String > findSourceRoots (Path start ) throws Exception {
138+ Set <String > roots = new LinkedHashSet <>();
139+ try (Stream <Path > walk = Files .walk (start )) {
140+ walk .filter (path -> path .toString ().endsWith (".java" ))
141+ .filter (file -> !isExcluded (file ))
142+ .forEach (file -> {
143+ // Check if already covered
144+ if (roots .stream ().anyMatch (root -> file .startsWith (Path .of (root )))) {
145+ return ;
146+ }
147+ String root = discoverRoot (file );
148+ if (root != null ) {
149+ roots .add (root );
150+ }
151+ });
112152 }
153+ return roots ;
154+ }
155+
156+ private String discoverRoot (Path file ) {
157+ try (BufferedReader reader = Files .newBufferedReader (file )) {
158+ String line ;
159+ int count = 0 ;
160+ while ((line = reader .readLine ()) != null && count < 100 ) {
161+ Matcher m = PACKAGE_PATTERN .matcher (line );
162+ if (m .find ()) {
163+ String pkg = m .group (1 );
164+ int depth = pkg .split ("\\ ." ).length ;
165+ Path root = file .getParent ();
166+ for (int i = 0 ; i < depth ; i ++) {
167+ if (root .getParent () != null ) root = root .getParent ();
168+ }
169+ return root .toAbsolutePath ().toString ();
170+ }
171+ count ++;
172+ }
173+ } catch (Exception e ) {
174+ // Log quietly and ignore
175+ }
176+ // If no package, root is parent
177+ return file .getParent ().toAbsolutePath ().toString ();
113178 }
114179
115180 private boolean isExcluded (Path path ) {
@@ -126,12 +191,4 @@ private boolean isExcluded(Path path) {
126191 }
127192 return false ;
128193 }
129-
130- private boolean hasJavaFiles (Path dir ) {
131- try (Stream <Path > files = Files .list (dir )) {
132- return files .anyMatch (p -> p .getFileName ().toString ().endsWith (".java" ));
133- } catch (Exception e ) {
134- return false ;
135- }
136- }
137194}
0 commit comments