Skip to content

Commit 70fac0f

Browse files
committed
Add record scanning
1 parent a6fac78 commit 70fac0f

File tree

3 files changed

+105
-35
lines changed

3 files changed

+105
-35
lines changed

Diff for: maven-plugin/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@
5454
<artifactId>slf4j-api</artifactId>
5555
<version>2.0.12</version>
5656
</dependency>
57+
<dependency>
58+
<groupId>org.burningwave</groupId>
59+
<artifactId>core</artifactId>
60+
<version>12.64.3</version>
61+
</dependency>
5762
</dependencies>
5863

5964
<build>

Diff for: maven-plugin/src/main/java/no/rune/record/matcher/GenerateRecordMatcherMojo.java

+98-35
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import org.apache.maven.plugins.annotations.Mojo;
77
import org.apache.maven.plugins.annotations.Parameter;
88
import org.apache.maven.project.MavenProject;
9+
import org.burningwave.core.assembler.ComponentSupplier;
10+
import org.burningwave.core.classes.ClassCriteria;
11+
import org.burningwave.core.classes.SearchConfig;
912
import org.slf4j.Logger;
1013
import org.slf4j.LoggerFactory;
1114

@@ -16,11 +19,16 @@
1619
import java.net.URLClassLoader;
1720
import java.nio.file.Files;
1821
import java.nio.file.Path;
22+
import java.util.Collections;
1923
import java.util.List;
2024
import java.util.Set;
2125
import java.util.stream.Stream;
2226

27+
import static java.lang.reflect.Modifier.isPrivate;
28+
import static java.util.Objects.requireNonNullElseGet;
2329
import static java.util.function.Predicate.not;
30+
import static java.util.stream.Collectors.joining;
31+
import static java.util.stream.Stream.concat;
2432
import static org.apache.maven.plugins.annotations.LifecyclePhase.GENERATE_TEST_SOURCES;
2533
import static org.apache.maven.plugins.annotations.ResolutionScope.COMPILE;
2634

