
software.amazon.awssdk.codegen.AddShapes Maven / Gradle / Ivy
/*
* Copyright 2010-2019 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;
import static software.amazon.awssdk.codegen.internal.TypeUtils.getDataTypeMapping;
import static software.amazon.awssdk.codegen.internal.Utils.capitalize;
import static software.amazon.awssdk.codegen.internal.Utils.isListShape;
import static software.amazon.awssdk.codegen.internal.Utils.isMapShape;
import static software.amazon.awssdk.codegen.internal.Utils.isScalar;
import java.util.List;
import java.util.Map;
import software.amazon.awssdk.codegen.internal.TypeUtils;
import software.amazon.awssdk.codegen.model.config.customization.CustomizationConfig;
import software.amazon.awssdk.codegen.model.intermediate.EnumModel;
import software.amazon.awssdk.codegen.model.intermediate.ListModel;
import software.amazon.awssdk.codegen.model.intermediate.MapModel;
import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
import software.amazon.awssdk.codegen.model.intermediate.ParameterHttpMapping;
import software.amazon.awssdk.codegen.model.intermediate.Protocol;
import software.amazon.awssdk.codegen.model.intermediate.ReturnTypeModel;
import software.amazon.awssdk.codegen.model.intermediate.ShapeModel;
import software.amazon.awssdk.codegen.model.intermediate.VariableModel;
import software.amazon.awssdk.codegen.model.service.Location;
import software.amazon.awssdk.codegen.model.service.Member;
import software.amazon.awssdk.codegen.model.service.ServiceModel;
import software.amazon.awssdk.codegen.model.service.Shape;
import software.amazon.awssdk.codegen.naming.NamingStrategy;
import software.amazon.awssdk.utils.StringUtils;
import software.amazon.awssdk.utils.Validate;
abstract class AddShapes {
private final IntermediateModelBuilder builder;
private final NamingStrategy namingStrategy;
AddShapes(IntermediateModelBuilder builder) {
this.builder = builder;
this.namingStrategy = builder.getNamingStrategy();
}
protected final TypeUtils getTypeUtils() {
return builder.getTypeUtils();
}
protected final NamingStrategy getNamingStrategy() {
return namingStrategy;
}
protected final ServiceModel getServiceModel() {
return builder.getService();
}
protected final CustomizationConfig getCustomizationConfig() {
return builder.getCustomConfig();
}
protected final ShapeModel generateShapeModel(String javaClassName, String shapeName) {
ShapeModel shapeModel = new ShapeModel(shapeName);
shapeModel.setShapeName(javaClassName);
Shape shape = getServiceModel().getShapes().get(shapeName);
shapeModel.setDocumentation(shape.getDocumentation());
shapeModel.setVariable(new VariableModel(getNamingStrategy().getVariableName(javaClassName),
javaClassName));
// contains the list of c2j member names that are required for this shape.
shapeModel.setRequired(shape.getRequired());
shapeModel.setDeprecated(shape.isDeprecated());
shapeModel.setWrapper(shape.isWrapper());
shapeModel.withIsEventStream(shape.isEventStream());
shapeModel.withIsEvent(shape.isEvent());
boolean hasHeaderMember = false;
boolean hasStatusCodeMember = false;
boolean hasPayloadMember = false;
boolean hasStreamingMember = false;
Map members = shape.getMembers();
if (members != null) {
for (Map.Entry memberEntry : members.entrySet()) {
String c2jMemberName = memberEntry.getKey();
Member c2jMemberDefinition = memberEntry.getValue();
Shape parentShape = shape;
MemberModel memberModel = generateMemberModel(c2jMemberName, c2jMemberDefinition,
getProtocol(), parentShape,
getServiceModel().getShapes());
if (memberModel.getHttp().getLocation() == Location.HEADER) {
hasHeaderMember = true;
} else if (memberModel.getHttp().getLocation() == Location.STATUS_CODE) {
hasStatusCodeMember = true;
} else if (memberModel.getHttp().getIsPayload()) {
hasPayloadMember = true;
if (memberModel.getHttp().getIsStreaming()) {
hasStreamingMember = true;
}
}
shapeModel.addMember(memberModel);
}
shapeModel.withHasHeaderMember(hasHeaderMember)
.withHasStatusCodeMember(hasStatusCodeMember)
.withHasPayloadMember(hasPayloadMember)
.withHasStreamingMember(hasStreamingMember);
}
List enumValues = shape.getEnumValues();
if (enumValues != null && !enumValues.isEmpty()) {
for (String enumValue : enumValues) {
shapeModel.addEnum(
new EnumModel(getNamingStrategy().getEnumValueName(enumValue), enumValue));
}
}
return shapeModel;
}
private MemberModel generateMemberModel(String c2jMemberName, Member c2jMemberDefinition,
String protocol, Shape parentShape,
Map allC2jShapes) {
String c2jShapeName = c2jMemberDefinition.getShape();
Shape shape = allC2jShapes.get(c2jShapeName);
String variableName = getNamingStrategy().getVariableName(c2jMemberName);
String variableType = getTypeUtils().getJavaDataType(allC2jShapes, c2jShapeName);
String variableDeclarationType = getTypeUtils()
.getJavaDataType(allC2jShapes, c2jShapeName, getCustomizationConfig());
//If member is idempotent, then it should be of string type
//Else throw IllegalArgumentException.
if (c2jMemberDefinition.isIdempotencyToken() &&
!variableType.equals(String.class.getSimpleName())) {
throw new IllegalArgumentException(c2jMemberName +
" is idempotent. It's shape should be string type but it is of " +
variableType + " type.");
}
MemberModel memberModel = new MemberModel();
memberModel.withC2jName(c2jMemberName)
.withC2jShape(c2jShapeName)
.withName(capitalize(c2jMemberName))
.withVariable(new VariableModel(variableName, variableType, variableDeclarationType)
.withDocumentation(c2jMemberDefinition.getDocumentation()))
.withSetterModel(new VariableModel(variableName, variableType, variableDeclarationType))
.withGetterModel(new ReturnTypeModel(variableType))
.withTimestampFormat(resolveTimestampFormat(c2jMemberDefinition, shape))
.withJsonValue(c2jMemberDefinition.getJsonValue());
memberModel.setDocumentation(c2jMemberDefinition.getDocumentation());
memberModel.setDeprecated(c2jMemberDefinition.isDeprecated());
memberModel.setSensitive(isSensitiveShapeOrContainer(c2jMemberDefinition, allC2jShapes));
memberModel
.withFluentGetterMethodName(namingStrategy.getFluentGetterMethodName(c2jMemberName, parentShape, shape))
.withFluentEnumGetterMethodName(namingStrategy.getFluentEnumGetterMethodName(c2jMemberName, parentShape, shape))
.withFluentSetterMethodName(namingStrategy.getFluentSetterMethodName(c2jMemberName, parentShape, shape))
.withFluentEnumSetterMethodName(namingStrategy.getFluentEnumSetterMethodName(c2jMemberName, parentShape, shape))
.withBeanStyleGetterMethodName(namingStrategy.getBeanStyleGetterMethodName(c2jMemberName, parentShape, shape))
.withBeanStyleSetterMethodName(namingStrategy.getBeanStyleSetterMethodName(c2jMemberName, parentShape, shape));
memberModel.setIdempotencyToken(c2jMemberDefinition.isIdempotencyToken());
memberModel.setEventPayload(c2jMemberDefinition.isEventPayload());
memberModel.setEventHeader(c2jMemberDefinition.isEventHeader());
memberModel.setEndpointDiscoveryId(c2jMemberDefinition.isEndpointDiscoveryId());
// Pass the xmlNameSpace from the member reference
if (c2jMemberDefinition.getXmlNamespace() != null) {
memberModel.setXmlNameSpaceUri(c2jMemberDefinition.getXmlNamespace().getUri());
}
// Additional member model metadata for list/map/enum types
fillContainerTypeMemberMetadata(allC2jShapes, c2jMemberDefinition.getShape(), memberModel,
protocol);
ParameterHttpMapping httpMapping = generateParameterHttpMapping(parentShape,
c2jMemberName,
c2jMemberDefinition,
protocol,
allC2jShapes);
String payload = parentShape.getPayload();
boolean shapeIsStreaming = shape.isStreaming();
boolean memberIsStreaming = c2jMemberDefinition.isStreaming();
boolean payloadIsStreaming = shapeIsStreaming || memberIsStreaming;
httpMapping.withPayload(payload != null && payload.equals(c2jMemberName))
.withStreaming(payloadIsStreaming);
memberModel.setHttp(httpMapping);
return memberModel;
}
private boolean isSensitiveShapeOrContainer(Member member, Map allC2jShapes) {
if (member == null) {
return false;
}
return member.isSensitive() ||
isSensitiveShapeOrContainer(allC2jShapes.get(member.getShape()), allC2jShapes);
}
private boolean isSensitiveShapeOrContainer(Shape c2jShape, Map allC2jShapes) {
if (c2jShape == null) {
return false;
}
return c2jShape.isSensitive() ||
isSensitiveShapeOrContainer(c2jShape.getListMember(), allC2jShapes) ||
isSensitiveShapeOrContainer(c2jShape.getMapKeyType(), allC2jShapes) ||
isSensitiveShapeOrContainer(c2jShape.getMapValueType(), allC2jShapes);
}
private String resolveTimestampFormat(Member c2jMemberDefinition, Shape c2jShape) {
return c2jMemberDefinition.getTimestampFormat() != null ?
c2jMemberDefinition.getTimestampFormat() : c2jShape.getTimestampFormat();
}
private ParameterHttpMapping generateParameterHttpMapping(Shape parentShape,
String memberName,
Member member,
String protocol,
Map allC2jShapes) {
ParameterHttpMapping mapping = new ParameterHttpMapping();
Shape memberShape = allC2jShapes.get(member.getShape());
mapping.withLocation(Location.forValue(member.getLocation()))
.withPayload(member.isPayload()).withStreaming(member.isStreaming())
.withFlattened(isFlattened(member, memberShape))
.withUnmarshallLocationName(deriveUnmarshallerLocationName(memberShape, memberName, member))
.withMarshallLocationName(
deriveMarshallerLocationName(memberShape, memberName, member, protocol))
.withIsGreedy(isGreedy(parentShape, allC2jShapes, mapping));
return mapping;
}
private boolean isFlattened(Member member, Shape memberShape) {
return member.isFlattened()
|| memberShape.isFlattened();
}
/**
* @param parentShape Shape containing the member in question.
* @param allC2jShapes All shapes in the service model.
* @param mapping Mapping being built.
* @return True if the member is bound to a greedy label, false otherwise.
*/
private boolean isGreedy(Shape parentShape, Map allC2jShapes, ParameterHttpMapping mapping) {
if (mapping.getLocation() == Location.URI) {
// If the location is URI we can assume the parent shape is an input shape.
String requestUri = findRequestUri(parentShape, allC2jShapes);
if (requestUri.contains(String.format("{%s+}", mapping.getMarshallLocationName()))) {
return true;
}
}
return false;
}
/**
* Given an input shape, finds the Request URI for the operation that input is referenced from.
*
* @param parentShape Input shape to find operation's request URI for.
* @param allC2jShapes All shapes in the service model.
* @return Request URI for operation.
* @throws RuntimeException If operation can't be found.
*/
private String findRequestUri(Shape parentShape, Map allC2jShapes) {
return builder.getService().getOperations().values().stream()
.filter(o -> o.getInput() != null)
.filter(o -> allC2jShapes.get(o.getInput().getShape()).equals(parentShape))
.map(o -> o.getHttp().getRequestUri())
.findFirst().orElseThrow(() -> new RuntimeException("Could not find request URI for input shape"));
}
private String deriveUnmarshallerLocationName(Shape memberShape, String memberName, Member member) {
String locationName;
if (memberShape.getListMember() != null && memberShape.isFlattened()) {
locationName = deriveLocationNameForListMember(memberShape, member);
} else {
locationName = member.getLocationName();
}
if (StringUtils.isNotBlank(locationName)) {
return locationName;
}
return memberName;
}
private String deriveMarshallerLocationName(Shape memberShape, String memberName, Member member, String protocol) {
String queryName = member.getQueryName();
if (StringUtils.isNotBlank(queryName)) {
return queryName;
}
String locationName;
if (Protocol.EC2.getValue().equalsIgnoreCase(protocol)) {
locationName = deriveLocationNameForEc2(member);
} else if (memberShape.getListMember() != null && memberShape.isFlattened()) {
locationName = deriveLocationNameForListMember(memberShape, member);
} else {
locationName = member.getLocationName();
}
if (StringUtils.isNotBlank(locationName)) {
return locationName;
}
return memberName;
}
private String deriveLocationNameForEc2(Member member) {
String locationName = member.getLocationName();
if (StringUtils.isNotBlank(locationName)) {
return StringUtils.upperCase(locationName.substring(0, 1)) +
locationName.substring(1);
}
return null;
}
private String deriveLocationNameForListMember(Shape memberShape, Member member) {
String locationName = memberShape.getListMember().getLocationName();
if (StringUtils.isNotBlank(locationName)) {
return locationName;
}
return member.getLocationName();
}
private void fillContainerTypeMemberMetadata(Map c2jShapes,
String memberC2jShapeName, MemberModel memberModel,
String protocol) {
Shape memberC2jShape = c2jShapes.get(memberC2jShapeName);
if (isListShape(memberC2jShape)) {
Member listMemberDefinition = memberC2jShape.getListMember();
String listMemberC2jShapeName = listMemberDefinition.getShape();
MemberModel listMemberModel = generateMemberModel("member", listMemberDefinition, protocol,
memberC2jShape, c2jShapes);
String listImpl = getDataTypeMapping(TypeUtils.TypeKey.LIST_DEFAULT_IMPL);
memberModel.setListModel(
new ListModel(getTypeUtils().getJavaDataType(c2jShapes, listMemberC2jShapeName),
memberC2jShape.getListMember().getLocationName(), listImpl,
getDataTypeMapping(TypeUtils.TypeKey.LIST_INTERFACE), listMemberModel));
} else if (isMapShape(memberC2jShape)) {
Member mapKeyMemberDefinition = memberC2jShape.getMapKeyType();
String mapKeyShapeName = mapKeyMemberDefinition.getShape();
Shape mapKeyShape = c2jShapes.get(mapKeyShapeName);
Member mapValueMemberDefinition = memberC2jShape.getMapValueType();
// Complex map keys are not supported.
Validate.isTrue(isScalar(mapKeyShape), "The key type of %s must be a scalar!", mapKeyShapeName);
MemberModel mapKeyModel = generateMemberModel("key", mapKeyMemberDefinition, protocol,
memberC2jShape, c2jShapes);
MemberModel mapValueModel = generateMemberModel("value", mapValueMemberDefinition, protocol,
memberC2jShape, c2jShapes);
String mapImpl = getDataTypeMapping(TypeUtils.TypeKey.MAP_DEFAULT_IMPL);
String keyLocation = memberC2jShape.getMapKeyType().getLocationName() != null ?
memberC2jShape.getMapKeyType().getLocationName() : "key";
String valueLocation = memberC2jShape.getMapValueType().getLocationName() != null ?
memberC2jShape.getMapValueType().getLocationName() : "value";
memberModel.setMapModel(new MapModel(mapImpl,
getDataTypeMapping(TypeUtils.TypeKey.MAP_INTERFACE),
keyLocation,
mapKeyModel,
valueLocation,
mapValueModel));
} else if (memberC2jShape.getEnumValues() != null) { // enum values
memberModel.withEnumType(getNamingStrategy().getJavaClassName(memberC2jShapeName));
}
}
protected String getProtocol() {
return getServiceModel().getMetadata().getProtocol();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy