Skip to content

Commit 8a4a81a

Browse files
authored
4 mockbuilder method (#9)
* ISSUES-4 add mockbuilder * ISSUES-4 add method mockBuilder * ISSUES-4 add method mockBuilder
1 parent 1f74588 commit 8a4a81a

17 files changed

+381
-117
lines changed

compiler/src/main/java/io/jbock/simple/processor/util/UniqueNameSet.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,12 @@ public String getUniqueName(CharSequence base) {
3434
}
3535
return name;
3636
}
37+
38+
/**
39+
* Adds {@code name} without any modification to the name set. Has no effect if {@code name} is
40+
* already present in the set.
41+
*/
42+
public void claim(CharSequence name) {
43+
uniqueNames.add(name.toString());
44+
}
3745
}

compiler/src/main/java/io/jbock/simple/processor/writing/ComponentImpl.java

Lines changed: 68 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import javax.lang.model.element.Modifier;
2323
import javax.lang.model.type.TypeMirror;
2424
import java.util.ArrayList;
25-
import java.util.Collection;
2625
import java.util.List;
2726
import java.util.Map;
2827
import java.util.function.Function;
@@ -39,19 +38,23 @@ public class ComponentImpl {
3938
private final ComponentElement component;
4039
private final Map<Key, NamedBinding> sorted;
4140
private final Function<Key, ParameterSpec> names;
41+
private final MockBuilder mockBuilder;
42+
private final Modifier[] modifiers;
4243

4344
private ComponentImpl(
4445
ComponentElement component,
4546
Map<Key, NamedBinding> sorted,
46-
Function<Key, ParameterSpec> names) {
47+
Function<Key, ParameterSpec> names,
48+
MockBuilder mockBuilder) {
4749
this.component = component;
4850
this.sorted = sorted;
4951
this.names = names;
52+
this.mockBuilder = mockBuilder;
53+
this.modifiers = component.element().getModifiers().stream()
54+
.filter(m -> m == PUBLIC).toArray(Modifier[]::new);
5055
}
5156

5257
TypeSpec generate() {
53-
Modifier[] modifiers = component.element().getModifiers().stream()
54-
.filter(m -> m == PUBLIC).toArray(Modifier[]::new);
5558
TypeSpec.Builder spec = TypeSpec.classBuilder(component.generatedClass())
5659
.addModifiers(modifiers)
5760
.addSuperinterface(component.element().asType());
@@ -83,22 +86,21 @@ TypeSpec generate() {
8386
spec.addType(createBuilderImpl(builder));
8487
});
8588
if (component.factoryElement().isEmpty() && component.builderElement().isEmpty()) {
86-
spec.addMethod(generateCreateMethod(modifiers));
89+
spec.addMethod(generateCreateMethod());
90+
spec.addMethod(generateMockCreateMethod());
8791
}
92+
spec.addType(mockBuilder.generate());
8893
spec.addAnnotation(AnnotationSpec.builder(Generated.class)
8994
.addMember("value", CodeBlock.of("$S", SimpleComponentProcessor.class.getCanonicalName()))
9095
.addMember("comments", CodeBlock.of("$S", "https://github.com/jbock-java/simple-component"))
9196
.build());
9297
spec.addModifiers(FINAL);
93-
spec.addMethod(generateConstructor());
94-
if (sorted.values().stream().anyMatch(binding -> !(binding.binding() instanceof ParameterBinding))) {
95-
spec.addMethod(generateAllParametersConstructor());
96-
}
98+
spec.addMethod(generateAllParametersConstructor());
9799
spec.addOriginatingElement(component.element());
98100
return spec.build();
99101
}
100102