@@ -37,6 +45,12 @@ public class GenerateRecordMatcherMojo extends AbstractMojo {
3745
@Parameter
3846
private Set<String> includes;
3947

48+
/**
49+
* Specify which packages (includes any sub packages) to scan for records
50+
*/
51+
@Parameter(defaultValue = "${project.groupId}")
52+
private Set<String> scanPackages;
53+
4054

4155
/**
4256
* The directory where the generated Hamcrest matchers will be written to.
@@ -63,35 +77,44 @@ public class GenerateRecordMatcherMojo extends AbstractMojo {
6377

6478
@Override
6579
public void execute() throws MojoExecutionException, MojoFailureException {
66-
if (includes != null && !includes.isEmpty()) {
67-
Path outputDirectory = resolveOutputDirectory();
68-
if (includeGeneratedCodeAsTestSources) {
69-
mavenProject.addTestCompileSourceRoot(outputDirectory.toString());
70-
LOG.debug("{} has been added as a compiler test sources directory", outputDirectory);
71-
}
72-
try {
73-
Files.createDirectories(outputDirectory);
74-
LOG.info("Generating Hamcrest matchers in {}", outputDirectory);
75-
} catch (IOException e) {
76-
throw new UncheckedIOException(
77-
"Unable to create output directory " + outputDirectory + ", " +
78-
"because " + e.getClass().getSimpleName() + ": " + e.getMessage(), e);
79-
}
80-
var generator = new RecordMatcherGenerator();
81-
resolveIncludedRecords().forEach(recordClass -> {
82-
var recordMatcherCompilationUnit = generator.generateFromRecord(recordClass);
80+
Path outputDirectory = resolveOutputDirectory();
81+
try {
82+
Files.createDirectories(outputDirectory);
83+
LOG.info("Generating Hamcrest matchers in {}", outputDirectory);
84+
} catch (IOException e) {
85+
throw new UncheckedIOException(
86+
"Unable to create output directory " + outputDirectory + ", " +
87+
"because " + e.getClass().getSimpleName() + ": " + e.getMessage(), e);
88+
}
89+
if (includeGeneratedCodeAsTestSources) {
90+
mavenProject.addTestCompileSourceRoot(outputDirectory.toString());
91+
LOG.debug("{} has been added as a compiler test sources directory", outputDirectory);
92+
}
93+
94+
var generator = new RecordMatcherGenerator();
95+
var writtenFiles = resolveIncludedRecords()
96+
.map(generator::generateFromRecord)
97+
.map(compilationUnit -> {
8398
try {
84-
var writtenFile = recordMatcherCompilationUnit.writeToBaseDirectory(outputDirectory);
85-
LOG.info("Generated matcher {}", outputDirectory.relativize(writtenFile));
99+
return compilationUnit.writeToBaseDirectory(outputDirectory);
86100
} catch (IOException e) {
87101
throw new UncheckedIOException(
88-
"Unable to write " + recordMatcherCompilationUnit + " to file, " +
102+
"Unable to write " + compilationUnit + " to file, " +
89103
"because " + e.getClass().getSimpleName() + ": " + e.getMessage(), e);
90104
}
91-
});
105+
})
106+
.toList();
107+
108+
109+
if (writtenFiles.isEmpty()) {
110+
LOG.warn("No matchers was generated!");
92111
} else {
93-
LOG.warn("No records to generate Hamcrest matchers from!");
112+
for (var writtenFile : writtenFiles) {
113+
LOG.info("Generated {}", outputDirectory.relativize(writtenFile));
114+
}
115+
LOG.info("Total files written: {}", writtenFiles.size());
94116
}
117+
95118
}
96119

97120
private Path resolveOutputDirectory() {
@@ -101,22 +124,62 @@ private Path resolveOutputDirectory() {
101124

102125
private Stream<Class<? extends Record>> resolveIncludedRecords() {
103126
ClassLoader classLoader = buildProjectClassLoader(mavenProject, this.getClass().getClassLoader());
104-
return includes.stream()
105-
.filter(not(String::isBlank))
106-
.map(String::trim)
107-
.map(recordClassName -> {
108-
try {
109-
return classLoader.loadClass(recordClassName);
110-
} catch (ClassNotFoundException e) {
111-
throw new IllegalArgumentException(
112-
"Unable to resolve Class from " + recordClassName +
113-
", because " + e.getClass().getSimpleName() + ": " + e.getMessage(), e);
114-
}
115-
})
116-
.map(c -> c.asSubclass(Record.class));
127+
return concat(
128+
scanForRecords(classLoader, scanPackages)
129+
.filter(foundRecord -> {
130+
var typeParams = foundRecord.getTypeParameters();
131+
if (typeParams.length != 0) {
132+
LOG.debug("Not including {}<{}> because type parameters are not supported",
133+
foundRecord.getName(), Stream.of(typeParams).map(t -> t.getName()).collect(joining(", ")));
134+
return false;
135+
}
136+
return true;
137+
}),
138+
requireNonNullElseGet(includes, Collections::<String>emptySet).stream()
139+
.filter(not(String::isBlank))
140+
.map(String::trim)
141+
.<Class<? extends Record>>map(recordClassName -> load(recordClassName, Record.class, classLoader))
142+
.filter(configuredInclusion -> {
143+
var typeParams = configuredInclusion.getTypeParameters();
144+
if (typeParams.length != 0) {
145+
throw new UnsupportedOperationException(
146+
"Can not include " + configuredInclusion.getName() +
147+
"<" + Stream.of(typeParams).map(t -> t.getName()).collect(joining(", ")) + "> " +
148+
"because type parameters are not supported");
149+
}
150+
return true;
151+
})
152+
.distinct());
153+
}
154+
155+
private static Stream<Class<? extends Record>> scanForRecords(ClassLoader classLoader, Set<String> packageNames) {
156+
if (packageNames.isEmpty()) {
157+
LOG.debug("No packages configured for scanning");
158+
return Stream.empty();
159+
}
160+
LOG.info("Scanning packages {} for records", packageNames);
161+
var components = ComponentSupplier.getInstance();
162+
var allRecordsInClassLoader = SearchConfig.forResources(packageNames.stream().map(p -> p.replace('.', '/')).toList())
163+
.by(ClassCriteria.create().allThoseThatMatch(cls -> Record.class.isAssignableFrom(cls) && !isPrivate(cls.getModifiers())))
164+
.useAsParentClassLoader(classLoader);
165+
166+
try (var searchResult = components.getClassHunter().findBy(allRecordsInClassLoader)) {
167+
return searchResult.getClasses().stream().map(c -> c.asSubclass(Record.class));
168+
}
169+
117170
}
118171

119172

173+
private static <C> Class<? extends C> load(String className, Class<C> target, ClassLoader classLoader) {
174+
try {
175+
return classLoader.loadClass(className).asSubclass(target);
176+
} catch (ClassNotFoundException e) {
177+
throw new IllegalArgumentException(
178+
"Unable to resolve Class from " + className +
179+
", because " + e.getClass().getSimpleName() + ": " + e.getMessage(), e);
180+
}
181+
}
182+
120183
private static ClassLoader buildProjectClassLoader(MavenProject project, ClassLoader parent) {
121184
try {
122185
@SuppressWarnings("unchecked")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
resource-releaser.enabled=false
2+
managed-logger.repository.enabled=false

0 commit comments

Comments
 (0)