/*
* Copyright (c) 2016. 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 com.amazonaws.codegen.customization.processors;
import com.amazonaws.codegen.customization.CodegenCustomizationProcessor;
import com.amazonaws.codegen.internal.Utils;
import com.amazonaws.codegen.model.config.customization.ShapeModifier;
import com.amazonaws.codegen.model.config.customization.ShapeModifier_ModifyModel;
import com.amazonaws.codegen.model.intermediate.EnumModel;
import com.amazonaws.codegen.model.intermediate.IntermediateModel;
import com.amazonaws.codegen.model.intermediate.MemberModel;
import com.amazonaws.codegen.model.intermediate.ShapeModel;
import com.amazonaws.codegen.model.service.Member;
import com.amazonaws.codegen.model.service.ServiceModel;
import com.amazonaws.codegen.model.service.Shape;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* This processor handles all the modification on the shape members in the
* pre-process step and then removes all the excluded shapes in the post-process
* step.
*
* This processor can also modify the names of the enum values generated by the
* code generator.
*/
final class ShapeModifiersProcessor implements CodegenCustomizationProcessor {
private final Map<String, ShapeModifier> shapeModifiers;
private static final String ALL = "*";
ShapeModifiersProcessor(
Map<String, ShapeModifier> shapeModifiers) {
this.shapeModifiers = shapeModifiers;
}
@Override
public void preprocess(ServiceModel serviceModel) {
if (shapeModifiers == null) return;
for (Entry<String, ShapeModifier> entry : shapeModifiers.entrySet()) {
String key = entry.getKey();
ShapeModifier modifier = entry.getValue();
if (ALL.equals(key)) {
for (Shape shape : serviceModel.getShapes().values()) {
preprocess_ModifyShapeMembers(serviceModel, shape, modifier);
}
} else {
Shape shape = serviceModel.getShapes().get(key);
if (shape == null) {
throw new IllegalStateException(
"ShapeModifier customization found for " + key
+ ", but this shape doesn't exist in the model!");
}
preprocess_ModifyShapeMembers(serviceModel, shape, modifier);
}
}
}
@Override
public void postprocess(IntermediateModel intermediateModel) {
if (shapeModifiers == null) return;
for (Entry<String, ShapeModifier> entry : shapeModifiers.entrySet()) {
String key = entry.getKey();
ShapeModifier modifier = entry.getValue();
if (ALL.equals(key)) continue;
ShapeModel shapeModel = null;
try {
shapeModel = Utils.findShapeModelByC2jName(
intermediateModel, key);
} catch (IllegalArgumentException e) {
throw new IllegalStateException(String.format(
"Cannot find c2j shape [%s] in the intermediate model when processing " +
"customization config shapeModifiers.%s",
key, key), e);
}
if (modifier.isExcludeShape()) {
shapeModel.getCustomization().setSkipGeneratingModelClass(true);
shapeModel.getCustomization().setSkipGeneratingMarshaller(true);
shapeModel.getCustomization().setSkipGeneratingUnmarshaller(true);
} else if (modifier.getModify() != null) {
// Modifies properties of a member in shape or shape enum.
// This customization currently support modifying enum name
// and marshall/unmarshall location of a member in the Shape.
for (Map<String, ShapeModifier_ModifyModel> modifies : modifier.getModify()) {
for (Entry<String, ShapeModifier_ModifyModel> memberEntry : modifies.entrySet()) {
String enumToModify = memberEntry.getKey();
ShapeModifier_ModifyModel modifyModel = memberEntry.getValue();
postprocess_ModifyMemberProperty(shapeModel, enumToModify, modifyModel);
}
}
}
}
}
/**
* Override name of the enums, marshall/unmarshall location of the
* members in the given shape model.
*/
private void postprocess_ModifyMemberProperty(ShapeModel shapeModel, String memberName,
ShapeModifier_ModifyModel modifyModel) {
if (modifyModel.getEmitEnumName() != null) {
EnumModel enumModel = shapeModel.findEnumModelByValue(memberName);
if (enumModel == null) {
throw new IllegalStateException(
String.format("Cannot find enum [%s] in the intermediate model when processing "
+ "customization config shapeModifiers.%s", memberName, memberName));
}
enumModel.setName(modifyModel.getEmitEnumName());
}
if (modifyModel.getMarshallLocationName() != null) {
MemberModel memberModel = shapeModel.findMemberModelByC2jName(memberName);
memberModel.getHttp().setMarshallLocationName(modifyModel.getMarshallLocationName());
}
if (modifyModel.getUnmarshallLocationName() != null) {
MemberModel memberModel = shapeModel.findMemberModelByC2jName(memberName);
memberModel.getHttp().setUnmarshallLocationName(modifyModel
.getUnmarshallLocationName());
}
}
/**
* Exclude/modify/inject shape members
*/
private void preprocess_ModifyShapeMembers(ServiceModel serviceModel, Shape shape, ShapeModifier modifier) {
if (modifier.getModify() != null) {
for (Map<String, ShapeModifier_ModifyModel> modifies : modifier.getModify()) {
for (Entry<String, ShapeModifier_ModifyModel> entry : modifies.entrySet()) {
String memberToModify = entry.getKey();
ShapeModifier_ModifyModel modifyModel = entry.getValue();
doModifyShapeMembers(serviceModel, shape, memberToModify, modifyModel);
}
}
}
if (modifier.getExclude() != null) {
for (String memberToExclude : modifier.getExclude()) {
if (shape.getRequired() != null &&
shape.getRequired().contains(memberToExclude)) {
throw new IllegalStateException(
"ShapeModifier.exclude customization found for "
+ memberToExclude
+ ", but this member is marked as required in the model!");
}
if (shape.getMembers() != null) {
shape.getMembers().remove(memberToExclude);
}
}
}
if (modifier.getInject() != null) {
for (Map<String, Member> injects : modifier.getInject()) {
if (shape.getMembers() == null) {
shape.setMembers(new HashMap<String, Member>());
}
shape.getMembers().putAll(injects);
}
}
}
private void doModifyShapeMembers(ServiceModel serviceModel, Shape shape, String memberToModify,
ShapeModifier_ModifyModel modifyModel) {
// Currrenly only supports emitPropertyName which is to rename the member
if (modifyModel.getEmitPropertyName() != null) {
Member member = shape.getMembers().remove(memberToModify);
// if location name is not present, set it to the original name
// to avoid breaking marshaller code
if (member.getLocationName() == null) {
member.setLocationName(memberToModify);
}
shape.getMembers().put(modifyModel.getEmitPropertyName(), member);
}
if (modifyModel.getEmitAsType() != null) {
// Must create a shape for the primitive type.
Shape newShapeForType = new Shape();
newShapeForType.setType(modifyModel.getEmitAsType());
final String shapeName = "SDK_" + modifyModel.getEmitAsType();
serviceModel.getShapes().put(shapeName, newShapeForType);
shape.getMembers().get(memberToModify).setShape(shapeName);
}
}
}