Skip to content

Commit f636b6d

Browse files
committed
JAVA-2272: Make DAO method return types pluggable
Motivation: We want to allow some degree of extensibility in the mapper. In particular, it would be nice if a custom mapper extension could handle new result types for DAO methods (e.g. `@Select`), without having to rewrite the whole method generator. Modifications: Extract a superinterface DaoReturnTypeKind from the current enum. Introduce a pluggable DaoReturnTypeParser in CodeGeneratorFactory. Modify DAO method generators, so that the set of supported return type kinds can be customized by overriding a method. Result: To support new return types, downstream projects will: - write their own implementation of DaoReturnTypeKind (most likely as another enum) - write their own DaoReturnTypeParser implementation - extend the default method generators to override getSupportedReturnTypes() - write their own CodeGeneratorFactory to return all those custom elements.
1 parent 984d64f commit f636b6d

24 files changed

Lines changed: 756 additions & 427 deletions

mapper-processor/src/main/java/com/datastax/oss/driver/internal/mapper/processor/CodeGeneratorFactory.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.datastax.oss.driver.api.mapper.annotations.Entity;
2020
import com.datastax.oss.driver.api.mapper.annotations.Mapper;
2121
import com.datastax.oss.driver.internal.mapper.processor.dao.DaoImplementationSharedCode;
22+
import com.datastax.oss.driver.internal.mapper.processor.dao.DaoReturnTypeParser;
2223
import com.datastax.oss.driver.internal.mapper.processor.mapper.MapperImplementationSharedCode;
2324
import java.util.Map;
2425
import java.util.Optional;
@@ -80,4 +81,6 @@ Optional<MethodGenerator> newDaoImplementationMethod(
8081
ExecutableElement methodElement,
8182
Map<Name, TypeElement> typeParameters,
8283
DaoImplementationSharedCode enclosingClass);
84+
85+
DaoReturnTypeParser getDaoReturnTypeParser();
8386
}

mapper-processor/src/main/java/com/datastax/oss/driver/internal/mapper/processor/DefaultCodeGeneratorFactory.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,11 @@
3131
import com.datastax.oss.driver.internal.mapper.processor.dao.DaoInsertMethodGenerator;
3232
import com.datastax.oss.driver.internal.mapper.processor.dao.DaoQueryMethodGenerator;
3333
import com.datastax.oss.driver.internal.mapper.processor.dao.DaoQueryProviderMethodGenerator;
34+
import com.datastax.oss.driver.internal.mapper.processor.dao.DaoReturnTypeParser;
3435
import com.datastax.oss.driver.internal.mapper.processor.dao.DaoSelectMethodGenerator;
3536
import com.datastax.oss.driver.internal.mapper.processor.dao.DaoSetEntityMethodGenerator;
3637
import com.datastax.oss.driver.internal.mapper.processor.dao.DaoUpdateMethodGenerator;
38+
import com.datastax.oss.driver.internal.mapper.processor.dao.DefaultDaoReturnTypeParser;
3739
import com.datastax.oss.driver.internal.mapper.processor.entity.EntityHelperGenerator;
3840
import com.datastax.oss.driver.internal.mapper.processor.mapper.MapperBuilderGenerator;
3941
import com.datastax.oss.driver.internal.mapper.processor.mapper.MapperDaoFactoryMethodGenerator;
@@ -49,9 +51,11 @@
4951
public class DefaultCodeGeneratorFactory implements CodeGeneratorFactory {
5052

5153
private final ProcessorContext context;
54+
private final DaoReturnTypeParser daoReturnTypeParser;
5255

5356
public DefaultCodeGeneratorFactory(ProcessorContext context) {
5457
this.context = context;
58+
this.daoReturnTypeParser = new DefaultDaoReturnTypeParser(context);
5559
}
5660

5761
@Override
@@ -124,4 +128,9 @@ public Optional<MethodGenerator> newDaoImplementationMethod(
124128
return Optional.empty();
125129
}
126130
}
131+
132+
@Override
133+
public DaoReturnTypeParser getDaoReturnTypeParser() {
134+
return daoReturnTypeParser;
135+
}
127136
}

mapper-processor/src/main/java/com/datastax/oss/driver/internal/mapper/processor/dao/DaoDeleteMethodGenerator.java

Lines changed: 31 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,30 @@
1515
*/
1616
package com.datastax.oss.driver.internal.mapper.processor.dao;
1717