101-
private MethodSpec generateCreateMethod(Modifier[] modifiers) {
103+
private MethodSpec generateCreateMethod() {
102104
List<CodeBlock> constructorParameters = new ArrayList<>();
103105
MethodSpec.Builder method = MethodSpec.methodBuilder("create");
104106
for (NamedBinding namedBinding : sorted.values()) {
@@ -121,24 +123,13 @@ private MethodSpec generateCreateMethod(Modifier[] modifiers) {
121123
.build();
122124
}
123125

124-
private MethodSpec generateConstructor() {
125-
MethodSpec.Builder constructor = MethodSpec.constructorBuilder().addModifiers(PRIVATE);
126-
for (NamedBinding namedBinding : sorted.values()) {
127-
Binding b = namedBinding.binding();
128-
Key key = b.key();
129-
String name = namedBinding.name();
130-
if (namedBinding.isComponentRequest()) {
131-
FieldSpec field = FieldSpec.builder(key.typeName(), name, PRIVATE, FINAL).build();
132-
constructor.addStatement("this.$N = $L", field, b.invocation(names));
133-
} else if (!(b instanceof ParameterBinding)) {
134-
ParameterSpec param = names.apply(key);
135-
constructor.addStatement("$T $N = $L", key.typeName(), param, b.invocation(names));
136-
}
137-
if (b instanceof ParameterBinding) {
138-
constructor.addParameter(names.apply(key));
139-
}
140-
}
141-
return constructor.build();
126+
MethodSpec generateMockCreateMethod() {
127+
MethodSpec.Builder method = MethodSpec.methodBuilder("mockBuilder");
128+
method.addJavadoc("Visible for testing. Do not call this method from production code.");
129+
method.addStatement("return new $T()", mockBuilder.getClassName());
130+
method.returns(mockBuilder.getClassName());
131+
method.addModifiers(STATIC);
132+
return method.build();
142133
}
143134

144135
private List<FieldSpec> getFields() {
@@ -168,7 +159,6 @@ private MethodSpec generateAllParametersConstructor() {
168159
}
169160

170161
private TypeSpec createFactoryImpl(FactoryElement factory) {
171-
Collection<ParameterBinding> parameterBindings = component.parameterBindings();
172162
TypeSpec.Builder spec = TypeSpec.classBuilder(factory.generatedClass());
173163
spec.addModifiers(PRIVATE, STATIC, FINAL);
174164
spec.addSuperinterface(factory.element().asType());
@@ -178,58 +168,85 @@ private TypeSpec createFactoryImpl(FactoryElement factory) {
178168
method.addModifiers(abstractMethod.getModifiers().stream()
179169
.filter(m -> m == PUBLIC || m == PROTECTED).collect(Collectors.toList()));
180170
method.returns(TypeName.get(component.element().asType()));
181-
method.addStatement("return new $T($L)", component.generatedClass(), parameterBindings.stream()
182-
.map(b -> CodeBlock.of("$N", names.apply(b.key())))
183-
.collect(CodeBlock.joining(", ")));
184-
for (ParameterBinding b : parameterBindings) {
185-
method.addParameter(names.apply(b.key()));
171+
List<CodeBlock> constructorParameters = new ArrayList<>();
172+
for (NamedBinding namedBinding : sorted.values()) {
173+
Binding b = namedBinding.binding();
174+
Key key = b.key();
175+
CodeBlock invocation = b.invocation(names);
176+
ParameterSpec param = names.apply(key);
177+
if (namedBinding.isComponentRequest()) {
178+
constructorParameters.add(CodeBlock.of("$N", names.apply(key)));
179+
}
180+
if (b instanceof ParameterBinding) {
181+
method.addParameter(names.apply(b.key()));
182+
} else {
183+
method.addStatement("$T $N = $L", key.typeName(), param, invocation);
184+
}
186185
}
186+
method.addStatement("return new $T($L)", component.generatedClass(), constructorParameters.stream()
187+
.collect(CodeBlock.joining(", ")));
187188
spec.addMethod(method.build());
188189
return spec.build();
189190
}
190191

191192
private TypeSpec createBuilderImpl(BuilderElement builder) {
192-
Collection<ParameterBinding> parameterBindings = component.parameterBindings();
193193
TypeMirror builderType = builder.element().asType();
194194
TypeSpec.Builder spec = TypeSpec.classBuilder(builder.generatedClass());
195-
for (ParameterBinding b : parameterBindings) {
196-
spec.addField(FieldSpec.builder(b.key().typeName(), names.apply(b.key()).name).build());
197-
MethodSpec.Builder setterMethod = MethodSpec.methodBuilder(b.element().getSimpleName().toString());
198-
setterMethod.addAnnotation(Override.class);
199-
setterMethod.addParameter(names.apply(b.key()));
200-
setterMethod.addStatement("this.$N = $N", names.apply(b.key()), names.apply(b.key()));
201-
setterMethod.addStatement("return this");
202-
setterMethod.returns(TypeName.get(builderType));
203-
setterMethod.addModifiers(b.element().getModifiers().stream()
204-
.filter(m -> m == PUBLIC || m == PROTECTED).collect(Collectors.toList()));
205-
spec.addMethod(setterMethod.build());
195+
MethodSpec.Builder buildMethod = MethodSpec.methodBuilder(builder.buildMethod().getSimpleName().toString());
196+
List<CodeBlock> constructorParameters = new ArrayList<>();
197+
for (NamedBinding namedBinding : sorted.values()) {
198+
Binding b = namedBinding.binding();
199+
Key key = b.key();
200+
CodeBlock invocation = b.invocation(names);
201+
ParameterSpec param = names.apply(key);
202+
if (namedBinding.isComponentRequest()) {
203+
constructorParameters.add(CodeBlock.of("$N", names.apply(key)));
204+
}
205+
if (b instanceof ParameterBinding) {
206+
spec.addField(FieldSpec.builder(b.key().typeName(), names.apply(b.key()).name).build());
207+
MethodSpec.Builder setterMethod = MethodSpec.methodBuilder(b.element().getSimpleName().toString());
208+
setterMethod.addAnnotation(Override.class);
209+
setterMethod.addParameter(names.apply(b.key()));
210+
setterMethod.addStatement("this.$N = $N", names.apply(b.key()), names.apply(b.key()));
211+
setterMethod.addStatement("return this");
212+
setterMethod.returns(TypeName.get(builderType));
213+
setterMethod.addModifiers(b.element().getModifiers().stream()
214+
.filter(m -> m == PUBLIC || m == PROTECTED).collect(Collectors.toList()));
215+
spec.addMethod(setterMethod.build());
216+
} else {
217+
buildMethod.addStatement("$T $N = $L", key.typeName(), param, invocation);
218+
}
206219
}
207220
spec.addModifiers(PRIVATE, STATIC, FINAL);
208221
spec.addSuperinterface(builderType);
209-
MethodSpec.Builder buildMethod = MethodSpec.methodBuilder(builder.buildMethod().getSimpleName().toString());
210222
buildMethod.addAnnotation(Override.class);
211223
buildMethod.addModifiers(builder.buildMethod().getModifiers().stream()
212224
.filter(m -> m == PUBLIC || m == PROTECTED).collect(Collectors.toList()));
213225
buildMethod.returns(TypeName.get(component.element().asType()));
214-
buildMethod.addStatement("return new $T($L)", component.generatedClass(), parameterBindings.stream()
215-
.map(b -> CodeBlock.of("$N", names.apply(b.key())))
226+
buildMethod.addStatement("return new $T($L)", component.generatedClass(), constructorParameters.stream()
216227
.collect(CodeBlock.joining(", ")));
217228
spec.addMethod(buildMethod.build());
218229
return spec.build();
219230
}
220231

221232
public static final class Factory {
222233
private final ComponentElement component;
234+
private final MockBuilder.Factory mockBuilderFactory;
223235

224236
@Inject
225-
public Factory(ComponentElement component) {
237+
public Factory(ComponentElement component, MockBuilder.Factory mockBuilderFactory) {
226238
this.component = component;
239+
this.mockBuilderFactory = mockBuilderFactory;
227240
}
228241

229242
ComponentImpl create(
230243
Map<Key, NamedBinding> sorted,
231244
Function<Key, ParameterSpec> names) {
232-
return new ComponentImpl(component, sorted, names);
245+
return new ComponentImpl(
246+
component,
247+
sorted,
248+
names,
249+
mockBuilderFactory.create(sorted, names));
233250
}
234251
}
235252
}

compiler/src/main/java/io/jbock/simple/processor/writing/Generator.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,12 @@ public TypeSpec generate(List<Binding> bindings) {
3535

3636
private Map<Key, NamedBinding> addNames(List<Binding> bindings) {
3737
UniqueNameSet uniqueNameSet = new UniqueNameSet();
38+
uniqueNameSet.claim("build");
3839
Map<Key, NamedBinding> result = new LinkedHashMap<>();
3940
for (Binding b : bindings) {
4041
String name = uniqueNameSet.getUniqueName(validJavaName(b.suggestedVariableName()));
41-
result.put(b.key(), new NamedBinding(b, name, component.isComponentRequest(b)));
42+
String auxName = uniqueNameSet.getUniqueName(name + "_isSet");
43+
result.put(b.key(), new NamedBinding(b, name, auxName, component.isComponentRequest(b)));
4244
}
4345
return result;
4446
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package io.jbock.simple.processor.writing;
2+
3+
import io.jbock.javapoet.ClassName;
4+
import io.jbock.javapoet.CodeBlock;
5+
import io.jbock.javapoet.FieldSpec;
6+
import io.jbock.javapoet.MethodSpec;
7+
import io.jbock.javapoet.ParameterSpec;
8+
import io.jbock.javapoet.TypeName;
9+
import io.jbock.javapoet.TypeSpec;
10+
import io.jbock.simple.Inject;
11+
import io.jbock.simple.processor.binding.Binding;
12+
import io.jbock.simple.processor.binding.ComponentElement;
13+
import io.jbock.simple.processor.binding.Key;
14+
import io.jbock.simple.processor.binding.ParameterBinding;
15+
16+
import javax.lang.model.element.Modifier;
17+
import java.util.ArrayList;
18+
import java.util.List;
19+
import java.util.Map;
20+
import java.util.function.Function;
21+
22+
import static javax.lang.model.element.Modifier.FINAL;
23+
import static javax.lang.model.element.Modifier.PRIVATE;
24+
import static javax.lang.model.element.Modifier.PUBLIC;
25+
import static javax.lang.model.element.Modifier.STATIC;
26+
27+
public class MockBuilder {
28+
29+
private final ComponentElement component;
30+
private final Map<Key, NamedBinding> sorted;
31+
private final Function<Key, ParameterSpec> names;
32+
private final Modifier[] modifiers;
33+
34+
MockBuilder(
35+
ComponentElement component,
36+
Map<Key, NamedBinding> sorted,
37+
Function<Key, ParameterSpec> names) {
38+
this.component = component;
39+
this.sorted = sorted;
40+
this.names = names;
41+
this.modifiers = component.element().getModifiers().stream()
42+
.filter(m -> m == PUBLIC).toArray(Modifier[]::new);
43+
}
44+
45+
TypeSpec generate() {
46+
TypeSpec.Builder spec = TypeSpec.classBuilder(getClassName())
47+
.addModifiers(modifiers)
48+
.addModifiers(STATIC, FINAL);
49+
spec.addMethod(buildMethod());
50+
spec.addFields(getFields());
51+
spec.addMethods(getMethods());
52+
return spec.build();
53+
}
54+
55+
ClassName getClassName() {
56+
return component.generatedClass().nestedClass("MockBuilder");
57+
}
58+
59+
private MethodSpec buildMethod() {
60+
List<CodeBlock> constructorParameters = new ArrayList<>();
61+
MethodSpec.Builder method = MethodSpec.methodBuilder("build");
62+
for (NamedBinding namedBinding : sorted.values()) {
63+
Binding b = namedBinding.binding();
64+
Key key = b.key();
65+
CodeBlock invocation = b.invocation(names);
66+
ParameterSpec param = names.apply(key);
67+
if (namedBinding.isComponentRequest()) {
68+
constructorParameters.add(CodeBlock.of("$N", names.apply(key)));
69+
}
70+
if (namedBinding.binding() instanceof ParameterBinding) {
71+
method.addParameter(names.apply(b.key()));
72+
} else if (!key.typeName().isPrimitive()) {
73+
method.addStatement("$1T $2N = this.$2N != null ? this.$2N : $3L", key.typeName(), param, invocation);
74+
} else {
75+
FieldSpec auxField = FieldSpec.builder(TypeName.BOOLEAN, namedBinding.auxName(), PRIVATE).build();
76+
method.addStatement("$1T $2N = this.$3N ? this.$2N : $4L", key.typeName(), param, auxField, invocation);
77+
}
78+
}
79+
return method
80+
.addModifiers(modifiers)
81+
.returns(TypeName.get(component.element().asType()))
82+
.addStatement("return new $T($L)",
83+
component.generatedClass(),
84+
constructorParameters.stream().collect(CodeBlock.joining(", ")))
85+
.build();
86+
}
87+
88+
private List<FieldSpec> getFields() {
89+
List<FieldSpec> fields = new ArrayList<>();
90+
for (NamedBinding namedBinding : sorted.values()) {
91+
if (namedBinding.binding() instanceof ParameterBinding) {
92+
continue;
93+
}
94+
TypeName type = namedBinding.binding().key().typeName();
95+
FieldSpec field = FieldSpec.builder(type, namedBinding.name(), PRIVATE).build();
96+
fields.add(field);
97+
if (namedBinding.binding().key().typeName().isPrimitive()) {
98+
FieldSpec auxField = FieldSpec.builder(TypeName.BOOLEAN, namedBinding.auxName(), PRIVATE).build();
99+
fields.add(auxField);
100+
}
101+
}
102+
return fields;
103+
}
104+
105+
private List<MethodSpec> getMethods() {
106+
List<MethodSpec> methods = new ArrayList<>();
107+
for (NamedBinding namedBinding : sorted.values()) {
108+
if (namedBinding.binding() instanceof ParameterBinding) {
109+
continue;
110+
}
111+
Binding b = namedBinding.binding();
112+
Key key = b.key();
113+
ParameterSpec param = names.apply(key);
114+
MethodSpec.Builder method = MethodSpec.methodBuilder(param.name)
115+
.addModifiers(modifiers)
116+
.addParameter(param)
117+
.addStatement("this.$1N = $1N", param);
118+
if (namedBinding.binding().key().typeName().isPrimitive()) {
119+
FieldSpec auxField = FieldSpec.builder(TypeName.BOOLEAN, namedBinding.auxName(), PRIVATE).build();
120+
method.addStatement("this.$N = $L", auxField, true);
121+
}
122+
methods.add(method.build());
123+
}
124+
return methods;
125+
}
126+
127+
public static final class Factory {
128+
private final ComponentElement component;
129+
130+
@Inject
131+
public Factory(ComponentElement component) {
132+
this.component = component;
133+
}
134+
135+
MockBuilder create(
136+
Map<Key, NamedBinding> sorted,
137+
Function<Key, ParameterSpec> names) {
138+
return new MockBuilder(component, sorted, names);
139+
}
140+
}
141+
}

0 commit comments

Comments
 (0)