xref: /aosp_15_r20/external/aws-sdk-java-v2/codegen/src/main/java/software/amazon/awssdk/codegen/internal/Utils.java (revision 8a52c7834d808308836a99fc2a6e0ed8db339086)
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.internal;
17 
18 import static java.util.stream.Collectors.toList;
19 
20 import java.io.Closeable;
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.UncheckedIOException;
25 import java.nio.file.Files;
26 import java.util.List;
27 import java.util.Map;
28 import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
29 import software.amazon.awssdk.codegen.model.intermediate.MapModel;
30 import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
31 import software.amazon.awssdk.codegen.model.intermediate.Metadata;
32 import software.amazon.awssdk.codegen.model.intermediate.ShapeMarshaller;
33 import software.amazon.awssdk.codegen.model.intermediate.ShapeModel;
34 import software.amazon.awssdk.codegen.model.intermediate.ShapeType;
35 import software.amazon.awssdk.codegen.model.service.Input;
36 import software.amazon.awssdk.codegen.model.service.Operation;
37 import software.amazon.awssdk.codegen.model.service.ServiceMetadata;
38 import software.amazon.awssdk.codegen.model.service.ServiceModel;
39 import software.amazon.awssdk.codegen.model.service.Shape;
40 import software.amazon.awssdk.codegen.model.service.XmlNamespace;
41 import software.amazon.awssdk.utils.IoUtils;
42 import software.amazon.awssdk.utils.StringUtils;
43 
44 public final class Utils {
45 
Utils()46     private Utils() {
47     }
48 
isScalar(Shape shape)49     public static boolean isScalar(Shape shape) {
50         // enums are treated as scalars in C2j.
51         return !(isListShape(shape) || isStructure(shape) || isMapShape(shape));
52     }
53 
isStructure(Shape shape)54     public static boolean isStructure(Shape shape) {
55         return shape.getType().equals("structure");
56     }
57 
isListShape(Shape shape)58     public static boolean isListShape(Shape shape) {
59         return shape.getType().equals("list");
60     }
61 
isMapShape(Shape shape)62     public static boolean isMapShape(Shape shape) {
63         return shape.getType().equals("map");
64     }
65 
isEnumShape(Shape shape)66     public static boolean isEnumShape(Shape shape) {
67         return shape.getEnumValues() != null;
68     }
69 
isExceptionShape(Shape shape)70     public static boolean isExceptionShape(Shape shape) {
71         return shape.isException() || shape.isFault();
72     }
73 
isOrContainsEnumShape(Shape shape, Map<String, Shape> allShapes)74     public static boolean isOrContainsEnumShape(Shape shape, Map<String, Shape> allShapes) {
75         boolean isEnum = isEnumShape(shape);
76         boolean isMapWithEnumMember = isMapShape(shape)
77              && (isOrContainsEnumShape(allShapes.get(shape.getMapKeyType().getShape()), allShapes)
78                  || isOrContainsEnumShape(allShapes.get(shape.getMapValueType().getShape()), allShapes));
79         boolean isListWithEnumMember = isListShape(shape)
80              && isOrContainsEnumShape(allShapes.get(shape.getListMember().getShape()), allShapes);
81 
82         return isEnum || isMapWithEnumMember || isListWithEnumMember;
83     }
84 
isOrContainsEnum(MemberModel member)85     public static boolean isOrContainsEnum(MemberModel member) {
86         boolean isEnum = member.getEnumType() != null;
87         return isEnum || isMapWithEnumShape(member) || isListWithEnumShape(member);
88     }
89 
isListWithEnumShape(MemberModel member)90     public static boolean isListWithEnumShape(MemberModel member) {
91         return member.isList() && member.getListModel().getListMemberModel().getEnumType() != null;
92     }
93 
isMapWithEnumShape(MemberModel member)94     public static boolean isMapWithEnumShape(MemberModel member) {
95         return member.isMap() && (isMapKeyWithEnumShape(member.getMapModel()) || isMapValueWithEnumShape(member.getMapModel()));
96     }
97 
isMapKeyWithEnumShape(MapModel mapModel)98     public static boolean isMapKeyWithEnumShape(MapModel mapModel) {
99         return mapModel.getKeyModel().getEnumType() != null;
100     }
101 
isMapValueWithEnumShape(MapModel mapModel)102     public static boolean isMapValueWithEnumShape(MapModel mapModel) {
103         return mapModel.getValueModel().getEnumType() != null;
104     }
105 
unCapitalize(String name)106     public static String unCapitalize(String name) {
107         if (name == null || name.trim().isEmpty()) {
108             throw new IllegalArgumentException("Name cannot be null or empty");
109         }
110         StringBuilder sb = new StringBuilder(name.length());
111 
112         int i = 0;
113         do {
114             sb.append(Character.toLowerCase(name.charAt(i++)));
115         } while ((i < name.length() && Character.isUpperCase(name.charAt(i)))
116                 // not followed by a lowercase character
117                 && !(i < name.length() - 1 && Character.isLowerCase(name.charAt(i + 1))));
118 
119         sb.append(name.substring(i));
120 
121         return sb.toString();
122     }
123 
capitalize(String name)124     public static String capitalize(String name) {
125         if (name == null || name.trim().isEmpty()) {
126             throw new IllegalArgumentException("Name cannot be null or empty");
127         }
128         return name.length() < 2 ? StringUtils.upperCase(name) : StringUtils.upperCase(name.substring(0, 1))
129                 + name.substring(1);
130     }
131 
removeLeading(String str, String toRemove)132     public static String removeLeading(String str, String toRemove) {
133         if (str == null) {
134             return null;
135         }
136         if (str.startsWith(toRemove)) {
137             return str.substring(toRemove.length());
138         }
139         return str;
140     }
141 
removeTrailing(String str, String toRemove)142     public static String removeTrailing(String str, String toRemove) {
143         if (str == null) {
144             return null;
145         }
146         if (str.endsWith(toRemove)) {
147             return str.substring(0, str.length() - toRemove.length());
148         }
149         return str;
150     }
151 
152     /**
153      * * @param serviceModel Service model to get prefix for.
154      * * @return Prefix to use when writing model files (service and intermediate).
155      */
getFileNamePrefix(ServiceModel serviceModel)156     public static String getFileNamePrefix(ServiceModel serviceModel) {
157         return String.format("%s-%s", serviceModel.getMetadata().getEndpointPrefix(), serviceModel.getMetadata().getApiVersion());
158     }
159 
160     /**
161      * Converts a directory to a Java package name.
162      *
163      * @param directoryPath Directory to convert.
164      * @return Package name
165      */
directoryToPackage(String directoryPath)166     public static String directoryToPackage(String directoryPath) {
167         return directoryPath.replace('/', '.');
168     }
169 
170     /**
171      * Converts a Java package name to a directory.
172      *
173      * @param packageName Java package to convert.
174      * @return directory
175      */
packageToDirectory(String packageName)176     public static String packageToDirectory(String packageName) {
177         return packageName.replace('.', '/');
178     }
179 
getDefaultEndpointWithoutHttpProtocol(String endpoint)180     public static String getDefaultEndpointWithoutHttpProtocol(String endpoint) {
181 
182         if (endpoint == null) {
183             return null;
184         }
185         if (endpoint.startsWith("http://")) {
186             return endpoint.substring("http://".length());
187         }
188         if (endpoint.startsWith("https://")) {
189             return endpoint.substring("https://".length());
190         }
191         return endpoint;
192     }
193 
createDirectory(String path)194     public static File createDirectory(String path) {
195         if (isNullOrEmpty(path)) {
196             throw new IllegalArgumentException(
197                     "Invalid path directory. Path directory cannot be null or empty");
198         }
199 
200         File dir = new File(path);
201         createDirectory(dir);
202         return dir;
203     }
204 
createDirectory(File dir)205     public static void createDirectory(File dir) {
206         if (!(dir.exists())) {
207             try {
208                 Files.createDirectories(dir.toPath());
209             } catch (IOException e) {
210                 throw new UncheckedIOException("Failed to create " + dir, e);
211             }
212         }
213     }
214 
createFile(String dir, String fileName)215     public static File createFile(String dir, String fileName) throws IOException {
216 
217         if (isNullOrEmpty(fileName)) {
218             throw new IllegalArgumentException(
219                     "Invalid file name. File name cannot be null or empty");
220         }
221 
222         createDirectory(dir);
223 
224         File file = new File(dir, fileName);
225 
226         if (!(file.exists())) {
227             if (!(file.createNewFile())) {
228                 throw new RuntimeException("Not able to create file . "
229                         + file.getAbsolutePath());
230             }
231         }
232 
233         return file;
234     }
235 
isNullOrEmpty(String str)236     public static boolean isNullOrEmpty(String str) {
237         return str == null || str.trim().isEmpty();
238     }
239 
closeQuietly(Closeable closeable)240     public static void closeQuietly(Closeable closeable) {
241         IoUtils.closeQuietly(closeable, null);
242     }
243 
244     /**
245      * Return an InputStream of the specified resource, failing if it can't be found.
246      *
247      * @param location Location of resource
248      */
getRequiredResourceAsStream(Class<?> clzz, String location)249     public static InputStream getRequiredResourceAsStream(Class<?> clzz, String location) {
250         InputStream resourceStream = clzz.getResourceAsStream(location);
251 
252         if (resourceStream == null) {
253             // Try with a leading "/"
254             if (!location.startsWith("/")) {
255                 resourceStream = clzz.getResourceAsStream("/" + location);
256             }
257             if (resourceStream == null) {
258                 throw new RuntimeException("Resource file was not found at location " + location);
259             }
260         }
261 
262         return resourceStream;
263     }
264 
265     /**
266      * Search for intermediate shape model by its c2j name.
267      *
268      * @return ShapeModel
269      * @throws IllegalArgumentException if the specified c2j name is not found in the intermediate model.
270      */
findShapeModelByC2jName(IntermediateModel intermediateModel, String shapeC2jName)271     public static ShapeModel findShapeModelByC2jName(IntermediateModel intermediateModel, String shapeC2jName)
272             throws IllegalArgumentException {
273         ShapeModel shapeModel = findShapeModelByC2jNameIfExists(intermediateModel, shapeC2jName);
274         if (shapeModel != null) {
275             return shapeModel;
276         } else {
277             throw new IllegalArgumentException(
278                     shapeC2jName + " shape (c2j name) does not exist in the intermediate model.");
279         }
280     }
281 
282     /**
283      * Search for intermediate shape model by its c2j name.
284      *
285      * @return ShapeModel or null if the shape doesn't exist (if it's primitive or container type for example)
286      */
findShapeModelByC2jNameIfExists(IntermediateModel intermediateModel, String shapeC2jName)287     public static ShapeModel findShapeModelByC2jNameIfExists(IntermediateModel intermediateModel, String shapeC2jName) {
288         for (ShapeModel shape : intermediateModel.getShapes().values()) {
289             if (shape.getC2jName().equals(shapeC2jName)) {
290                 return shape;
291             }
292         }
293         return null;
294     }
295 
296     /**
297      * Search for a shape model by its C2J name, excluding request and response shapes, which are not candidates to be members
298      * of another shape.
299      *
300      * @return ShapeModel or null if the shape doesn't exist (if it's primitive or container type for example)
301      */
findMemberShapeModelByC2jNameIfExists(IntermediateModel intermediateModel, String shapeC2jName)302     public static ShapeModel findMemberShapeModelByC2jNameIfExists(IntermediateModel intermediateModel, String shapeC2jName) {
303         ShapeModel candidate = null;
304         for (ShapeModel shape : intermediateModel.getShapes().values()) {
305             if (shape.getShapeType() != ShapeType.Request
306                     && shape.getShapeType() != ShapeType.Response
307                     && shape.getC2jName().equals(shapeC2jName)) {
308                 if (candidate != null) {
309                     throw new IllegalStateException("Conflicting candidates for member model with C2J name " + shapeC2jName + ": "
310                                                     + candidate + " and " + shape);
311                 }
312                 candidate = shape;
313             }
314         }
315         return candidate;
316     }
317 
findShapesByC2jName(IntermediateModel intermediateModel, String shapeC2jName)318     public static List<ShapeModel> findShapesByC2jName(IntermediateModel intermediateModel, String shapeC2jName) {
319         return intermediateModel.getShapes().values().stream().filter(s -> s.getC2jName().equals(shapeC2jName)).collect(toList());
320     }
321 
322     /**
323      * Create the ShapeMarshaller to the input shape from the specified Operation.
324      * The input shape in the operation could be empty.
325      */
createInputShapeMarshaller(ServiceMetadata service, Operation operation)326     public static ShapeMarshaller createInputShapeMarshaller(ServiceMetadata service, Operation operation) {
327 
328         if (operation == null) {
329             throw new IllegalArgumentException(
330                     "The operation parameter must be specified!");
331         }
332 
333         ShapeMarshaller marshaller = new ShapeMarshaller()
334                 .withAction(operation.getName())
335                 .withVerb(operation.getHttp().getMethod())
336                 .withRequestUri(operation.getHttp().getRequestUri());
337         Input input = operation.getInput();
338         if (input != null) {
339             marshaller.setLocationName(input.getLocationName());
340             // Pass the xmlNamespace trait from the input reference
341             XmlNamespace xmlNamespace = input.getXmlNamespace();
342             if (xmlNamespace != null) {
343                 marshaller.setXmlNameSpaceUri(xmlNamespace.getUri());
344             }
345         }
346         if (Metadata.isNotRestProtocol(service.getProtocol())) {
347             marshaller.setTarget(StringUtils.isEmpty(service.getTargetPrefix()) ?
348                                  operation.getName() :
349                                  service.getTargetPrefix() + "." + operation.getName());
350         }
351         return marshaller;
352 
353     }
354 }
355