1 /* 2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"). 5 * You may not use this file except in compliance with the License. 6 * A copy of the License is located at 7 * 8 * http://aws.amazon.com/apache2.0 9 * 10 * or in the "license" file accompanying this file. This file is distributed 11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 * express or implied. See the License for the specific language governing 13 * permissions and limitations under the License. 14 */ 15 16 package software.amazon.awssdk.codegen.poet.model; 17 18 import com.squareup.javapoet.ClassName; 19 import com.squareup.javapoet.FieldSpec; 20 import com.squareup.javapoet.ParameterizedTypeName; 21 import com.squareup.javapoet.TypeName; 22 import com.squareup.javapoet.WildcardTypeName; 23 import java.io.InputStream; 24 import java.math.BigDecimal; 25 import java.math.BigInteger; 26 import java.nio.ByteBuffer; 27 import java.time.Instant; 28 import java.util.ArrayList; 29 import java.util.Collection; 30 import java.util.List; 31 import java.util.Map; 32 import java.util.stream.Stream; 33 import javax.lang.model.element.Modifier; 34 import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel; 35 import software.amazon.awssdk.codegen.model.intermediate.MapModel; 36 import software.amazon.awssdk.codegen.model.intermediate.MemberModel; 37 import software.amazon.awssdk.codegen.poet.PoetExtension; 38 import software.amazon.awssdk.core.SdkBytes; 39 import software.amazon.awssdk.core.document.Document; 40 41 /** 42 * Helper class for resolving Poet {@link TypeName}s for use in model classes. 43 */ 44 public class TypeProvider { 45 private final IntermediateModel intermediateModel; 46 private final PoetExtension poetExtensions; 47 TypeProvider(IntermediateModel intermediateModel)48 public TypeProvider(IntermediateModel intermediateModel) { 49 this.intermediateModel = intermediateModel; 50 this.poetExtensions = new PoetExtension(this.intermediateModel); 51 } 52 listImplClassName()53 public ClassName listImplClassName() { 54 return ClassName.get(ArrayList.class); 55 } 56 enumReturnType(MemberModel memberModel)57 public TypeName enumReturnType(MemberModel memberModel) { 58 return typeName(memberModel, new TypeNameOptions().useEnumTypes(true)); 59 } 60 returnType(MemberModel memberModel)61 public TypeName returnType(MemberModel memberModel) { 62 return typeName(memberModel, new TypeNameOptions().useEnumTypes(false)); 63 } 64 fieldType(MemberModel memberModel)65 public TypeName fieldType(MemberModel memberModel) { 66 return typeName(memberModel, new TypeNameOptions().useEnumTypes(false)); 67 } 68 parameterType(MemberModel memberModel)69 public TypeName parameterType(MemberModel memberModel) { 70 return parameterType(memberModel, false); 71 } 72 parameterType(MemberModel memberModel, boolean preserveEnum)73 public TypeName parameterType(MemberModel memberModel, boolean preserveEnum) { 74 return typeName(memberModel, new TypeNameOptions().useCollectionForList(true) 75 .useSubtypeWildcardsForCollections(true) 76 .useEnumTypes(preserveEnum)); 77 } 78 mapEntryWithConcreteTypes(MapModel mapModel)79 public TypeName mapEntryWithConcreteTypes(MapModel mapModel) { 80 TypeName keyType = fieldType(mapModel.getKeyModel()); 81 TypeName valueType = fieldType(mapModel.getValueModel()); 82 return ParameterizedTypeName.get(ClassName.get(Map.Entry.class), keyType, valueType); 83 } 84 getTypeNameForSimpleType(String simpleType)85 public TypeName getTypeNameForSimpleType(String simpleType) { 86 return Stream.of(String.class, 87 Boolean.class, 88 Integer.class, 89 Long.class, 90 Short.class, 91 Byte.class, 92 BigInteger.class, 93 Double.class, 94 Float.class, 95 BigDecimal.class, 96 SdkBytes.class, 97 InputStream.class, 98 Instant.class, 99 Document.class) 100 .filter(cls -> cls.getName().equals(simpleType) || cls.getSimpleName().equals(simpleType)) 101 .map(ClassName::get) 102 .findFirst() 103 .orElseThrow(() -> new RuntimeException("Unsupported simple fieldType " + simpleType)); 104 } 105 asField(MemberModel memberModel, Modifier... modifiers)106 public FieldSpec asField(MemberModel memberModel, Modifier... modifiers) { 107 FieldSpec.Builder builder = FieldSpec.builder(fieldType(memberModel), 108 memberModel.getVariable().getVariableName()); 109 110 if (modifiers != null) { 111 builder.addModifiers(modifiers); 112 } 113 114 return builder.build(); 115 } 116 isContainerType(MemberModel m)117 private static boolean isContainerType(MemberModel m) { 118 return m.isList() || m.isMap(); 119 } 120 typeName(MemberModel model)121 public TypeName typeName(MemberModel model) { 122 return typeName(model, new TypeNameOptions()); 123 } 124 typeName(MemberModel model, TypeNameOptions options)125 public TypeName typeName(MemberModel model, TypeNameOptions options) { 126 if (model.isSdkBytesType() && options.useByteBufferTypes) { 127 return ClassName.get(ByteBuffer.class); 128 } 129 130 if (model.getEnumType() != null && options.useEnumTypes) { 131 return poetExtensions.getModelClass(model.getEnumType()); 132 } 133 134 if (model.isSimple()) { 135 return getTypeNameForSimpleType(model.getVariable().getVariableType()); 136 } 137 138 if (model.isList()) { 139 MemberModel entryModel = model.getListModel().getListMemberModel(); 140 TypeName entryType = typeName(entryModel, options); 141 142 if (options.useSubtypeWildcardsForCollections && isContainerType(entryModel) || 143 options.useSubtypeWildcardsForBuilders && entryModel.hasBuilder()) { 144 entryType = WildcardTypeName.subtypeOf(entryType); 145 } 146 147 Class<?> collectionType = options.useCollectionForList ? Collection.class : List.class; 148 149 return ParameterizedTypeName.get(ClassName.get(collectionType), entryType); 150 } 151 152 if (model.isMap()) { 153 MemberModel keyModel = model.getMapModel().getKeyModel(); 154 MemberModel valueModel = model.getMapModel().getValueModel(); 155 TypeName keyType = typeName(keyModel, options); 156 TypeName valueType = typeName(valueModel, options); 157 158 if (options.useSubtypeWildcardsForCollections && isContainerType(keyModel) || 159 options.useSubtypeWildcardsForBuilders && keyModel.hasBuilder()) { 160 keyType = WildcardTypeName.subtypeOf(keyType); 161 } 162 163 if (options.useSubtypeWildcardsForCollections && isContainerType(valueModel) || 164 options.useSubtypeWildcardsForBuilders && valueModel.hasBuilder()) { 165 valueType = WildcardTypeName.subtypeOf(valueType); 166 } 167 168 return ParameterizedTypeName.get(ClassName.get(Map.class), keyType, valueType); 169 } 170 171 if (model.hasBuilder()) { 172 ClassName shapeClass = poetExtensions.getModelClass(model.getC2jShape()); 173 switch (options.shapeTransformation) { 174 case NONE: return shapeClass; 175 case USE_BUILDER: return shapeClass.nestedClass("Builder"); 176 case USE_BUILDER_IMPL: return shapeClass.nestedClass("BuilderImpl"); 177 default: throw new IllegalStateException(); 178 } 179 } 180 181 throw new IllegalArgumentException("Unsupported member model: " + model); 182 } 183 184 public enum ShapeTransformation { 185 USE_BUILDER, 186 USE_BUILDER_IMPL, 187 NONE 188 } 189 190 public static final class TypeNameOptions { 191 private ShapeTransformation shapeTransformation = ShapeTransformation.NONE; 192 private boolean useCollectionForList = false; 193 private boolean useSubtypeWildcardsForCollections = false; 194 private boolean useByteBufferTypes = false; 195 private boolean useEnumTypes = false; 196 private boolean useSubtypeWildcardsForBuilders = false; 197 shapeTransformation(ShapeTransformation shapeTransformation)198 public TypeNameOptions shapeTransformation(ShapeTransformation shapeTransformation) { 199 this.shapeTransformation = shapeTransformation; 200 return this; 201 } 202 useCollectionForList(boolean useCollectionForList)203 public TypeNameOptions useCollectionForList(boolean useCollectionForList) { 204 this.useCollectionForList = useCollectionForList; 205 return this; 206 } 207 useSubtypeWildcardsForCollections(boolean useSubtypeWildcardsForCollections)208 public TypeNameOptions useSubtypeWildcardsForCollections(boolean useSubtypeWildcardsForCollections) { 209 this.useSubtypeWildcardsForCollections = useSubtypeWildcardsForCollections; 210 return this; 211 } 212 useSubtypeWildcardsForBuilders(boolean useSubtypeWildcardsForBuilders)213 public TypeNameOptions useSubtypeWildcardsForBuilders(boolean useSubtypeWildcardsForBuilders) { 214 this.useSubtypeWildcardsForBuilders = useSubtypeWildcardsForBuilders; 215 return this; 216 } 217 useEnumTypes(boolean useEnumTypes)218 public TypeNameOptions useEnumTypes(boolean useEnumTypes) { 219 this.useEnumTypes = useEnumTypes; 220 return this; 221 } 222 useByteBufferTypes(boolean useByteBufferTypes)223 public TypeNameOptions useByteBufferTypes(boolean useByteBufferTypes) { 224 this.useByteBufferTypes = useByteBufferTypes; 225 return this; 226 } 227 } 228 } 229