18-
import static com.datastax.oss.driver.internal.mapper.processor.dao.ReturnTypeKind.BOOLEAN;
19-
import static com.datastax.oss.driver.internal.mapper.processor.dao.ReturnTypeKind.FUTURE_OF_ASYNC_RESULT_SET;
20-
import static com.datastax.oss.driver.internal.mapper.processor.dao.ReturnTypeKind.FUTURE_OF_BOOLEAN;
21-
import static com.datastax.oss.driver.internal.mapper.processor.dao.ReturnTypeKind.FUTURE_OF_VOID;
22-
import static com.datastax.oss.driver.internal.mapper.processor.dao.ReturnTypeKind.RESULT_SET;
23-
import static com.datastax.oss.driver.internal.mapper.processor.dao.ReturnTypeKind.VOID;
18+
import static com.datastax.oss.driver.internal.mapper.processor.dao.DefaultDaoReturnTypeKind.BOOLEAN;
19+
import static com.datastax.oss.driver.internal.mapper.processor.dao.DefaultDaoReturnTypeKind.FUTURE_OF_ASYNC_RESULT_SET;
20+
import static com.datastax.oss.driver.internal.mapper.processor.dao.DefaultDaoReturnTypeKind.FUTURE_OF_BOOLEAN;
21+
import static com.datastax.oss.driver.internal.mapper.processor.dao.DefaultDaoReturnTypeKind.FUTURE_OF_VOID;
22+
import static com.datastax.oss.driver.internal.mapper.processor.dao.DefaultDaoReturnTypeKind.RESULT_SET;
23+
import static com.datastax.oss.driver.internal.mapper.processor.dao.DefaultDaoReturnTypeKind.VOID;
2424

2525
import com.datastax.oss.driver.api.core.cql.BoundStatement;
2626
import com.datastax.oss.driver.api.core.cql.BoundStatementBuilder;
2727
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
2828
import com.datastax.oss.driver.api.mapper.annotations.Delete;
29-
import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures;
3029
import com.datastax.oss.driver.internal.mapper.processor.ProcessorContext;
3130
import com.datastax.oss.driver.internal.mapper.processor.entity.EntityDefinition;
3231
import com.datastax.oss.driver.internal.mapper.processor.entity.PropertyDefinition;
3332
import com.datastax.oss.driver.internal.mapper.processor.util.generation.GeneratedCodePatterns;
33+
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableSet;
3434
import com.squareup.javapoet.ClassName;
3535
import com.squareup.javapoet.CodeBlock;
3636
import com.squareup.javapoet.MethodSpec;
3737
import com.squareup.javapoet.TypeName;
38-
import java.util.EnumSet;
3938
import java.util.List;
4039
import java.util.Map;
4140
import java.util.Optional;
41+
import java.util.Set;
4242
import java.util.stream.Collectors;
4343
import javax.lang.model.element.AnnotationMirror;
4444
import javax.lang.model.element.AnnotationValue;
@@ -50,10 +50,6 @@
5050

