From f30bb25f5884c3756139c23a43faea06f17d625d Mon Sep 17 00:00:00 2001 From: yibole Date: Fri, 10 Jul 2026 15:20:17 -0700 Subject: [PATCH 1/4] 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/4] 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) { From 19ad4abb745d933dfe0cc04ec80b1383720cec98 Mon Sep 17 00:00:00 2001 From: yibole Date: Tue, 14 Jul 2026 16:10:50 -0700 Subject: [PATCH 3/4] change class name to shapeinfo --- .../amazon/awssdk/codegen/AddShapes.java | 6 +- .../codegen/naming/DefaultNamingStrategy.java | 20 ++-- .../awssdk/codegen/naming/NamingStrategy.java | 16 +-- .../awssdk/codegen/naming/ShapeInfo.java | 97 ++++++++++++++++++ .../naming/DefaultNamingStrategyTest.java | 16 +-- .../awssdk/codegen/naming/ShapeInfoTest.java | 99 +++++++++++++++++++ 6 files changed, 225 insertions(+), 29 deletions(-) create mode 100644 codegen/src/main/java/software/amazon/awssdk/codegen/naming/ShapeInfo.java create mode 100644 codegen/src/test/java/software/amazon/awssdk/codegen/naming/ShapeInfoTest.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 aad0b91c8626..bf3f63f3da30 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/AddShapes.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/AddShapes.java @@ -42,7 +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.naming.ShapeInfo; import software.amazon.awssdk.codegen.utils.ProtocolUtils; import software.amazon.awssdk.codegen.validation.ModelInvalidException; import software.amazon.awssdk.codegen.validation.ValidationEntry; @@ -159,8 +159,8 @@ private MemberModel generateMemberModel(String c2jMemberName, Member c2jMemberDe Map allC2jShapes) { String c2jShapeName = c2jMemberDefinition.getShape(); Shape shape = allC2jShapes.get(c2jShapeName); - ShapeContext parentCtx = ShapeContext.ofC2j(parentShape, allC2jShapes); - ShapeContext shapeCtx = ShapeContext.ofC2j(shape, allC2jShapes); + ShapeInfo parentCtx = ShapeInfo.ofC2j(parentShape, allC2jShapes); + ShapeInfo shapeCtx = ShapeInfo.ofC2j(shape, allC2jShapes); String variableName = getNamingStrategy().getVariableName(c2jMemberName, parentCtx); String variableType = getTypeUtils().getJavaDataType(allC2jShapes, c2jShapeName); String variableDeclarationType = getTypeUtils().getJavaDataType(allC2jShapes, c2jShapeName); 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 e6ac37d4e5cd..419599fcac03 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 @@ -299,7 +299,7 @@ public String getVariableName(String name) { } @Override - public String getVariableName(String name, ShapeContext parentShape) { + public String getVariableName(String name, ShapeInfo parentShape) { if (isJavaKeyword(name) || isDisallowedNameForShape(unCapitalize(name), parentShape)) { return unCapitalize(name + CONFLICTING_NAME_SUFFIX); @@ -341,7 +341,7 @@ public String getShapeClassName(String shapeName) { } @Override - public String getFluentGetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape) { + public String getFluentGetterMethodName(String memberName, ShapeInfo parentShape, ShapeInfo shape) { String getterMethodName = Utils.unCapitalize(memberName); getterMethodName = rewriteInvalidMemberName(getterMethodName, parentShape); @@ -358,7 +358,7 @@ public String getFluentGetterMethodName(String memberName, ShapeContext parentSh } @Override - public String getFluentEnumGetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape) { + public String getFluentEnumGetterMethodName(String memberName, ShapeInfo parentShape, ShapeInfo shape) { if (!shape.isOrContainsEnum()) { return null; } @@ -369,14 +369,14 @@ public String getFluentEnumGetterMethodName(String memberName, ShapeContext pare } @Override - public String getExistenceCheckMethodName(String memberName, ShapeContext parentShape) { + public String getExistenceCheckMethodName(String memberName, ShapeInfo parentShape) { String existenceCheckMethodName = Utils.unCapitalize(memberName); existenceCheckMethodName = rewriteInvalidMemberName(existenceCheckMethodName, parentShape); return String.format("has%s", Utils.capitalize(existenceCheckMethodName)); } @Override - public String getBeanStyleGetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape) { + public String getBeanStyleGetterMethodName(String memberName, ShapeInfo parentShape, ShapeInfo shape) { String fluentGetterMethodName; if (shape.isOrContainsEnum()) { // Use the enum (modeled) name for bean-style getters @@ -388,13 +388,13 @@ public String getBeanStyleGetterMethodName(String memberName, ShapeContext paren } @Override - public String getBeanStyleSetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape) { + public String getBeanStyleSetterMethodName(String memberName, ShapeInfo parentShape, ShapeInfo shape) { String beanStyleGetter = getBeanStyleGetterMethodName(memberName, parentShape, shape); return String.format("set%s", beanStyleGetter.substring("get".length())); } @Override - public String getFluentSetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape) { + public String getFluentSetterMethodName(String memberName, ShapeInfo parentShape, ShapeInfo shape) { String setterMethodName = Utils.unCapitalize(memberName); setterMethodName = rewriteInvalidMemberName(setterMethodName, parentShape); @@ -407,7 +407,7 @@ public String getFluentSetterMethodName(String memberName, ShapeContext parentSh } @Override - public String getFluentEnumSetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape) { + public String getFluentEnumSetterMethodName(String memberName, ShapeInfo parentShape, ShapeInfo shape) { if (!shape.isOrContainsEnum()) { return null; } @@ -427,7 +427,7 @@ public String getUnionEnumTypeName(MemberModel memberModel) { return screamCase(memberModel.getName()); } - private String rewriteInvalidMemberName(String memberName, ShapeContext parentShape) { + private String rewriteInvalidMemberName(String memberName, ShapeInfo parentShape) { if (isJavaKeyword(memberName) || isDisallowedNameForShape(memberName, parentShape)) { return Utils.unCapitalize(memberName + CONFLICTING_NAME_SUFFIX); } @@ -435,7 +435,7 @@ private String rewriteInvalidMemberName(String memberName, ShapeContext parentSh return memberName; } - private boolean isDisallowedNameForShape(String name, ShapeContext parentShape) { + private boolean isDisallowedNameForShape(String name, ShapeInfo 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 3cd174e77165..e1ec77c289f7 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 @@ -128,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, ShapeContext parentShape); + String getVariableName(String name, ShapeInfo parentShape); /** * @param enumValue Enum value as defined in the service model used to derive the java name. @@ -148,7 +148,7 @@ public interface NamingStrategy { * @param shape The shape associated with the member. * @return Name of the getter method for a model class member. */ - String getFluentGetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape); + String getFluentGetterMethodName(String memberName, ShapeInfo parentShape, ShapeInfo shape); /** * @param memberName The full member to get the name for. @@ -156,25 +156,25 @@ public interface NamingStrategy { * @param shape The shape associated with the member. * @return Name of the getter method for an enum model class member. */ - String getFluentEnumGetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape); + String getFluentEnumGetterMethodName(String memberName, ShapeInfo parentShape, ShapeInfo shape); /** * @param memberName Member name to name getter for. * @return Name of the JavaBean getter method for model class member. */ - String getBeanStyleGetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape); + String getBeanStyleGetterMethodName(String memberName, ShapeInfo parentShape, ShapeInfo shape); /** * @param memberName Member name to name setter for. * @return Name of the JavaBean setter method for model class member. */ - String getBeanStyleSetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape); + String getBeanStyleSetterMethodName(String memberName, ShapeInfo parentShape, ShapeInfo 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, ShapeContext parentShape, ShapeContext shape); + String getFluentSetterMethodName(String memberName, ShapeInfo parentShape, ShapeInfo shape); /** * @param memberName The full member to get the name for. @@ -182,7 +182,7 @@ public interface NamingStrategy { * @param shape The shape associated with the member. * @return Name of the getter method for an enum model class member. */ - String getFluentEnumSetterMethodName(String memberName, ShapeContext parentShape, ShapeContext shape); + String getFluentEnumSetterMethodName(String memberName, ShapeInfo parentShape, ShapeInfo shape); /** * Stuttering is intentional, returns the name of the {@link SdkField} field. @@ -207,7 +207,7 @@ public interface NamingStrategy { * @param parentShape The shape containing the member. * @return Name of an existence check method. */ - String getExistenceCheckMethodName(String memberName, ShapeContext parentShape); + String getExistenceCheckMethodName(String memberName, ShapeInfo 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/ShapeInfo.java b/codegen/src/main/java/software/amazon/awssdk/codegen/naming/ShapeInfo.java new file mode 100644 index 000000000000..58ee07539e87 --- /dev/null +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/naming/ShapeInfo.java @@ -0,0 +1,97 @@ +/* + * 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; + +/** + * 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 ShapeInfo { + + /** + * @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 ShapeInfo}. 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 ShapeInfo ofC2j(Shape shape, Map allShapes) { + return new ShapeInfo() { + @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 abb89c0908d1..8ce4a7956c78 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 @@ -134,8 +134,8 @@ public void test_GetFluentSetterMethodName_NoEnum() { when(mockShape.getEnumValues()).thenReturn(null); when(mockShape.getType()).thenReturn("foo"); - ShapeContext parentCtx = ShapeContext.ofC2j(mockParentShape, mockShapeMap); - ShapeContext shapeCtx = ShapeContext.ofC2j(mockShape, mockShapeMap); + ShapeInfo parentCtx = ShapeInfo.ofC2j(mockParentShape, mockShapeMap); + ShapeInfo shapeCtx = ShapeInfo.ofC2j(mockShape, mockShapeMap); assertThat(strat.getFluentSetterMethodName("AwesomeMethod", parentCtx, shapeCtx)).isEqualTo("awesomeMethod"); } @@ -152,8 +152,8 @@ public void test_GetFluentSetterMethodName_NoEnum_WithList() { when(member.getShape()).thenReturn("MockStringShape"); - ShapeContext parentCtx = ShapeContext.ofC2j(mockParentShape, mockShapeMap); - ShapeContext shapeCtx = ShapeContext.ofC2j(mockShape, mockShapeMap); + ShapeInfo parentCtx = ShapeInfo.ofC2j(mockParentShape, mockShapeMap); + ShapeInfo shapeCtx = ShapeInfo.ofC2j(mockShape, mockShapeMap); assertThat(strat.getFluentSetterMethodName("AwesomeMethod", parentCtx, shapeCtx)).isEqualTo("awesomeMethod"); } @@ -162,8 +162,8 @@ public void test_GetFluentSetterMethodName_WithEnumShape_NoListOrMap() { when(mockShape.getEnumValues()).thenReturn(new ArrayList<>()); when(mockShape.getType()).thenReturn("foo"); - ShapeContext parentCtx = ShapeContext.ofC2j(mockParentShape, mockShapeMap); - ShapeContext shapeCtx = ShapeContext.ofC2j(mockShape, mockShapeMap); + ShapeInfo parentCtx = ShapeInfo.ofC2j(mockParentShape, mockShapeMap); + ShapeInfo shapeCtx = ShapeInfo.ofC2j(mockShape, mockShapeMap); assertThat(strat.getFluentSetterMethodName("AwesomeMethod", parentCtx, shapeCtx)).isEqualTo("awesomeMethod"); } @@ -180,8 +180,8 @@ public void test_GetFluentSetterMethodName_WithEnumShape_WithList() { when(member.getShape()).thenReturn("MockStringShape"); - ShapeContext parentCtx = ShapeContext.ofC2j(mockParentShape, mockShapeMap); - ShapeContext shapeCtx = ShapeContext.ofC2j(mockShape, mockShapeMap); + ShapeInfo parentCtx = ShapeInfo.ofC2j(mockParentShape, mockShapeMap); + ShapeInfo shapeCtx = ShapeInfo.ofC2j(mockShape, mockShapeMap); assertThat(strat.getFluentSetterMethodName("AwesomeMethod", parentCtx, shapeCtx)).isEqualTo( "awesomeMethodWithStrings"); } diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/naming/ShapeInfoTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/naming/ShapeInfoTest.java new file mode 100644 index 000000000000..bbe58006d501 --- /dev/null +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/naming/ShapeInfoTest.java @@ -0,0 +1,99 @@ +/* + * 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; + +class ShapeInfoTest { + + 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(ShapeInfo.ofC2j(union, emptyMap()).isUnion()).isTrue(); + assertThat(ShapeInfo.ofC2j(union, emptyMap()).isException()).isFalse(); + assertThat(ShapeInfo.ofC2j(exception, emptyMap()).isException()).isTrue(); + assertThat(ShapeInfo.ofC2j(exception, emptyMap()).isUnion()).isFalse(); + } + + @Test + void listAndMapPredicates_notSwapped() { + Map shapes = singletonMap("String", shape("string")); + + ShapeInfo list = ShapeInfo.ofC2j(listOf("String"), shapes); + ShapeInfo map = ShapeInfo.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(ShapeInfo.ofC2j(listOf("Status"), shapes).isOrContainsEnum()).isTrue(); + assertThat(ShapeInfo.ofC2j(mapOf("Name", "Status"), shapes).isOrContainsEnum()).isTrue(); + + assertThat(ShapeInfo.ofC2j(listOf("Name"), shapes).isOrContainsEnum()).isFalse(); + } +} From 0ef17ba751224fbbc7bbf3037e1f37df28275975 Mon Sep 17 00:00:00 2001 From: yibole Date: Tue, 14 Jul 2026 16:11:57 -0700 Subject: [PATCH 4/4] delete files --- .../awssdk/codegen/naming/ShapeContext.java | 97 ------------------ .../codegen/naming/ShapeContextTest.java | 99 ------------------- 2 files changed, 196 deletions(-) delete mode 100644 codegen/src/main/java/software/amazon/awssdk/codegen/naming/ShapeContext.java delete mode 100644 codegen/src/test/java/software/amazon/awssdk/codegen/naming/ShapeContextTest.java 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 deleted file mode 100644 index 2cb5ee945318..000000000000 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/naming/ShapeContext.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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; - -/** - * 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 { - - /** - * @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/ShapeContextTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/naming/ShapeContextTest.java deleted file mode 100644 index d7ce6724ea5a..000000000000 --- a/codegen/src/test/java/software/amazon/awssdk/codegen/naming/ShapeContextTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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; - -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(); - } -}