From f30bb25f5884c3756139c23a43faea06f17d625d Mon Sep 17 00:00:00 2001 From: yibole Date: Fri, 10 Jul 2026 15:20:17 -0700 Subject: [PATCH 1/2] refactor namestrategy to not dependent on c2j shapes --- .../amazon/awssdk/codegen/AddShapes.java | 25 +++-- .../codegen/naming/DefaultNamingStrategy.java | 41 ++++--- .../awssdk/codegen/naming/NamingStrategy.java | 20 ++-- .../awssdk/codegen/naming/ShapeContext.java | 95 ++++++++++++++++ .../naming/DefaultNamingStrategyTest.java | 21 ++-- .../codegen/naming/ShapeContextTest.java | 105 ++++++++++++++++++ 6 files changed, 257 insertions(+), 50 deletions(-) create mode 100644 codegen/src/main/java/software/amazon/awssdk/codegen/naming/ShapeContext.java create mode 100644 codegen/src/test/java/software/amazon/awssdk/codegen/naming/ShapeContextTest.java diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/AddShapes.java b/codegen/src/main/java/software/amazon/awssdk/codegen/AddShapes.java index e6daed69595e..aad0b91c8626 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/AddShapes.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/AddShapes.java @@ -42,6 +42,7 @@ import software.amazon.awssdk.codegen.model.service.ServiceModel; import software.amazon.awssdk.codegen.model.service.Shape; import software.amazon.awssdk.codegen.naming.NamingStrategy; +import software.amazon.awssdk.codegen.naming.ShapeContext; import software.amazon.awssdk.codegen.utils.ProtocolUtils; import software.amazon.awssdk.codegen.validation.ModelInvalidException; import software.amazon.awssdk.codegen.validation.ValidationEntry; @@ -158,7 +159,9 @@ private MemberModel generateMemberModel(String c2jMemberName, Member c2jMemberDe Map allC2jShapes) { String c2jShapeName = c2jMemberDefinition.getShape(); Shape shape = allC2jShapes.get(c2jShapeName); - String variableName = getNamingStrategy().getVariableName(c2jMemberName, parentShape); + ShapeContext parentCtx = ShapeContext.ofC2j(parentShape, allC2jShapes); + ShapeContext shapeCtx = ShapeContext.ofC2j(shape, allC2jShapes); + String variableName = getNamingStrategy().getVariableName(c2jMemberName, parentCtx); String variableType = getTypeUtils().getJavaDataType(allC2jShapes, c2jShapeName); String variableDeclarationType = getTypeUtils().getJavaDataType(allC2jShapes, c2jShapeName); @@ -187,13 +190,13 @@ private MemberModel generateMemberModel(String c2jMemberName, Member c2jMemberDe memberModel.setDeprecatedMessage(c2jMemberDefinition.getDeprecatedMessage()); memberModel.setSensitive(isSensitiveShapeOrContainer(c2jMemberDefinition, allC2jShapes)); memberModel - .withFluentGetterMethodName(namingStrategy.getFluentGetterMethodName(c2jMemberName, parentShape, shape)) - .withFluentEnumGetterMethodName(namingStrategy.getFluentEnumGetterMethodName(c2jMemberName, parentShape, shape)) - .withFluentSetterMethodName(namingStrategy.getFluentSetterMethodName(c2jMemberName, parentShape, shape)) - .withFluentEnumSetterMethodName(namingStrategy.getFluentEnumSetterMethodName(c2jMemberName, parentShape, shape)) - .withExistenceCheckMethodName(namingStrategy.getExistenceCheckMethodName(c2jMemberName, parentShape)) - .withBeanStyleGetterMethodName(namingStrategy.getBeanStyleGetterMethodName(c2jMemberName, parentShape, shape)) - .withBeanStyleSetterMethodName(namingStrategy.getBeanStyleSetterMethodName(c2jMemberName, parentShape, shape)); + .withFluentGetterMethodName(namingStrategy.getFluentGetterMethodName(c2jMemberName, parentCtx, shapeCtx)) + .withFluentEnumGetterMethodName(namingStrategy.getFluentEnumGetterMethodName(c2jMemberName, parentCtx, shapeCtx)) + .withFluentSetterMethodName(namingStrategy.getFluentSetterMethodName(c2jMemberName, parentCtx, shapeCtx)) + .withFluentEnumSetterMethodName(namingStrategy.getFluentEnumSetterMethodName(c2jMemberName, parentCtx, shapeCtx)) + .withExistenceCheckMethodName(namingStrategy.getExistenceCheckMethodName(c2jMemberName, parentCtx)) + .withBeanStyleGetterMethodName(namingStrategy.getBeanStyleGetterMethodName(c2jMemberName, parentCtx, shapeCtx)) + .withBeanStyleSetterMethodName(namingStrategy.getBeanStyleSetterMethodName(c2jMemberName, parentCtx, shapeCtx)); memberModel.setIdempotencyToken(c2jMemberDefinition.isIdempotencyToken()); memberModel.setEventPayload(c2jMemberDefinition.isEventpayload()); memberModel.setEventHeader(c2jMemberDefinition.isEventheader()); @@ -229,11 +232,11 @@ private MemberModel generateMemberModel(String c2jMemberName, Member c2jMemberDe memberModel.setDeprecatedName(deprecatedName); memberModel.setDeprecatedFluentGetterMethodName( - namingStrategy.getFluentGetterMethodName(deprecatedName, parentShape, shape)); + namingStrategy.getFluentGetterMethodName(deprecatedName, parentCtx, shapeCtx)); memberModel.setDeprecatedFluentSetterMethodName( - namingStrategy.getFluentSetterMethodName(deprecatedName, parentShape, shape)); + namingStrategy.getFluentSetterMethodName(deprecatedName, parentCtx, shapeCtx)); memberModel.setDeprecatedBeanStyleSetterMethodName( - namingStrategy.getBeanStyleSetterMethodName(deprecatedName, parentShape, shape)); + namingStrategy.getBeanStyleSetterMethodName(deprecatedName, parentCtx, shapeCtx)); } ParameterHttpMapping httpMapping = generateParameterHttpMapping(parentShape, diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/naming/DefaultNamingStrategy.java b/codegen/src/main/java/software/amazon/awssdk/codegen/naming/DefaultNamingStrategy.java index b9fcf41e6e0f..e6ac37d4e5cd 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/naming/DefaultNamingStrategy.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/naming/DefaultNamingStrategy.java @@ -43,7 +43,6 @@ import software.amazon.awssdk.codegen.model.intermediate.MemberModel; import software.amazon.awssdk.codegen.model.intermediate.Metadata; import software.amazon.awssdk.codegen.model.service.ServiceModel; -import software.amazon.awssdk.codegen.model.service.Shape; import software.amazon.awssdk.codegen.validation.ModelInvalidException; import software.amazon.awssdk.codegen.validation.ValidationEntry; import software.amazon.awssdk.codegen.validation.ValidationErrorId; @@ -300,7 +299,7 @@ public String getVariableName(String name) { } @Override - public String getVariableName(String name, Shape parentShape) { + public String getVariableName(String name, ShapeContext parentShape) { if (isJavaKeyword(name) || isDisallowedNameForShape(unCapitalize(name), parentShape)) { return unCapitalize(name + CONFLICTING_NAME_SUFFIX); @@ -342,15 +341,15 @@ public String getShapeClassName(String shapeName) { } @Override - public String getFluentGetterMethodName(String memberName, Shape parentShape, Shape shape) { + public String getFluentGetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape) { String getterMethodName = Utils.unCapitalize(memberName); getterMethodName = rewriteInvalidMemberName(getterMethodName, parentShape); - if (Utils.isOrContainsEnumShape(shape, serviceModel.getShapes())) { + if (shape.isOrContainsEnum()) { getterMethodName += "AsString"; - if (Utils.isListShape(shape) || Utils.isMapShape(shape)) { + if (shape.isList() || shape.isMap()) { getterMethodName += "s"; } } @@ -359,8 +358,8 @@ public String getFluentGetterMethodName(String memberName, Shape parentShape, Sh } @Override - public String getFluentEnumGetterMethodName(String memberName, Shape parentShape, Shape shape) { - if (!Utils.isOrContainsEnumShape(shape, serviceModel.getShapes())) { + public String getFluentEnumGetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape) { + if (!shape.isOrContainsEnum()) { return null; } @@ -370,39 +369,37 @@ public String getFluentEnumGetterMethodName(String memberName, Shape parentShape } @Override - public String getExistenceCheckMethodName(String memberName, Shape parentShape) { + public String getExistenceCheckMethodName(String memberName, ShapeContext parentShape) { String existenceCheckMethodName = Utils.unCapitalize(memberName); existenceCheckMethodName = rewriteInvalidMemberName(existenceCheckMethodName, parentShape); return String.format("has%s", Utils.capitalize(existenceCheckMethodName)); } @Override - public String getBeanStyleGetterMethodName(String memberName, Shape parentShape, Shape c2jShape) { + public String getBeanStyleGetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape) { String fluentGetterMethodName; - if (Utils.isOrContainsEnumShape(c2jShape, serviceModel.getShapes())) { + if (shape.isOrContainsEnum()) { // Use the enum (modeled) name for bean-style getters - fluentGetterMethodName = getFluentEnumGetterMethodName(memberName, parentShape, c2jShape); + fluentGetterMethodName = getFluentEnumGetterMethodName(memberName, parentShape, shape); } else { - fluentGetterMethodName = getFluentGetterMethodName(memberName, parentShape, c2jShape); + fluentGetterMethodName = getFluentGetterMethodName(memberName, parentShape, shape); } return String.format("get%s", Utils.capitalize(fluentGetterMethodName)); } @Override - public String getBeanStyleSetterMethodName(String memberName, Shape parentShape, Shape c2jShape) { - String beanStyleGetter = getBeanStyleGetterMethodName(memberName, parentShape, c2jShape); + public String getBeanStyleSetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape) { + String beanStyleGetter = getBeanStyleGetterMethodName(memberName, parentShape, shape); return String.format("set%s", beanStyleGetter.substring("get".length())); } @Override - public String getFluentSetterMethodName(String memberName, Shape parentShape, Shape shape) { + public String getFluentSetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape) { String setterMethodName = Utils.unCapitalize(memberName); setterMethodName = rewriteInvalidMemberName(setterMethodName, parentShape); - if (Utils.isOrContainsEnumShape(shape, serviceModel.getShapes()) && - (Utils.isListShape(shape) || Utils.isMapShape(shape))) { - + if (shape.isOrContainsEnum() && (shape.isList() || shape.isMap())) { setterMethodName += "WithStrings"; } @@ -410,8 +407,8 @@ public String getFluentSetterMethodName(String memberName, Shape parentShape, Sh } @Override - public String getFluentEnumSetterMethodName(String memberName, Shape parentShape, Shape shape) { - if (!Utils.isOrContainsEnumShape(shape, serviceModel.getShapes())) { + public String getFluentEnumSetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape) { + if (!shape.isOrContainsEnum()) { return null; } @@ -430,7 +427,7 @@ public String getUnionEnumTypeName(MemberModel memberModel) { return screamCase(memberModel.getName()); } - private String rewriteInvalidMemberName(String memberName, Shape parentShape) { + private String rewriteInvalidMemberName(String memberName, ShapeContext parentShape) { if (isJavaKeyword(memberName) || isDisallowedNameForShape(memberName, parentShape)) { return Utils.unCapitalize(memberName + CONFLICTING_NAME_SUFFIX); } @@ -438,7 +435,7 @@ private String rewriteInvalidMemberName(String memberName, Shape parentShape) { return memberName; } - private boolean isDisallowedNameForShape(String name, Shape parentShape) { + private boolean isDisallowedNameForShape(String name, ShapeContext parentShape) { // Reserve the name "type" for union shapes. if (parentShape.isUnion() && "type".equals(name)) { return true; diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/naming/NamingStrategy.java b/codegen/src/main/java/software/amazon/awssdk/codegen/naming/NamingStrategy.java index 5b22363970f0..3cd174e77165 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/naming/NamingStrategy.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/naming/NamingStrategy.java @@ -17,7 +17,6 @@ import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel; import software.amazon.awssdk.codegen.model.intermediate.MemberModel; -import software.amazon.awssdk.codegen.model.service.Shape; import software.amazon.awssdk.core.SdkField; /** @@ -129,7 +128,7 @@ public interface NamingStrategy { * @param parentShape The shape containing the member, used to check for shape-specific reserved names. * @return Appropriate name to use for a Java variable or field. */ - String getVariableName(String name, Shape parentShape); + String getVariableName(String name, ShapeContext parentShape); /** * @param enumValue Enum value as defined in the service model used to derive the java name. @@ -145,42 +144,45 @@ public interface NamingStrategy { /** * @param memberName Member name to name getter for. + * @param parentShape Shape containing the member. * @param shape The shape associated with the member. * @return Name of the getter method for a model class member. */ - String getFluentGetterMethodName(String memberName, Shape parentShape, Shape shape); + String getFluentGetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape); /** * @param memberName The full member to get the name for. + * @param parentShape Shape containing the member. * @param shape The shape associated with the member. * @return Name of the getter method for an enum model class member. */ - String getFluentEnumGetterMethodName(String memberName, Shape parentShape, Shape shape); + String getFluentEnumGetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape); /** * @param memberName Member name to name getter for. * @return Name of the JavaBean getter method for model class member. */ - String getBeanStyleGetterMethodName(String memberName, Shape parentShape, Shape c2jShape); + String getBeanStyleGetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape); /** * @param memberName Member name to name setter for. * @return Name of the JavaBean setter method for model class member. */ - String getBeanStyleSetterMethodName(String memberName, Shape parentShape, Shape c2jShape); + String getBeanStyleSetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape); /** * @param memberName Member name to name fluent setter for. * @return Appropriate name to use for fluent setter method (i.e. withFoo) for a model class member. */ - String getFluentSetterMethodName(String memberName, Shape parentShape, Shape shape); + String getFluentSetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape); /** * @param memberName The full member to get the name for. + * @param parentShape Shape containing the member. * @param shape The shape associated with the member. * @return Name of the getter method for an enum model class member. */ - String getFluentEnumSetterMethodName(String memberName, Shape parentShape, Shape shape); + String getFluentEnumSetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape); /** * Stuttering is intentional, returns the name of the {@link SdkField} field. @@ -205,7 +207,7 @@ public interface NamingStrategy { * @param parentShape The shape containing the member. * @return Name of an existence check method. */ - String getExistenceCheckMethodName(String memberName, Shape parentShape); + String getExistenceCheckMethodName(String memberName, ShapeContext parentShape); /** * Retrieve the service's signing name that should be used based on the model. diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/naming/ShapeContext.java b/codegen/src/main/java/software/amazon/awssdk/codegen/naming/ShapeContext.java new file mode 100644 index 000000000000..5f10d155a656 --- /dev/null +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/naming/ShapeContext.java @@ -0,0 +1,95 @@ +/* + * 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.naming; + +import java.util.Map; +import software.amazon.awssdk.codegen.internal.Utils; +import software.amazon.awssdk.codegen.model.service.Shape; + +/** + * Minimal view of a shape that {@link NamingStrategy} needs when deriving + * names. Decouples the naming strategy from the concrete + * {@link software.amazon.awssdk.codegen.model.service.Shape} type so a + * Smithy-backed implementation can supply the same information without + * constructing a C2J shape. + */ +public interface ShapeContext { + + /** + * @return true if the shape is a union (member named {@code "type"} is reserved). + */ + boolean isUnion(); + + /** + * @return true if the shape is an exception (exception-reserved method names apply). + */ + boolean isException(); + + /** + * @return true if the shape is or transitively contains an enum shape. For + * list and map shapes, this recurses into member targets. + */ + boolean isOrContainsEnum(); + + /** + * @return true if the shape is a list. + */ + boolean isList(); + + /** + * @return true if the shape is a map. + */ + boolean isMap(); + + /** + * Adapts a C2J {@link Shape} into a {@link ShapeContext}. Uses the same + * predicates {@link Utils} exposes today, so behavior is unchanged from + * pre-refactor call sites. + * + * @param shape the C2J shape (must not be null when the caller intends to + * query any of the shape-relative predicates). + * @param allShapes the service's full shape map, needed for the + * {@code isOrContainsEnum} recursion into list/map targets. + */ + static ShapeContext ofC2j(Shape shape, Map allShapes) { + return new ShapeContext() { + @Override + public boolean isUnion() { + return shape.isUnion(); + } + + @Override + public boolean isException() { + return shape.isException(); + } + + @Override + public boolean isOrContainsEnum() { + return Utils.isOrContainsEnumShape(shape, allShapes); + } + + @Override + public boolean isList() { + return Utils.isListShape(shape); + } + + @Override + public boolean isMap() { + return Utils.isMapShape(shape); + } + }; + } +} diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/naming/DefaultNamingStrategyTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/naming/DefaultNamingStrategyTest.java index 8760ce9cb4a2..abb89c0908d1 100644 --- a/codegen/src/test/java/software/amazon/awssdk/codegen/naming/DefaultNamingStrategyTest.java +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/naming/DefaultNamingStrategyTest.java @@ -131,16 +131,16 @@ public void enumNamesConvertCorrectly() { @Test public void test_GetFluentSetterMethodName_NoEnum() { - when(serviceModel.getShapes()).thenReturn(mockShapeMap); when(mockShape.getEnumValues()).thenReturn(null); when(mockShape.getType()).thenReturn("foo"); - assertThat(strat.getFluentSetterMethodName("AwesomeMethod", mockParentShape, mockShape)).isEqualTo("awesomeMethod"); + ShapeContext parentCtx = ShapeContext.ofC2j(mockParentShape, mockShapeMap); + ShapeContext shapeCtx = ShapeContext.ofC2j(mockShape, mockShapeMap); + assertThat(strat.getFluentSetterMethodName("AwesomeMethod", parentCtx, shapeCtx)).isEqualTo("awesomeMethod"); } @Test public void test_GetFluentSetterMethodName_NoEnum_WithList() { - when(serviceModel.getShapes()).thenReturn(mockShapeMap); when(mockShapeMap.get(eq("MockStringShape"))).thenReturn(mockStringShape); when(mockShape.getEnumValues()).thenReturn(null); @@ -152,21 +152,23 @@ public void test_GetFluentSetterMethodName_NoEnum_WithList() { when(member.getShape()).thenReturn("MockStringShape"); - assertThat(strat.getFluentSetterMethodName("AwesomeMethod", mockParentShape, mockShape)).isEqualTo("awesomeMethod"); + ShapeContext parentCtx = ShapeContext.ofC2j(mockParentShape, mockShapeMap); + ShapeContext shapeCtx = ShapeContext.ofC2j(mockShape, mockShapeMap); + assertThat(strat.getFluentSetterMethodName("AwesomeMethod", parentCtx, shapeCtx)).isEqualTo("awesomeMethod"); } @Test public void test_GetFluentSetterMethodName_WithEnumShape_NoListOrMap() { - when(serviceModel.getShapes()).thenReturn(mockShapeMap); when(mockShape.getEnumValues()).thenReturn(new ArrayList<>()); when(mockShape.getType()).thenReturn("foo"); - assertThat(strat.getFluentSetterMethodName("AwesomeMethod", mockParentShape, mockShape)).isEqualTo("awesomeMethod"); + ShapeContext parentCtx = ShapeContext.ofC2j(mockParentShape, mockShapeMap); + ShapeContext shapeCtx = ShapeContext.ofC2j(mockShape, mockShapeMap); + assertThat(strat.getFluentSetterMethodName("AwesomeMethod", parentCtx, shapeCtx)).isEqualTo("awesomeMethod"); } @Test public void test_GetFluentSetterMethodName_WithEnumShape_WithList() { - when(serviceModel.getShapes()).thenReturn(mockShapeMap); when(mockShapeMap.get(eq("MockStringShape"))).thenReturn(mockStringShape); when(mockShape.getEnumValues()).thenReturn(null); @@ -178,7 +180,10 @@ public void test_GetFluentSetterMethodName_WithEnumShape_WithList() { when(member.getShape()).thenReturn("MockStringShape"); - assertThat(strat.getFluentSetterMethodName("AwesomeMethod", mockParentShape, mockShape)).isEqualTo("awesomeMethodWithStrings"); + ShapeContext parentCtx = ShapeContext.ofC2j(mockParentShape, mockShapeMap); + ShapeContext shapeCtx = ShapeContext.ofC2j(mockShape, mockShapeMap); + assertThat(strat.getFluentSetterMethodName("AwesomeMethod", parentCtx, shapeCtx)).isEqualTo( + "awesomeMethodWithStrings"); } @Test diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/naming/ShapeContextTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/naming/ShapeContextTest.java new file mode 100644 index 000000000000..3c35a04ef076 --- /dev/null +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/naming/ShapeContextTest.java @@ -0,0 +1,105 @@ +/* + * 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.naming; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.codegen.model.service.Member; +import software.amazon.awssdk.codegen.model.service.Shape; + +/** + * Verifies the {@link ShapeContext#ofC2j(Shape, Map)} adapter routes each of + * the five predicates to the correct backing accessor. Guards against wiring + * mistakes (swapped predicates, dropped {@code allShapes} argument) that would + * otherwise silently propagate through {@link NamingStrategy}. + */ +class ShapeContextTest { + + private static Shape shape(String type) { + Shape s = new Shape(); + s.setType(type); + return s; + } + + private static Shape enumShape() { + Shape s = shape("string"); + s.setEnumValues(asList("A", "B")); + return s; + } + + private static Member memberTargeting(String targetShapeName) { + Member m = new Member(); + m.setShape(targetShapeName); + return m; + } + + private static Shape listOf(String memberShapeName) { + Shape s = shape("list"); + s.setListMember(memberTargeting(memberShapeName)); + return s; + } + + private static Shape mapOf(String keyShapeName, String valueShapeName) { + Shape s = shape("map"); + s.setMapKeyType(memberTargeting(keyShapeName)); + s.setMapValueType(memberTargeting(valueShapeName)); + return s; + } + + @Test + void unionAndExceptionPredicates_wireToBackingShape() { + Shape union = shape("structure"); + union.setUnion(true); + Shape exception = shape("structure"); + exception.setException(true); + + assertThat(ShapeContext.ofC2j(union, emptyMap()).isUnion()).isTrue(); + assertThat(ShapeContext.ofC2j(union, emptyMap()).isException()).isFalse(); + assertThat(ShapeContext.ofC2j(exception, emptyMap()).isException()).isTrue(); + assertThat(ShapeContext.ofC2j(exception, emptyMap()).isUnion()).isFalse(); + } + + @Test + void listAndMapPredicates_notSwapped() { + Map shapes = singletonMap("String", shape("string")); + + ShapeContext list = ShapeContext.ofC2j(listOf("String"), shapes); + ShapeContext map = ShapeContext.ofC2j(mapOf("String", "String"), shapes); + + assertThat(list.isList()).isTrue(); + assertThat(list.isMap()).isFalse(); + assertThat(map.isMap()).isTrue(); + assertThat(map.isList()).isFalse(); + } + + @Test + void isOrContainsEnum_recursesThroughAllShapes() { + Map shapes = new HashMap<>(); + shapes.put("Status", enumShape()); + shapes.put("Name", shape("string")); + + assertThat(ShapeContext.ofC2j(listOf("Status"), shapes).isOrContainsEnum()).isTrue(); + assertThat(ShapeContext.ofC2j(mapOf("Name", "Status"), shapes).isOrContainsEnum()).isTrue(); + + assertThat(ShapeContext.ofC2j(listOf("Name"), shapes).isOrContainsEnum()).isFalse(); + } +} From 34876b3ad21b9cf5340ec57d2641d0740c16f0ca Mon Sep 17 00:00:00 2001 From: yibole Date: Fri, 10 Jul 2026 15:32:22 -0700 Subject: [PATCH 2/2] modify java doc --- .../amazon/awssdk/codegen/naming/ShapeContext.java | 12 +++++++----- .../awssdk/codegen/naming/ShapeContextTest.java | 6 ------ 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/naming/ShapeContext.java b/codegen/src/main/java/software/amazon/awssdk/codegen/naming/ShapeContext.java index 5f10d155a656..2cb5ee945318 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/naming/ShapeContext.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/naming/ShapeContext.java @@ -20,11 +20,13 @@ import software.amazon.awssdk.codegen.model.service.Shape; /** - * Minimal view of a shape that {@link NamingStrategy} needs when deriving - * names. Decouples the naming strategy from the concrete - * {@link software.amazon.awssdk.codegen.model.service.Shape} type so a - * Smithy-backed implementation can supply the same information without - * constructing a C2J shape. + * A read-only view of a service model shape, exposing the predicates + * ({@link #isUnion()}, {@link #isException()}, {@link #isList()}, + * {@link #isMap()}, {@link #isOrContainsEnum()}) that {@link NamingStrategy} + * consults when deriving Java names for members and accessors. + * + *

Adapters for specific model formats are provided as static factories; + * see {@link #ofC2j(Shape, Map)}. */ public interface ShapeContext { diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/naming/ShapeContextTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/naming/ShapeContextTest.java index 3c35a04ef076..d7ce6724ea5a 100644 --- a/codegen/src/test/java/software/amazon/awssdk/codegen/naming/ShapeContextTest.java +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/naming/ShapeContextTest.java @@ -26,12 +26,6 @@ import software.amazon.awssdk.codegen.model.service.Member; import software.amazon.awssdk.codegen.model.service.Shape; -/** - * Verifies the {@link ShapeContext#ofC2j(Shape, Map)} adapter routes each of - * the five predicates to the correct backing accessor. Guards against wiring - * mistakes (swapped predicates, dropped {@code allShapes} argument) that would - * otherwise silently propagate through {@link NamingStrategy}. - */ class ShapeContextTest { private static Shape shape(String type) {