diff --git a/rewrite-benchmarks/src/jmh/java/org/openrewrite/benchmarks/java/JavaSourceSetBenchmark.java b/rewrite-benchmarks/src/jmh/java/org/openrewrite/benchmarks/java/JavaSourceSetBenchmark.java index e21aab4dd12..75ffeb5074b 100644 --- a/rewrite-benchmarks/src/jmh/java/org/openrewrite/benchmarks/java/JavaSourceSetBenchmark.java +++ b/rewrite-benchmarks/src/jmh/java/org/openrewrite/benchmarks/java/JavaSourceSetBenchmark.java @@ -29,10 +29,4 @@ public void setup() { public void jarIOBenchmark() { JavaSourceSet.build("main", classpath); } - - @Benchmark - public void classgraphBenchmark() { - //noinspection deprecation - JavaSourceSet.build("main", classpath, new JavaTypeCache(), false); - } } diff --git a/rewrite-core/src/main/java/org/openrewrite/Parser.java b/rewrite-core/src/main/java/org/openrewrite/Parser.java index bff00a37bd3..49e4478dcc2 100644 --- a/rewrite-core/src/main/java/org/openrewrite/Parser.java +++ b/rewrite-core/src/main/java/org/openrewrite/Parser.java @@ -215,7 +215,8 @@ public Path getRelativePath(@Nullable Path relativeTo) { } public EncodingDetectingInputStream getSource(ExecutionContext ctx) { - return new EncodingDetectingInputStream(source.get(), ParsingExecutionContextView.view(ctx).getCharset()); + Charset charset = ParsingExecutionContextView.view(ctx).getCharset(); + return new EncodingDetectingInputStream(source.get(), charset != null ? charset : StandardCharsets.UTF_8); } @Override diff --git a/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11Parser.java b/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11Parser.java index 0fe766a526c..a74d176024b 100644 --- a/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11Parser.java +++ b/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11Parser.java @@ -380,6 +380,8 @@ public ReloadableJava11Parser build() { private static class ByteArrayCapableJavacFileManager extends JavacFileManager { private final List classByteClasspath; + private final IdentityHashMap inferBinaryNameCache = new IdentityHashMap<>(); + private final HashMap> listCache = new HashMap<>(); public ByteArrayCapableJavacFileManager(Context context, boolean register, @@ -396,19 +398,51 @@ public String inferBinaryName(Location location, JavaFileObject file) { if (file instanceof PackageAwareJavaFileObject) { return ((PackageAwareJavaFileObject) file).getClassName(); } - return super.inferBinaryName(location, file); + String cached = inferBinaryNameCache.get(file); + if (cached != null) { + return cached; + } + String result = super.inferBinaryName(location, file); + if (result != null) { + inferBinaryNameCache.put(file, result); + } + return result; + } + + @Override + public void flush() { + super.flush(); + inferBinaryNameCache.clear(); + listCache.clear(); + } + + @Override + public void setLocationFromPaths(Location location, Collection paths) throws IOException { + super.setLocationFromPaths(location, paths); + listCache.clear(); } @Override public Iterable list(Location location, String packageName, Set kinds, boolean recurse) throws IOException { + String key = location.getName() + ':' + packageName + ':' + kinds + ':' + recurse; + List cached = listCache.get(key); + if (cached != null) { + return cached; + } + List result; if (StandardLocation.CLASS_PATH.equals(location)) { Iterable listed = super.list(location, packageName, kinds, recurse); - return Stream.concat(classByteClasspath.stream() + result = Stream.concat(classByteClasspath.stream() .filter(jfo -> jfo.getPackage().equals(packageName)), StreamSupport.stream(listed.spliterator(), false) ).collect(toList()); + } else { + Iterable listed = super.list(location, packageName, kinds, recurse); + result = listed instanceof List ? (List) listed : + StreamSupport.stream(listed.spliterator(), false).collect(toList()); } - return super.list(location, packageName, kinds, recurse); + listCache.put(key, result); + return result; } } diff --git a/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17Parser.java b/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17Parser.java index 73f23946e1e..2faa68b0e52 100644 --- a/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17Parser.java +++ b/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17Parser.java @@ -351,6 +351,10 @@ public ReloadableJava17Parser build() { private static class ByteArrayCapableJavacFileManager extends JavacFileManager { private final List classByteClasspath; + private final IdentityHashMap inferBinaryNameCache = new IdentityHashMap<>(); + private final HashMap> listCache = new HashMap<>(); + + private record ListCacheKey(Location location, String packageName, Set kinds, boolean recurse) {} public ByteArrayCapableJavacFileManager(Context context, boolean register, @@ -367,20 +371,51 @@ public String inferBinaryName(Location location, JavaFileObject file) { if (file instanceof PackageAwareJavaFileObject) { return ((PackageAwareJavaFileObject) file).getClassName(); } - return super.inferBinaryName(location, file); + String cached = inferBinaryNameCache.get(file); + if (cached != null) { + return cached; + } + String result = super.inferBinaryName(location, file); + if (result != null) { + inferBinaryNameCache.put(file, result); + } + return result; + } + + @Override + public void flush() { + super.flush(); + inferBinaryNameCache.clear(); + listCache.clear(); + } + + @Override + public void setLocationFromPaths(Location location, Collection paths) throws IOException { + super.setLocationFromPaths(location, paths); + listCache.clear(); } @Override public Iterable list(Location location, String packageName, Set kinds, boolean recurse) throws IOException { - if (StandardLocation.CLASS_PATH.equals(location)) { + ListCacheKey key = new ListCacheKey(location, packageName, kinds, recurse); + List cached = listCache.get(key); + if (cached != null) { + return cached; + } + List result; + if (StandardLocation.CLASS_PATH.equals(location) && !classByteClasspath.isEmpty()) { Iterable listed = super.list(location, packageName, kinds, recurse); - return classByteClasspath.isEmpty() ? listed : - Stream.concat(classByteClasspath.stream() + result = Stream.concat(classByteClasspath.stream() .filter(jfo -> jfo.getPackage().equals(packageName)), StreamSupport.stream(listed.spliterator(), false) ).collect(toList()); + } else { + Iterable listed = super.list(location, packageName, kinds, recurse); + result = listed instanceof List ? (List) listed : + StreamSupport.stream(listed.spliterator(), false).collect(toList()); } - return super.list(location, packageName, kinds, recurse); + listCache.put(key, result); + return result; } } diff --git a/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21Parser.java b/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21Parser.java index fb4e271213c..68b4d23f0a9 100644 --- a/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21Parser.java +++ b/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21Parser.java @@ -351,6 +351,10 @@ public ReloadableJava21Parser build() { private static class ByteArrayCapableJavacFileManager extends JavacFileManager { private final List classByteClasspath; + private final IdentityHashMap inferBinaryNameCache = new IdentityHashMap<>(); + private final HashMap> listCache = new HashMap<>(); + + private record ListCacheKey(Location location, String packageName, Set kinds, boolean recurse) {} public ByteArrayCapableJavacFileManager(Context context, boolean register, @@ -367,20 +371,51 @@ public String inferBinaryName(Location location, JavaFileObject file) { if (file instanceof PackageAwareJavaFileObject) { return ((PackageAwareJavaFileObject) file).getClassName(); } - return super.inferBinaryName(location, file); + String cached = inferBinaryNameCache.get(file); + if (cached != null) { + return cached; + } + String result = super.inferBinaryName(location, file); + if (result != null) { + inferBinaryNameCache.put(file, result); + } + return result; + } + + @Override + public void flush() { + super.flush(); + inferBinaryNameCache.clear(); + listCache.clear(); + } + + @Override + public void setLocationFromPaths(Location location, Collection paths) throws IOException { + super.setLocationFromPaths(location, paths); + listCache.clear(); } @Override public Iterable list(Location location, String packageName, Set kinds, boolean recurse) throws IOException { - if (StandardLocation.CLASS_PATH.equals(location)) { + ListCacheKey key = new ListCacheKey(location, packageName, kinds, recurse); + List cached = listCache.get(key); + if (cached != null) { + return cached; + } + List result; + if (StandardLocation.CLASS_PATH.equals(location) && !classByteClasspath.isEmpty()) { Iterable listed = super.list(location, packageName, kinds, recurse); - return classByteClasspath.isEmpty() ? listed : - Stream.concat(classByteClasspath.stream() + result = Stream.concat(classByteClasspath.stream() .filter(jfo -> jfo.getPackage().equals(packageName)), StreamSupport.stream(listed.spliterator(), false) ).collect(toList()); + } else { + Iterable listed = super.list(location, packageName, kinds, recurse); + result = listed instanceof List ? (List) listed : + StreamSupport.stream(listed.spliterator(), false).collect(toList()); } - return super.list(location, packageName, kinds, recurse); + listCache.put(key, result); + return result; } } diff --git a/rewrite-java-25/src/main/java/org/openrewrite/java/isolated/ReloadableJava25Parser.java b/rewrite-java-25/src/main/java/org/openrewrite/java/isolated/ReloadableJava25Parser.java index 745ffcc72c2..9b5285cacf0 100644 --- a/rewrite-java-25/src/main/java/org/openrewrite/java/isolated/ReloadableJava25Parser.java +++ b/rewrite-java-25/src/main/java/org/openrewrite/java/isolated/ReloadableJava25Parser.java @@ -351,6 +351,10 @@ public ReloadableJava25Parser build() { private static class ByteArrayCapableJavacFileManager extends JavacFileManager { private final List classByteClasspath; + private final IdentityHashMap inferBinaryNameCache = new IdentityHashMap<>(); + private final HashMap> listCache = new HashMap<>(); + + private record ListCacheKey(Location location, String packageName, Set kinds, boolean recurse) {} public ByteArrayCapableJavacFileManager(Context context, boolean register, @@ -367,20 +371,51 @@ public String inferBinaryName(Location location, JavaFileObject file) { if (file instanceof PackageAwareJavaFileObject) { return ((PackageAwareJavaFileObject) file).getClassName(); } - return super.inferBinaryName(location, file); + String cached = inferBinaryNameCache.get(file); + if (cached != null) { + return cached; + } + String result = super.inferBinaryName(location, file); + if (result != null) { + inferBinaryNameCache.put(file, result); + } + return result; + } + + @Override + public void flush() { + super.flush(); + inferBinaryNameCache.clear(); + listCache.clear(); + } + + @Override + public void setLocationFromPaths(Location location, Collection paths) throws IOException { + super.setLocationFromPaths(location, paths); + listCache.clear(); } @Override public Iterable list(Location location, String packageName, Set kinds, boolean recurse) throws IOException { - if (StandardLocation.CLASS_PATH.equals(location)) { + ListCacheKey key = new ListCacheKey(location, packageName, kinds, recurse); + List cached = listCache.get(key); + if (cached != null) { + return cached; + } + List result; + if (StandardLocation.CLASS_PATH.equals(location) && !classByteClasspath.isEmpty()) { Iterable listed = super.list(location, packageName, kinds, recurse); - return classByteClasspath.isEmpty() ? listed : - Stream.concat(classByteClasspath.stream() + result = Stream.concat(classByteClasspath.stream() .filter(jfo -> jfo.getPackage().equals(packageName)), StreamSupport.stream(listed.spliterator(), false) ).collect(toList()); + } else { + Iterable listed = super.list(location, packageName, kinds, recurse); + result = listed instanceof List ? (List) listed : + StreamSupport.stream(listed.spliterator(), false).collect(toList()); } - return super.list(location, packageName, kinds, recurse); + listCache.put(key, result); + return result; } } diff --git a/rewrite-java-8/src/main/java/org/openrewrite/java/ReloadableJava8Parser.java b/rewrite-java-8/src/main/java/org/openrewrite/java/ReloadableJava8Parser.java index 15f05bb8edd..a5cb35ba170 100644 --- a/rewrite-java-8/src/main/java/org/openrewrite/java/ReloadableJava8Parser.java +++ b/rewrite-java-8/src/main/java/org/openrewrite/java/ReloadableJava8Parser.java @@ -310,6 +310,8 @@ public void reset(Collection uris) { private static class ByteArrayCapableJavacFileManager extends JavacFileManager { private final List classByteClasspath; + private final IdentityHashMap inferBinaryNameCache = new IdentityHashMap<>(); + private final HashMap> listCache = new HashMap<>(); public ByteArrayCapableJavacFileManager(Context context, boolean register, @@ -331,20 +333,46 @@ public String inferBinaryName(Location location, JavaFileObject file) { if (file instanceof PackageAwareJavaFileObject) { return ((PackageAwareJavaFileObject) file).getClassName(); } - return super.inferBinaryName(location, file); + String cached = inferBinaryNameCache.get(file); + if (cached != null) { + return cached; + } + String result = super.inferBinaryName(location, file); + if (result != null) { + inferBinaryNameCache.put(file, result); + } + return result; + } + + @Override + public void flush() { + super.flush(); + inferBinaryNameCache.clear(); + listCache.clear(); } @Override public Iterable list(Location location, String packageName, Set kinds, boolean recurse) throws IOException { + String cacheKey = location.getName() + ':' + packageName + ':' + kinds + ':' + recurse; + List cached = listCache.get(cacheKey); + if (cached != null) { + return cached; + } + List result; if (StandardLocation.CLASS_PATH == location) { Iterable listed = super.list(location, packageName, kinds, recurse); - return Stream.concat( + result = Stream.concat( classByteClasspath.stream() .filter(jfo -> jfo.getPackage().equals(packageName)), StreamSupport.stream(listed.spliterator(), false) ).collect(toList()); + } else { + Iterable listed = super.list(location, packageName, kinds, recurse); + result = listed instanceof List ? (List) listed : + StreamSupport.stream(listed.spliterator(), false).collect(toList()); } - return super.list(location, packageName, kinds, recurse); + listCache.put(cacheKey, result); + return result; } } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/Assertions.java b/rewrite-java/src/main/java/org/openrewrite/java/Assertions.java index 1e4ba58c09a..dfd0eeff068 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/Assertions.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/Assertions.java @@ -243,7 +243,7 @@ public static SourceSpec sourceSet(SourceSpec sourceSpec, String sourceSet public static UncheckedConsumer> addTypesToSourceSet(String sourceSetName, List extendsFrom, List classpath) { return sourceFiles -> { - JavaSourceSet sourceSet = JavaSourceSet.build(sourceSetName, classpath, new JavaTypeCache(), false); + JavaSourceSet sourceSet = JavaSourceSet.build(sourceSetName, classpath); for (int i = 0; i < sourceFiles.size(); i++) { SourceFile sourceFile = sourceFiles.get(i); diff --git a/rewrite-java/src/main/java/org/openrewrite/java/JavaParser.java b/rewrite-java/src/main/java/org/openrewrite/java/JavaParser.java index 92fc9ec6c36..0442e91b9e5 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/JavaParser.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/JavaParser.java @@ -366,21 +366,22 @@ default Path sourcePathFromSourceText(Path prefix, String sourceCode) { return resolveSourcePathFromSourceText(prefix, sourceCode); } + Pattern SOURCE_PATH_PACKAGE_PATTERN = Pattern.compile("^package\\s+([^;]+);"); + Pattern SOURCE_PATH_CLASS_PATTERN = Pattern.compile("(class|interface|enum|record)\\s*(<[^>]*>)?\\s+(\\w+)"); + Pattern SOURCE_PATH_PUBLIC_CLASS_PATTERN = Pattern.compile("public\\s+" + SOURCE_PATH_CLASS_PATTERN.pattern()); + static Path resolveSourcePathFromSourceText(Path prefix, String sourceCode) { - Pattern packagePattern = Pattern.compile("^package\\s+([^;]+);"); - Pattern classPattern = Pattern.compile("(class|interface|enum|record)\\s*(<[^>]*>)?\\s+(\\w+)"); - Pattern publicClassPattern = Pattern.compile("public\\s+" + classPattern.pattern()); Function simpleName = sourceStr -> { - Matcher classMatcher = publicClassPattern.matcher(sourceStr); + Matcher classMatcher = SOURCE_PATH_PUBLIC_CLASS_PATTERN.matcher(sourceStr); if (classMatcher.find()) { return classMatcher.group(3); } - classMatcher = classPattern.matcher(sourceStr); + classMatcher = SOURCE_PATH_CLASS_PATTERN.matcher(sourceStr); return classMatcher.find() ? classMatcher.group(3) : null; }; - Matcher packageMatcher = packagePattern.matcher(sourceCode); + Matcher packageMatcher = SOURCE_PATH_PACKAGE_PATTERN.matcher(sourceCode); String pkg = packageMatcher.find() ? packageMatcher.group(1).replace('.', '/') + "/" : ""; String className = Optional.ofNullable(simpleName.apply(sourceCode)) diff --git a/rewrite-java/src/main/java/org/openrewrite/java/marker/JavaSourceSet.java b/rewrite-java/src/main/java/org/openrewrite/java/marker/JavaSourceSet.java index 97c82154eac..c1a93f4cbb1 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/marker/JavaSourceSet.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/marker/JavaSourceSet.java @@ -55,51 +55,6 @@ public class JavaSourceSet implements SourceSet { */ Map> gavToTypes; - /** - * Extract type information from the provided classpath. - * Uses ClassGraph to compute the classpath. - *

- * Does not support gavToTypes or typeToGav mapping - * - * @param fullTypeInformation Not used, does not do anything, to be deleted - * @param ignore Not used, does not do anything, to be deleted - */ - @Deprecated - public static JavaSourceSet build(String sourceSetName, Collection classpath, - JavaTypeCache ignore, boolean fullTypeInformation) { - if (fullTypeInformation) { - throw new UnsupportedOperationException(); - } - - List typeNames; - if (!classpath.iterator().hasNext()) { - // Only load JRE-provided types - try (ScanResult scanResult = new ClassGraph() - .enableClassInfo() - .enableSystemJarsAndModules() - .acceptPackages("java") - .ignoreClassVisibility() - .scan()) { - typeNames = packagesToTypeDeclarations(scanResult); - } - } else { - // Load types from the classpath - try (ScanResult scanResult = new ClassGraph() - .overrideClasspath(classpath) - .enableSystemJarsAndModules() - .enableClassInfo() - .ignoreClassVisibility() - .scan()) { - typeNames = packagesToTypeDeclarations(scanResult); - } - } - - // Peculiarly, Classgraph will not return a ClassInfo for java.lang.Object, although it does for all other java.lang types - typeNames.add("java.lang.Object"); - return new JavaSourceSet(randomId(), sourceSetName, typesFrom(typeNames), emptyMap()); - } - - /* * Create a map of package names to types contained within that package. Type names are not fully qualified, except for type parameter bounds. * e.g.: "java.util" -> [List, Date] diff --git a/rewrite-kotlin/src/main/java/org/openrewrite/kotlin/KotlinParser.java b/rewrite-kotlin/src/main/java/org/openrewrite/kotlin/KotlinParser.java index b3c32512617..f854e066604 100644 --- a/rewrite-kotlin/src/main/java/org/openrewrite/kotlin/KotlinParser.java +++ b/rewrite-kotlin/src/main/java/org/openrewrite/kotlin/KotlinParser.java @@ -255,8 +255,7 @@ public JavaSourceSet getSourceSet(ExecutionContext ctx) { if (ctx.getMessage(SKIP_SOURCE_SET_TYPE_GENERATION, false)) { sourceSetProvenance = new JavaSourceSet(Tree.randomId(), sourceSet, emptyList(), emptyMap()); } else { - sourceSetProvenance = JavaSourceSet.build(sourceSet, classpath == null ? emptyList() : classpath, - typeCache, false); + sourceSetProvenance = JavaSourceSet.build(sourceSet, classpath == null ? emptyList() : classpath); } } return sourceSetProvenance;