1818import lombok .extern .slf4j .Slf4j ;
1919import org .jetbrains .annotations .NotNull ;
2020import org .openrewrite .ExecutionContext ;
21+ import org .openrewrite .FileAttributes ;
2122import org .openrewrite .Parser ;
2223import org .openrewrite .SourceFile ;
2324import org .openrewrite .internal .lang .Nullable ;
2425import org .openrewrite .java .JavaParser ;
2526import org .openrewrite .java .internal .JavaTypeCache ;
2627import org .openrewrite .java .marker .JavaSourceSet ;
28+ import org .openrewrite .java .tree .J ;
29+ import org .openrewrite .java .tree .JavaType ;
2730import org .openrewrite .marker .Generated ;
2831import org .openrewrite .marker .Marker ;
2932import org .openrewrite .marker .Markers ;
3033import org .openrewrite .xml .tree .Xml ;
3134import org .springframework .core .io .Resource ;
3235import org .springframework .sbm .utils .ResourceUtil ;
3336
37+ import java .io .InputStream ;
38+ import java .nio .file .Files ;
3439import java .nio .file .Path ;
3540import java .nio .file .Paths ;
36- import java .util .ArrayList ;
37- import java .util .Collection ;
38- import java .util .List ;
39- import java .util .Set ;
41+ import java .util .*;
4042import java .util .function .Predicate ;
43+ import java .util .function .Supplier ;
4144import java .util .function .UnaryOperator ;
4245import java .util .stream .Collectors ;
4346import java .util .stream .Stream ;
@@ -73,7 +76,7 @@ public <T extends SourceFile> UnaryOperator<T> addProvenance(
7376 /**
7477 * Parse Java sources and resources under {@code src/main} of current module.
7578 */
76- public List < SourceFile > processMainSources (
79+ public SourceSetParsingResult processMainSources (
7780 Path baseDir ,
7881 List <Resource > resources ,
7982 Xml .Document moduleBuildFile ,
@@ -116,20 +119,40 @@ public List<SourceFile> processMainSources(
116119 javaParserBuilder .typeCache (typeCache );
117120
118121 Iterable <Parser .Input > inputs = mainJavaSources .stream ()
119- .map (r -> new Parser .Input (ResourceUtil .getPath (r ), () -> ResourceUtil .getInputStream (r )))
122+ .map (r -> {
123+ FileAttributes fileAttributes = null ;
124+ Path path = ResourceUtil .getPath (r );
125+ boolean isSynthetic = Files .exists (path );
126+ Supplier <InputStream > inputStreamSupplier = () -> ResourceUtil .getInputStream (r );
127+ Parser .Input input = new Parser .Input (path , fileAttributes , inputStreamSupplier , isSynthetic );
128+ return input ;
129+ })
120130 .toList ();
121131
122- Stream <? extends SourceFile > cus = Stream .of (javaParserBuilder )
123- .map (JavaParser .Builder ::build )
124- .flatMap (parser -> parser .parseInputs (inputs , baseDir , executionContext ))
125- .peek (s -> alreadyParsed .add (baseDir .resolve (s .getSourcePath ())));
132+ Set <JavaType .FullyQualified > localClassesCp = new HashSet <>();
133+ JavaSourceSet javaSourceSet = sourceSet ("main" , dependencies , typeCache );
134+ List <? extends SourceFile > cus = javaParserBuilder .build ()
135+ .parseInputs (inputs , baseDir , executionContext )
136+ .peek (s -> {
137+ ((J .CompilationUnit )s ).getClasses ()
138+ .stream ()
139+ .map (J .ClassDeclaration ::getType )
140+ .forEach (localClassesCp ::add );
141+
142+ alreadyParsed .add (baseDir .resolve (s .getSourcePath ()));
143+ })
144+ .toList ();
126145
146+ // TODO: This is a hack:
147+ // Parsed java sources are not themselves on the classpath (here).
148+ // The actual parsing happens when the stream is terminated (toList),
149+ // therefore the toList() must be called before the parsed compilation units can be added to the classpath
127150 List <Marker > mainProjectProvenance = new ArrayList <>(provenanceMarkers );
128- mainProjectProvenance .add (sourceSet ("main" , dependencies , typeCache ));
151+ javaSourceSet = appendToClasspath (localClassesCp , javaSourceSet );
152+ mainProjectProvenance .add (javaSourceSet );
129153
130- // FIXME: 945 Why target and not all main?
131154 List <Path > parsedJavaPaths = javaSourcesInTarget .stream ().map (ResourceUtil ::getPath ).toList ();
132- Stream <SourceFile > parsedJava = cus .map (addProvenance (baseDir , mainProjectProvenance , parsedJavaPaths ));
155+ Stream <SourceFile > parsedJava = cus .stream (). map (addProvenance (baseDir , mainProjectProvenance , parsedJavaPaths ));
133156 log .debug ("[%s] Scanned %d java source files in main scope." .formatted (currentProject , mainJavaSources .size ()));
134157
135158 //Filter out any generated source files from the returned list, as we do not want to apply the recipe to the
@@ -148,7 +171,22 @@ public List<SourceFile> processMainSources(
148171 log .debug ("[%s] Scanned %d resource files in main scope." .formatted (currentProject , (alreadyParsed .size () - sourcesParsedBefore )));
149172 // Any resources parsed from "main/resources" should also have the main source set added to them.
150173 sourceFiles .addAll (parsedResourceFiles );
151- return sourceFiles ;
174+ return new SourceSetParsingResult (sourceFiles , javaSourceSet .getClasspath ());
175+ }
176+
177+ /**
178+ * Add entries that don't exist in the classpath of {@code javaSourceSet} from {@code appendingClasspath}.
179+ */
180+ @ NotNull
181+ private static JavaSourceSet appendToClasspath (Set <JavaType .FullyQualified > appendingClasspath , JavaSourceSet javaSourceSet ) {
182+ List <JavaType .FullyQualified > curCp = javaSourceSet .getClasspath ();
183+ appendingClasspath .forEach (f -> {
184+ if (!curCp .contains (f )) {
185+ curCp .add (f );
186+ }
187+ });
188+ javaSourceSet = javaSourceSet .withClasspath (new ArrayList <>(curCp ));
189+ return javaSourceSet ;
152190 }
153191
154192 @ NotNull
@@ -160,7 +198,7 @@ private static JavaSourceSet sourceSet(String name, List<Path> dependencies, Jav
160198 /**
161199 * Parse Java sources and resource files under {@code src/test}.
162200 */
163- public List < SourceFile > processTestSources (
201+ public SourceSetParsingResult processTestSources (
164202 Path baseDir ,
165203 Xml .Document moduleBuildFile ,
166204 JavaParser .Builder <? extends JavaParser , ?> javaParserBuilder ,
@@ -169,8 +207,8 @@ public List<SourceFile> processTestSources(
169207 Set <Path > alreadyParsed ,
170208 ExecutionContext executionContext ,
171209 MavenProject currentProject ,
172- List <Resource > resources
173- ) {
210+ List <Resource > resources ,
211+ List < JavaType . FullyQualified > classpath ) {
174212 log .info ("Processing test sources in module '%s'" .formatted (currentProject .getProjectId ()));
175213
176214 List <Path > testDependencies = currentProject .getTestClasspathElements ();
@@ -186,14 +224,25 @@ public List<SourceFile> processTestSources(
186224 .map (r -> new Parser .Input (ResourceUtil .getPath (r ), () -> ResourceUtil .getInputStream (r )))
187225 .toList ();
188226
189- Stream <? extends SourceFile > cus = Stream .of (javaParserBuilder )
190- .map (JavaParser .Builder ::build )
191- .flatMap (parser -> parser .parseInputs (inputs , baseDir , executionContext ));
227+ final List <JavaType .FullyQualified > localClassesCp = new ArrayList <>();
228+ List <? extends SourceFile > cus = javaParserBuilder .build ()
229+ .parseInputs (inputs , baseDir , executionContext )
230+ .peek (s -> {
231+ ((J .CompilationUnit ) s ).getClasses ()
232+ .stream ()
233+ .map (J .ClassDeclaration ::getType )
234+ .forEach (localClassesCp ::add );
235+ alreadyParsed .add (baseDir .resolve (s .getSourcePath ()));
236+ })
237+ .toList ();
192238
193239 List <Marker > markers = new ArrayList <>(provenanceMarkers );
194- markers .add (sourceSet ("test" , testDependencies , typeCache ));
195240
196- Stream <SourceFile > parsedJava = cus .map (addProvenance (baseDir , markers , null ));
241+ JavaSourceSet javaSourceSet = sourceSet ("test" , testDependencies , typeCache );
242+ Set <JavaType .FullyQualified > curClasspath = Stream .concat (classpath .stream (), localClassesCp .stream ()).collect (Collectors .toSet ());
243+ javaSourceSet = appendToClasspath (curClasspath , javaSourceSet );
244+ markers .add (javaSourceSet );
245+ Stream <SourceFile > parsedJava = cus .stream ().map (addProvenance (baseDir , markers , null ));
197246
198247 log .debug ("[%s] Scanned %d java source files in test scope." .formatted (currentProject , testJavaSources .size ()));
199248 Stream <SourceFile > sourceFiles = parsedJava ;
@@ -205,7 +254,7 @@ public List<SourceFile> processTestSources(
205254 log .debug ("[%s] Scanned %d resource files in test scope." .formatted (currentProject , (alreadyParsed .size () - sourcesParsedBefore )));
206255 sourceFiles = Stream .concat (sourceFiles , parsedResourceFiles );
207256 List <SourceFile > result = sourceFiles .toList ();
208- return result ;
257+ return new SourceSetParsingResult ( result , javaSourceSet . getClasspath ()) ;
209258 }
210259
211260
0 commit comments