5151
public class DaoDeleteMethodGenerator extends DaoMethodGenerator {
5252

53-
private static final EnumSet<ReturnTypeKind> SUPPORTED_RETURN_TYPES =
54-
EnumSet.of(
55-
VOID, FUTURE_OF_VOID, BOOLEAN, FUTURE_OF_BOOLEAN, RESULT_SET, FUTURE_OF_ASYNC_RESULT_SET);
56-
5753
public DaoDeleteMethodGenerator(
5854
ExecutableElement methodElement,
5955
Map<Name, TypeElement> typeParameters,
@@ -62,6 +58,11 @@ public DaoDeleteMethodGenerator(
6258
super(methodElement, typeParameters, enclosingClass, context);
6359
}
6460

61+
protected Set<DaoReturnTypeKind> getSupportedReturnTypes() {
62+
return ImmutableSet.of(
63+
VOID, FUTURE_OF_VOID, BOOLEAN, FUTURE_OF_BOOLEAN, RESULT_SET, FUTURE_OF_ASYNC_RESULT_SET);
64+
}
65+
6566
@Override
6667
public Optional<MethodSpec> generate() {
6768

@@ -99,7 +100,7 @@ public Optional<MethodSpec> generate() {
99100
return Optional.empty();
100101
}
101102
VariableElement firstParameter = parameters.get(0);
102-
entityElement = asEntityElement(firstParameter);
103+
entityElement = EntityUtils.asEntityElement(firstParameter, typeParameters);
103104
hasEntityParameter = (entityElement != null);
104105
if (hasEntityParameter) {
105106
entityDefinition = context.getEntityFactory().getDefinition(entityElement);
@@ -142,15 +143,9 @@ public Optional<MethodSpec> generate() {
142143
}
143144

144145
// Validate the return type:
145-
ReturnType returnType = parseReturnType(methodElement.getReturnType());
146-
if (!SUPPORTED_RETURN_TYPES.contains(returnType.kind)) {
147-
context
148-
.getMessager()
149-
.error(
150-
methodElement,
151-
"Invalid return type: %s methods must return void, boolean or a result set, "
152-
+ "or a CompletableFuture/CompletionStage of Void, Boolean or AsyncResultSet",
153-
Delete.class.getSimpleName());
146+
DaoReturnType returnType =
147+
parseAndValidateReturnType(getSupportedReturnTypes(), Delete.class.getSimpleName());
148+
if (returnType == null) {
154149
return Optional.empty();
155150
}
156151

@@ -162,18 +157,14 @@ public Optional<MethodSpec> generate() {
162157
(methodBuilder, requestName) ->
163158
generatePrepareRequest(methodBuilder, requestName, helperFieldName));
164159

165-
MethodSpec.Builder deleteBuilder =
166-
GeneratedCodePatterns.override(methodElement, typeParameters);
167-
if (returnType.kind.isAsync) {
168-
deleteBuilder.beginControlFlow("try");
169-
}
160+
CodeBlock.Builder methodBodyBuilder = CodeBlock.builder();
170161

171-
deleteBuilder.addStatement(
162+
methodBodyBuilder.addStatement(
172163
"$T boundStatementBuilder = $L.boundStatementBuilder()",
173164
BoundStatementBuilder.class,
174165
statementName);
175166
if (statementAttributeParam != null) {
176-
deleteBuilder.addStatement(
167+
methodBodyBuilder.addStatement(
177168
"boundStatementBuilder = populateBoundStatementWithAttributes(boundStatementBuilder, $L)",
178169
statementAttributeParam.getSimpleName().toString());
179170
}
@@ -186,7 +177,7 @@ public Optional<MethodSpec> generate() {
186177
property.getType(),
187178
CodeBlock.of("$L.$L()", firstParameter.getSimpleName(), property.getGetterName()),
188179
"boundStatementBuilder",
189-
deleteBuilder,
180+
methodBodyBuilder,
190181
enclosingClass);
191182
}
192183
nextParameterIndex = 1;
@@ -202,7 +193,7 @@ public Optional<MethodSpec> generate() {
202193
GeneratedCodePatterns.bindParameters(
203194
parameters.subList(0, primaryKeyNames.size()),
204195
primaryKeyNames,
205-
deleteBuilder,
196+
methodBodyBuilder,
206197
enclosingClass,
207198
context,
208199
false);
@@ -222,25 +213,22 @@ public Optional<MethodSpec> generate() {
222213
}
223214
GeneratedCodePatterns.bindParameters(
224215
parameters.subList(nextParameterIndex, parameters.size()),
225-
deleteBuilder,
216+
methodBodyBuilder,
226217
enclosingClass,
227218
context,
228219
false);
229220
}
230221

231-
deleteBuilder
232-
.addCode("\n")
222+
methodBodyBuilder
223+
.add("\n")
233224
.addStatement("$T boundStatement = boundStatementBuilder.build()", BoundStatement.class);
234225

235-
returnType.kind.addExecuteStatement(deleteBuilder, helperFieldName);
226+
returnType.getKind().addExecuteStatement(methodBodyBuilder, helperFieldName);
236227

237-
if (returnType.kind.isAsync) {
238-
deleteBuilder
239-
.nextControlFlow("catch ($T t)", Throwable.class)
240-
.addStatement("return $T.failedFuture(t)", CompletableFutures.class)
241-
.endControlFlow();
242-
}
243-
return Optional.of(deleteBuilder.build());
228+
CodeBlock methodBody = returnType.getKind().wrapWithErrorHandling(methodBodyBuilder.build());
229+
230+
return Optional.of(
231+
GeneratedCodePatterns.override(methodElement, typeParameters).addCode(methodBody).build());
244232
}
245233

246234
private TypeElement getEntityFromAnnotation() {
@@ -265,7 +253,7 @@ private TypeElement getEntityFromAnnotation() {
265253
return null;
266254
}
267255
TypeMirror mirror = (TypeMirror) values.get(0).getValue();
268-
TypeElement element = asEntityElement(mirror);
256+
TypeElement element = EntityUtils.asEntityElement(mirror, typeParameters);
269257
if (values.size() > 1) {
270258
context
271259
.getMessager()

mapper-processor/src/main/java/com/datastax/oss/driver/internal/mapper/processor/dao/DaoGetEntityMethodGenerator.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public Optional<MethodSpec> generate() {
9494
// Validate the return type. Make sure it matches the parameter type
9595
Transformation transformation = null;
9696
TypeMirror returnType = methodElement.getReturnType();
97-
TypeElement entityElement = asEntityElement(returnType);
97+
TypeElement entityElement = EntityUtils.asEntityElement(returnType, typeParameters);
9898
if (entityElement != null) {
9999
transformation = parameterIsGettable ? Transformation.NONE : Transformation.ONE;
100100
} else if (returnType.getKind() == TypeKind.DECLARED) {
@@ -111,7 +111,7 @@ public Optional<MethodSpec> generate() {
111111
ResultSet.class.getSimpleName());
112112
return Optional.empty();
113113
}
114-
entityElement = typeArgumentAsEntityElement(returnType);
114+
entityElement = EntityUtils.typeArgumentAsEntityElement(returnType, typeParameters);
115115
transformation = Transformation.MAP;
116116
} else if (context.getClassUtils().isSame(element, MappedAsyncPagingIterable.class)) {
117117
if (!parameterIsAsyncResultSet) {
@@ -125,7 +125,7 @@ public Optional<MethodSpec> generate() {
125125
AsyncResultSet.class.getSimpleName());
126126
return Optional.empty();
127127
}
128-
entityElement = typeArgumentAsEntityElement(returnType);
128+
entityElement = EntityUtils.typeArgumentAsEntityElement(returnType, typeParameters);
129129
transformation = Transformation.MAP;
130130
}
131131
}

0 commit comments

Comments
 (0)