Skip to content

Commit 152e409

Browse files
authored
Codegen direct index access and map member model (#5720)
* Add codegen support for direct index access and map member model * Add codegen tests
1 parent 89f9fca commit 152e409

25 files changed

+1736
-912
lines changed

codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointRulesClientTestSpec.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.fasterxml.jackson.core.TreeNode;
2121
import com.fasterxml.jackson.jr.stree.JrsArray;
2222
import com.fasterxml.jackson.jr.stree.JrsObject;
23+
import com.fasterxml.jackson.jr.stree.JrsString;
2324
import com.fasterxml.jackson.jr.stree.JrsValue;
2425
import com.squareup.javapoet.AnnotationSpec;
2526
import com.squareup.javapoet.ClassName;
@@ -68,6 +69,7 @@
6869
import software.amazon.awssdk.core.rules.testing.util.EmptyPublisher;
6970
import software.amazon.awssdk.core.sync.RequestBody;
7071
import software.amazon.awssdk.regions.Region;
72+
import software.amazon.awssdk.utils.ImmutableMap;
7173
import software.amazon.awssdk.utils.Validate;
7274

7375
public class EndpointRulesClientTestSpec implements ClassSpec {
@@ -781,8 +783,26 @@ private CodeBlock createMemberValue(MemberModel memberModel, TreeNode valueNode)
781783
}
782784

783785
if (memberModel.isMap()) {
784-
// Not necessary at the moment
785-
throw new RuntimeException("Don't know how to create map member.");
786+
Iterator<Map.Entry<String, JrsValue>> fieldsIter = ((JrsObject) valueNode).fields();
787+
MemberModel keyModel = memberModel.getMapModel().getKeyModel();
788+
MemberModel valueModel = memberModel.getMapModel().getValueModel();
789+
790+
b.add("$T.of(", ImmutableMap.class);
791+
boolean firstKey = true;
792+
while (fieldsIter.hasNext()) {
793+
Map.Entry<String, JrsValue> entry = fieldsIter.next();
794+
JrsString keyNode = new JrsString(entry.getKey());
795+
796+
if (!firstKey) {
797+
b.add(", ");
798+
}
799+
firstKey = false;
800+
b.add(createMemberValue(keyModel, keyNode));
801+
b.add(", ");
802+
b.add(createMemberValue(valueModel, entry.getValue()));
803+
}
804+
b.add(")");
805+
return b.build();
786806
}
787807

788808
return createModelClass(model.getShapes().get(memberModel.getC2jShape()), valueNode);

codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules2/AssignTypesVisitor.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import java.util.ArrayList;
1919
import java.util.List;
2020
import java.util.Map;
21-
import java.util.stream.Collectors;
2221
import software.amazon.awssdk.utils.ToString;
2322

2423
/**
@@ -64,7 +63,6 @@ public RuleExpression visitVariableReferenceExpression(VariableReferenceExpressi
6463
type = tableBuilder.param(variableName);
6564
}
6665
if (type == null) {
67-
errors.add(String.format("Undefined variable `%s`", variableName));
6866
return e;
6967
}
7068
return e.toBuilder()
@@ -90,12 +88,6 @@ public RuleExpression visitFunctionCallExpression(FunctionCallExpression e) {
9088
return expr;
9189
}
9290

93-
List<RuleType> args = expr.arguments().stream().map(RuleExpression::type).collect(Collectors.toList());
94-
if (!function.matches(args)) {
95-
addError("Arguments for function `%s` doesn't match, expected: `%s`, got: `%s`",
96-
name,
97-
function.arguments().values(), args);
98-
}
9991
return expr.toBuilder().type(function.returns()).build();
10092
}
10193

@@ -160,7 +152,13 @@ public RuleExpression visitMemberAccessExpression(MemberAccessExpression e) {
160152
addError("Cannot find type for expression `%s`", expr.source());
161153
} else {
162154
String name = expr.name();
163-
RuleType memberType = type.property(name);
155+
RuleType memberType;
156+
if (expr.directIndex()) {
157+
memberType = type;
158+
} else {
159+
memberType = type.property(name);
160+
}
161+
164162
if (memberType == null) {
165163
addError("Cannot find a member with name `%s` for type `%s`", name, type);
166164
} else {

codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules2/CodeGeneratorVisitor.java

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,10 @@ public Void visitVariableReferenceExpression(VariableReferenceExpression e) {
141141
@Override
142142
public Void visitMemberAccessExpression(MemberAccessExpression e) {
143143
e.source().accept(this);
144-
builder.add(".$L()", e.name());
144+
if (!e.directIndex()) {
145+
builder.add(".$L()", e.name());
146+
}
147+
145148
return null;
146149
}
147150

@@ -197,25 +200,25 @@ public Void visitLetExpression(LetExpression expr) {
197200
RuleType type = symbolTable.locals().get(key);
198201
builder.addStatement("$T $L = null", type.javaType(), key);
199202
}
200-
builder.add("if (");
201-
boolean isFirst = true;
203+
204+
int count = 0;
202205
for (Map.Entry<String, RuleExpression> kvp : expr.bindings().entrySet()) {
203206
String k = kvp.getKey();
204207
RuleExpression v = kvp.getValue();
205-
if (!isFirst) {
206-
builder.add(" && ");
207-
}
208+
builder.add("if (");
208209
builder.add("($L = ", k);
209210
v.accept(this);
210211
builder.add(") != null");
211-
isFirst = false;
212+
213+
builder.beginControlFlow(")");
214+
builder.addStatement("locals = locals.toBuilder().$1L($1L).build()", k);
215+
216+
if (++count < expr.bindings().size()) {
217+
builder.nextControlFlow("else");
218+
builder.addStatement("return RuleResult.carryOn()");
219+
builder.endControlFlow();
220+
}
212221
}
213-
builder.beginControlFlow(")");
214-
builder.add("locals = locals.toBuilder()");
215-
expr.bindings().forEach((k, v) -> {
216-
builder.add(".$1L($1L)", k);
217-
});
218-
builder.addStatement(".build()");
219222
return null;
220223
}
221224

codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules2/ExpressionParser.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,15 @@ private static RuleExpression getAttrExpression(List<TreeNode> argv) {
316316
tokenizer.expectAtEof("indexed access");
317317
return indexedAccessBuilder.build();
318318
}
319+
if (tokenizer.isDirectIndexedAccess()) {
320+
IndexedAccessExpression.Builder indexedAccessBuilder = IndexedAccessExpression.builder();
321+
tokenizer.consumeDirectIndexed(i -> indexedAccessBuilder
322+
.source(memberAccessBuilder.directIndex(true).build())
323+
.index(i)
324+
.build());
325+
tokenizer.expectAtEof("indexed access");
326+
return indexedAccessBuilder.build();
327+
}
319328
if (tokenizer.isIdentifier()) {
320329
tokenizer.consumeIdentifier(memberAccessBuilder::name);
321330
tokenizer.expectAtEof("member access");

codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules2/MemberAccessExpression.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ public final class MemberAccessExpression implements RuleExpression {
2525
private final RuleType type;
2626
private final RuleExpression source;
2727
private final String name;
28+
private final boolean directIndex;
2829

2930
MemberAccessExpression(Builder builder) {
3031
this.type = builder.type;
3132
this.source = Validate.paramNotNull(builder.source, "source");
32-
this.name = Validate.paramNotNull(builder.name, "name");
33+
this.directIndex = builder.directIndex != null ? builder.directIndex : false;
34+
this.name = this.directIndex ? builder.name : Validate.paramNotNull(builder.name, "name");
3335
}
3436

3537
public static Builder builder() {
@@ -68,6 +70,10 @@ public String name() {
6870
return name;
6971
}
7072

73+
public boolean directIndex() {
74+
return directIndex;
75+
}
76+
7177
public Builder toBuilder() {
7278
return new Builder(this);
7379
}
@@ -88,6 +94,9 @@ public boolean equals(Object o) {
8894

8995
MemberAccessExpression that = (MemberAccessExpression) o;
9096

97+
if (directIndex != that.directIndex) {
98+
return false;
99+
}
91100
if (!Objects.equals(type, that.type)) {
92101
return false;
93102
}
@@ -102,13 +111,15 @@ public int hashCode() {
102111
int result = type != null ? type.hashCode() : 0;
103112
result = 31 * result + source.hashCode();
104113
result = 31 * result + name.hashCode();
114+
result = 31 * result + Boolean.hashCode(directIndex);
105115
return result;
106116
}
107117

108118
public static class Builder {
109119
private RuleType type;
110120
private RuleExpression source;
111121
private String name;
122+
private Boolean directIndex;
112123

113124
private Builder() {
114125
}
@@ -117,6 +128,7 @@ private Builder(MemberAccessExpression expr) {
117128
this.type = expr.type;
118129
this.source = expr.source;
119130
this.name = expr.name;
131+
this.directIndex = expr.directIndex;
120132
}
121133

122134
public Builder source(RuleExpression source) {
@@ -134,6 +146,11 @@ public Builder type(RuleType type) {
134146
return this;
135147
}
136148

149+
public Builder directIndex(Boolean directIndex) {
150+
this.directIndex = directIndex;
151+
return this;
152+
}
153+
137154
public MemberAccessExpression build() {
138155
return new MemberAccessExpression(this);
139156
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules2/RewriteRuleExpressionVisitor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public RuleExpression visitMemberAccessExpression(MemberAccessExpression e) {
7676
.builder()
7777
.name(e.name())
7878
.type(e.type())
79+
.directIndex(e.directIndex())
7980
.source(e.source().accept(this));
8081
return builder.build();
8182
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules2/Tokenizer.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,19 @@ public void consumeIndexed(BiConsumer<String, Integer> consumer) {
211211
index += 4;
212212
}
213213

214+
// e.g., [123]
215+
public boolean isDirectIndexedAccess() {
216+
return matches(TokenKind.OPEN_SQUARE, TokenKind.NUMBER, TokenKind.CLOSE_SQUARE);
217+
}
218+
219+
public void consumeDirectIndexed(Consumer<Integer> consumer) {
220+
if (!isDirectIndexedAccess()) {
221+
throw new IllegalStateException("not at direct indexed");
222+
}
223+
consumer.accept(Integer.parseInt(tokens.get(index + 1).value));
224+
index += 3;
225+
}
226+
214227
// e.g., {url#scheme}
215228
public boolean isNamedAccess() {
216229
return matches(TokenKind.OPEN_CURLY, TokenKind.IDENTIFIER, TokenKind.HASH, TokenKind.IDENTIFIER, TokenKind.CLOSE_CURLY);

codegen/src/test/java/software/amazon/awssdk/codegen/poet/rules2/TokenizerTest.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,19 @@ public void recognizesIndexedExpression() {
3636
assertTrue(tokenizer.isIndexedAccess());
3737
tokenizer.consumeIndexed((name, index) -> {
3838
assertEquals("foobar", name);
39-
assertEquals(index, 123);
39+
assertEquals(123, index);
4040
});
4141
assertTrue(tokenizer.atEof());
4242
}
4343

44+
@Test
45+
public void recognizesDirectIndexedExpression() {
46+
Tokenizer tokenizer = new Tokenizer("[123]");
47+
assertTrue(tokenizer.isDirectIndexedAccess());
48+
tokenizer.consumeDirectIndexed(i -> assertEquals(123, i));
49+
assertTrue(tokenizer.atEof());
50+
}
51+
4452
@Test
4553
public void recognizesNamedAccessExpression() {
4654
Tokenizer tokenizer = new Tokenizer("{url#authority}");
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
{
22
"endpointAuthSchemeConfig": {
33
"knownEndpointProperties": "software.module.test.KNOWN_PROPS"
4-
}
4+
},
5+
"endpointParameters": {
6+
"ArnList": {
7+
"required": false,
8+
"documentation": "Parameter from the customization config",
9+
"type": "StringArray"
10+
}
11+
}
512
}

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/query/customization.config

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
"required": false,
1111
"documentation": "Parameter from the customization config",
1212
"type": "StringArray"
13+
},
14+
"ArnList": {
15+
"required": false,
16+
"documentation": "Parameter from the customization config",
17+
"type": "StringArray"
1318
}
1419
},
1520
"customOperationContextParams": [

0 commit comments

Comments
 (0)