Skip to content

Commit

Permalink
ISSUES-4 add method mockBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
h908714124 committed Nov 29, 2023
1 parent a109023 commit d844f16
Show file tree
Hide file tree
Showing 16 changed files with 204 additions and 119 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,12 @@ public String getUniqueName(CharSequence base) {
}
return name;
}

/**
* Adds {@code name} without any modification to the name set. Has no effect if {@code name} is
* already present in the set.
*/
public void claim(CharSequence name) {
uniqueNames.add(name.toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import javax.lang.model.element.Modifier;
import javax.lang.model.type.TypeMirror;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
Expand Down Expand Up @@ -89,17 +88,14 @@ TypeSpec generate() {
if (component.factoryElement().isEmpty() && component.builderElement().isEmpty()) {
spec.addMethod(generateCreateMethod());
spec.addMethod(generateMockCreateMethod());
spec.addType(mockBuilder.generate());
}
spec.addType(mockBuilder.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"))
.build());
spec.addModifiers(FINAL);
spec.addMethod(generateConstructor());
if (sorted.values().stream().anyMatch(binding -> !(binding.binding() instanceof ParameterBinding))) {
spec.addMethod(generateAllParametersConstructor());
}
spec.addMethod(generateAllParametersConstructor());
spec.addOriginatingElement(component.element());
return spec.build();
}
Expand Down Expand Up @@ -128,33 +124,14 @@ private MethodSpec generateCreateMethod() {
}

MethodSpec generateMockCreateMethod() {
MethodSpec.Builder method = MethodSpec.methodBuilder("mockCreate");
method.addModifiers(modifiers);
MethodSpec.Builder method = MethodSpec.methodBuilder("mockBuilder");
method.addJavadoc("Visible for testing. Do not call this method from production code.");
method.addStatement("return new $T()", mockBuilder.getClassName());
method.returns(mockBuilder.getClassName());
method.addModifiers(STATIC);
return method.build();
}

private MethodSpec generateConstructor() {
MethodSpec.Builder constructor = MethodSpec.constructorBuilder().addModifiers(PRIVATE);
for (NamedBinding namedBinding : sorted.values()) {
Binding b = namedBinding.binding();
Key key = b.key();
String name = namedBinding.name();
if (namedBinding.isComponentRequest()) {
FieldSpec field = FieldSpec.builder(key.typeName(), name, PRIVATE, FINAL).build();
constructor.addStatement("this.$N = $L", field, b.invocation(names));
} else if (!(b instanceof ParameterBinding)) {
ParameterSpec param = names.apply(key);
constructor.addStatement("$T $N = $L", key.typeName(), param, b.invocation(names));
}
if (b instanceof ParameterBinding) {
constructor.addParameter(names.apply(key));
}
}
return constructor.build();
}

private List<FieldSpec> getFields() {
List<FieldSpec> fields = new ArrayList<>();
for (NamedBinding namedBinding : sorted.values()) {
Expand Down Expand Up @@ -182,7 +159,6 @@ private MethodSpec generateAllParametersConstructor() {
}

private TypeSpec createFactoryImpl(FactoryElement factory) {
Collection<ParameterBinding> parameterBindings = component.parameterBindings();
TypeSpec.Builder spec = TypeSpec.classBuilder(factory.generatedClass());
spec.addModifiers(PRIVATE, STATIC, FINAL);
spec.addSuperinterface(factory.element().asType());
Expand All @@ -192,41 +168,62 @@ private TypeSpec createFactoryImpl(FactoryElement factory) {
method.addModifiers(abstractMethod.getModifiers().stream()
.filter(m -> m == PUBLIC || m == PROTECTED).collect(Collectors.toList()));
method.returns(TypeName.get(component.element().asType()));
method.addStatement("return new $T($L)", component.generatedClass(), parameterBindings.stream()
.map(b -> CodeBlock.of("$N", names.apply(b.key())))
.collect(CodeBlock.joining(", ")));
for (ParameterBinding b : parameterBindings) {
method.addParameter(names.apply(b.key()));
List<CodeBlock> constructorParameters = new ArrayList<>();
for (NamedBinding namedBinding : sorted.values()) {
Binding b = namedBinding.binding();
Key key = b.key();
CodeBlock invocation = b.invocation(names);
ParameterSpec param = names.apply(key);
if (namedBinding.isComponentRequest()) {
constructorParameters.add(CodeBlock.of("$N", names.apply(key)));
}
if (b instanceof ParameterBinding) {
method.addParameter(names.apply(b.key()));
} else {
method.addStatement("$T $N = $L", key.typeName(), param, invocation);
}
}
method.addStatement("return new $T($L)", component.generatedClass(), constructorParameters.stream()
.collect(CodeBlock.joining(", ")));
spec.addMethod(method.build());
return spec.build();
}

private TypeSpec createBuilderImpl(BuilderElement builder) {
Collection<ParameterBinding> parameterBindings = component.parameterBindings();
TypeMirror builderType = builder.element().asType();
TypeSpec.Builder spec = TypeSpec.classBuilder(builder.generatedClass());
for (ParameterBinding b : parameterBindings) {
spec.addField(FieldSpec.builder(b.key().typeName(), names.apply(b.key()).name).build());
MethodSpec.Builder setterMethod = MethodSpec.methodBuilder(b.element().getSimpleName().toString());
setterMethod.addAnnotation(Override.class);
setterMethod.addParameter(names.apply(b.key()));
setterMethod.addStatement("this.$N = $N", names.apply(b.key()), names.apply(b.key()));
setterMethod.addStatement("return this");
setterMethod.returns(TypeName.get(builderType));
setterMethod.addModifiers(b.element().getModifiers().stream()
.filter(m -> m == PUBLIC || m == PROTECTED).collect(Collectors.toList()));
spec.addMethod(setterMethod.build());
MethodSpec.Builder buildMethod = MethodSpec.methodBuilder(builder.buildMethod().getSimpleName().toString());
List<CodeBlock> constructorParameters = new ArrayList<>();
for (NamedBinding namedBinding : sorted.values()) {
Binding b = namedBinding.binding();
Key key = b.key();
CodeBlock invocation = b.invocation(names);
ParameterSpec param = names.apply(key);
if (namedBinding.isComponentRequest()) {
constructorParameters.add(CodeBlock.of("$N", names.apply(key)));
}
if (b instanceof ParameterBinding) {
spec.addField(FieldSpec.builder(b.key().typeName(), names.apply(b.key()).name).build());
MethodSpec.Builder setterMethod = MethodSpec.methodBuilder(b.element().getSimpleName().toString());
setterMethod.addAnnotation(Override.class);
setterMethod.addParameter(names.apply(b.key()));
setterMethod.addStatement("this.$N = $N", names.apply(b.key()), names.apply(b.key()));
setterMethod.addStatement("return this");
setterMethod.returns(TypeName.get(builderType));
setterMethod.addModifiers(b.element().getModifiers().stream()
.filter(m -> m == PUBLIC || m == PROTECTED).collect(Collectors.toList()));
spec.addMethod(setterMethod.build());
} else {
buildMethod.addStatement("$T $N = $L", key.typeName(), param, invocation);
}
}
spec.addModifiers(PRIVATE, STATIC, FINAL);
spec.addSuperinterface(builderType);
MethodSpec.Builder buildMethod = MethodSpec.methodBuilder(builder.buildMethod().getSimpleName().toString());
buildMethod.addAnnotation(Override.class);
buildMethod.addModifiers(builder.buildMethod().getModifiers().stream()
.filter(m -> m == PUBLIC || m == PROTECTED).collect(Collectors.toList()));
buildMethod.returns(TypeName.get(component.element().asType()));
buildMethod.addStatement("return new $T($L)", component.generatedClass(), parameterBindings.stream()
.map(b -> CodeBlock.of("$N", names.apply(b.key())))
buildMethod.addStatement("return new $T($L)", component.generatedClass(), constructorParameters.stream()
.collect(CodeBlock.joining(", ")));
spec.addMethod(buildMethod.build());
return spec.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public TypeSpec generate(List<Binding> bindings) {

private Map<Key, NamedBinding> addNames(List<Binding> bindings) {
UniqueNameSet uniqueNameSet = new UniqueNameSet();
uniqueNameSet.claim("build");
Map<Key, NamedBinding> result = new LinkedHashMap<>();
for (Binding b : bindings) {
String name = uniqueNameSet.getUniqueName(validJavaName(b.suggestedVariableName()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.jbock.javapoet.ClassName;
import io.jbock.javapoet.CodeBlock;
import io.jbock.javapoet.FieldSpec;
import io.jbock.javapoet.MethodSpec;
import io.jbock.javapoet.ParameterSpec;
import io.jbock.javapoet.TypeName;
Expand All @@ -10,6 +11,7 @@
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.ParameterBinding;

import javax.lang.model.element.Modifier;
import java.util.ArrayList;
Expand All @@ -18,6 +20,7 @@
import java.util.function.Function;

import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;

Expand All @@ -28,7 +31,7 @@ public class MockBuilder {
private final Function<Key, ParameterSpec> names;
private final Modifier[] modifiers;

public MockBuilder(
MockBuilder(
ComponentElement component,
Map<Key, NamedBinding> sorted,
Function<Key, ParameterSpec> names) {
Expand All @@ -44,6 +47,8 @@ TypeSpec generate() {
.addModifiers(modifiers)
.addModifiers(STATIC, FINAL);
spec.addMethod(buildMethod());
spec.addFields(getFields());
spec.addMethods(getMethods());
return spec.build();
}

Expand All @@ -62,7 +67,14 @@ private MethodSpec buildMethod() {
if (namedBinding.isComponentRequest()) {
constructorParameters.add(CodeBlock.of("$N", names.apply(key)));
}
method.addStatement("$T $N = $L", key.typeName(), param, invocation);
if (namedBinding.binding() instanceof ParameterBinding) {
method.addParameter(names.apply(b.key()));
} else if (!key.typeName().isPrimitive()) {
method.addStatement("$1T $2N = this.$2N != null ? this.$2N : $3L", key.typeName(), param, invocation);
} else {
// TODO allow mocking primitives
method.addStatement("$T $N = $L", key.typeName(), param, invocation);
}
}
return method
.addModifiers(modifiers)
Expand All @@ -73,6 +85,37 @@ private MethodSpec buildMethod() {
.build();
}

private List<FieldSpec> getFields() {
List<FieldSpec> fields = new ArrayList<>();
for (NamedBinding namedBinding : sorted.values()) {
if (namedBinding.binding() instanceof ParameterBinding) {
continue;
}
TypeName type = namedBinding.binding().key().typeName();
FieldSpec field = FieldSpec.builder(type, namedBinding.name(), PRIVATE).build();
fields.add(field);
}
return fields;
}

private List<MethodSpec> getMethods() {
List<MethodSpec> methods = new ArrayList<>();
for (NamedBinding namedBinding : sorted.values()) {
if (namedBinding.binding() instanceof ParameterBinding) {
continue;
}
Binding b = namedBinding.binding();
Key key = b.key();
ParameterSpec param = names.apply(key);
MethodSpec method = MethodSpec.methodBuilder(param.name)
.addModifiers(modifiers)
.addParameter(param)
.addStatement("this.$1N = $1N", param)
.build();
methods.add(method);
}
return methods;
}

public static final class Factory {
private final ComponentElement component;
Expand Down
18 changes: 10 additions & 8 deletions compiler/src/test/java/io/jbock/simple/processor/BuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ void noParameters() {
"final class TestClass_AComponent_Impl implements TestClass.AComponent {",
" private final TestClass.A a;",
"",
" private TestClass_AComponent_Impl() {",
" this.a = new TestClass.A();",
" private TestClass_AComponent_Impl(TestClass.A a) {",
" this.a = a;",
" }",
"",
" @Override",
Expand All @@ -60,14 +60,15 @@ void noParameters() {
" private static final class Builder_Impl implements TestClass.AComponent.Builder {",
" @Override",
" public TestClass.AComponent build() {",
" return new TestClass_AComponent_Impl();",
" TestClass.A a = new TestClass.A();",
" return new TestClass_AComponent_Impl(a);",
" }",
" }",
"}");
}

@Test
void factoryParameterIdentity() {
void builderParameterIdentity() {
JavaFileObject component = forSourceLines("test.TestClass",
"package test;",
"",
Expand Down Expand Up @@ -128,7 +129,7 @@ void factoryParameterIdentity() {


@Test
void factoryParameter() {
void builderParameter() {
JavaFileObject component = forSourceLines("test.TestClass",
"package test;",
"",
Expand Down Expand Up @@ -161,8 +162,8 @@ void factoryParameter() {
"public final class TestClass_AComponent_Impl implements TestClass.AComponent {",
" private final TestClass.A a;",
"",
" private TestClass_AComponent_Impl(String s) {",
" this.a = new TestClass.A(s);",
" private TestClass_AComponent_Impl(TestClass.A a) {",
" this.a = a;",
" }",
"",
" @Override",
Expand All @@ -185,7 +186,8 @@ void factoryParameter() {
"",
" @Override",
" public TestClass.AComponent build() {",
" return new TestClass_AComponent_Impl(s);",
" TestClass.A a = new TestClass.A(s);",
" return new TestClass_AComponent_Impl(a);",
" }",
" }",
"}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ void noParameters() {
"final class TestClass_AComponent_Impl implements TestClass.AComponent {",
" private final TestClass.A a;",
"",
" private TestClass_AComponent_Impl() {",
" this.a = new TestClass.A();",
" private TestClass_AComponent_Impl(TestClass.A a) {",
" this.a = a;",
" }",
"",
" @Override",
Expand All @@ -60,7 +60,8 @@ void noParameters() {
" private static final class Factory_Impl implements TestClass.AComponent.Factory {",
" @Override",
" public TestClass.AComponent create() {",
" return new TestClass_AComponent_Impl();",
" TestClass.A a = new TestClass.A();",
" return new TestClass_AComponent_Impl(a);",
" }",
" }",
"}");
Expand Down Expand Up @@ -150,8 +151,8 @@ void factoryParameter() {
"final class TestClass_AComponent_Impl implements TestClass.AComponent {",
" private final TestClass.A a;",
"",
" private TestClass_AComponent_Impl(String s) {",
" this.a = new TestClass.A(s);",
" private TestClass_AComponent_Impl(TestClass.A a) {",
" this.a = a;",
" }",
"",
" @Override",
Expand All @@ -166,7 +167,8 @@ void factoryParameter() {
" private static final class Factory_Impl implements TestClass.AComponent.Factory {",
" @Override",
" public TestClass.AComponent create(String s) {",
" return new TestClass_AComponent_Impl(s);",
" TestClass.A a = new TestClass.A(s);",
" return new TestClass_AComponent_Impl(a);",
" }",
" }",
"}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,8 @@ void staticMethodBindings() {
"final class TestClass_AComponent_Impl implements TestClass.AComponent {",
" private final TestClass.A a;",
"",
" private TestClass_AComponent_Impl() {",
" TestClass.C c = TestClass.C.createC();",
" TestClass.B b = TestClass.B.createB(c);",
" this.a = new TestClass.A(b);",
" private TestClass_AComponent_Impl(TestClass.A a) {",
" this.a = a;",
" }",
"",
" @Override",
Expand Down
Loading

0 comments on commit d844f16

Please sign in to comment.