From 8fc97abf3ea5641620e1a18a70c512aed83c807c Mon Sep 17 00:00:00 2001 From: h908714124 Date: Sun, 24 Dec 2023 17:48:32 +0100 Subject: [PATCH] make componentElement available in keyFactory (#56) --- .../simple/processor/ContextComponent.java | 19 +- .../simple/processor/ProcessorComponent.java | 10 +- .../processor/binding/ComponentElement.java | 137 +----------- .../processor/binding/InjectBindingCache.java | 30 +++ .../binding/InjectBindingFactory.java | 14 +- .../simple/processor/binding/KeyCache.java | 87 ++++++++ .../simple/processor/binding/KeyFactory.java | 199 +++++++++++++----- .../simple/processor/graph/GraphFactory.java | 16 +- .../processor/graph/TopologicalSorter.java | 10 +- .../processor/step/BindingRegistry.java | 10 +- .../simple/processor/step/ComponentStep.java | 8 +- .../processor/writing/ComponentImpl.java | 19 +- .../simple/processor/writing/Generator.java | 10 +- 13 files changed, 314 insertions(+), 255 deletions(-) create mode 100644 compiler/src/main/java/io/jbock/simple/processor/binding/InjectBindingCache.java create mode 100644 compiler/src/main/java/io/jbock/simple/processor/binding/KeyCache.java diff --git a/compiler/src/main/java/io/jbock/simple/processor/ContextComponent.java b/compiler/src/main/java/io/jbock/simple/processor/ContextComponent.java index 245a6a7..67f0ea4 100644 --- a/compiler/src/main/java/io/jbock/simple/processor/ContextComponent.java +++ b/compiler/src/main/java/io/jbock/simple/processor/ContextComponent.java @@ -3,7 +3,6 @@ import io.jbock.simple.Component; import io.jbock.simple.Inject; import io.jbock.simple.processor.binding.ComponentElement; -import io.jbock.simple.processor.binding.InjectBindingFactory; import io.jbock.simple.processor.binding.KeyFactory; import io.jbock.simple.processor.graph.TopologicalSorter; import io.jbock.simple.processor.util.TypeTool; @@ -20,40 +19,30 @@ interface Builder { Builder tool(TypeTool tool); - Builder injectBindingFactory(InjectBindingFactory injectBindingFactory); - - Builder keyFactory(KeyFactory keyFactory); - ContextComponent build(); } - ComponentElement componentElement(); + KeyFactory keyFactory(); + ComponentElement componentElement(); + Generator generator(); TopologicalSorter topologicalSorter(); final class Factory { private final TypeTool tool; - private final InjectBindingFactory injectBindingFactory; - private final KeyFactory keyFactory; @Inject public Factory( - TypeTool tool, - InjectBindingFactory injectBindingFactory, - KeyFactory keyFactory) { + TypeTool tool) { this.tool = tool; - this.injectBindingFactory = injectBindingFactory; - this.keyFactory = keyFactory; } public ContextComponent create(TypeElement typeElement) { return ContextComponent_Impl.builder() .typeElement(typeElement) .tool(tool) - .injectBindingFactory(injectBindingFactory) - .keyFactory(keyFactory) .build(); } } diff --git a/compiler/src/main/java/io/jbock/simple/processor/ProcessorComponent.java b/compiler/src/main/java/io/jbock/simple/processor/ProcessorComponent.java index 76a7d19..a2a5c98 100644 --- a/compiler/src/main/java/io/jbock/simple/processor/ProcessorComponent.java +++ b/compiler/src/main/java/io/jbock/simple/processor/ProcessorComponent.java @@ -2,8 +2,8 @@ import io.jbock.simple.Component; import io.jbock.simple.Provides; -import io.jbock.simple.processor.binding.InjectBindingFactory; -import io.jbock.simple.processor.binding.KeyFactory; +import io.jbock.simple.processor.binding.InjectBindingCache; +import io.jbock.simple.processor.binding.KeyCache; import io.jbock.simple.processor.step.ComponentFactoryStep; import io.jbock.simple.processor.step.ComponentStep; import io.jbock.simple.processor.step.InjectStep; @@ -48,10 +48,10 @@ static Types provideTypes(ProcessingEnvironment processingEnvironment) { @Provides static List caches( - InjectBindingFactory injectBindingFactory, - KeyFactory keyFactory, + InjectBindingCache injectBindingCache, + KeyCache keyCache, SafeElements safeElements) { - return List.of(injectBindingFactory, keyFactory, safeElements); + return List.of(injectBindingCache, keyCache, safeElements); } ComponentStep componentStep(); diff --git a/compiler/src/main/java/io/jbock/simple/processor/binding/ComponentElement.java b/compiler/src/main/java/io/jbock/simple/processor/binding/ComponentElement.java index 13f58dd..34682b1 100644 --- a/compiler/src/main/java/io/jbock/simple/processor/binding/ComponentElement.java +++ b/compiler/src/main/java/io/jbock/simple/processor/binding/ComponentElement.java @@ -3,29 +3,15 @@ import io.jbock.javapoet.ClassName; import io.jbock.simple.Component; import io.jbock.simple.Inject; -import io.jbock.simple.Provides; -import io.jbock.simple.processor.util.ValidationFailure; -import io.jbock.simple.processor.util.Visitors; -import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; -import javax.lang.model.type.TypeKind; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; import java.util.function.Supplier; import static io.jbock.simple.processor.util.Suppliers.memoize; -import static javax.lang.model.util.ElementFilter.methodsIn; public final class ComponentElement { private final TypeElement element; - private final KeyFactory keyFactory; private final Supplier generatedClass = memoize(() -> { ClassName className = ClassName.get(element()); @@ -34,141 +20,20 @@ public final class ComponentElement { .peerClass(String.join("_", className.simpleNames()) + "_Impl"); }); - private final Supplier> factoryElement = memoize(() -> { - for (Element el : element().getEnclosedElements()) { - boolean hasFactoryAnnotation = el.getAnnotation(Component.Factory.class) != null; - if (!hasFactoryAnnotation) { - continue; - } - TypeElement tel = Visitors.TYPE_ELEMENT_VISITOR.visit(el); - if (tel == null) { - continue; - } - return Optional.of(new FactoryElement(tel, generatedClass(), keyFactory())); - } - return Optional.empty(); - }); - - - private final Supplier> builderElement = memoize(() -> { - for (Element el : element().getEnclosedElements()) { - boolean hasBuilderAnnotation = el.getAnnotation(Component.Builder.class) != null; - if (!hasBuilderAnnotation) { - continue; - } - TypeElement tel = Visitors.TYPE_ELEMENT_VISITOR.visit(el); - if (tel == null) { - continue; - } - return Optional.of(new BuilderElement(tel, generatedClass(), keyFactory())); - } - return Optional.empty(); - }); - - private final Supplier> providesBindings = memoize(() -> { - List methods = methodsIn(element().getEnclosedElements()); - Map result = new LinkedHashMap<>(); - for (ExecutableElement method : methods) { - if (method.getAnnotation(Provides.class) == null) { - continue; // ignore - } - Key key = keyFactory().getKey(method); - InjectBinding b = keyFactory().createBinding(method); - result.put(key, b); - } - return result; - }); - - private final Supplier> requests = memoize(() -> { - List methods = methodsIn(element().getEnclosedElements()); - Map result = new LinkedHashMap<>(); - for (ExecutableElement method : methods) { - if (method.getModifiers().contains(Modifier.STATIC)) { - continue; // ignore - } - if (method.getAnnotation(Provides.class) != null) { - continue; // ignore - } - if (!method.getParameters().isEmpty()) { - throw new ValidationFailure("Request method may not have any parameters", method); - } - if (method.getModifiers().contains(Modifier.DEFAULT)) { - throw new ValidationFailure("Default modifier is not allowed here", method); - } - if (method.getReturnType().getKind() == TypeKind.VOID) { - throw new ValidationFailure("Request method may not return void", method); - } - Key key = keyFactory().getKey(method); - result.put(key, new DependencyRequest(key, method, method)); - } - return result; - }); - - private final Supplier> parameterBindings = memoize(() -> { - List pBindings = factoryElement() - .map(FactoryElement::parameterBindings) - .or(() -> builderElement().map(BuilderElement::parameterBindings)) - .orElse(List.of()); - Map result = new LinkedHashMap<>(); - for (ParameterBinding b : pBindings) { - ParameterBinding previousBinding = result.put(b.key(), b); - if (previousBinding != null) { - Element p = previousBinding.element(); - throw new ValidationFailure("The binding is in conflict with another parameter: " + - p.asType() + ' ' + p.getSimpleName(), b.element()); - } - } - return result; - }); - @Inject public ComponentElement( - TypeElement element, - KeyFactory keyFactory) { + TypeElement element) { this.element = element; - this.keyFactory = keyFactory; } public TypeElement element() { return element; } - public Optional factoryElement() { - return factoryElement.get(); - } - - public Optional builderElement() { - return builderElement.get(); - } - - public boolean isComponentRequest(Binding binding) { - return requests.get().containsKey(binding.key()); - } - - public Collection requests() { - return requests.get().values(); - } - - public Map providesBindings() { - return providesBindings.get(); - } - public ClassName generatedClass() { return generatedClass.get(); } - private KeyFactory keyFactory() { - return keyFactory; - } - - public Optional parameterBinding(Key key) { - return Optional.ofNullable(parameterBindings.get().get(key)); - } - - public Collection parameterBindings() { - return parameterBindings.get().values(); - } - public boolean publicMockBuilder() { Component annotation = element.getAnnotation(Component.class); if (annotation == null) { diff --git a/compiler/src/main/java/io/jbock/simple/processor/binding/InjectBindingCache.java b/compiler/src/main/java/io/jbock/simple/processor/binding/InjectBindingCache.java new file mode 100644 index 0000000..068d890 --- /dev/null +++ b/compiler/src/main/java/io/jbock/simple/processor/binding/InjectBindingCache.java @@ -0,0 +1,30 @@ +package io.jbock.simple.processor.binding; + +import io.jbock.simple.Inject; +import io.jbock.simple.processor.util.ClearableCache; + +import javax.lang.model.element.TypeElement; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +public class InjectBindingCache implements ClearableCache { + + private final Map> injectBindingCache = new HashMap<>(); + + @Inject + public InjectBindingCache() { + } + + Map computeIfAbsent( + TypeElement typeElement, + Function> f) { + return injectBindingCache.computeIfAbsent(typeElement, f); + } + + + @Override + public void clearCache() { + injectBindingCache.clear(); + } +} diff --git a/compiler/src/main/java/io/jbock/simple/processor/binding/InjectBindingFactory.java b/compiler/src/main/java/io/jbock/simple/processor/binding/InjectBindingFactory.java index fabf279..1c5ed22 100644 --- a/compiler/src/main/java/io/jbock/simple/processor/binding/InjectBindingFactory.java +++ b/compiler/src/main/java/io/jbock/simple/processor/binding/InjectBindingFactory.java @@ -1,12 +1,10 @@ package io.jbock.simple.processor.binding; import io.jbock.simple.Inject; -import io.jbock.simple.processor.util.ClearableCache; import io.jbock.simple.processor.util.TypeTool; 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; @@ -14,10 +12,9 @@ import static io.jbock.simple.processor.util.Visitors.TYPE_ELEMENT_VISITOR; -public final class InjectBindingFactory implements ClearableCache { - - private final Map> injectBindingCache = new HashMap<>(); +public final class InjectBindingFactory { + private final InjectBindingCache injectBindingCache; private final TypeTool tool; private final KeyFactory keyFactory; private final InjectBindingScanner injectBindingScanner; @@ -25,9 +22,11 @@ public final class InjectBindingFactory implements ClearableCache { @Inject public InjectBindingFactory( TypeTool tool, + InjectBindingCache injectBindingCache, KeyFactory keyFactory, InjectBindingScanner injectBindingScanner) { this.tool = tool; + this.injectBindingCache = injectBindingCache; this.keyFactory = keyFactory; this.injectBindingScanner = injectBindingScanner; } @@ -54,9 +53,4 @@ public Optional binding(Key key) { return Optional.ofNullable(m.get(key)); }); } - - @Override - public void clearCache() { - injectBindingCache.clear(); - } } diff --git a/compiler/src/main/java/io/jbock/simple/processor/binding/KeyCache.java b/compiler/src/main/java/io/jbock/simple/processor/binding/KeyCache.java new file mode 100644 index 0000000..6738a87 --- /dev/null +++ b/compiler/src/main/java/io/jbock/simple/processor/binding/KeyCache.java @@ -0,0 +1,87 @@ +package io.jbock.simple.processor.binding; + +import io.jbock.simple.Inject; +import io.jbock.simple.processor.util.ClearableCache; +import io.jbock.simple.processor.util.SimpleAnnotation; +import io.jbock.simple.processor.util.TypeTool; +import io.jbock.simple.processor.util.ValidationFailure; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import static io.jbock.simple.processor.util.Visitors.TYPE_ELEMENT_VISITOR; +import static javax.lang.model.element.ElementKind.CONSTRUCTOR; + +public class KeyCache implements ClearableCache { + + private final Map keyCache = new HashMap<>(); + private final TypeTool tool; + + @Inject + public KeyCache(TypeTool tool) { + this.tool = tool; + } + + public Key getKey(ExecutableElement element) { + Key key = keyCache.get(element); + if (key != null) { + return key; + } + TypeMirror returnType; + if (element.getKind() == CONSTRUCTOR) { + returnType = element.getEnclosingElement().asType(); + } else { + returnType = element.getReturnType(); + } + key = Key.create(returnType, getQualifier(element)); + keyCache.put(element, key); + return key; + } + + Key getKey(VariableElement parameter) { + Key key = keyCache.get(parameter); + if (key != null) { + return key; + } + key = Key.create(parameter.asType(), getQualifier(parameter)); + keyCache.put(parameter, key); + return key; + } + + private Optional getQualifier(Element element) { + List qualifiers = element.getAnnotationMirrors().stream() + .filter(this::hasQualifierAnnotation) + .map(mirror -> SimpleAnnotation.create(mirror, tool.elements(), tool.types())) + .collect(Collectors.toList()); + if (qualifiers.isEmpty()) { + return Optional.empty(); + } + if (qualifiers.size() == 1) { + return Optional.of(qualifiers.get(0)); + } + throw new ValidationFailure("Found more than one qualifier annotation", element); + } + + + private boolean hasQualifierAnnotation(AnnotationMirror mirror) { + DeclaredType type = mirror.getAnnotationType(); + TypeElement element = TYPE_ELEMENT_VISITOR.visit(type.asElement()); + return tool.hasQualifierAnnotation(element); + } + + @Override + public void clearCache() { + keyCache.clear(); + } +} diff --git a/compiler/src/main/java/io/jbock/simple/processor/binding/KeyFactory.java b/compiler/src/main/java/io/jbock/simple/processor/binding/KeyFactory.java index 357ec43..87144fe 100644 --- a/compiler/src/main/java/io/jbock/simple/processor/binding/KeyFactory.java +++ b/compiler/src/main/java/io/jbock/simple/processor/binding/KeyFactory.java @@ -1,100 +1,183 @@ package io.jbock.simple.processor.binding; +import io.jbock.simple.Component; import io.jbock.simple.Inject; -import io.jbock.simple.processor.util.ClearableCache; -import io.jbock.simple.processor.util.SimpleAnnotation; +import io.jbock.simple.Provides; import io.jbock.simple.processor.util.TypeTool; import io.jbock.simple.processor.util.ValidationFailure; +import io.jbock.simple.processor.util.Visitors; -import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeMirror; -import java.util.HashMap; +import javax.lang.model.type.TypeKind; +import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; +import java.util.function.Supplier; -import static io.jbock.simple.processor.util.Visitors.TYPE_ELEMENT_VISITOR; -import static javax.lang.model.element.ElementKind.CONSTRUCTOR; +import static io.jbock.simple.processor.util.Suppliers.memoize; +import static javax.lang.model.util.ElementFilter.methodsIn; -public class KeyFactory implements ClearableCache { +public class KeyFactory { private final TypeTool tool; - private final Map keyCache = new HashMap<>(); + private final ComponentElement componentElement; + private final KeyCache keyCache; @Inject - public KeyFactory(TypeTool tool) { + public KeyFactory( + TypeTool tool, + ComponentElement componentElement, + KeyCache keyCache) { this.tool = tool; + this.componentElement = componentElement; + this.keyCache = keyCache; } - private Optional getQualifier(Element element) { - List qualifiers = element.getAnnotationMirrors().stream() - .filter(this::hasQualifierAnnotation) - .map(mirror -> SimpleAnnotation.create(mirror, tool.elements(), tool.types())) - .collect(Collectors.toList()); - if (qualifiers.isEmpty()) { - return Optional.empty(); - } - if (qualifiers.size() == 1) { - return Optional.of(qualifiers.get(0)); - } - throw new ValidationFailure("Found more than one qualifier annotation", element); + Key getKey(VariableElement parameter) { + return keyCache.getKey(parameter); } - public Key getKey(ExecutableElement element) { - Key key = keyCache.get(element); - if (key != null) { - return key; + InjectBinding createBinding(ExecutableElement m) { + Key key = keyCache.getKey(m); + if (m.getKind() == ElementKind.CONSTRUCTOR) { + if (key.qualifier().isPresent()) { + throw new ValidationFailure("Constructors can't have qualifiers", m); + } } - TypeMirror returnType; - if (element.getKind() == CONSTRUCTOR) { - returnType = element.getEnclosingElement().asType(); - } else { - returnType = element.getReturnType(); + return new InjectBinding(key, this, m); + } + + TypeTool tool() { + return tool; + } + + private final Supplier> factoryElement = memoize(() -> { + for (Element el : componentElement().element().getEnclosedElements()) { + boolean hasFactoryAnnotation = el.getAnnotation(Component.Factory.class) != null; + if (!hasFactoryAnnotation) { + continue; + } + TypeElement tel = Visitors.TYPE_ELEMENT_VISITOR.visit(el); + if (tel == null) { + continue; + } + return Optional.of(new FactoryElement(tel, componentElement().generatedClass(), this)); } - key = Key.create(returnType, getQualifier(element)); - keyCache.put(element, key); - return key; + return Optional.empty(); + }); + + + public Optional factoryElement() { + return factoryElement.get(); } - Key getKey(VariableElement parameter) { - Key key = keyCache.get(parameter); - if (key != null) { - return key; + private final Supplier> parameterBindings = memoize(() -> { + List pBindings = factoryElement() + .map(FactoryElement::parameterBindings) + .or(() -> builderElement().map(BuilderElement::parameterBindings)) + .orElse(List.of()); + Map result = new LinkedHashMap<>(); + for (ParameterBinding b : pBindings) { + ParameterBinding previousBinding = result.put(b.key(), b); + if (previousBinding != null) { + Element p = previousBinding.element(); + throw new ValidationFailure("The binding is in conflict with another parameter: " + + p.asType() + ' ' + p.getSimpleName(), b.element()); + } } - key = Key.create(parameter.asType(), getQualifier(parameter)); - keyCache.put(parameter, key); - return key; + return result; + }); + + public Optional parameterBinding(Key key) { + return Optional.ofNullable(parameterBindings.get().get(key)); } - private boolean hasQualifierAnnotation(AnnotationMirror mirror) { - DeclaredType type = mirror.getAnnotationType(); - TypeElement element = TYPE_ELEMENT_VISITOR.visit(type.asElement()); - return tool.hasQualifierAnnotation(element); + public Collection parameterBindings() { + return parameterBindings.get().values(); } - InjectBinding createBinding(ExecutableElement m) { - Key key = getKey(m); - if (m.getKind() == ElementKind.CONSTRUCTOR) { - if (key.qualifier().isPresent()) { - throw new ValidationFailure("Constructors can't have qualifiers", m); + private final Supplier> builderElement = memoize(() -> { + for (Element el : componentElement().element().getEnclosedElements()) { + boolean hasBuilderAnnotation = el.getAnnotation(Component.Builder.class) != null; + if (!hasBuilderAnnotation) { + continue; + } + TypeElement tel = Visitors.TYPE_ELEMENT_VISITOR.visit(el); + if (tel == null) { + continue; } + return Optional.of(new BuilderElement(tel, componentElement().generatedClass(), this)); } - return new InjectBinding(key, this, m); + return Optional.empty(); + }); + + private final Supplier> providesBindings = memoize(() -> { + List methods = methodsIn(componentElement().element().getEnclosedElements()); + Map result = new LinkedHashMap<>(); + for (ExecutableElement method : methods) { + if (method.getAnnotation(Provides.class) == null) { + continue; // ignore + } + Key key = keyCache().getKey(method); + InjectBinding b = createBinding(method); + result.put(key, b); + } + return result; + }); + + private final Supplier> requests = memoize(() -> { + List methods = methodsIn(componentElement().element().getEnclosedElements()); + Map result = new LinkedHashMap<>(); + for (ExecutableElement method : methods) { + if (method.getModifiers().contains(Modifier.STATIC)) { + continue; // ignore + } + if (method.getAnnotation(Provides.class) != null) { + continue; // ignore + } + if (!method.getParameters().isEmpty()) { + throw new ValidationFailure("Request method may not have any parameters", method); + } + if (method.getModifiers().contains(Modifier.DEFAULT)) { + throw new ValidationFailure("Default modifier is not allowed here", method); + } + if (method.getReturnType().getKind() == TypeKind.VOID) { + throw new ValidationFailure("Request method may not return void", method); + } + Key key = keyCache().getKey(method); + result.put(key, new DependencyRequest(key, method, method)); + } + return result; + }); + + public Optional builderElement() { + return builderElement.get(); } - TypeTool tool() { - return tool; + public boolean isComponentRequest(Binding binding) { + return requests.get().containsKey(binding.key()); + } + + public Collection requests() { + return requests.get().values(); + } + + public Map providesBindings() { + return providesBindings.get(); + } + + private ComponentElement componentElement() { + return componentElement; } - @Override - public void clearCache() { - keyCache.clear(); + private KeyCache keyCache() { + return keyCache; } } diff --git a/compiler/src/main/java/io/jbock/simple/processor/graph/GraphFactory.java b/compiler/src/main/java/io/jbock/simple/processor/graph/GraphFactory.java index e203974..2012025 100644 --- a/compiler/src/main/java/io/jbock/simple/processor/graph/GraphFactory.java +++ b/compiler/src/main/java/io/jbock/simple/processor/graph/GraphFactory.java @@ -2,10 +2,10 @@ import io.jbock.simple.Inject; import io.jbock.simple.processor.binding.Binding; -import io.jbock.simple.processor.binding.ComponentElement; import io.jbock.simple.processor.binding.DependencyRequest; import io.jbock.simple.processor.binding.InjectBindingFactory; import io.jbock.simple.processor.binding.Key; +import io.jbock.simple.processor.binding.KeyFactory; import io.jbock.simple.processor.binding.ProviderBinding; import io.jbock.simple.processor.util.ProviderType; import io.jbock.simple.processor.util.TypeTool; @@ -20,27 +20,27 @@ public class GraphFactory { - private final ComponentElement component; private final TypeTool tool; + private final KeyFactory keyFactory; private final InjectBindingFactory injectBindingFactory; private final Map> bindingCache = new HashMap<>(); private final MissingBindingPrinter missingBindingPrinter; @Inject public GraphFactory( - ComponentElement component, TypeTool tool, + KeyFactory keyFactory, InjectBindingFactory injectBindingFactory, MissingBindingPrinter missingBindingPrinter) { - this.component = component; this.tool = tool; + this.keyFactory = keyFactory; this.injectBindingFactory = injectBindingFactory; this.missingBindingPrinter = missingBindingPrinter; } private Optional getBinding(DependencyRequest request) { - return bindingCache.computeIfAbsent(request.key(), key -> component.parameterBinding(key) - .or(() -> Optional.ofNullable(component.providesBindings().get(key))) + return bindingCache.computeIfAbsent(request.key(), key -> keyFactory.parameterBinding(key) + .or(() -> Optional.ofNullable(keyFactory.providesBindings().get(key))) .or(() -> injectBindingFactory.binding(key)) .or(() -> providerBinding(key))); } @@ -52,8 +52,8 @@ private Optional providerBinding(Key key) { } ProviderType provider = providerType.orElseThrow(); Key innerKey = key.changeType(provider.innerType()); - return component.parameterBinding(innerKey) - .or(() -> Optional.ofNullable(component.providesBindings().get(innerKey))) + return keyFactory.parameterBinding(innerKey) + .or(() -> Optional.ofNullable(keyFactory.providesBindings().get(innerKey))) .or(() -> injectBindingFactory.binding(innerKey)) .map(b -> new ProviderBinding(key, b, provider)); } diff --git a/compiler/src/main/java/io/jbock/simple/processor/graph/TopologicalSorter.java b/compiler/src/main/java/io/jbock/simple/processor/graph/TopologicalSorter.java index 3cb359d..125b240 100644 --- a/compiler/src/main/java/io/jbock/simple/processor/graph/TopologicalSorter.java +++ b/compiler/src/main/java/io/jbock/simple/processor/graph/TopologicalSorter.java @@ -4,6 +4,7 @@ import io.jbock.simple.processor.binding.Binding; import io.jbock.simple.processor.binding.ComponentElement; import io.jbock.simple.processor.binding.DependencyRequest; +import io.jbock.simple.processor.binding.KeyFactory; import io.jbock.simple.processor.binding.ParameterBinding; import java.util.ArrayDeque; @@ -15,23 +16,26 @@ public final class TopologicalSorter { private final GraphFactory graphFactory; private final ComponentElement component; + private final KeyFactory keyFactory; @Inject public TopologicalSorter( GraphFactory graphFactory, - ComponentElement component) { + ComponentElement component, + KeyFactory keyFactory) { this.graphFactory = graphFactory; this.component = component; + this.keyFactory = keyFactory; } public List sortedBindings() { AccessibilityValidator validator = AccessibilityValidator.create(component); Graph graph = Graph.newGraph(); - for (ParameterBinding request : component.parameterBindings()) { + for (ParameterBinding request : keyFactory.parameterBindings()) { // preserve parameter order graph.nodes().add(request); } - for (DependencyRequest request : component.requests()) { + for (DependencyRequest request : keyFactory.requests()) { graph.addAll(graphFactory.getGraph(request)); } for (Binding binding : graph.nodes()) { diff --git a/compiler/src/main/java/io/jbock/simple/processor/step/BindingRegistry.java b/compiler/src/main/java/io/jbock/simple/processor/step/BindingRegistry.java index 7eeba18..8c41f57 100644 --- a/compiler/src/main/java/io/jbock/simple/processor/step/BindingRegistry.java +++ b/compiler/src/main/java/io/jbock/simple/processor/step/BindingRegistry.java @@ -2,7 +2,7 @@ import io.jbock.simple.Inject; import io.jbock.simple.processor.binding.Key; -import io.jbock.simple.processor.binding.KeyFactory; +import io.jbock.simple.processor.binding.KeyCache; import io.jbock.simple.processor.util.ValidationFailure; import javax.lang.model.element.Element; @@ -13,15 +13,15 @@ public class BindingRegistry { private final Map bindings = new HashMap<>(); - private final KeyFactory keyFactory; + private final KeyCache keyCache; @Inject - public BindingRegistry(KeyFactory keyFactory) { - this.keyFactory = keyFactory; + public BindingRegistry(KeyCache keyCache) { + this.keyCache = keyCache; } void register(ExecutableElement method) { - Key key = keyFactory.getKey(method); + Key key = keyCache.getKey(method); Element previous = bindings.put(key, method); if (previous != null && !previous.equals(method)) { throw new ValidationFailure("Duplicate binding for " + key, method); diff --git a/compiler/src/main/java/io/jbock/simple/processor/step/ComponentStep.java b/compiler/src/main/java/io/jbock/simple/processor/step/ComponentStep.java index 54df843..56427dc 100644 --- a/compiler/src/main/java/io/jbock/simple/processor/step/ComponentStep.java +++ b/compiler/src/main/java/io/jbock/simple/processor/step/ComponentStep.java @@ -6,7 +6,7 @@ import io.jbock.simple.Inject; import io.jbock.simple.processor.ContextComponent; import io.jbock.simple.processor.binding.Binding; -import io.jbock.simple.processor.binding.ComponentElement; +import io.jbock.simple.processor.binding.KeyFactory; import io.jbock.simple.processor.util.SpecWriter; import io.jbock.simple.processor.util.TypeTool; import io.jbock.simple.processor.util.ValidationFailure; @@ -72,8 +72,8 @@ public Set process(Map> elementsByAnnota private void process(TypeElement typeElement) { typeElementValidator.validate(typeElement); ContextComponent context = contextComponentFactory.create(typeElement); - ComponentElement component = context.componentElement(); - component.factoryElement().ifPresent(factory -> { + KeyFactory keyFactory = context.keyFactory(); + keyFactory.factoryElement().ifPresent(factory -> { ExecutableElement method = factory.singleAbstractMethod(); if (!tool.types().isSameType(method.getReturnType(), typeElement.asType())) { throw new ValidationFailure("Factory method must return the component type", method); @@ -87,6 +87,6 @@ private void process(TypeElement typeElement) { Generator generator = context.generator(); List bindings = context.topologicalSorter().sortedBindings(); TypeSpec typeSpec = generator.generate(bindings); - specWriter.write(component.generatedClass(), typeSpec); + specWriter.write(context.componentElement().generatedClass(), typeSpec); } } diff --git a/compiler/src/main/java/io/jbock/simple/processor/writing/ComponentImpl.java b/compiler/src/main/java/io/jbock/simple/processor/writing/ComponentImpl.java index e266678..f19e9a5 100644 --- a/compiler/src/main/java/io/jbock/simple/processor/writing/ComponentImpl.java +++ b/compiler/src/main/java/io/jbock/simple/processor/writing/ComponentImpl.java @@ -15,6 +15,7 @@ import io.jbock.simple.processor.binding.DependencyRequest; import io.jbock.simple.processor.binding.FactoryElement; import io.jbock.simple.processor.binding.Key; +import io.jbock.simple.processor.binding.KeyFactory; import io.jbock.simple.processor.binding.ParameterBinding; import javax.annotation.processing.Generated; @@ -37,6 +38,7 @@ public class ComponentImpl { private static final String CREATE_METHOD = "create"; private static final String MOCK_BUILDER_METHOD = "mockBuilder"; + private final KeyFactory keyFactory; private final ComponentElement component; private final Map sorted; private final Function names; @@ -46,12 +48,14 @@ public class ComponentImpl { private final Modifier[] modifiers; private ComponentImpl( + KeyFactory keyFactory, ComponentElement component, Map sorted, Function names, MockBuilder mockBuilder, BuilderImpl builderImpl, FactoryImpl factoryImpl) { + this.keyFactory = keyFactory; this.component = component; this.sorted = sorted; this.names = names; @@ -69,18 +73,18 @@ TypeSpec generate() { .addSuperinterface(component.element().asType()); spec.addFields(getFields()); spec.addMethods(generateGetters()); - component.factoryElement().ifPresent(factory -> { + keyFactory.factoryElement().ifPresent(factory -> { spec.addMethod(generateFactoryMethod(factory)); spec.addType(factoryImpl.generate(factory)); if (component.mockBuilder()) { spec.addMethod(generateMockBuilderMethodFactory()); } }); - component.builderElement().ifPresent(builder -> { + keyFactory.builderElement().ifPresent(builder -> { spec.addMethod(generateBuilderMethod(builder)); spec.addType(builderImpl.generate(builder, mockBuilder)); }); - if (component.factoryElement().isEmpty() && component.builderElement().isEmpty()) { + if (keyFactory.factoryElement().isEmpty() && keyFactory.builderElement().isEmpty()) { spec.addMethod(generateCreateMethod()); if (component.mockBuilder()) { spec.addMethod(generateMockBuilderMethod()); @@ -99,8 +103,8 @@ TypeSpec generate() { } private List generateGetters() { - List result = new ArrayList<>(component.requests().size()); - for (DependencyRequest r : component.requests()) { + List result = new ArrayList<>(keyFactory.requests().size()); + for (DependencyRequest r : keyFactory.requests()) { MethodSpec.Builder method = MethodSpec.methodBuilder(r.requestingElement().getSimpleName().toString()); method.addStatement("return $L", sorted.get(r.key()).name()); method.returns(r.key().typeName()); @@ -217,6 +221,7 @@ private MethodSpec generateAllParametersConstructor() { } public static final class Factory { + private final KeyFactory keyFactory; private final ComponentElement component; private final MockBuilder.Factory mockBuilderFactory; private final BuilderImpl.Factory builderImplFactory; @@ -224,10 +229,12 @@ public static final class Factory { @Inject public Factory( + KeyFactory keyFactory, ComponentElement component, MockBuilder.Factory mockBuilderFactory, BuilderImpl.Factory builderImplFactory, FactoryImpl.Factory factoryImplFactory) { + this.keyFactory = keyFactory; this.component = component; this.mockBuilderFactory = mockBuilderFactory; this.builderImplFactory = builderImplFactory; @@ -238,7 +245,7 @@ ComponentImpl create( Map sorted, Function names) { return new ComponentImpl( - component, + keyFactory, component, sorted, names, mockBuilderFactory.create(sorted, names), diff --git a/compiler/src/main/java/io/jbock/simple/processor/writing/Generator.java b/compiler/src/main/java/io/jbock/simple/processor/writing/Generator.java index 7363afc..6e708e8 100644 --- a/compiler/src/main/java/io/jbock/simple/processor/writing/Generator.java +++ b/compiler/src/main/java/io/jbock/simple/processor/writing/Generator.java @@ -4,8 +4,8 @@ import io.jbock.javapoet.TypeSpec; import io.jbock.simple.Inject; import io.jbock.simple.processor.binding.Binding; -import io.jbock.simple.processor.binding.ComponentElement; import io.jbock.simple.processor.binding.Key; +import io.jbock.simple.processor.binding.KeyFactory; import io.jbock.simple.processor.util.UniqueNameSet; import javax.lang.model.SourceVersion; @@ -18,14 +18,14 @@ public class Generator { private final ComponentImpl.Factory componentImpl; - private final ComponentElement component; + private final KeyFactory keyFactory; @Inject public Generator( ComponentImpl.Factory componentImpl, - ComponentElement component) { + KeyFactory keyFactory) { this.componentImpl = componentImpl; - this.component = component; + this.keyFactory = keyFactory; } public TypeSpec generate(List bindings) { @@ -42,7 +42,7 @@ private Map addNames(List bindings) { for (Binding b : bindings) { String name = uniqueNameSet.getUniqueName(validJavaName(b.suggestedVariableName())); String auxName = uniqueNameSet.getUniqueName(name + "_isSet"); - result.put(b.key(), new NamedBinding(b, name, auxName, component.isComponentRequest(b))); + result.put(b.key(), new NamedBinding(b, name, auxName, keyFactory.isComponentRequest(b))); } return result; }