All Downloads are FREE. Search and download functionalities are using the official Maven repository.

software.amazon.awssdk.codegen.poet.rules.EndpointRulesSpecUtils Maven / Gradle / Ivy

/*
 * 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.poet.rules;

import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.jr.stree.JrsArray;
import com.fasterxml.jackson.jr.stree.JrsBoolean;
import com.fasterxml.jackson.jr.stree.JrsString;
import com.fasterxml.jackson.jr.stree.JrsValue;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringJoiner;
import java.util.concurrent.CompletableFuture;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import javax.lang.model.element.Modifier;
import software.amazon.awssdk.codegen.internal.Utils;
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
import software.amazon.awssdk.codegen.model.intermediate.Metadata;
import software.amazon.awssdk.codegen.model.rules.endpoints.BuiltInParameter;
import software.amazon.awssdk.codegen.model.rules.endpoints.ParameterModel;
import software.amazon.awssdk.endpoints.Endpoint;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.utils.Validate;
import software.amazon.awssdk.utils.internal.CodegenNamingUtils;

public class EndpointRulesSpecUtils {
    private final IntermediateModel intermediateModel;

    public EndpointRulesSpecUtils(IntermediateModel intermediateModel) {
        this.intermediateModel = intermediateModel;
    }

    public String basePackage() {
        return intermediateModel.getMetadata().getFullEndpointRulesPackageName();
    }

    public ClassName rulesRuntimeClassName(String name) {
        return ClassName.get(intermediateModel.getMetadata().getFullInternalEndpointRulesPackageName(),
                             name);
    }

    public ClassName parametersClassName() {
        return ClassName.get(basePackage(), intermediateModel.getMetadata().getServiceName() + "EndpointParams");
    }

    public ClassName providerInterfaceName() {
        return ClassName.get(basePackage(), intermediateModel.getMetadata().getServiceName() + "EndpointProvider");
    }

    public ClassName providerDefaultImplName() {
        Metadata md = intermediateModel.getMetadata();
        return ClassName.get(md.getFullInternalEndpointRulesPackageName(),
                             "Default" + providerInterfaceName().simpleName());
    }

    public ClassName resolverInterceptorName() {
        Metadata md = intermediateModel.getMetadata();
        return ClassName.get(md.getFullInternalEndpointRulesPackageName(),
                             md.getServiceName() + "ResolveEndpointInterceptor");
    }

    public ClassName requestModifierInterceptorName() {
        Metadata md = intermediateModel.getMetadata();
        return ClassName.get(md.getFullInternalEndpointRulesPackageName(),
                             md.getServiceName() + "RequestSetEndpointInterceptor");
    }

    public ClassName clientEndpointTestsName() {
        Metadata md = intermediateModel.getMetadata();
        return ClassName.get(md.getFullEndpointRulesPackageName(),
                             md.getServiceName() + "ClientEndpointTests");
    }

    public ClassName endpointProviderTestsName() {
        Metadata md = intermediateModel.getMetadata();
        return ClassName.get(md.getFullEndpointRulesPackageName(),
                             md.getServiceName() + "EndpointProviderTests");
    }

    public ClassName clientContextParamsName() {
        Metadata md = intermediateModel.getMetadata();
        return ClassName.get(md.getFullEndpointRulesPackageName(),
                             md.getServiceName() + "ClientContextParams");
    }

    public String paramMethodName(String param) {
        return Utils.unCapitalize(CodegenNamingUtils.pascalCase(param));
    }

    public String clientContextParamMethodName(String param) {
        return Utils.unCapitalize(CodegenNamingUtils.pascalCase(param));
    }

    public String clientContextParamName(String paramName) {
        return intermediateModel.getNamingStrategy().getEnumValueName(paramName);
    }

    public TypeName toJavaType(String type) {
        switch (type.toLowerCase(Locale.ENGLISH)) {
            case "boolean":
                return TypeName.get(Boolean.class);
            case "string":
                return TypeName.get(String.class);
            case "stringarray":
                return ParameterizedTypeName.get(ClassName.get(List.class), TypeName.get(String.class));
            default:
                throw new RuntimeException("Unknown type: " + type);
        }
    }

    public CodeBlock valueCreationCode(String type, CodeBlock param) {
        String methodName;
        switch (type.toLowerCase(Locale.ENGLISH)) {
            case "boolean":
                methodName = "fromBool";
                break;
            case "string":
                methodName = "fromStr";
                break;
            case "stringarray":
                methodName = "fromArray";
                param = CodeBlock.of("$L.stream().map($T::fromStr).collect($T.toList())",
                                     param,
                                     rulesRuntimeClassName("Value"),
                                     Collectors.class);
                break;
            default:
                throw new RuntimeException("Don't know how to create a Value instance from type " + type);
        }

        return CodeBlock.builder()
                        .add("$T.$N($L)", rulesRuntimeClassName("Value"), methodName, param)
                        .build();
    }

    public TypeName parameterType(ParameterModel param) {
        if (param.getBuiltInEnum() == null || param.getBuiltInEnum() != BuiltInParameter.AWS_REGION) {
            return toJavaType(param.getType());
        }

        if (param.getBuiltInEnum() == BuiltInParameter.AWS_REGION) {
            return ClassName.get(Region.class);
        }
        return toJavaType(param.getType());
    }

    public CodeBlock treeNodeToLiteral(TreeNode treeNode) {
        CodeBlock.Builder b = CodeBlock.builder();

        switch (treeNode.asToken()) {
            case VALUE_STRING:
                b.add("$S", Validate.isInstanceOf(JrsString.class, treeNode, "Expected string").getValue());
                break;
            case VALUE_TRUE:
            case VALUE_FALSE:
                b.add("$L", Validate.isInstanceOf(JrsBoolean.class, treeNode, "Expected boolean").booleanValue());
                break;
            case START_ARRAY:
                handleArrayDefaultValue(b, "stringarray",
                                        Validate.isInstanceOf(JrsArray.class, treeNode, "Expected string array"));
                break;
            default:
                throw new RuntimeException("Don't know how to set default value for parameter of type "
                                           + treeNode.asToken());
        }
        return b.build();
    }


    public boolean isS3() {
        return "S3".equals(intermediateModel.getMetadata().getServiceName());
    }

    public boolean isS3Control() {
        return "S3Control".equals(intermediateModel.getMetadata().getServiceName());
    }

    public boolean useS3Express() {
        return intermediateModel.getCustomizationConfig().getS3ExpressAuthSupport();
    }

    public TypeName resolverReturnType() {
        return ParameterizedTypeName.get(CompletableFuture.class, Endpoint.class);
    }

    public List rulesEngineResourceFiles() {
        URL currentJarUrl = EndpointRulesSpecUtils.class.getProtectionDomain().getCodeSource().getLocation();
        try (JarFile jarFile = new JarFile(currentJarUrl.getFile())) {
            return jarFile.stream()
                          .map(ZipEntry::getName)
                          .filter(e -> e.startsWith("software/amazon/awssdk/codegen/rules/"))
                          .collect(Collectors.toList());
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    public List rulesEngineResourceFiles2() {
        URL currentJarUrl = EndpointRulesSpecUtils.class.getProtectionDomain().getCodeSource().getLocation();
        try (JarFile jarFile = new JarFile(currentJarUrl.getFile())) {
            return jarFile.stream()
                          .map(ZipEntry::getName)
                          .filter(e -> e.startsWith("software/amazon/awssdk/codegen/rules2/"))
                          .collect(Collectors.toList());
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    public Map parameters() {
        return intermediateModel.getEndpointRuleSetModel().getParameters();
    }

    public boolean isDeclaredParam(String paramName) {
        Map parameters = intermediateModel.getEndpointRuleSetModel().getParameters();
        return parameters.containsKey(paramName);
    }

    /**
     * Creates a data-class level field for the given parameter. For instance
     *
     * 
     *     private final Region region;
     * 
*/ public FieldSpec parameterClassField(String name, ParameterModel model) { return parameterFieldSpecBuilder(name, model) .addModifiers(Modifier.PRIVATE) .addModifiers(Modifier.FINAL) .build(); } /** * Creates a data-class method to access the given parameter. For instance * *
     *     public Region region() {…};
     * 
*/ public MethodSpec parameterClassAccessorMethod(String name, ParameterModel model) { MethodSpec.Builder b = parameterMethodBuilder(name, model); b.returns(parameterType(model)); b.addStatement("return $N", variableName(name)); return b.build(); } /** * Creates a data-interface method to access the given parameter. For instance * *
     *     Region region();
     * 
*/ public MethodSpec parameterInterfaceAccessorMethod(String name, ParameterModel model) { MethodSpec.Builder b = parameterMethodBuilder(name, model); b.returns(parameterType(model)); b.addModifiers(Modifier.ABSTRACT); return b.build(); } /** * Creates a builder-class level field for the given parameter initialized to its default value when present. For instance * *
     *    private Boolean useGlobalEndpoint = false;
     * 
*/ public FieldSpec parameterBuilderFieldSpec(String name, ParameterModel model) { return parameterFieldSpecBuilder(name, model) .initializer(parameterDefaultValueCode(model)) .build(); } /** * Creates a builder-interface method to set the given parameter. For instance * *
     *    Builder region(Region region);
     * 
* */ public MethodSpec parameterBuilderSetterMethodDeclaration(ClassName containingClass, String name, ParameterModel model) { MethodSpec.Builder b = parameterMethodBuilder(name, model); b.addModifiers(Modifier.ABSTRACT); b.addParameter(parameterSpec(name, model)); b.returns(containingClass.nestedClass("Builder")); return b.build(); } /** * Creates a builder-class method to set the given parameter. For instance * *
     *    public Builder region(Region region) {…};
     * 
*/ public MethodSpec parameterBuilderSetterMethod(ClassName containingClass, String name, ParameterModel model) { String memberName = variableName(name); MethodSpec.Builder b = parameterMethodBuilder(name, model) .addAnnotation(Override.class) .addParameter(parameterSpec(name, model)) .returns(containingClass.nestedClass("Builder")) .addStatement("this.$1N = $1N", memberName); TreeNode defaultValue = model.getDefault(); if (defaultValue != null) { b.beginControlFlow("if (this.$N == null)", memberName); b.addStatement("this.$N = $L", memberName, parameterDefaultValueCode(model)); b.endControlFlow(); } b.addStatement("return this"); return b.build(); } /** * Used internally to create a field for the given parameter. Returns the builder that can be further tailor to be used for * data-classes or for builder-classes. */ private FieldSpec.Builder parameterFieldSpecBuilder(String name, ParameterModel model) { return FieldSpec.builder(parameterType(model), variableName(name)) .addModifiers(Modifier.PRIVATE); } /** * Used internally to create the spec for a parameter to be used in a method for the given param model. For instance, for * {@code ParameterModel} for {@code Region} it creates this parameter for the builder setter. * *
     *    public Builder region(
     *          Region region // <<--- This
     *          ) {…};
     * 
*/ private ParameterSpec parameterSpec(String name, ParameterModel model) { return ParameterSpec.builder(parameterType(model), variableName(name)).build(); } /** * Used internally to create a accessor method for the given parameter model. Returns the builder that can be further * tailor to be used for data-classes/interfaces and builder-classes/interfaces. */ private MethodSpec.Builder parameterMethodBuilder(String name, ParameterModel model) { MethodSpec.Builder b = MethodSpec.methodBuilder(paramMethodName(name)); b.addModifiers(Modifier.PUBLIC); if (model.getDeprecated() != null) { b.addAnnotation(Deprecated.class); } return b; } /** * Used internally to create the code to initialize the default value modeled for the given parameter. For instance, if the * modeled default for region is "us-east-1", it will create * *
     *     Region.of("us-east-1")
     * 
* * and if the modeled default value for a boolean parameter useGlobalEndpoint is "false", it will create * *
     *     false
     * 
*/ private CodeBlock parameterDefaultValueCode(ParameterModel parameterModel) { CodeBlock.Builder b = CodeBlock.builder(); TreeNode defaultValue = parameterModel.getDefault(); if (defaultValue == null) { return b.build(); } switch (defaultValue.asToken()) { case VALUE_STRING: String stringValue = ((JrsString) defaultValue).getValue(); if (parameterModel.getBuiltInEnum() == BuiltInParameter.AWS_REGION) { b.add("$T.of($S)", Region.class, stringValue); } else { b.add("$S", stringValue); } break; case VALUE_TRUE: case VALUE_FALSE: b.add("$L", ((JrsBoolean) defaultValue).booleanValue()); break; case START_ARRAY: handleArrayDefaultValue(b, parameterModel.getType(), (JrsArray) defaultValue); break; default: throw new RuntimeException("Don't know how to set default value for parameter of type " + defaultValue.asToken()); } return b.build(); } /** * Returns the name as a variable name using the intermediate model naming strategy. */ public String variableName(String name) { return intermediateModel.getNamingStrategy().getVariableName(name); } private void handleArrayDefaultValue(CodeBlock.Builder b, String parameterType, JrsArray defaultValue) { if ("stringarray".equalsIgnoreCase(parameterType)) { Iterator elementValuesIter = defaultValue.elements(); b.add("$T.asList(", Arrays.class); StringJoiner joinerStr = new StringJoiner(","); while (elementValuesIter.hasNext()) { joinerStr.add("\"" + elementValuesIter.next().asText() + "\""); } b.add(joinerStr.toString()); b.add(")"); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy