Skip to content

Commit

Permalink
ISSUES-17 allow sibling binding
Browse files Browse the repository at this point in the history
  • Loading branch information
h908714124 committed Dec 3, 2023
1 parent 88689db commit 8e59e4a
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@
import io.jbock.simple.Inject;
import io.jbock.simple.processor.util.ClearableCache;
import io.jbock.simple.processor.util.TypeTool;
import io.jbock.simple.processor.util.Util;

import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import static io.jbock.simple.processor.util.Visitors.EXECUTABLE_ELEMENT_VISITOR;
import static io.jbock.simple.processor.util.Visitors.TYPE_ELEMENT_VISITOR;
Expand All @@ -22,38 +20,30 @@ public final class InjectBindingFactory implements ClearableCache {
private final Map<TypeElement, Map<Key, InjectBinding>> injectBindingCache = new HashMap<>();

private final TypeTool tool;
private final KeyFactory keyFactory;
private final InjectBinding.Factory injectBindingFactory;

@Inject
public InjectBindingFactory(
TypeTool tool,
KeyFactory keyFactory,
InjectBinding.Factory injectBindingFactory) {
this.tool = tool;
this.keyFactory = keyFactory;
this.injectBindingFactory = injectBindingFactory;
}

public Map<Key, InjectBinding> injectBindings(TypeElement typeElement) {
Map<Key, InjectBinding> result = injectBindingCache.get(typeElement);
if (result != null) {
return result;
}
List<? extends ExecutableElement> allMembers = tool.elements().getAllMembers(typeElement).stream()
.filter(tool::hasInjectAnnotation)
.map(EXECUTABLE_ELEMENT_VISITOR::visit)
.filter(Objects::nonNull)
.collect(Collectors.toList());
if (allMembers.isEmpty()) {
return Map.of();
}
result = new LinkedHashMap<>();
for (ExecutableElement method : allMembers) {
InjectBinding b = injectBindingFactory.create(method);
result.put(b.key(), b); // duplicates handled elsewhere
return injectBindingCache.computeIfAbsent(typeElement, this::injectBindingsMiss);
}

private Map<Key, InjectBinding> injectBindingsMiss(TypeElement typeElement) {
Map<Key, InjectBinding> result = new LinkedHashMap<>();
for (TypeElement element : Util.getWithEnclosing(typeElement)) {
tool.elements().getAllMembers(element).stream()
.filter(tool::hasInjectAnnotation)
.map(EXECUTABLE_ELEMENT_VISITOR::visit)
.filter(Objects::nonNull)
.map(injectBindingFactory::create)
.forEach(b -> result.put(b.key(), b) /* duplicates handled elsewhere */);
}
injectBindingCache.put(typeElement, result);
return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ ValidationFailure fail(List<DependencyRequest> dependencyTrace) {
private ValidationFailure failInternal(List<DependencyRequest> trace) {
DependencyRequest request = trace.get(0);
StringBuilder message = new StringBuilder();
message.append(request.key().toString()).append(" cannot be provided.");
message.append("No binding found for " + request.key().toString() + ".");
for (int i = 0; i < trace.size(); i++) {
DependencyRequest r = trace.get(i);
String formatted = format(r, i == trace.size() - 1 ? "requested" : "injected");
Expand Down
18 changes: 18 additions & 0 deletions compiler/src/main/java/io/jbock/simple/processor/util/Util.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.jbock.simple.processor.util;

import javax.lang.model.element.TypeElement;
import java.util.List;

public final class Util {

public static List<TypeElement> getWithEnclosing(TypeElement typeElement) {
if (typeElement == null) {
return List.of();
}
TypeElement el = Visitors.TYPE_ELEMENT_VISITOR.visit(typeElement.getEnclosingElement());
return el == null ? List.of(typeElement) : List.of(typeElement, el);
}

private Util() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import io.jbock.simple.processor.binding.InjectBindingFactory;
import io.jbock.simple.processor.binding.Key;
import io.jbock.simple.processor.util.TypeTool;
import io.jbock.simple.processor.util.Util;
import io.jbock.simple.processor.util.ValidationFailure;
import io.jbock.simple.processor.util.Visitors;

Expand All @@ -13,6 +14,7 @@
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import java.util.List;
import java.util.Map;

import static io.jbock.simple.processor.util.TypeNames.JAKARTA_INJECT;
Expand All @@ -38,37 +40,30 @@ public void validateConstructor(ExecutableElement element) {

public void validateStaticMethod(ExecutableElement method) {
validate(method);
TypeElement typeElement = Visitors.TYPE_ELEMENT_VISITOR.visit(method.getEnclosingElement());
// List<TypeElement> hierarchyMethod = getEnclosingElements(typeElement);
// List<TypeElement> hierarchyRt = tool.types().asElement(method.getReturnType())
// .map(Visitors.TYPE_ELEMENT_VISITOR::visit)
// .map(this::getEnclosingElements)
// .orElse(List.of());
if (!method.getModifiers().contains(Modifier.STATIC)) {
throw new ValidationFailure("The factory method must be static", method);
}
if (method.getReturnType().getKind() == TypeKind.VOID) {
throw new ValidationFailure("The factory method may not return void", method);
}
if (!tool.types().isSameType(method.getReturnType(), typeElement.asType())) {
if (!isSibling(method)) {
throw new ValidationFailure("The factory method must return the type of its enclosing class", method);
}
}

/*
private List<TypeElement> getEnclosingElements(TypeElement typeElement) {
if (typeElement == null) {
return List.of();
}
List<TypeElement> acc = new ArrayList<>(2);
acc.add(typeElement);
TypeElement el = typeElement;
if ((el = Visitors.TYPE_ELEMENT_VISITOR.visit(el.getEnclosingElement())) != null) {
acc.add(el);
private boolean isSibling(ExecutableElement method) {
TypeElement typeElement = Visitors.TYPE_ELEMENT_VISITOR.visit(method.getEnclosingElement());
List<TypeElement> hierarchyRt = tool.types().asElement(method.getReturnType())
.map(Visitors.TYPE_ELEMENT_VISITOR::visit)
.map(Util::getWithEnclosing)
.orElse(List.of());
for (TypeElement r : hierarchyRt) {
if (r.equals(typeElement)) {
return true;
}
}
return acc;
return false;
}
*/

private void validate(ExecutableElement element) {
TypeElement typeElement = Visitors.TYPE_ELEMENT_VISITOR.visit(element.getEnclosingElement());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

import static javax.lang.model.element.Modifier.FINAL;
Expand Down Expand Up @@ -91,7 +92,8 @@ TypeSpec generate() {
}
spec.addAnnotation(AnnotationSpec.builder(Generated.class)
.addMember("value", CodeBlock.of("$S", SimpleComponentProcessor.class.getCanonicalName()))
.addMember("comments", CodeBlock.of("$S", "https://github.com/jbock-java/simple-component"))
.addMember("comments", CodeBlock.of("$S", "https://github.com/jbock-java/simple-component " +
Objects.toString(getClass().getPackage().getImplementationVersion(), "")))
.build());
spec.addMethod(generateAllParametersConstructor());
spec.addOriginatingElement(component.element());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,6 @@ void providerProvider() {

Compilation compilation = simpleCompiler().compile(component);
assertThat(compilation).failed();
assertThat(compilation).hadErrorContaining("io.jbock.simple.Provider<io.jbock.simple.Provider<test.TestClass.B>> cannot be provided.");
assertThat(compilation).hadErrorContaining("No binding found for io.jbock.simple.Provider<io.jbock.simple.Provider<test.TestClass.B>>");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ void missingBinding() {

Compilation compilation = simpleCompiler().compile(component);
assertThat(compilation).failed();
assertThat(compilation).hadErrorContainingMatch("java.lang.String cannot be provided.")
assertThat(compilation).hadErrorContainingMatch("No binding found for java.lang.String.")
.inFile(component)
.onLineContaining("interface AComponent");
}
Expand Down Expand Up @@ -77,7 +77,7 @@ void bindsMethodAppearsInTrace() {
assertThat(compilation)
.hadErrorContaining(
TestUtils.message(
"java.lang.String cannot be provided.",
"No binding found for java.lang.String.",
" java.lang.String is injected at",
" TestImplementation(java.lang.String)",
" p.TestImplementation is injected at",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ void qualifierFail() {

Compilation compilation = simpleCompiler().compile(component);
assertThat(compilation).failed();
assertThat(compilation).hadErrorContaining("io.jbock.simple.Provider<test.TestClass.B> with qualifier @Named(\"b\") cannot be provided.")
assertThat(compilation).hadErrorContaining("No binding found for io.jbock.simple.Provider<test.TestClass.B> with qualifier @Named(\"b\").")
.inFile(component)
.onLineContaining("interface AComponent");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,57 @@ void clashResolvedByQualifiers() {
" }",
"}");
}

@Test
void injectMethodIsSibling() {
JavaFileObject component = forSourceLines("test.TestClass",
"package test;",
"",
"import io.jbock.simple.Component;",
"import io.jbock.simple.Inject;",
"import io.jbock.simple.Named;",
"",
"final class TestClass {",
"",
" static class A {",
" }",
"",
" static class B {",
" }",
"",
" @Inject static A createA(@Named(\"1\") B b1, @Named(\"2\") B b2) { return null; }",
" @Inject @Named(\"1\") static B createB1() { return null; }",
" @Inject @Named(\"2\") static B createB2() { return null; }",
"",
" @Component",
" interface AComponent {",
" A getA();",
" }",
"}");
Compilation compilation = simpleCompiler().compile(component);
assertThat(compilation).succeeded();
assertThat(compilation).generatedSourceFile("test.TestClass_AComponent_Impl")
.containsLines(
"package test;",
"",
"final class TestClass_AComponent_Impl implements TestClass.AComponent {",
" private final TestClass.A a;",
"",
" private TestClass_AComponent_Impl(TestClass.A a) {",
" this.a = a;",
" }",
"",
" @Override",
" public TestClass.A getA() {",
" return a;",
" }",
"",
" static TestClass.AComponent create() {",
" TestClass.B b = TestClass.createB1();",
" TestClass.B b2 = TestClass.createB2();",
" TestClass.A a = TestClass.createA(b, b2);",
" return new TestClass_AComponent_Impl(a);",
" }",
"}");
}
}

0 comments on commit 8e59e4a

Please sign in to comment.