Skip to content

Commit 8613c11

Browse files
SentryManrbygrave
andauthored
[Generator] Refactor Metadata Parsing (#646)
* validate qualifiers at compile time * remove transitive aop and events from inject test * fix tests * fix optional types * final test * fix event publishers not getting detected * refactor reading external modules * Revert "refactor reading external modules" This reverts commit 96c3a94. * Add Util.addQualifierSuffix() helper methods * refactor reading external modules * refactor reading external modules * Formatting * Formatting and tidy unused * Formatting and tidy unused --------- Co-authored-by: Rob Bygrave <[email protected]>
1 parent 116cbe4 commit 8613c11

File tree

9 files changed

+132
-94
lines changed

9 files changed

+132
-94
lines changed

inject-generator/src/main/java/io/avaje/inject/generator/Dependency.java

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,32 @@ final class Dependency {
77

88
private final String name;
99
private boolean softDependency;
10-
private final boolean conditionalDependency;
1110

1211
Dependency(String type) {
13-
this(type, "");
12+
final String nameStr;
13+
if (type.startsWith(SOFT_DEPENDENCY)) {
14+
this.softDependency = true;
15+
nameStr = type.substring(5);
16+
} else if (type.startsWith(CONDITIONAL_DEPENDENCY)) {
17+
this.softDependency = true;
18+
nameStr = type.substring(4);
19+
} else {
20+
this.softDependency = false;
21+
nameStr = type;
22+
}
23+
this.name = nameStr.replace(", ", ",");
1424
}
1525

1626
Dependency(String type, String qualifier) {
1727
String nameStr;
1828
if (type.startsWith(SOFT_DEPENDENCY)) {
1929
this.softDependency = true;
20-
this.conditionalDependency = false;
2130
nameStr = ProcessorUtils.trimAnnotations(type.substring(5));
2231
} else if (type.startsWith(CONDITIONAL_DEPENDENCY)) {
2332
this.softDependency = true;
24-
this.conditionalDependency = true;
2533
nameStr = ProcessorUtils.trimAnnotations(type.substring(4));
2634
} else {
2735
this.softDependency = false;
28-
this.conditionalDependency = false;
2936
nameStr = ProcessorUtils.trimAnnotations(type);
3037
}
3138
this.name = Util.addQualifierSuffixTrim(qualifier, nameStr);
@@ -34,7 +41,6 @@ final class Dependency {
3441
Dependency(String name, String qualifier, boolean softDependency) {
3542
this.name = Util.addQualifierSuffixTrim(qualifier, ProcessorUtils.trimAnnotations(name));
3643
this.softDependency = softDependency;
37-
this.conditionalDependency = false;
3844
}
3945

4046
@Override
@@ -55,15 +61,6 @@ boolean isSoftDependency() {
5561
return softDependency;
5662
}
5763

58-
/**
59-
* Return true if a conditional dependency which can be empty.
60-
*
61-
* <p>A conditional dependency isn't absolutely required to wire beans.
62-
*/
63-
public boolean isConditionalDependency() {
64-
return conditionalDependency;
65-
}
66-
6764
String dependsOn() {
6865
return toString();
6966
}

inject-generator/src/main/java/io/avaje/inject/generator/ExternalProvider.java

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@
66
import java.lang.reflect.Type;
77
import java.util.ArrayList;
88
import java.util.Arrays;
9+
import java.util.Collection;
910

10-
import java.net.URI;
11-
import java.nio.file.Paths;
1211
import static java.util.List.of;
1312

1413
import java.util.List;
1514
import java.util.Map;
15+
import java.util.Objects;
16+
import java.util.Optional;
1617
import java.util.Set;
18+
import java.util.stream.Stream;
1719

18-
import javax.tools.StandardLocation;
19-
20+
import javax.lang.model.element.TypeElement;
21+
import javax.lang.model.util.ElementFilter;
2022
import io.avaje.inject.spi.AvajeModule;
2123
import io.avaje.inject.spi.InjectPlugin;
2224

@@ -42,6 +44,7 @@ final class ExternalProvider {
4244
"io.avaje.validation.Validator",
4345
"io.avaje.inject.aop.AspectProvider<io.avaje.validation.ValidMethod>")),
4446
entry("io.avaje.validation.http.HttpValidatorProvider", of("io.avaje.http.api.Validator")));
47+
private static final List<MetaData> externalMeta = new ArrayList<>();
4548

4649
private ExternalProvider() {}
4750

@@ -69,21 +72,10 @@ static void registerModuleProvidedTypes(Set<String> providedTypes) {
6972
}
7073
for (final var module : modules) {
7174
final var name = module.getClass().getTypeName();
72-
final var provides = new ArrayList<String>();
7375
APContext.logNote("Detected Module: " + name);
74-
for (final var provide : module.provides()) {
75-
providedTypes.add(provide.getTypeName());
76-
provides.add(provide.getTypeName());
77-
}
78-
for (final var provide : module.autoProvides()) {
79-
providedTypes.add(provide.getTypeName());
80-
provides.add(provide.getTypeName());
81-
}
82-
for (final var provide : module.autoProvidesAspects()) {
83-
final var aspectType = Util.wrapAspect(provide.getTypeName());
84-
providedTypes.add(aspectType);
85-
provides.add(aspectType);
86-
}
76+
registerExternalMetaData(name);
77+
readMetaDataProvides(providedTypes);
78+
final var provides = new ArrayList<>(providedTypes);
8779
final var requires = Arrays.stream(module.requires()).map(Type::getTypeName).collect(toList());
8880

8981
Arrays.stream(module.autoRequires()).map(Type::getTypeName).forEach(requires::add);
@@ -133,4 +125,25 @@ private static boolean pluginExists(String relativeName) {
133125
return false;
134126
}
135127
}
128+
129+
static void registerExternalMetaData(String name) {
130+
Optional.ofNullable(APContext.typeElement(name))
131+
.map(TypeElement::getEnclosedElements)
132+
.map(ElementFilter::methodsIn)
133+
.stream()
134+
.flatMap(List::stream)
135+
.map(DependencyMetaPrism::getInstanceOn)
136+
.filter(Objects::nonNull)
137+
.map(MetaData::new)
138+
.forEach(externalMeta::add);
139+
}
140+
141+
static void readMetaDataProvides(Collection<String> providedTypes) {
142+
externalMeta.forEach(meta -> {
143+
providedTypes.add(meta.key());
144+
providedTypes.add(meta.type());
145+
providedTypes.addAll(Util.addQualifierSuffix(meta.provides(), meta.name()));
146+
providedTypes.addAll(Util.addQualifierSuffix(meta.autoProvides(), meta.name()));
147+
});
148+
}
136149
}

inject-generator/src/main/java/io/avaje/inject/generator/InjectProcessor.java

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import javax.lang.model.util.Elements;
1616

1717
import static java.util.stream.Collectors.joining;
18-
1918
import java.io.IOException;
2019
import java.nio.file.Files;
2120
import java.nio.file.StandardOpenOption;
@@ -54,6 +53,7 @@ public final class InjectProcessor extends AbstractProcessor {
5453
private final Set<String> pluginFileProvided = new HashSet<>();
5554
private final Set<String> moduleFileProvided = new HashSet<>();
5655
private boolean performModuleValidation;
56+
private final List<ModuleData> moduleData = new ArrayList<>();
5757

5858
@Override
5959
public SourceVersion getSupportedSourceVersion() {
@@ -66,7 +66,7 @@ public synchronized void init(ProcessingEnvironment processingEnv) {
6666
APContext.init(processingEnv);
6767
loadProvidedFiles();
6868
ProcessingContext.init(moduleFileProvided, performModuleValidation);
69-
loadOrderFiles();
69+
moduleData.forEach(ProcessingContext::addModule);
7070
this.elementUtils = processingEnv.getElementUtils();
7171
this.allScopes = new AllScopes();
7272
this.defaultScope = allScopes.defaultScope();
@@ -98,21 +98,18 @@ public synchronized void init(ProcessingEnvironment processingEnv) {
9898
void loadProvidedFiles() {
9999
performModuleValidation = lines("avaje-plugin-exists.txt").isEmpty();
100100
pluginFileProvided.addAll(lines("avaje-plugin-provides.txt"));
101-
moduleFileProvided.addAll(lines("avaje-module-provides.txt"));
102-
}
103101

104-
/**
105-
* Loads order files generated by avaje-inject-maven-plugin
106-
*/
107-
private void loadOrderFiles() {
108-
Stream.concat(
109-
lines("avaje-module-dependencies.csv").stream().skip(1),
110-
lines("avaje-module-dependencies.csv").stream().skip(1))
102+
lines("avaje-module-dependencies.csv").stream()
103+
.skip(1)
111104
.filter(s -> !s.startsWith("External Module Type"))
112105
.distinct()
113106
.map(l -> l.split("\\|"))
114107
.map(ModuleData::new)
115-
.forEach(ProcessingContext::addModule);
108+
.forEach(m -> {
109+
ExternalProvider.registerExternalMetaData(m.name());
110+
ExternalProvider.readMetaDataProvides(moduleFileProvided);
111+
this.moduleData.add(m);
112+
});
116113
}
117114

118115
private List<String> lines(String relativeName) {

inject-generator/src/main/java/io/avaje/inject/generator/MetaData.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ void update(BeanReader beanReader) {
138138
this.importedComponent = beanReader.importedComponent();
139139
}
140140

141+
String name() {
142+
return name;
143+
}
144+
141145
String type() {
142146
return type;
143147
}
@@ -282,7 +286,7 @@ void setProvides(List<String> provides) {
282286
this.provides = provides;
283287
}
284288

285-
void setDependsOn(List<String> dependsOn, String name) {
289+
void setDependsOn(List<String> dependsOn) {
286290
this.dependsOn = dependsOn.stream().map(Dependency::new).collect(Collectors.toList());
287291
}
288292

inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,13 @@
33
import javax.lang.model.element.*;
44
import javax.lang.model.type.TypeKind;
55
import javax.lang.model.type.TypeMirror;
6-
7-
import static java.util.stream.Collectors.toList;
8-
96
import java.util.ArrayList;
107
import java.util.Collections;
118
import java.util.List;
129
import java.util.Set;
13-
import java.util.stream.Stream;
1410

11+
import static io.avaje.inject.generator.Constants.CONDITIONAL_DEPENDENCY;
1512
import static io.avaje.inject.generator.ProcessingContext.asElement;
16-
import static io.avaje.inject.generator.Constants.*;
1713

1814
final class MethodReader {
1915

@@ -166,7 +162,7 @@ MetaData createMeta() {
166162
var dep = Util.addQualifierSuffix(param.named, Util.trimWildcard(param.paramType));
167163
dependsOn.add(dep);
168164
}
169-
metaData.setDependsOn(dependsOn, name);
165+
metaData.setDependsOn(dependsOn);
170166
metaData.setProvides(
171167
typeReader == null
172168
? Collections.emptyList()
@@ -228,7 +224,7 @@ void builderAddBeanProvider(Append writer) {
228224
writer.indent(indent).append(" builder");
229225
if (prototype) {
230226
writer.append(".asPrototype()");
231-
} else if(secondary) {
227+
} else if (secondary) {
232228
writer.append(".asSecondary()");
233229
}
234230

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package io.avaje.inject.generator.models.valid;
2+
3+
import io.avaje.inject.Bean;
4+
import io.avaje.inject.Factory;
5+
import jakarta.inject.Named;
6+
7+
@Factory
8+
public class IncorrectCircularDependency {
9+
10+
@Bean
11+
@Named("parent") // for clarity, not necessary
12+
public String parent(@Named("child") String child) {
13+
throw new AssertionError("Method body unimportant");
14+
}
15+
16+
@Bean
17+
@Named("child")
18+
public String child() {
19+
throw new AssertionError("Method body unimportant");
20+
}
21+
22+
// @Bean compilation rightly fails when this is uncommented
23+
// public String child1(@Named("child2") String child) {
24+
// throw new AssertionError("Method body unimportant");
25+
// }
26+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package io.avaje.inject.generator.models.valid;
2+
3+
import jakarta.inject.Inject;
4+
import jakarta.inject.Named;
5+
import jakarta.inject.Singleton;
6+
7+
@Singleton
8+
public class TestCircleFactory {
9+
10+
private final String parent;
11+
12+
@Inject
13+
public TestCircleFactory(@Named("parent") String parent) {
14+
this.parent = parent;
15+
}
16+
17+
public String getParent() {
18+
return parent;
19+
}
20+
}

inject-gradle-plugin/src/main/java/io/avaje/inject/plugin/AvajeInjectPlugin.java

Lines changed: 24 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,10 @@ private void writeProvides(Project project) {
4747
}
4848

4949
try (var classLoader = classLoader(project);
50-
var moduleWriter = createFileWriter(outputDir.getPath(), "avaje-module-provides.txt");
5150
var pluginWriter = createFileWriter(outputDir.getPath(), "avaje-plugin-provides.txt");
5251
var moduleCSV = createFileWriter(outputDir.getPath(), "avaje-module-dependencies.csv")) {
53-
5452
writeProvidedPlugins(classLoader, pluginWriter);
55-
writeProvidedModules(classLoader, moduleWriter);
56-
writeModuleCSV(moduleCSV);
57-
53+
writeModuleCSV(classLoader, moduleCSV);
5854
} catch (IOException e) {
5955
throw new GradleException("Failed to write avaje-module-provides", e);
6056
}
@@ -91,7 +87,29 @@ private void writeProvidedPlugins(ClassLoader classLoader, FileWriter pluginWrit
9187
}
9288
}
9389

94-
private void writeProvidedModules(ClassLoader classLoader, FileWriter moduleWriter) throws IOException {
90+
private static String wrapAspect(String aspect) {
91+
return "io.avaje.inject.aop.AspectProvider<" + aspect + ">";
92+
}
93+
94+
private URLClassLoader classLoader(Project project) {
95+
final URL[] urls = createClassPath(project);
96+
return new URLClassLoader(urls, Thread.currentThread().getContextClassLoader());
97+
}
98+
99+
private static URL[] createClassPath(Project project) {
100+
try {
101+
Set<File> compileClasspath = project.getConfigurations().getByName("compileClasspath").resolve();
102+
final List<URL> urls = new ArrayList<>(compileClasspath.size());
103+
for (File file : compileClasspath) {
104+
urls.add(file.toURI().toURL());
105+
}
106+
return urls.toArray(new URL[0]);
107+
} catch (MalformedURLException e) {
108+
throw new GradleException("Error building classpath", e);
109+
}
110+
}
111+
112+
private void writeModuleCSV(ClassLoader classLoader, FileWriter moduleWriter) throws IOException {
95113
final Set<String> providedTypes = new HashSet<>();
96114

97115
final List<AvajeModule> avajeModules = new ArrayList<>();
@@ -134,35 +152,6 @@ private void writeProvidedModules(ClassLoader classLoader, FileWriter moduleWrit
134152
modules.add(new ModuleData(name, provides, requires));
135153
}
136154

137-
for (final String providedType : providedTypes) {
138-
moduleWriter.write(providedType);
139-
moduleWriter.write("\n");
140-
}
141-
}
142-
143-
private static String wrapAspect(String aspect) {
144-
return "io.avaje.inject.aop.AspectProvider<" + aspect + ">";
145-
}
146-
147-
private URLClassLoader classLoader(Project project) {
148-
final URL[] urls = createClassPath(project);
149-
return new URLClassLoader(urls, Thread.currentThread().getContextClassLoader());
150-
}
151-
152-
private static URL[] createClassPath(Project project) {
153-
try {
154-
Set<File> compileClasspath = project.getConfigurations().getByName("compileClasspath").resolve();
155-
final List<URL> urls = new ArrayList<>(compileClasspath.size());
156-
for (File file : compileClasspath) {
157-
urls.add(file.toURI().toURL());
158-
}
159-
return urls.toArray(new URL[0]);
160-
} catch (MalformedURLException e) {
161-
throw new GradleException("Error building classpath", e);
162-
}
163-
}
164-
165-
private void writeModuleCSV(FileWriter moduleWriter) throws IOException {
166155
moduleWriter.write("\nExternal Module Type|Provides|Requires");
167156
for (ModuleData avajeModule : modules) {
168157
moduleWriter.write("\n");

0 commit comments

Comments
 (0)