
software.amazon.awssdk.codegen.poet.rules.RuleSetCreationSpec 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.JsonToken;
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.JrsNumber;
import com.fasterxml.jackson.jr.stree.JrsObject;
import com.fasterxml.jackson.jr.stree.JrsString;
import com.fasterxml.jackson.jr.stree.JrsValue;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.MethodSpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.Modifier;
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
import software.amazon.awssdk.codegen.model.rules.endpoints.ConditionModel;
import software.amazon.awssdk.codegen.model.rules.endpoints.EndpointModel;
import software.amazon.awssdk.codegen.model.rules.endpoints.ParameterDeprecatedModel;
import software.amazon.awssdk.codegen.model.rules.endpoints.ParameterModel;
import software.amazon.awssdk.codegen.model.rules.endpoints.RuleModel;
import software.amazon.awssdk.codegen.model.service.EndpointRuleSetModel;
import software.amazon.awssdk.utils.MapUtils;
public class RuleSetCreationSpec {
private static final String RULE_METHOD_PREFIX = "endpointRule_";
private final EndpointRulesSpecUtils endpointRulesSpecUtils;
private final EndpointRuleSetModel ruleSetModel;
private int ruleCounter = 0;
private final List helperMethods = new ArrayList<>();
public RuleSetCreationSpec(IntermediateModel intermediateModel) {
this.endpointRulesSpecUtils = new EndpointRulesSpecUtils(intermediateModel);
this.ruleSetModel = intermediateModel.getEndpointRuleSetModel();
}
public CodeBlock ruleSetCreationExpr() {
CodeBlock.Builder b = CodeBlock.builder();
b.add("$T.builder()", endpointRulesSpecUtils.rulesRuntimeClassName("EndpointRuleset"))
.add(".version($S)", ruleSetModel.getVersion())
.add(".serviceId($S)", ruleSetModel.getServiceId())
.add(".parameters($L)", parameters(ruleSetModel.getParameters()));
ruleSetModel.getRules().stream()
.map(this::rule)
.forEach(m -> b.add(".addRule($N())", m.name));
b.add(".build()");
return b.build();
}
public List helperMethods() {
return helperMethods;
}
private CodeBlock parameters(Map params) {
CodeBlock.Builder b = CodeBlock.builder();
b.add("$T.builder()", endpointRulesSpecUtils.rulesRuntimeClassName("Parameters"));
params.forEach((name, model) -> {
b.add(".addParameter($L)", parameter(name, model));
});
b.add(".build()");
return b.build();
}
private CodeBlock parameter(String name, ParameterModel model) {
CodeBlock.Builder b = CodeBlock.builder();
b.add("$T.builder()", endpointRulesSpecUtils.rulesRuntimeClassName("Parameter"))
.add(".name($S)", name)
.add(".type($T.fromValue($S))", endpointRulesSpecUtils.rulesRuntimeClassName("ParameterType"), model.getType())
.add(".required($L)", Boolean.TRUE.equals(model.isRequired()));
if (model.getBuiltIn() != null) {
b.add(".builtIn($S)", model.getBuiltIn());
}
if (model.getDocumentation() != null) {
b.add(".documentation($S)", model.getDocumentation());
}
if (model.getDefault() != null) {
TreeNode defaultValue = model.getDefault();
JsonToken token = defaultValue.asToken();
CodeBlock value;
if (token == JsonToken.VALUE_FALSE || token == JsonToken.VALUE_TRUE) {
value = endpointRulesSpecUtils.valueCreationCode("boolean",
CodeBlock.builder()
.add("$L", ((JrsBoolean) defaultValue).booleanValue())
.build());
} else if (token == JsonToken.VALUE_STRING) {
value = endpointRulesSpecUtils.valueCreationCode("string",
CodeBlock.builder()
.add("$S", ((JrsString) defaultValue).getValue())
.build());
} else if (token == JsonToken.START_ARRAY) {
validateStringArrayType(model);
value = endpointRulesSpecUtils.valueCreationCode("stringarray",
buildStringArrayDefaultValue((JrsArray) defaultValue));
} else {
throw new RuntimeException("Can't set default value type " + token.name());
}
b.add(".defaultValue($L)", value);
}
if (model.getDeprecated() != null) {
ParameterDeprecatedModel deprecated = model.getDeprecated();
b.add(".deprecated(new $T($S, $S))",
endpointRulesSpecUtils.rulesRuntimeClassName("Parameter.Deprecated"),
deprecated.getMessage(),
deprecated.getSince());
}
b.add(".build()");
return b.build();
}
private MethodSpec rule(RuleModel model) {
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(nextRuleMethodName());
methodBuilder.addModifiers(Modifier.PRIVATE, Modifier.STATIC);
methodBuilder.returns(endpointRulesSpecUtils.rulesRuntimeClassName("Rule"));
CodeBlock.Builder b = CodeBlock.builder();
b.add("$T.builder()", endpointRulesSpecUtils.rulesRuntimeClassName("Rule"));
model.getConditions().forEach(c -> b.add(".addCondition($L)", condition(c)));
if ("error".equals(model.getType())) {
b.add(".error($S)", model.getError());
} else if ("tree".equals(model.getType())) {
CodeBlock.Builder rulesArray = CodeBlock.builder()
.add("$T.asList(", Arrays.class);
int nRules = model.getRules().size();
for (int i = 0; i < nRules; ++i) {
MethodSpec childRule = rule(model.getRules().get(i));
rulesArray.add("$N()", childRule.name);
if (i + 1 < nRules) {
rulesArray.add(", ");
}
}
rulesArray.add(")");
b.add(".treeRule($L)", rulesArray.build());
} else if ("endpoint".equals(model.getType())) {
CodeBlock endpoint = endpoint(model.getEndpoint());
b.add(".endpoint($L)", endpoint);
}
MethodSpec m = methodBuilder.addStatement("return $L", b.build()).build();
helperMethods.add(m);
return m;
}
private CodeBlock endpoint(EndpointModel model) {
CodeBlock.Builder b = CodeBlock.builder();
b.add("$T.builder()", endpointRulesSpecUtils.rulesRuntimeClassName("EndpointResult"));
TreeNode url = model.getUrl();
b.add(".url($L)", expr(url));
if (model.getHeaders() != null) {
model.getHeaders().forEach((name, valueList) -> {
valueList.forEach(value -> b.add(".addHeaderValue($S, $L)", name, expr(value)));
});
}
if (model.getProperties() != null) {
// Explicitly only support known properties
model.getProperties().forEach((name, property) -> {
switch (name) {
case "authSchemes":
b.add(".addProperty($T.of($S), $T.fromTuple($T.asList(",
endpointRulesSpecUtils.rulesRuntimeClassName("Identifier"),
"authSchemes",
endpointRulesSpecUtils.rulesRuntimeClassName("Literal"),
Arrays.class);
Iterator authSchemesIter = ((JrsArray) property).elements();
while (authSchemesIter.hasNext()) {
b.add("$T.fromRecord($T.of(", endpointRulesSpecUtils.rulesRuntimeClassName("Literal"),
MapUtils.class);
JrsObject authScheme = (JrsObject) authSchemesIter.next();
Iterator authSchemeFieldsIter = authScheme.fieldNames();
while (authSchemeFieldsIter.hasNext()) {
String schemeProp = authSchemeFieldsIter.next();
JrsValue propValue = authScheme.get(schemeProp);
b.add("$T.of($S), ", endpointRulesSpecUtils.rulesRuntimeClassName("Identifier"), schemeProp);
if ("signingRegionSet".equalsIgnoreCase(schemeProp)) {
b.add("$T.fromTuple($T.asList(", endpointRulesSpecUtils.rulesRuntimeClassName("Literal"),
Arrays.class);
Iterator signingRegions = ((JrsArray) propValue).elements();
while (signingRegions.hasNext()) {
JrsString region = (JrsString) signingRegions.next();
b.add("$T.fromStr($S)", endpointRulesSpecUtils.rulesRuntimeClassName("Literal"),
region.getValue());
if (signingRegions.hasNext()) {
b.add(", ");
}
}
b.add("))");
} else if ("disableDoubleEncoding".equalsIgnoreCase(schemeProp)) {
b.add("$T.fromBool($L)", endpointRulesSpecUtils.rulesRuntimeClassName("Literal"),
((JrsBoolean) propValue).booleanValue());
} else {
b.add("$T.fromStr($S)", endpointRulesSpecUtils.rulesRuntimeClassName("Literal"),
((JrsString) propValue).getValue());
}
if (authSchemeFieldsIter.hasNext()) {
b.add(", ");
}
}
b.add("))");
if (authSchemesIter.hasNext()) {
b.add(", ");
}
}
b.add(")))");
break;
case "bucketType":
b.add(".addProperty($T.of($S), $T.fromStr($S))",
endpointRulesSpecUtils.rulesRuntimeClassName("Identifier"),
"bucketType",
endpointRulesSpecUtils.rulesRuntimeClassName("Literal"),
((JrsString) property).getValue());
break;
case "useS3ExpressSessionAuth":
b.add(".addProperty($T.of($S), $T.fromBool($L))",
endpointRulesSpecUtils.rulesRuntimeClassName("Identifier"),
"useS3ExpressSessionAuth",
endpointRulesSpecUtils.rulesRuntimeClassName("Literal"),
((JrsBoolean) property).booleanValue());
break;
case "backend":
b.add(".addProperty($T.of($S), $T.fromStr($S))",
endpointRulesSpecUtils.rulesRuntimeClassName("Identifier"),
"backend",
endpointRulesSpecUtils.rulesRuntimeClassName("Literal"),
((JrsString) property).getValue());
break;
default:
break;
}
});
}
b.add(".build()");
return b.build();
}
private CodeBlock condition(ConditionModel model) {
CodeBlock.Builder b = CodeBlock.builder();
b.add("$T.builder()", endpointRulesSpecUtils.rulesRuntimeClassName("Condition"))
.add(".fn($L.validate())", fnNode(model));
if (model.getAssign() != null) {
b.add(".result($S)", model.getAssign());
}
b.add(".build()");
return b.build();
}
private CodeBlock fnNode(ConditionModel model) {
CodeBlock.Builder b = CodeBlock.builder();
b.add("$T.builder()", endpointRulesSpecUtils.rulesRuntimeClassName("FnNode"))
.add(".fn($S)", model.getFn())
.add(".argv($T.asList(", Arrays.class);
List args = model.getArgv();
for (int i = 0; i < args.size(); ++i) {
b.add("$L", expr(args.get(i)));
if (i + 1 < args.size()) {
b.add(",");
}
}
b.add("))");
b.add(".build()");
return b.build();
}
private CodeBlock expr(TreeNode n) {
if (n.isValueNode()) {
return valueExpr((JrsValue) n);
}
if (n.isObject()) {
return objectExpr((JrsObject) n);
}
throw new RuntimeException("Don't know how to create expression from " + n);
}
private CodeBlock valueExpr(JrsValue n) {
CodeBlock.Builder b = CodeBlock.builder();
b.add("$T.of(", endpointRulesSpecUtils.rulesRuntimeClassName("Expr"));
JsonToken token = n.asToken();
switch (token) {
case VALUE_STRING:
b.add("$S", ((JrsString) n).getValue());
break;
case VALUE_NUMBER_INT:
b.add("$L", ((JrsNumber) n).getValue().intValue());
break;
case VALUE_TRUE:
case VALUE_FALSE:
b.add("$L", ((JrsBoolean) n).booleanValue());
break;
default:
throw new RuntimeException("Don't know how to create expression JSON type " + token);
}
b.add(")");
return b.build();
}
private CodeBlock objectExpr(JrsObject n) {
CodeBlock.Builder b = CodeBlock.builder();
JrsValue ref = n.get("ref");
JrsValue fn = n.get("fn");
if (ref != null) {
b.add("$T.ref($T.of($S))",
endpointRulesSpecUtils.rulesRuntimeClassName("Expr"),
endpointRulesSpecUtils.rulesRuntimeClassName("Identifier"),
ref.asText());
} else if (fn != null) {
String name = fn.asText();
CodeBlock.Builder fnNode = CodeBlock.builder();
fnNode.add("$T.builder()", endpointRulesSpecUtils.rulesRuntimeClassName("FnNode"))
.add(".fn($S)", name);
JrsArray argv = (JrsArray) n.get("argv");
fnNode.add(".argv($T.asList(", Arrays.class);
Iterator iter = argv.elements();
while (iter.hasNext()) {
fnNode.add(expr(iter.next()));
if (iter.hasNext()) {
fnNode.add(",");
}
}
fnNode.add(")).build().validate()");
b.add(fnNode.build());
}
return b.build();
}
private String nextRuleMethodName() {
String n = String.format("%s%d", RULE_METHOD_PREFIX, ruleCounter);
ruleCounter += 1;
return n;
}
private static void validateStringArrayType(ParameterModel model) {
if (!"stringarray".equalsIgnoreCase(model.getType())) {
throw new RuntimeException(String.format("Only String array is supported but the type received is %s",
model.getType()));
}
}
private CodeBlock buildStringArrayDefaultValue(JrsArray defaultValue) {
CodeBlock.Builder builder = CodeBlock.builder();
Iterator elementValuesIter = defaultValue.elements();
if (elementValuesIter.hasNext()) {
builder.add("$T.asList(", Arrays.class);
}
while (elementValuesIter.hasNext()) {
JrsValue v = elementValuesIter.next();
builder.add("\"" + v.asText() + "\"");
if (elementValuesIter.hasNext()) {
builder.add(",");
}
}
builder.add(")");
return builder.build();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy