Skip to content

Include event type in visitor name #2091

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ public static CodegenCustomizationProcessor getProcessorFor(
new ShapeModifiersProcessor(config.getShapeModifiers()),
new ShapeSubstitutionsProcessor(config.getShapeSubstitutions()),
new OperationModifiersProcessor(config.getOperationModifiers()),
new RemoveExceptionMessagePropertyProcessor()
new RemoveExceptionMessagePropertyProcessor(),
new ExcludeEventNameFromVisitMethodProcessor()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.codegen.customization.processors;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.codegen.customization.CodegenCustomizationProcessor;
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
import software.amazon.awssdk.codegen.model.intermediate.ShapeModel;
import software.amazon.awssdk.codegen.model.service.ServiceModel;

/**
* This process enforces constraints placed on the "excludeEventNameFromVisitMethod"; i.e. that no two members
* of the same event stream sharing the same shape have this customization enabled for them. This processor does not
* modify the the service or intermediate model.
*/
public class ExcludeEventNameFromVisitMethodProcessor implements CodegenCustomizationProcessor {
private static final String CUSTOMIZATION_NAME = "ExcludeEventNameFromVisitMethod";
private static final Logger log = LoggerFactory.getLogger(ExcludeEventNameFromVisitMethodProcessor.class);

@Override
public void preprocess(ServiceModel serviceModel) {
// no-op
}

@Override
public void postprocess(IntermediateModel intermediateModel) {
Map<String, List<String>> excludeEventNameFromVisitMethod = intermediateModel.getCustomizationConfig()
.getExcludeEventNameFromVisitMethod();

excludeEventNameFromVisitMethod.forEach((eventStream, members) -> {
ShapeModel shapeModel = getShapeByC2jName(intermediateModel, eventStream);

if (shapeModel == null || !shapeModel.isEventStream()) {
log.warn("Encountered " + CUSTOMIZATION_NAME + " for unrecognized eventstream " + eventStream);
return;
}

Map<String, Integer> shapeToEventCount = new HashMap<>();

members.forEach(m -> {
MemberModel event = shapeModel.getMemberByC2jName(m);

if (event != null) {
String shapeName = event.getC2jShape();
int count = shapeToEventCount.getOrDefault(shapeName, 0);
shapeToEventCount.put(shapeName, ++count);
} else {
String msg = String.format("Encountered %s customization for unrecognized eventstream member %s#%s",
CUSTOMIZATION_NAME, eventStream, m);
log.warn(msg);
}
});

shapeToEventCount.forEach((shape, count) -> {
if (count > 1) {
throw new IllegalArgumentException(CUSTOMIZATION_NAME + " customization declared for "
+ eventStream + ", but more than it targets more than one member with the shape " + shape);
}
});
});
}

private ShapeModel getShapeByC2jName(IntermediateModel intermediateModel, String c2jName) {
return intermediateModel.getShapes().values().stream()
.filter(s -> s.getC2jName().equals(c2jName))
.findAny()
.orElse(null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,16 @@ public class CustomizationConfig {
*/
private boolean allowEndpointOverrideForEndpointDiscoveryRequiredOperations = false;

/**
* Customization to instruct the code generator *not* to include the event's name in the {@code Visitor#visit*()}
* method; i.e. the signature of the method will be {@code void visit(<Event Shape type>)}.
* <p>
* <b>NOTE</b>This customization is primarily here to preserve backwards compatibility with existing code before the
* generation scheme for the visitor methods was changed. There should be no good reason to use this customization
* for any other purpose.
*/
private Map<String, List<String>> excludeEventNameFromVisitMethod = new HashMap<>();

private CustomizationConfig() {
}

Expand Down Expand Up @@ -454,4 +464,12 @@ public void setAllowEndpointOverrideForEndpointDiscoveryRequiredOperations(
this.allowEndpointOverrideForEndpointDiscoveryRequiredOperations =
allowEndpointOverrideForEndpointDiscoveryRequiredOperations;
}

public Map<String, List<String>> getExcludeEventNameFromVisitMethod() {
return excludeEventNameFromVisitMethod;
}

public void setExcludeEventNameFromVisitMethod(Map<String, List<String>> excludeEventNameFromVisitMethod) {
this.excludeEventNameFromVisitMethod = excludeEventNameFromVisitMethod;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.awscore.eventstream.EventStreamResponseHandler;
import software.amazon.awssdk.codegen.emitters.GeneratorTaskParams;
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
import software.amazon.awssdk.codegen.model.intermediate.OperationModel;
import software.amazon.awssdk.codegen.poet.ClassSpec;
import software.amazon.awssdk.codegen.poet.PoetExtensions;
Expand All @@ -33,6 +34,7 @@
*/
public class EventStreamResponseHandlerSpec implements ClassSpec {

private final IntermediateModel intermediateModel;
private final PoetExtensions poetExt;
private final OperationModel operationModel;
private final String apiName;
Expand All @@ -41,6 +43,7 @@ public class EventStreamResponseHandlerSpec implements ClassSpec {
private final ClassName eventStreamBaseClass;

public EventStreamResponseHandlerSpec(GeneratorTaskParams params, OperationModel operationModel) {
this.intermediateModel = params.getModel();
this.poetExt = params.getPoetExtensions();
this.operationModel = operationModel;
this.apiName = poetExt.getApiName(operationModel);
Expand All @@ -61,7 +64,7 @@ public TypeSpec poetSpec() {
.addJavadoc("Response handler for the $L API.", apiName)
.addMethod(builderMethodSpec())
.addType(new EventStreamResponseHandlerBuilderInterfaceSpec(poetExt, operationModel).poetSpec())
.addType(new EventStreamVisitorInterfaceSpec(poetExt, operationModel).poetSpec())
.addType(new EventStreamVisitorInterfaceSpec(intermediateModel, poetExt, operationModel).poetSpec())
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,11 @@ public static Optional<ShapeModel> getBaseEventStreamShape(IntermediateModel mod
}

/**
* Returns the stream of event member shapes ('event: true') excluding exceptions
* Returns the stream of event members ('event: true') excluding exceptions
* from the input event stream shape ('eventstream: true').
*/
public static Stream<ShapeModel> getEvents(ShapeModel eventStreamShape) {
return getEventMembers(eventStreamShape).map(MemberModel::getShape);
public static Stream<MemberModel> getEvents(ShapeModel eventStreamShape) {
return getEventMembers(eventStreamShape);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,17 @@
import javax.lang.model.element.Modifier;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.codegen.emitters.GeneratorTaskParams;
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
import software.amazon.awssdk.codegen.model.intermediate.OperationModel;
import software.amazon.awssdk.codegen.model.intermediate.ShapeModel;
import software.amazon.awssdk.codegen.poet.PoetExtensions;
import software.amazon.awssdk.codegen.poet.PoetUtils;

/**
* Generates the implementation for the builder of an event stream visitor.
*/
public class EventStreamVisitorBuilderImplSpec extends EventStreamVisitorBuilderInterfaceSpec {

private final IntermediateModel intermediateModel;
private final PoetExtensions poetExt;
private final OperationModel opModel;
private final ClassName visitorType;
Expand All @@ -43,6 +44,7 @@ public class EventStreamVisitorBuilderImplSpec extends EventStreamVisitorBuilder

public EventStreamVisitorBuilderImplSpec(GeneratorTaskParams params, OperationModel operationModel) {
super(params.getPoetExtensions(), operationModel);
this.intermediateModel = params.getModel();
this.poetExt = params.getPoetExtensions();
this.opModel = operationModel;
this.visitorType = poetExt.eventStreamResponseHandlerVisitorType(opModel);
Expand All @@ -68,7 +70,7 @@ private class VisitorFromBuilderImplSpec extends EventStreamVisitorInterfaceSpec
private final MethodSpec.Builder constrBuilder;

VisitorFromBuilderImplSpec() {
super(poetExt, opModel);
super(intermediateModel, poetExt, opModel);
this.constrBuilder = MethodSpec.constructorBuilder()
.addParameter(enclosingClassName(), "builder")
.addStatement("this.onDefault = builder.onDefault != null ?\n"
Expand Down Expand Up @@ -98,10 +100,10 @@ protected MethodSpec.Builder applyVisitDefaultMethodSpecUpdates(MethodSpec.Build
@Override
protected MethodSpec.Builder applyVisitSubTypeMethodSpecUpdates(TypeSpec.Builder typeBuilder,
MethodSpec.Builder methodBuilder,
ShapeModel s) {
ClassName eventSubType = poetExt.getModelClass(s.getShapeName());
MemberModel event) {
ClassName eventSubType = poetExt.getModelClass(event.getShape().getShapeName());
TypeName eventConsumerType = consumerType(eventSubType);
FieldSpec consumerField = FieldSpec.builder(eventConsumerType, "on" + eventSubType.simpleName())
FieldSpec consumerField = FieldSpec.builder(eventConsumerType, eventConsumerName(event))
.addModifiers(Modifier.PRIVATE, Modifier.FINAL)
.build();
typeBuilder.addField(consumerField);
Expand All @@ -127,10 +129,10 @@ private ClassName enclosingClassName() {
@Override
protected MethodSpec.Builder applyOnSubTypeMethodSpecUpdates(TypeSpec.Builder typeBuilder,
MethodSpec.Builder methodBuilder,
ShapeModel eventSubTypeShape) {
ClassName eventSubType = poetExt.getModelClass(eventSubTypeShape.getShapeName());
MemberModel event) {
ClassName eventSubType = poetExt.getModelClass(event.getShape().getShapeName());
TypeName eventConsumerType = consumerType(eventSubType);
FieldSpec consumerField = FieldSpec.builder(eventConsumerType, "on" + eventSubType.simpleName())
FieldSpec consumerField = FieldSpec.builder(eventConsumerType, eventConsumerName(event))
.addModifiers(Modifier.PRIVATE)
.build();
typeBuilder.addField(consumerField);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@
import java.util.function.Consumer;
import javax.lang.model.element.Modifier;
import software.amazon.awssdk.codegen.docs.DocumentationBuilder;
import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
import software.amazon.awssdk.codegen.model.intermediate.OperationModel;
import software.amazon.awssdk.codegen.model.intermediate.ShapeModel;
import software.amazon.awssdk.codegen.poet.ClassSpec;
import software.amazon.awssdk.codegen.poet.PoetExtensions;
import software.amazon.awssdk.codegen.poet.PoetUtils;
import software.amazon.awssdk.utils.internal.CodegenNamingUtils;

/**
* Spec for builder interface for visitor.
Expand All @@ -52,8 +54,8 @@ public final TypeSpec poetSpec() {
.addMethod(applyBuildMethodSpecUpdates(createBuildMethodSpec()).build());

EventStreamUtils.getEvents(eventStreamShape)
.forEach(s -> typeBuilder.addMethod(
applyOnSubTypeMethodSpecUpdates(typeBuilder, createOnSubTypeMethodSpec(s), s)
.forEach(e -> typeBuilder.addMethod(
applyOnSubTypeMethodSpecUpdates(typeBuilder, createOnSubTypeMethodSpec(e), e)
.build()));

return typeBuilder.build();
Expand Down Expand Up @@ -105,8 +107,8 @@ protected MethodSpec.Builder applyBuildMethodSpecUpdates(MethodSpec.Builder buil

protected MethodSpec.Builder applyOnSubTypeMethodSpecUpdates(TypeSpec.Builder typeBuilder,
MethodSpec.Builder methodBuilder,
ShapeModel eventSubTypeShape) {
ClassName eventSubType = poetExt.getModelClass(eventSubTypeShape.getShapeName());
MemberModel event) {
ClassName eventSubType = poetExt.getModelClass(event.getShape().getShapeName());
String javadocs = new DocumentationBuilder()
.description("Callback to invoke when a {@link $T} is visited.")
.param("c", "Callback to process the event.")
Expand All @@ -116,13 +118,16 @@ protected MethodSpec.Builder applyOnSubTypeMethodSpecUpdates(TypeSpec.Builder ty
.addJavadoc(javadocs, eventSubType);
}

private MethodSpec.Builder createOnSubTypeMethodSpec(ShapeModel eventSubTypeShape) {
ClassName eventSubType = poetExt.getModelClass(eventSubTypeShape.getShapeName());
private MethodSpec.Builder createOnSubTypeMethodSpec(MemberModel event) {
ClassName eventSubType = poetExt.getModelClass(event.getShape().getShapeName());
ParameterizedTypeName eventConsumerType = ParameterizedTypeName.get(ClassName.get(Consumer.class), eventSubType);
return MethodSpec.methodBuilder("on" + eventSubType.simpleName())
return MethodSpec.methodBuilder(eventConsumerName(event))
.addModifiers(Modifier.PUBLIC)
.addParameter(ParameterSpec.builder(eventConsumerType, "c").build())
.returns(visitorBuilderType);
}

protected final String eventConsumerName(MemberModel event) {
return "on" + CodegenNamingUtils.pascalCase(event.getName());
}
}
Loading