Date: Mon, 3 Aug 2026 14:40:04 -0400
Subject: [PATCH 19/28] Add characterization tests pinning generated model
output per protocol
---
.../ProtocolTraitsCharacterizationTest.java | 306 ++++++++++++++++++
1 file changed, 306 insertions(+)
create mode 100644 tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java
new file mode 100644
index 00000000000..bf7543ad855
--- /dev/null
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java
@@ -0,0 +1,306 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+package com.amazonaws.util.awsclientsmithygenerator.generators.model;
+
+import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriterDelegator;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers.RequestRenderer;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers.ResultRenderer;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers.SubObjectRenderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import software.amazon.smithy.build.MockManifest;
+import software.amazon.smithy.model.Model;
+import software.amazon.smithy.model.shapes.OperationShape;
+import software.amazon.smithy.model.shapes.ServiceShape;
+import software.amazon.smithy.model.shapes.ShapeId;
+import software.amazon.smithy.model.shapes.StringShape;
+import software.amazon.smithy.model.shapes.StructureShape;
+import software.amazon.smithy.model.traits.Trait;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Characterization tests: they assert the generated C++ text produced TODAY, so that
+ * the ProtocolTraits refactor can be proven output-preserving.
+ *
+ * If one of these fails during the refactor, the refactor changed generated output.
+ * Fix the production code, NOT the assertion.
+ */
+class ProtocolTraitsCharacterizationTest {
+
+ // ---------- fixtures ----------
+
+ /** Returns the Smithy protocol trait that makes ProtocolResolver resolve to {@code p}. */
+ private static Trait traitFor(Protocol p) {
+ switch (p) {
+ case JSON:
+ return software.amazon.smithy.aws.traits.protocols.RestJson1Trait.builder().build();
+ case CBOR:
+ return software.amazon.smithy.protocol.traits.Rpcv2CborTrait.builder().build();
+ case REST_XML:
+ return software.amazon.smithy.aws.traits.protocols.RestXmlTrait.builder().build();
+ case QUERY_XML:
+ return new software.amazon.smithy.aws.traits.protocols.AwsQueryTrait();
+ case EC2:
+ return new software.amazon.smithy.aws.traits.protocols.Ec2QueryTrait();
+ default:
+ throw new IllegalArgumentException("Unhandled protocol: " + p);
+ }
+ }
+
+ /**
+ * A minimal service with one operation whose input and output each reference a
+ * nested structure, so SubObject / Request / Result renderers all have work to do.
+ */
+ private static Model modelFor(Protocol p) {
+ StringShape str = StringShape.builder().id("com.example#Str").build();
+ StructureShape nested = StructureShape.builder()
+ .id("com.example#Nested")
+ .addMember("value", str.getId())
+ .build();
+ StructureShape input = StructureShape.builder()
+ .id("com.example#DoThingInput")
+ .addMember("name", str.getId())
+ .addMember("nested", nested.getId())
+ .build();
+ StructureShape output = StructureShape.builder()
+ .id("com.example#DoThingOutput")
+ .addMember("nested", nested.getId())
+ .build();
+ OperationShape op = OperationShape.builder()
+ .id("com.example#DoThing")
+ .input(input.getId())
+ .output(output.getId())
+ .build();
+ ServiceShape service = ServiceShape.builder()
+ .id("com.example#Example")
+ .version("2024-01-01")
+ .addTrait(traitFor(p))
+ .addOperation(op.getId())
+ .build();
+ return Model.builder().addShapes(str, nested, input, output, op, service).build();
+ }
+
+ private static ServiceShape serviceOf(Model model) {
+ return model.expectShape(ShapeId.from("com.example#Example"), ServiceShape.class);
+ }
+
+ /** Renders every model file for the protocol and returns file-name -> contents. */
+ private static java.util.Map renderAll(Protocol p) {
+ Model model = modelFor(p);
+ ServiceShape service = serviceOf(model);
+ Protocol resolved = ProtocolResolver.resolve(service, model);
+ assertEquals(p, resolved, "Fixture trait did not resolve to the intended protocol");
+
+ ShapeClassifier.ClassifiedShapes classified = ShapeClassifier.classify(model, service);
+ MockManifest manifest = new MockManifest();
+ CppWriterDelegator delegator = new CppWriterDelegator(manifest);
+
+ // NOTE (Tasks 4-7): when a renderer's ctor switches from Protocol to
+ // ProtocolTraits, change ONLY the argument below -- never the assertions.
+ new SubObjectRenderer(classified.subObjects(), model, service, resolved,
+ "Example", "AWS_EXAMPLE_API", "Example", "example").render(delegator);
+ new RequestRenderer(classified.requests(), model, service, resolved,
+ "Example", "AWS_EXAMPLE_API", "example").render(delegator);
+ new ResultRenderer(classified.results(), model, service, resolved,
+ "Example", "AWS_EXAMPLE_API", "example").render(delegator);
+
+ delegator.flushWriters();
+
+ java.util.Map out = new java.util.TreeMap<>();
+ for (java.nio.file.Path path : manifest.getFiles()) {
+ String name = path.getFileName().toString();
+ out.put(name, manifest.getFileString(path).orElseThrow());
+ }
+ return out;
+ }
+
+ private static String file(Protocol p, String fileName) {
+ java.util.Map files = renderAll(p);
+ String content = files.get(fileName);
+ assertTrue(content != null,
+ "Expected generated file " + fileName + " for " + p + "; got " + files.keySet());
+ return content;
+ }
+
+ // ---------- every protocol produces the same file set ----------
+
+ @ParameterizedTest
+ @EnumSource(Protocol.class)
+ void everyProtocol_generatesTheSameFileSet(Protocol p) {
+ assertEquals(
+ java.util.Set.of("Nested.h", "Nested.cpp",
+ "DoThingRequest.h", "DoThingRequest.cpp",
+ "DoThingResult.h", "DoThingResult.cpp"),
+ renderAll(p).keySet(),
+ "File set drifted for " + p);
+ }
+
+ // ---------- sub-object: serde decls, includes, usings, fwd decls ----------
+
+ @ParameterizedTest
+ @EnumSource(value = Protocol.class, names = {"JSON", "CBOR"})
+ void jsonLike_subObjectHeader_hasJsonSerdeDeclsAndFwdDecls(Protocol p) {
+ String h = file(p, "Nested.h");
+ assertTrue(h.contains("AWS_EXAMPLE_API Nested() = default;"), h);
+ assertTrue(h.contains("AWS_EXAMPLE_API Nested(Aws::Utils::Json::JsonView jsonValue);"), h);
+ assertTrue(h.contains("AWS_EXAMPLE_API Nested& operator=(Aws::Utils::Json::JsonView jsonValue);"), h);
+ assertTrue(h.contains("AWS_EXAMPLE_API Aws::Utils::Json::JsonValue Jsonize() const;"), h);
+ assertTrue(h.contains("class JsonValue;"), h);
+ assertTrue(h.contains("class JsonView;"), h);
+ assertFalse(h.contains("XmlNode"), "JSON-like header must not mention XML: " + h);
+ }
+
+ @Test
+ void restXml_subObjectHeader_hasAddToNodeAndXmlNodeFwdDecl() {
+ String h = file(Protocol.REST_XML, "Nested.h");
+ assertTrue(h.contains("AWS_EXAMPLE_API Nested(const Aws::Utils::Xml::XmlNode& xmlNode);"), h);
+ assertTrue(h.contains("AWS_EXAMPLE_API Nested& operator=(const Aws::Utils::Xml::XmlNode& xmlNode);"), h);
+ assertTrue(h.contains("AWS_EXAMPLE_API void AddToNode(Aws::Utils::Xml::XmlNode& parentNode) const;"), h);
+ assertTrue(h.contains("class XmlNode;"), h);
+ assertFalse(h.contains("Jsonize"), "REST_XML header must not mention Jsonize: " + h);
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Protocol.class, names = {"QUERY_XML", "EC2"})
+ void queryLike_subObjectHeader_hasBothOutputToStreamOverloads(Protocol p) {
+ String h = file(p, "Nested.h");
+ assertTrue(h.contains("AWS_EXAMPLE_API Nested(const Aws::Utils::Xml::XmlNode& xmlNode);"), h);
+ assertTrue(h.contains("AWS_EXAMPLE_API void OutputToStream(Aws::OStream& ostream, "
+ + "const char* location, unsigned index, const char* locationValue) const;"), h);
+ assertTrue(h.contains("AWS_EXAMPLE_API void OutputToStream(Aws::OStream& ostream, "
+ + "const char* location) const;"), h);
+ assertFalse(h.contains("AddToNode"), "Query-like header must not use AddToNode: " + h);
+ assertFalse(h.contains("Jsonize"), "Query-like header must not mention Jsonize: " + h);
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Protocol.class, names = {"JSON", "CBOR"})
+ void jsonLike_subObjectSource_hasJsonIncludeUsingAndStubs(Protocol p) {
+ String c = file(p, "Nested.cpp");
+ assertTrue(c.contains("#include "), c);
+ assertTrue(c.contains("using namespace Aws::Utils::Json;"), c);
+ assertTrue(c.contains("using namespace Aws::Utils;"), c);
+ assertTrue(c.contains("Nested::Nested(JsonView jsonValue) { *this = jsonValue; }"), c);
+ assertTrue(c.contains("Nested& Nested::operator=(JsonView jsonValue) {"), c);
+ assertTrue(c.contains("JsonValue Nested::Jsonize() const {"), c);
+ assertTrue(c.contains("JsonValue payload;"), c);
+ assertFalse(c.contains("XmlSerializer.h"), c);
+ }
+
+ @Test
+ void restXml_subObjectSource_hasXmlIncludeUsingAndStubs() {
+ String c = file(Protocol.REST_XML, "Nested.cpp");
+ assertTrue(c.contains("#include "), c);
+ assertTrue(c.contains("using namespace Aws::Utils::Xml;"), c);
+ assertTrue(c.contains("Nested::Nested(const XmlNode& xmlNode) { *this = xmlNode; }"), c);
+ assertTrue(c.contains("Nested& Nested::operator=(const XmlNode& xmlNode) {"), c);
+ assertTrue(c.contains("void Nested::AddToNode(XmlNode& parentNode) const {"), c);
+ assertFalse(c.contains("JsonSerializer.h"), c);
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Protocol.class, names = {"QUERY_XML", "EC2"})
+ void queryLike_subObjectSource_hasBothOutputToStreamBodies(Protocol p) {
+ String c = file(p, "Nested.cpp");
+ assertTrue(c.contains("#include "), c);
+ assertTrue(c.contains("void Nested::OutputToStream(Aws::OStream& ostream, const char* location, "
+ + "unsigned index, const char* locationValue) const {"), c);
+ assertTrue(c.contains("void Nested::OutputToStream(Aws::OStream& ostream, "
+ + "const char* location) const {"), c);
+ }
+
+ // ---------- result: AmazonWebServiceResult<> payload type ----------
+
+ @ParameterizedTest
+ @EnumSource(value = Protocol.class, names = {"JSON", "CBOR"})
+ void jsonLike_resultHeader_usesJsonValuePayloadAndForwardDeclaresJsonValue(Protocol p) {
+ String h = file(p, "DoThingResult.h");
+ assertTrue(h.contains("AWS_EXAMPLE_API DoThingResult(const "
+ + "Aws::AmazonWebServiceResult& result);"), h);
+ assertTrue(h.contains("AWS_EXAMPLE_API DoThingResult& operator=(const "
+ + "Aws::AmazonWebServiceResult& result);"), h);
+ assertTrue(h.contains("class JsonValue;"), h);
+ // Result headers forward-declare only the document type, never JsonView.
+ assertFalse(h.contains("class JsonView;"),
+ "Result header must not forward-declare JsonView: " + h);
+ assertFalse(h.contains("XmlDocument"), h);
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Protocol.class, names = {"REST_XML", "QUERY_XML", "EC2"})
+ void xmlLike_resultHeader_usesXmlDocumentPayloadAndForwardDeclaresXmlDocument(Protocol p) {
+ String h = file(p, "DoThingResult.h");
+ assertTrue(h.contains("AWS_EXAMPLE_API DoThingResult(const "
+ + "Aws::AmazonWebServiceResult& result);"), h);
+ assertTrue(h.contains("AWS_EXAMPLE_API DoThingResult& operator=(const "
+ + "Aws::AmazonWebServiceResult& result);"), h);
+ assertTrue(h.contains("class XmlDocument;"), h);
+ assertFalse(h.contains("JsonValue"), h);
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Protocol.class, names = {"JSON", "CBOR"})
+ void jsonLike_resultSource_hasJsonUsingsAndCtorImpls(Protocol p) {
+ String c = file(p, "DoThingResult.cpp");
+ assertTrue(c.contains("#include "), c);
+ assertTrue(c.contains("using namespace Aws::Example::Model;"), c);
+ assertTrue(c.contains("using namespace Aws::Utils::Json;"), c);
+ assertTrue(c.contains("using namespace Aws::Utils;"), c);
+ assertTrue(c.contains("using namespace Aws;"), c);
+ assertTrue(c.contains("DoThingResult::DoThingResult(const "
+ + "Aws::AmazonWebServiceResult& result) {"), c);
+ assertTrue(c.contains("DoThingResult& DoThingResult::operator=(const "
+ + "Aws::AmazonWebServiceResult& result) {"), c);
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Protocol.class, names = {"REST_XML", "QUERY_XML", "EC2"})
+ void xmlLike_resultSource_hasXmlUsingsAndCtorImpls(Protocol p) {
+ String c = file(p, "DoThingResult.cpp");
+ assertTrue(c.contains("#include "), c);
+ assertTrue(c.contains("using namespace Aws::Utils::Xml;"), c);
+ assertTrue(c.contains("DoThingResult::DoThingResult(const "
+ + "Aws::AmazonWebServiceResult& result) {"), c);
+ assertTrue(c.contains("DoThingResult& DoThingResult::operator=(const "
+ + "Aws::AmazonWebServiceResult& result) {"), c);
+ }
+
+ // ---------- request: only the serde include + usings are protocol-specific ----------
+
+ @ParameterizedTest
+ @EnumSource(value = Protocol.class, names = {"JSON", "CBOR"})
+ void jsonLike_requestSource_hasJsonIncludeAndUsings(Protocol p) {
+ String c = file(p, "DoThingRequest.cpp");
+ assertTrue(c.contains("#include "), c);
+ assertTrue(c.contains("using namespace Aws::Example::Model;"), c);
+ assertTrue(c.contains("using namespace Aws::Utils::Json;"), c);
+ assertTrue(c.contains("using namespace Aws::Utils;"), c);
+ assertFalse(c.contains("XmlSerializer.h"), c);
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Protocol.class, names = {"REST_XML", "QUERY_XML", "EC2"})
+ void xmlLike_requestSource_hasXmlIncludeAndUsings(Protocol p) {
+ String c = file(p, "DoThingRequest.cpp");
+ assertTrue(c.contains("#include "), c);
+ assertTrue(c.contains("using namespace Aws::Utils::Xml;"), c);
+ assertFalse(c.contains("JsonSerializer.h"), c);
+ }
+
+ /** The request header is protocol-agnostic today; pin that so a regression is loud. */
+ @ParameterizedTest
+ @EnumSource(Protocol.class)
+ void requestHeader_isProtocolAgnostic(Protocol p) {
+ String h = file(p, "DoThingRequest.h");
+ assertTrue(h.contains("Aws::String SerializePayload() const override;"), h);
+ assertFalse(h.contains("JsonSerializer.h"), "Request header must not include serde headers: " + h);
+ assertFalse(h.contains("XmlSerializer.h"), "Request header must not include serde headers: " + h);
+ }
+}
From e3c44f1e0555f1ae9818aec1e54ba6a09767b91b Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Mon, 3 Aug 2026 14:43:43 -0400
Subject: [PATCH 20/28] Add ProtocolTraits interface and JSON implementation
---
.../model/protocol/JsonProtocolTraits.java | 110 +++++++++++++++
.../model/protocol/ProtocolTraits.java | 103 ++++++++++++++
.../protocol/JsonProtocolTraitsTest.java | 126 ++++++++++++++++++
3 files changed, 339 insertions(+)
create mode 100644 tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/JsonProtocolTraits.java
create mode 100644 tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/ProtocolTraits.java
create mode 100644 tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/JsonProtocolTraitsTest.java
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/JsonProtocolTraits.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/JsonProtocolTraits.java
new file mode 100644
index 00000000000..5d2bb4ad828
--- /dev/null
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/JsonProtocolTraits.java
@@ -0,0 +1,110 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+package com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol;
+
+import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriter;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
+
+/**
+ * JSON-flavored serde rendering. Serves both {@link Protocol#JSON} and
+ * {@link Protocol#CBOR}, which share an identical C++ surface today (CBOR reuses the
+ * JSON model types and differs only on the wire).
+ */
+public final class JsonProtocolTraits implements ProtocolTraits {
+
+ private final Protocol protocol;
+
+ public JsonProtocolTraits(Protocol protocol) {
+ if (protocol != Protocol.JSON && protocol != Protocol.CBOR) {
+ throw new IllegalArgumentException(
+ "JsonProtocolTraits only serves JSON and CBOR, got: " + protocol);
+ }
+ this.protocol = protocol;
+ }
+
+ @Override
+ public Protocol protocol() {
+ return protocol;
+ }
+
+ @Override
+ public String serdeNamespace() {
+ return protocol.getSerdeNamespace();
+ }
+
+ @Override
+ public void writeShapeForwardDeclarations(CppWriter writer) {
+ writer.writeNamespaceOpen("Utils");
+ writer.writeNamespaceOpen("Json");
+ writer.write("class JsonValue;");
+ writer.write("class JsonView;");
+ writer.writeNamespaceClose("Json");
+ writer.writeNamespaceClose("Utils");
+ }
+
+ @Override
+ public void writeResultForwardDeclarations(CppWriter writer) {
+ writer.writeNamespaceOpen("Utils");
+ writer.writeNamespaceOpen("Json");
+ writer.write("class JsonValue;");
+ writer.writeNamespaceClose("Json");
+ writer.writeNamespaceClose("Utils");
+ }
+
+ @Override
+ public void writeSerdeMethodDecls(CppWriter writer, String exportMacro, String className,
+ Runnable beforeSerializeMethod) {
+ writer.write("$L $L() = default;", exportMacro, className);
+ writer.write("$L $L($L jsonValue);", exportMacro, className, protocol.getViewType());
+ writer.write("$L $L& operator=($L jsonValue);", exportMacro, className, protocol.getViewType());
+ if (beforeSerializeMethod != null) {
+ beforeSerializeMethod.run();
+ }
+ writer.write("$L $L $L() const;", exportMacro, protocol.getValueType(),
+ protocol.getSerializeMethodName());
+ }
+
+ @Override
+ public void writeResultSerdeDecls(CppWriter writer, String exportMacro, String className) {
+ writer.write("$L $L(const Aws::AmazonWebServiceResult<$L>& result);",
+ exportMacro, className, protocol.getValueType());
+ writer.write("$L $L& operator=(const Aws::AmazonWebServiceResult<$L>& result);",
+ exportMacro, className, protocol.getValueType());
+ }
+
+ @Override
+ public void writeSerdeInclude(CppWriter writer) {
+ writer.write("#include ");
+ }
+
+ @Override
+ public void writeSerdeUsingDeclarations(CppWriter writer) {
+ writer.write("using namespace Aws::Utils::Json;");
+ writer.write("using namespace Aws::Utils;");
+ }
+
+ @Override
+ public void writeSerdeMethodImpls(CppWriter writer, String className) {
+ writer.write("$L::$L(JsonView jsonValue) { *this = jsonValue; }", className, className);
+ writer.write("");
+ writer.openBlock("$L& $L::operator=(JsonView jsonValue) {", "}", className, className, () -> {
+ writer.write("return *this;");
+ });
+ writer.write("");
+ writer.openBlock("JsonValue $L::Jsonize() const {", "}", className, () -> {
+ writer.write("JsonValue payload;");
+ writer.write("return payload;");
+ });
+ }
+
+ @Override
+ public void writeResultSerdeImpls(CppWriter writer, String className) {
+ writer.openBlock("$L::$L(const Aws::AmazonWebServiceResult& result) {", "}",
+ className, className, () -> writer.write("*this = result;"));
+ writer.write("");
+ writer.openBlock("$L& $L::operator=(const Aws::AmazonWebServiceResult& result) {", "}",
+ className, className, () -> writer.write("return *this;"));
+ }
+}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/ProtocolTraits.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/ProtocolTraits.java
new file mode 100644
index 00000000000..bd9fda2bac4
--- /dev/null
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/ProtocolTraits.java
@@ -0,0 +1,103 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+package com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol;
+
+import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriter;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
+
+/**
+ * Owns every protocol-specific rendering decision for generated model code.
+ *
+ * Renderers receive a {@code ProtocolTraits} and call these methods; they never
+ * branch on {@link Protocol} themselves. One implementation exists per serde
+ * family (JSON-like, REST-XML, query-XML), not per protocol, so the
+ * conditional arms that used to be repeated across renderers are now classes.
+ *
+ *
Obtain an instance from
+ * {@code ProtocolResolver.traitsFor(ProtocolResolver.resolve(service, model))}.
+ */
+public interface ProtocolTraits {
+
+ /** The protocol this instance was resolved for. */
+ Protocol protocol();
+
+ /** C++ namespace holding the serde utilities, e.g. {@code Aws::Utils::Json}. */
+ String serdeNamespace();
+
+ // ------------------------------------------------------------------
+ // Header side
+ // ------------------------------------------------------------------
+
+ /**
+ * Forward-declares the serde types a shape (sub-object / request / event) header
+ * needs: both the value and the view type for JSON-like protocols, the node type
+ * for XML-like ones. Emits its own {@code Utils}/{@code Json}|{@code Xml}
+ * namespace nesting; the caller has already opened {@code Aws}.
+ */
+ void writeShapeForwardDeclarations(CppWriter writer);
+
+ /**
+ * Forward-declares the serde types a result header needs. This differs
+ * from {@link #writeShapeForwardDeclarations}: results reference the document
+ * type ({@code JsonValue} / {@code XmlDocument}) and never the view type.
+ */
+ void writeResultForwardDeclarations(CppWriter writer);
+
+ /**
+ * Emits the serde method declarations for a model class: the default ctor, the
+ * deserializing ctor, {@code operator=}, and the serialize method
+ * ({@code Jsonize} / {@code AddToNode} / {@code OutputToStream}).
+ *
+ * @param beforeSerializeMethod optional hook run after the deserializing ctor and
+ * {@code operator=} but before the serialize method, used to inject extra
+ * constructors at the mainline position. Ignored when {@code null}.
+ */
+ void writeSerdeMethodDecls(CppWriter writer, String exportMacro, String className,
+ Runnable beforeSerializeMethod);
+
+ /** Emits a result class's {@code AmazonWebServiceResult<...>} ctor and {@code operator=} declarations. */
+ void writeResultSerdeDecls(CppWriter writer, String exportMacro, String className);
+
+ // ------------------------------------------------------------------
+ // Source side
+ // ------------------------------------------------------------------
+
+ /** Emits the {@code #include} for this protocol's serializer header. */
+ void writeSerdeInclude(CppWriter writer);
+
+ /** Emits the {@code using namespace} declarations this protocol's serde bodies need. */
+ void writeSerdeUsingDeclarations(CppWriter writer);
+
+ /**
+ * Emits the serde method bodies matching {@link #writeSerdeMethodDecls}.
+ * Bodies are currently empty stubs pending schema-based serde.
+ */
+ void writeSerdeMethodImpls(CppWriter writer, String className);
+
+ /** Emits the result ctor / {@code operator=} bodies matching {@link #writeResultSerdeDecls}. */
+ void writeResultSerdeImpls(CppWriter writer, String className);
+
+ // ------------------------------------------------------------------
+ // Protocol-agnostic today; kept here so callers have one place to look.
+ // ------------------------------------------------------------------
+
+ /**
+ * Emits a placeholder for an event-stream event case body: a TODO marker plus a
+ * compilable callback invocation with a default-constructed event.
+ */
+ default void writeEventPayloadDecode(CppWriter writer, String eventShapeName,
+ String callbackMember) {
+ writer.write("// TODO: protocol-specific event payload deserialization");
+ writer.write("$L($L{});", callbackMember, eventShapeName);
+ }
+
+ /**
+ * Emits a placeholder in place of the protocol-specific error-payload parse. The
+ * header-derived error code/message still flow to MarshallError at the call site.
+ */
+ default void writeErrorPayloadParse(CppWriter writer) {
+ writer.write("// TODO: protocol-specific error payload deserialization");
+ }
+}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/JsonProtocolTraitsTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/JsonProtocolTraitsTest.java
new file mode 100644
index 00000000000..89b527be037
--- /dev/null
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/JsonProtocolTraitsTest.java
@@ -0,0 +1,126 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+package com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol;
+
+import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriter;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class JsonProtocolTraitsTest {
+
+ private static String render(java.util.function.Consumer body) {
+ CppWriter writer = new CppWriter();
+ body.accept(writer);
+ return writer.toString();
+ }
+
+ private final ProtocolTraits json = new JsonProtocolTraits(Protocol.JSON);
+ private final ProtocolTraits cbor = new JsonProtocolTraits(Protocol.CBOR);
+
+ @Test
+ void reportsItsOwnProtocolIdentity() {
+ assertEquals(Protocol.JSON, json.protocol());
+ assertEquals(Protocol.CBOR, cbor.protocol());
+ }
+
+ @Test
+ void serdeNamespace_isJsonUtils() {
+ assertEquals("Aws::Utils::Json", json.serdeNamespace());
+ assertEquals("Aws::Utils::Json", cbor.serdeNamespace());
+ }
+
+ @Test
+ void serdeMethodDecls_emitJsonCtorAssignAndJsonize() {
+ String out = render(w -> json.writeSerdeMethodDecls(w, "AWS_EXAMPLE_API", "Nested", null));
+ assertTrue(out.contains("AWS_EXAMPLE_API Nested() = default;"), out);
+ assertTrue(out.contains("AWS_EXAMPLE_API Nested(Aws::Utils::Json::JsonView jsonValue);"), out);
+ assertTrue(out.contains("AWS_EXAMPLE_API Nested& operator=(Aws::Utils::Json::JsonView jsonValue);"), out);
+ assertTrue(out.contains("AWS_EXAMPLE_API Aws::Utils::Json::JsonValue Jsonize() const;"), out);
+ }
+
+ @Test
+ void serdeMethodDecls_runsHookBeforeSerializeMethod() {
+ CppWriter writer = new CppWriter();
+ json.writeSerdeMethodDecls(writer, "AWS_EXAMPLE_API", "Nested",
+ () -> writer.write("AWS_EXAMPLE_API Nested(const Http::HeaderValueCollection& responseHeaders);"));
+ String out = writer.toString();
+ int hook = out.indexOf("HeaderValueCollection");
+ int jsonize = out.indexOf("Jsonize() const;");
+ assertTrue(hook >= 0, "Hook output missing: " + out);
+ assertTrue(jsonize > hook, "Hook must run before the serialize method: " + out);
+ }
+
+ @Test
+ void serdeMethodImpls_emitEmptyJsonBodies() {
+ String out = render(w -> json.writeSerdeMethodImpls(w, "Nested"));
+ assertTrue(out.contains("Nested::Nested(JsonView jsonValue) { *this = jsonValue; }"), out);
+ assertTrue(out.contains("Nested& Nested::operator=(JsonView jsonValue) {"), out);
+ assertTrue(out.contains("JsonValue Nested::Jsonize() const {"), out);
+ assertTrue(out.contains("JsonValue payload;"), out);
+ assertTrue(out.contains("return payload;"), out);
+ }
+
+ @Test
+ void shapeForwardDeclarations_declareValueAndView() {
+ String out = render(json::writeShapeForwardDeclarations);
+ assertTrue(out.contains("class JsonValue;"), out);
+ assertTrue(out.contains("class JsonView;"), out);
+ assertTrue(out.contains("namespace Utils"), out);
+ assertTrue(out.contains("namespace Json"), out);
+ }
+
+ @Test
+ void resultForwardDeclarations_declareValueOnly() {
+ String out = render(json::writeResultForwardDeclarations);
+ assertTrue(out.contains("class JsonValue;"), out);
+ assertFalse(out.contains("class JsonView;"),
+ "Result headers must not forward-declare JsonView: " + out);
+ }
+
+ @Test
+ void resultSerdeDecls_useJsonValuePayload() {
+ String out = render(w -> json.writeResultSerdeDecls(w, "AWS_EXAMPLE_API", "DoThingResult"));
+ assertTrue(out.contains("AWS_EXAMPLE_API DoThingResult(const "
+ + "Aws::AmazonWebServiceResult& result);"), out);
+ assertTrue(out.contains("AWS_EXAMPLE_API DoThingResult& operator=(const "
+ + "Aws::AmazonWebServiceResult& result);"), out);
+ }
+
+ @Test
+ void resultSerdeImpls_useJsonValuePayload() {
+ String out = render(w -> json.writeResultSerdeImpls(w, "DoThingResult"));
+ assertTrue(out.contains("DoThingResult::DoThingResult(const "
+ + "Aws::AmazonWebServiceResult& result) {"), out);
+ assertTrue(out.contains("*this = result;"), out);
+ assertTrue(out.contains("DoThingResult& DoThingResult::operator=(const "
+ + "Aws::AmazonWebServiceResult& result) {"), out);
+ assertTrue(out.contains("return *this;"), out);
+ }
+
+ @Test
+ void serdeIncludeAndUsings_areJsonFlavored() {
+ assertTrue(render(json::writeSerdeInclude)
+ .contains("#include "));
+ String usings = render(json::writeSerdeUsingDeclarations);
+ assertTrue(usings.contains("using namespace Aws::Utils::Json;"), usings);
+ assertTrue(usings.contains("using namespace Aws::Utils;"), usings);
+ }
+
+ @Test
+ void payloadStubs_areProtocolAgnostic() {
+ String event = render(w -> json.writeEventPayloadDecode(w, "ShardEvent", "m_onShardEvent"));
+ assertTrue(event.contains("// TODO: protocol-specific event payload deserialization"), event);
+ assertTrue(event.contains("m_onShardEvent(ShardEvent{});"), event);
+ assertFalse(event.contains("JsonValue"), "Event stub must stay protocol-agnostic: " + event);
+
+ String err = render(json::writeErrorPayloadParse);
+ assertTrue(err.contains("// TODO: protocol-specific error payload deserialization"), err);
+ assertFalse(err.contains("JsonView"), "Error stub must stay protocol-agnostic: " + err);
+ }
+}
From e1362a6680553f3d0d319cb0ff6101fb44bd933d Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Mon, 3 Aug 2026 14:57:36 -0400
Subject: [PATCH 21/28] Add XML ProtocolTraits implementations and traitsFor
selection point
---
.../generators/model/ProtocolResolver.java | 43 ++++-
.../protocol/QueryXmlProtocolTraits.java | 113 ++++++++++++
.../model/protocol/RestXmlProtocolTraits.java | 95 +++++++++++
.../model/ProtocolResolverTest.java | 38 +++++
.../model/protocol/XmlProtocolTraitsTest.java | 161 ++++++++++++++++++
5 files changed, 447 insertions(+), 3 deletions(-)
create mode 100644 tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/QueryXmlProtocolTraits.java
create mode 100644 tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/RestXmlProtocolTraits.java
create mode 100644 tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/XmlProtocolTraitsTest.java
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolResolver.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolResolver.java
index 9f394798547..0704643cd3a 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolResolver.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolResolver.java
@@ -13,12 +13,18 @@
import software.amazon.smithy.aws.traits.protocols.RestJson1Trait;
import software.amazon.smithy.aws.traits.protocols.RestXmlTrait;
import software.amazon.smithy.protocol.traits.Rpcv2CborTrait;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol.JsonProtocolTraits;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol.ProtocolTraits;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol.QueryXmlProtocolTraits;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol.RestXmlProtocolTraits;
/**
- * Resolves the wire protocol for a Smithy service shape.
+ * Resolves the wire protocol for a Smithy service shape, and maps a resolved
+ * protocol to the {@link ProtocolTraits} strategy that owns its rendering.
*
- * The resolved {@link Protocol} enum carries all serde-related metadata needed
- * by downstream template generators (namespace, view/value types, method names).
+ *
{@link #traitsFor} is the single protocol-to-behavior switch in the generator;
+ * downstream generators hold a {@code ProtocolTraits} and never branch on
+ * {@link Protocol} themselves.
*/
public final class ProtocolResolver {
@@ -114,4 +120,35 @@ public static Protocol resolve(ServiceShape service, Model model) {
// Default: JSON (covers RestJson1, AwsJson1_0, AwsJson1_1)
return Protocol.JSON;
}
+
+ /**
+ * Returns the rendering strategy for a resolved protocol.
+ *
+ *
This is the only place in the generator that switches on
+ * {@link Protocol}. Every other class receives a {@link ProtocolTraits} and calls
+ * it, so adding a protocol means adding a case here plus (if its C++ surface is
+ * genuinely new) one implementation class.
+ *
+ * @param protocol the protocol returned by {@link #resolve}
+ * @return the strategy that owns this protocol's serde rendering
+ * @throws UnsupportedOperationException if the protocol has no strategy
+ */
+ public static ProtocolTraits traitsFor(Protocol protocol) {
+ if (protocol == null) {
+ throw new UnsupportedOperationException("Cannot resolve ProtocolTraits for a null protocol");
+ }
+ switch (protocol) {
+ case JSON:
+ case CBOR:
+ return new JsonProtocolTraits(protocol);
+ case REST_XML:
+ return new RestXmlProtocolTraits();
+ case QUERY_XML:
+ case EC2:
+ return new QueryXmlProtocolTraits(protocol);
+ default:
+ throw new UnsupportedOperationException(
+ "No ProtocolTraits implementation for protocol: " + protocol);
+ }
+ }
}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/QueryXmlProtocolTraits.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/QueryXmlProtocolTraits.java
new file mode 100644
index 00000000000..26de9170478
--- /dev/null
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/QueryXmlProtocolTraits.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+package com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol;
+
+import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriter;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
+
+/**
+ * Query-XML serde rendering. Serves both {@link Protocol#QUERY_XML} and
+ * {@link Protocol#EC2}: shapes serialize into a flat, indexed query string via two
+ * {@code OutputToStream} overloads rather than into a node tree.
+ */
+public final class QueryXmlProtocolTraits implements ProtocolTraits {
+
+ private final Protocol protocol;
+
+ public QueryXmlProtocolTraits(Protocol protocol) {
+ if (protocol != Protocol.QUERY_XML && protocol != Protocol.EC2) {
+ throw new IllegalArgumentException(
+ "QueryXmlProtocolTraits only serves QUERY_XML and EC2, got: " + protocol);
+ }
+ this.protocol = protocol;
+ }
+
+ @Override
+ public Protocol protocol() {
+ return protocol;
+ }
+
+ @Override
+ public String serdeNamespace() {
+ return protocol.getSerdeNamespace();
+ }
+
+ @Override
+ public void writeShapeForwardDeclarations(CppWriter writer) {
+ writer.writeNamespaceOpen("Utils");
+ writer.writeNamespaceOpen("Xml");
+ writer.write("class XmlNode;");
+ writer.writeNamespaceClose("Xml");
+ writer.writeNamespaceClose("Utils");
+ }
+
+ @Override
+ public void writeResultForwardDeclarations(CppWriter writer) {
+ writer.writeNamespaceOpen("Utils");
+ writer.writeNamespaceOpen("Xml");
+ writer.write("class XmlDocument;");
+ writer.writeNamespaceClose("Xml");
+ writer.writeNamespaceClose("Utils");
+ }
+
+ @Override
+ public void writeSerdeMethodDecls(CppWriter writer, String exportMacro, String className,
+ Runnable beforeSerializeMethod) {
+ writer.write("$L $L() = default;", exportMacro, className);
+ writer.write("$L $L(const Aws::Utils::Xml::XmlNode& xmlNode);", exportMacro, className);
+ writer.write("$L $L& operator=(const Aws::Utils::Xml::XmlNode& xmlNode);", exportMacro, className);
+ if (beforeSerializeMethod != null) {
+ beforeSerializeMethod.run();
+ }
+ writer.write("$L void OutputToStream(Aws::OStream& ostream, const char* location, "
+ + "unsigned index, const char* locationValue) const;", exportMacro);
+ writer.write("$L void OutputToStream(Aws::OStream& ostream, const char* location) const;", exportMacro);
+ }
+
+ @Override
+ public void writeResultSerdeDecls(CppWriter writer, String exportMacro, String className) {
+ writer.write("$L $L(const Aws::AmazonWebServiceResult& result);",
+ exportMacro, className);
+ writer.write("$L $L& operator=(const Aws::AmazonWebServiceResult& result);",
+ exportMacro, className);
+ }
+
+ @Override
+ public void writeSerdeInclude(CppWriter writer) {
+ writer.write("#include ");
+ }
+
+ @Override
+ public void writeSerdeUsingDeclarations(CppWriter writer) {
+ writer.write("using namespace Aws::Utils::Xml;");
+ writer.write("using namespace Aws::Utils;");
+ }
+
+ @Override
+ public void writeSerdeMethodImpls(CppWriter writer, String className) {
+ writer.write("$L::$L(const XmlNode& xmlNode) { *this = xmlNode; }", className, className);
+ writer.write("");
+ writer.openBlock("$L& $L::operator=(const XmlNode& xmlNode) {", "}", className, className, () -> {
+ writer.write("return *this;");
+ });
+ writer.write("");
+ writer.openBlock("void $L::OutputToStream(Aws::OStream& ostream, const char* location, "
+ + "unsigned index, const char* locationValue) const {", "}", className, () -> {
+ });
+ writer.write("");
+ writer.openBlock("void $L::OutputToStream(Aws::OStream& ostream, const char* location) const {",
+ "}", className, () -> {
+ });
+ }
+
+ @Override
+ public void writeResultSerdeImpls(CppWriter writer, String className) {
+ writer.openBlock("$L::$L(const Aws::AmazonWebServiceResult& result) {", "}",
+ className, className, () -> writer.write("*this = result;"));
+ writer.write("");
+ writer.openBlock("$L& $L::operator=(const Aws::AmazonWebServiceResult& result) {", "}",
+ className, className, () -> writer.write("return *this;"));
+ }
+}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/RestXmlProtocolTraits.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/RestXmlProtocolTraits.java
new file mode 100644
index 00000000000..06fc3f7cec8
--- /dev/null
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/RestXmlProtocolTraits.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+package com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol;
+
+import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriter;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
+
+/**
+ * REST-XML serde rendering. Shapes serialize by attaching themselves to a parent
+ * node via {@code AddToNode}.
+ */
+public final class RestXmlProtocolTraits implements ProtocolTraits {
+
+ @Override
+ public Protocol protocol() {
+ return Protocol.REST_XML;
+ }
+
+ @Override
+ public String serdeNamespace() {
+ return Protocol.REST_XML.getSerdeNamespace();
+ }
+
+ @Override
+ public void writeShapeForwardDeclarations(CppWriter writer) {
+ writer.writeNamespaceOpen("Utils");
+ writer.writeNamespaceOpen("Xml");
+ writer.write("class XmlNode;");
+ writer.writeNamespaceClose("Xml");
+ writer.writeNamespaceClose("Utils");
+ }
+
+ @Override
+ public void writeResultForwardDeclarations(CppWriter writer) {
+ writer.writeNamespaceOpen("Utils");
+ writer.writeNamespaceOpen("Xml");
+ writer.write("class XmlDocument;");
+ writer.writeNamespaceClose("Xml");
+ writer.writeNamespaceClose("Utils");
+ }
+
+ @Override
+ public void writeSerdeMethodDecls(CppWriter writer, String exportMacro, String className,
+ Runnable beforeSerializeMethod) {
+ writer.write("$L $L() = default;", exportMacro, className);
+ writer.write("$L $L(const Aws::Utils::Xml::XmlNode& xmlNode);", exportMacro, className);
+ writer.write("$L $L& operator=(const Aws::Utils::Xml::XmlNode& xmlNode);", exportMacro, className);
+ if (beforeSerializeMethod != null) {
+ beforeSerializeMethod.run();
+ }
+ writer.write("$L void AddToNode(Aws::Utils::Xml::XmlNode& parentNode) const;", exportMacro);
+ }
+
+ @Override
+ public void writeResultSerdeDecls(CppWriter writer, String exportMacro, String className) {
+ writer.write("$L $L(const Aws::AmazonWebServiceResult& result);",
+ exportMacro, className);
+ writer.write("$L $L& operator=(const Aws::AmazonWebServiceResult& result);",
+ exportMacro, className);
+ }
+
+ @Override
+ public void writeSerdeInclude(CppWriter writer) {
+ writer.write("#include ");
+ }
+
+ @Override
+ public void writeSerdeUsingDeclarations(CppWriter writer) {
+ writer.write("using namespace Aws::Utils::Xml;");
+ writer.write("using namespace Aws::Utils;");
+ }
+
+ @Override
+ public void writeSerdeMethodImpls(CppWriter writer, String className) {
+ writer.write("$L::$L(const XmlNode& xmlNode) { *this = xmlNode; }", className, className);
+ writer.write("");
+ writer.openBlock("$L& $L::operator=(const XmlNode& xmlNode) {", "}", className, className, () -> {
+ writer.write("return *this;");
+ });
+ writer.write("");
+ writer.openBlock("void $L::AddToNode(XmlNode& parentNode) const {", "}", className, () -> {
+ });
+ }
+
+ @Override
+ public void writeResultSerdeImpls(CppWriter writer, String className) {
+ writer.openBlock("$L::$L(const Aws::AmazonWebServiceResult& result) {", "}",
+ className, className, () -> writer.write("*this = result;"));
+ writer.write("");
+ writer.openBlock("$L& $L::operator=(const Aws::AmazonWebServiceResult& result) {", "}",
+ className, className, () -> writer.write("return *this;"));
+ }
+}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolResolverTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolResolverTest.java
index cc0f6711a13..354df389ede 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolResolverTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolResolverTest.java
@@ -174,4 +174,42 @@ void protocol_ec2_isXmlLike() {
assertTrue(Protocol.EC2.isXmlLike());
assertFalse(Protocol.EC2.isJsonLike());
}
+
+ // ---------- traitsFor: the single protocol -> strategy selection point ----------
+
+ @Test
+ void traitsFor_jsonAndCbor_returnJsonTraits() {
+ assertInstanceOf(com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol
+ .JsonProtocolTraits.class, ProtocolResolver.traitsFor(Protocol.JSON));
+ assertInstanceOf(com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol
+ .JsonProtocolTraits.class, ProtocolResolver.traitsFor(Protocol.CBOR));
+ }
+
+ @Test
+ void traitsFor_restXml_returnsRestXmlTraits() {
+ assertInstanceOf(com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol
+ .RestXmlProtocolTraits.class, ProtocolResolver.traitsFor(Protocol.REST_XML));
+ }
+
+ @Test
+ void traitsFor_queryAndEc2_returnQueryXmlTraits() {
+ assertInstanceOf(com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol
+ .QueryXmlProtocolTraits.class, ProtocolResolver.traitsFor(Protocol.QUERY_XML));
+ assertInstanceOf(com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol
+ .QueryXmlProtocolTraits.class, ProtocolResolver.traitsFor(Protocol.EC2));
+ }
+
+ /** Every protocol must have a strategy, and each must report its own identity back. */
+ @Test
+ void traitsFor_coversEveryProtocol_andPreservesIdentity() {
+ for (Protocol p : Protocol.values()) {
+ assertEquals(p, ProtocolResolver.traitsFor(p).protocol(),
+ "traitsFor(" + p + ") returned traits reporting a different protocol");
+ }
+ }
+
+ @Test
+ void traitsFor_rejectsNull() {
+ assertThrows(UnsupportedOperationException.class, () -> ProtocolResolver.traitsFor(null));
+ }
}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/XmlProtocolTraitsTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/XmlProtocolTraitsTest.java
new file mode 100644
index 00000000000..be397d8dad8
--- /dev/null
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/XmlProtocolTraitsTest.java
@@ -0,0 +1,161 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+package com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol;
+
+import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriter;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class XmlProtocolTraitsTest {
+
+ private static String render(java.util.function.Consumer body) {
+ CppWriter writer = new CppWriter();
+ body.accept(writer);
+ return writer.toString();
+ }
+
+ private final ProtocolTraits restXml = new RestXmlProtocolTraits();
+ private final ProtocolTraits queryXml = new QueryXmlProtocolTraits(Protocol.QUERY_XML);
+ private final ProtocolTraits ec2 = new QueryXmlProtocolTraits(Protocol.EC2);
+
+ // ---------- identity ----------
+
+ @Test
+ void reportIdentityAndNamespace() {
+ assertEquals(Protocol.REST_XML, restXml.protocol());
+ assertEquals(Protocol.QUERY_XML, queryXml.protocol());
+ assertEquals(Protocol.EC2, ec2.protocol());
+ assertEquals("Aws::Utils::Xml", restXml.serdeNamespace());
+ assertEquals("Aws::Utils::Xml", queryXml.serdeNamespace());
+ assertEquals("Aws::Utils::Xml", ec2.serdeNamespace());
+ }
+
+ @Test
+ void queryXmlTraits_rejectsNonQueryProtocols() {
+ assertThrows(IllegalArgumentException.class, () -> new QueryXmlProtocolTraits(Protocol.JSON));
+ assertThrows(IllegalArgumentException.class, () -> new QueryXmlProtocolTraits(Protocol.REST_XML));
+ }
+
+ // ---------- REST_XML: AddToNode ----------
+
+ @Test
+ void restXml_serdeMethodDecls_emitAddToNode() {
+ String out = render(w -> restXml.writeSerdeMethodDecls(w, "AWS_EXAMPLE_API", "Nested", null));
+ assertTrue(out.contains("AWS_EXAMPLE_API Nested() = default;"), out);
+ assertTrue(out.contains("AWS_EXAMPLE_API Nested(const Aws::Utils::Xml::XmlNode& xmlNode);"), out);
+ assertTrue(out.contains("AWS_EXAMPLE_API Nested& operator=(const Aws::Utils::Xml::XmlNode& xmlNode);"), out);
+ assertTrue(out.contains("AWS_EXAMPLE_API void AddToNode(Aws::Utils::Xml::XmlNode& parentNode) const;"), out);
+ assertFalse(out.contains("Jsonize"), out);
+ assertFalse(out.contains("OutputToStream"), out);
+ }
+
+ @Test
+ void restXml_serdeMethodImpls_emitAddToNodeBody() {
+ String out = render(w -> restXml.writeSerdeMethodImpls(w, "Nested"));
+ assertTrue(out.contains("Nested::Nested(const XmlNode& xmlNode) { *this = xmlNode; }"), out);
+ assertTrue(out.contains("Nested& Nested::operator=(const XmlNode& xmlNode) {"), out);
+ assertTrue(out.contains("void Nested::AddToNode(XmlNode& parentNode) const {"), out);
+ }
+
+ @Test
+ void restXml_serdeMethodDecls_runsHookBeforeSerializeMethod() {
+ CppWriter writer = new CppWriter();
+ restXml.writeSerdeMethodDecls(writer, "AWS_EXAMPLE_API", "Nested",
+ () -> writer.write("AWS_EXAMPLE_API Nested(const Http::HeaderValueCollection& responseHeaders);"));
+ String out = writer.toString();
+ int hook = out.indexOf("HeaderValueCollection");
+ int serialize = out.indexOf("AddToNode");
+ assertTrue(hook >= 0 && serialize > hook,
+ "Hook must run before the serialize method: " + out);
+ }
+
+ // ---------- QUERY_XML / EC2: two OutputToStream overloads ----------
+
+ @ParameterizedTest
+ @EnumSource(value = Protocol.class, names = {"QUERY_XML", "EC2"})
+ void queryLike_serdeMethodDecls_emitBothOutputToStreamOverloads(Protocol p) {
+ ProtocolTraits traits = new QueryXmlProtocolTraits(p);
+ String out = render(w -> traits.writeSerdeMethodDecls(w, "AWS_EXAMPLE_API", "Nested", null));
+ assertTrue(out.contains("AWS_EXAMPLE_API Nested(const Aws::Utils::Xml::XmlNode& xmlNode);"), out);
+ assertTrue(out.contains("AWS_EXAMPLE_API void OutputToStream(Aws::OStream& ostream, "
+ + "const char* location, unsigned index, const char* locationValue) const;"), out);
+ assertTrue(out.contains("AWS_EXAMPLE_API void OutputToStream(Aws::OStream& ostream, "
+ + "const char* location) const;"), out);
+ assertFalse(out.contains("AddToNode"), out);
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Protocol.class, names = {"QUERY_XML", "EC2"})
+ void queryLike_serdeMethodImpls_emitBothOutputToStreamBodies(Protocol p) {
+ ProtocolTraits traits = new QueryXmlProtocolTraits(p);
+ String out = render(w -> traits.writeSerdeMethodImpls(w, "Nested"));
+ assertTrue(out.contains("Nested::Nested(const XmlNode& xmlNode) { *this = xmlNode; }"), out);
+ assertTrue(out.contains("void Nested::OutputToStream(Aws::OStream& ostream, const char* location, "
+ + "unsigned index, const char* locationValue) const {"), out);
+ assertTrue(out.contains("void Nested::OutputToStream(Aws::OStream& ostream, "
+ + "const char* location) const {"), out);
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Protocol.class, names = {"QUERY_XML", "EC2"})
+ void queryLike_serdeMethodDecls_runsHookBeforeSerializeMethod(Protocol p) {
+ ProtocolTraits traits = new QueryXmlProtocolTraits(p);
+ CppWriter writer = new CppWriter();
+ traits.writeSerdeMethodDecls(writer, "AWS_EXAMPLE_API", "Nested",
+ () -> writer.write("AWS_EXAMPLE_API Nested(const Http::HeaderValueCollection& responseHeaders);"));
+ String out = writer.toString();
+ int hook = out.indexOf("HeaderValueCollection");
+ int serialize = out.indexOf("OutputToStream");
+ assertTrue(hook >= 0 && serialize > hook,
+ "Hook must run before the serialize method: " + out);
+ }
+
+ // ---------- shared XML behavior ----------
+
+ @Test
+ void allXmlTraits_shareForwardDeclarationsIncludeAndUsings() {
+ for (ProtocolTraits traits : java.util.List.of(restXml, queryXml, ec2)) {
+ String shapeFwd = render(traits::writeShapeForwardDeclarations);
+ assertTrue(shapeFwd.contains("class XmlNode;"), shapeFwd);
+ assertFalse(shapeFwd.contains("JsonValue"), shapeFwd);
+
+ String resultFwd = render(traits::writeResultForwardDeclarations);
+ assertTrue(resultFwd.contains("class XmlDocument;"), resultFwd);
+ assertFalse(resultFwd.contains("class XmlNode;"),
+ "Result headers declare the document type, not the node type: " + resultFwd);
+
+ assertTrue(render(traits::writeSerdeInclude)
+ .contains("#include "));
+
+ String usings = render(traits::writeSerdeUsingDeclarations);
+ assertTrue(usings.contains("using namespace Aws::Utils::Xml;"), usings);
+ assertTrue(usings.contains("using namespace Aws::Utils;"), usings);
+ }
+ }
+
+ @Test
+ void allXmlTraits_useXmlDocumentForResults() {
+ for (ProtocolTraits traits : java.util.List.of(restXml, queryXml, ec2)) {
+ String decls = render(w -> traits.writeResultSerdeDecls(w, "AWS_EXAMPLE_API", "DoThingResult"));
+ assertTrue(decls.contains("AWS_EXAMPLE_API DoThingResult(const "
+ + "Aws::AmazonWebServiceResult& result);"), decls);
+ assertTrue(decls.contains("AWS_EXAMPLE_API DoThingResult& operator=(const "
+ + "Aws::AmazonWebServiceResult& result);"), decls);
+
+ String impls = render(w -> traits.writeResultSerdeImpls(w, "DoThingResult"));
+ assertTrue(impls.contains("DoThingResult::DoThingResult(const "
+ + "Aws::AmazonWebServiceResult& result) {"), impls);
+ assertTrue(impls.contains("DoThingResult& DoThingResult::operator=(const "
+ + "Aws::AmazonWebServiceResult& result) {"), impls);
+ }
+ }
+}
From af927ae0927dfecf887581142564dc3a24ea80e4 Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Mon, 3 Aug 2026 14:59:28 -0400
Subject: [PATCH 22/28] Migrate SubObjectRenderer to ProtocolTraits
---
.../generators/model/ModelGenerator.java | 5 +-
.../model/renderers/SubObjectRenderer.java | 47 ++++---------------
.../ProtocolTraitsCharacterizationTest.java | 4 +-
3 files changed, 16 insertions(+), 40 deletions(-)
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java
index b9bc248e29f..27c34da886c 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java
@@ -12,6 +12,7 @@
import com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers.RequestRenderer;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers.ResultRenderer;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers.SubObjectRenderer;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol.ProtocolTraits;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.ServiceShape;
@@ -34,6 +35,7 @@ public class ModelGenerator {
private final String exportMacro;
private final String namespace;
private final Protocol protocol;
+ private final ProtocolTraits protocolTraits;
public ModelGenerator(Model model, ServiceShape service, CppWriterDelegator writerDelegator,
String serviceName, String smithyServiceName,
@@ -46,6 +48,7 @@ public ModelGenerator(Model model, ServiceShape service, CppWriterDelegator writ
this.exportMacro = exportMacro;
this.namespace = namespace;
this.protocol = ProtocolResolver.resolve(service, model);
+ this.protocolTraits = ProtocolResolver.traitsFor(this.protocol);
}
public void generateAll() {
@@ -64,7 +67,7 @@ private List buildRenderers(ClassifiedShapes classified) {
classified.enums(), namespace, exportMacro, smithyServiceName));
renderers.add(new SubObjectRenderer(
- classified.subObjects(), model, service, protocol,
+ classified.subObjects(), model, service, protocolTraits,
namespace, exportMacro, serviceName, smithyServiceName));
renderers.add(new RequestRenderer(
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/SubObjectRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/SubObjectRenderer.java
index 5c4d3fab022..ab491742286 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/SubObjectRenderer.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/SubObjectRenderer.java
@@ -8,8 +8,7 @@
import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriterDelegator;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.CppTypeMapper;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.MemberRenderer;
-import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
-import com.amazonaws.util.awsclientsmithygenerator.generators.model.SerdeStub;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol.ProtocolTraits;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.ShapeRenderer;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.ServiceShape;
@@ -28,19 +27,19 @@ public final class SubObjectRenderer implements ShapeRenderer {
private final List subObjects;
private final Model model;
private final ServiceShape service;
- private final Protocol protocol;
+ private final ProtocolTraits protocolTraits;
private final String namespace;
private final String exportMacro;
private final String serviceName;
private final String smithyServiceName;
public SubObjectRenderer(List subObjects, Model model, ServiceShape service,
- Protocol protocol, String namespace, String exportMacro,
+ ProtocolTraits protocolTraits, String namespace, String exportMacro,
String serviceName, String smithyServiceName) {
this.subObjects = subObjects;
this.model = model;
this.service = service;
- this.protocol = protocol;
+ this.protocolTraits = protocolTraits;
this.namespace = namespace;
this.exportMacro = exportMacro;
this.serviceName = serviceName;
@@ -83,7 +82,7 @@ private void renderHeader(CppWriterDelegator writerDelegator, StructureShape sha
writer.write("");
writer.writeNamespaceOpen("Aws");
- renderForwardDeclarations(writer);
+ protocolTraits.writeShapeForwardDeclarations(writer);
writer.writeNamespaceOpen(namespace);
writer.writeNamespaceOpen("Model");
writer.write("");
@@ -92,7 +91,7 @@ private void renderHeader(CppWriterDelegator writerDelegator, StructureShape sha
writer.openBlock("class $L {", "};", className, () -> {
writer.write("public:");
- SerdeStub.renderHeaderDeclarations(writer, protocol, exportMacro, className);
+ protocolTraits.writeSerdeMethodDecls(writer, exportMacro, className, null);
writer.write("");
MemberRenderer.renderPublicSection(writer, shape, model, exportMacro, className);
writer.dedent();
@@ -113,23 +112,13 @@ private void renderSource(CppWriterDelegator writerDelegator, StructureShape sha
String fileName = "source/model/" + className + ".cpp";
writerDelegator.useFileWriter(fileName, writer -> {
- if (protocol.isJsonLike()) {
- writer.write("#include ");
- } else {
- writer.write("#include ");
- }
+ protocolTraits.writeSerdeInclude(writer);
writer.write("#include ", smithyServiceName, className);
writer.write("");
writer.write("#include ");
writer.write("");
- if (protocol.isJsonLike()) {
- writer.write("using namespace Aws::Utils::Json;");
- writer.write("using namespace Aws::Utils;");
- } else {
- writer.write("using namespace Aws::Utils::Xml;");
- writer.write("using namespace Aws::Utils;");
- }
+ protocolTraits.writeSerdeUsingDeclarations(writer);
writer.write("");
writer.writeNamespaceOpen("Aws");
@@ -137,8 +126,7 @@ private void renderSource(CppWriterDelegator writerDelegator, StructureShape sha
writer.writeNamespaceOpen("Model");
writer.write("");
- SerdeStub.renderSourceImplementation(writer, shape, model, protocol,
- className, serviceName, smithyServiceName);
+ protocolTraits.writeSerdeMethodImpls(writer, className);
writer.write("");
writer.writeNamespaceClose("Model");
@@ -147,21 +135,4 @@ private void renderSource(CppWriterDelegator writerDelegator, StructureShape sha
});
}
- private void renderForwardDeclarations(CppWriter writer) {
- if (protocol.isJsonLike()) {
- writer.writeNamespaceOpen("Utils");
- writer.writeNamespaceOpen("Json");
- writer.write("class JsonValue;");
- writer.write("class JsonView;");
- writer.writeNamespaceClose("Json");
- writer.writeNamespaceClose("Utils");
- } else {
- writer.writeNamespaceOpen("Utils");
- writer.writeNamespaceOpen("Xml");
- writer.write("class XmlNode;");
- writer.writeNamespaceClose("Xml");
- writer.writeNamespaceClose("Utils");
- }
- }
-
}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java
index bf7543ad855..06cb4ee3547 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java
@@ -9,6 +9,7 @@
import com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers.RequestRenderer;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers.ResultRenderer;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers.SubObjectRenderer;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol.ProtocolTraits;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
@@ -104,7 +105,8 @@ private static java.util.Map renderAll(Protocol p) {
// NOTE (Tasks 4-7): when a renderer's ctor switches from Protocol to
// ProtocolTraits, change ONLY the argument below -- never the assertions.
- new SubObjectRenderer(classified.subObjects(), model, service, resolved,
+ ProtocolTraits traits = ProtocolResolver.traitsFor(resolved);
+ new SubObjectRenderer(classified.subObjects(), model, service, traits,
"Example", "AWS_EXAMPLE_API", "Example", "example").render(delegator);
new RequestRenderer(classified.requests(), model, service, resolved,
"Example", "AWS_EXAMPLE_API", "example").render(delegator);
From d8b6285fc391f0743e7b0cbcb53523581b57dc76 Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Mon, 3 Aug 2026 15:01:08 -0400
Subject: [PATCH 23/28] Migrate ResultRenderer to ProtocolTraits
---
.../generators/model/ModelGenerator.java | 2 +-
.../model/renderers/ResultRenderer.java | 69 +++----------------
.../ProtocolTraitsCharacterizationTest.java | 2 +-
3 files changed, 13 insertions(+), 60 deletions(-)
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java
index 27c34da886c..3809c124825 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java
@@ -75,7 +75,7 @@ private List buildRenderers(ClassifiedShapes classified) {
namespace, exportMacro, smithyServiceName));
renderers.add(new ResultRenderer(
- classified.results(), model, service, protocol,
+ classified.results(), model, service, protocolTraits,
namespace, exportMacro, smithyServiceName));
renderers.add(new EventStreamRenderer(
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/ResultRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/ResultRenderer.java
index f1741bcb084..726f16d4f9f 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/ResultRenderer.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/ResultRenderer.java
@@ -4,11 +4,10 @@
*/
package com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers;
-import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriter;
import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriterDelegator;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.CppTypeMapper;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.MemberRenderer;
-import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol.ProtocolTraits;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.ShapeClassifier.ResultInfo;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.ShapeRenderer;
import software.amazon.smithy.model.Model;
@@ -29,18 +28,18 @@ public final class ResultRenderer implements ShapeRenderer {
private final List results;
private final Model model;
private final ServiceShape service;
- private final Protocol protocol;
+ private final ProtocolTraits protocolTraits;
private final String namespace;
private final String exportMacro;
private final String smithyServiceName;
public ResultRenderer(List results, Model model, ServiceShape service,
- Protocol protocol, String namespace, String exportMacro,
+ ProtocolTraits protocolTraits, String namespace, String exportMacro,
String smithyServiceName) {
this.results = results;
this.model = model;
this.service = service;
- this.protocol = protocol;
+ this.protocolTraits = protocolTraits;
this.namespace = namespace;
this.exportMacro = exportMacro;
this.smithyServiceName = smithyServiceName;
@@ -79,7 +78,7 @@ private void renderHeader(CppWriterDelegator writerDelegator,
writer.write("template ");
writer.write("class AmazonWebServiceResult;");
writer.write("");
- renderForwardDeclarations(writer);
+ protocolTraits.writeResultForwardDeclarations(writer);
writer.writeNamespaceOpen(namespace);
writer.writeNamespaceOpen("Model");
@@ -91,13 +90,7 @@ private void renderHeader(CppWriterDelegator writerDelegator,
writer.openBlock("class $L {", "};", className, () -> {
writer.write("public:");
writer.write("$L $L() = default;", exportMacro, className);
- if (protocol.isJsonLike()) {
- writer.write("$L $L(const Aws::AmazonWebServiceResult& result);", exportMacro, className);
- writer.write("$L $L& operator=(const Aws::AmazonWebServiceResult& result);", exportMacro, className);
- } else {
- writer.write("$L $L(const Aws::AmazonWebServiceResult& result);", exportMacro, className);
- writer.write("$L $L& operator=(const Aws::AmazonWebServiceResult& result);", exportMacro, className);
- }
+ protocolTraits.writeResultSerdeDecls(writer, exportMacro, className);
writer.write("");
MemberRenderer.renderPublicSectionForResult(writer, shape, model, exportMacro, className);
@@ -148,59 +141,19 @@ private void renderSource(CppWriterDelegator writerDelegator,
writer.write("#include ");
writer.write("#include ");
writer.write("#include ");
- if (protocol.isJsonLike()) {
- writer.write("#include ");
- } else {
- writer.write("#include ");
- }
+ protocolTraits.writeSerdeInclude(writer);
writer.write("#include ");
writer.write("#include ", smithyServiceName, className);
writer.write("");
writer.write("#include ");
writer.write("");
- if (protocol.isJsonLike()) {
- writer.write("using namespace Aws::$L::Model;", namespace);
- writer.write("using namespace Aws::Utils::Json;");
- writer.write("using namespace Aws::Utils;");
- writer.write("using namespace Aws;");
- } else {
- writer.write("using namespace Aws::$L::Model;", namespace);
- writer.write("using namespace Aws::Utils::Xml;");
- writer.write("using namespace Aws::Utils;");
- writer.write("using namespace Aws;");
- }
+ writer.write("using namespace Aws::$L::Model;", namespace);
+ protocolTraits.writeSerdeUsingDeclarations(writer);
+ writer.write("using namespace Aws;");
writer.write("");
- if (protocol.isJsonLike()) {
- writer.openBlock("$L::$L(const Aws::AmazonWebServiceResult& result) {", "}",
- className, className, () -> writer.write("*this = result;"));
- writer.write("");
- writer.openBlock("$L& $L::operator=(const Aws::AmazonWebServiceResult& result) {", "}",
- className, className, () -> writer.write("return *this;"));
- } else {
- writer.openBlock("$L::$L(const Aws::AmazonWebServiceResult& result) {", "}",
- className, className, () -> writer.write("*this = result;"));
- writer.write("");
- writer.openBlock("$L& $L::operator=(const Aws::AmazonWebServiceResult& result) {", "}",
- className, className, () -> writer.write("return *this;"));
- }
+ protocolTraits.writeResultSerdeImpls(writer, className);
writer.write("");
});
}
-
- private void renderForwardDeclarations(CppWriter writer) {
- if (protocol.isJsonLike()) {
- writer.writeNamespaceOpen("Utils");
- writer.writeNamespaceOpen("Json");
- writer.write("class JsonValue;");
- writer.writeNamespaceClose("Json");
- writer.writeNamespaceClose("Utils");
- } else {
- writer.writeNamespaceOpen("Utils");
- writer.writeNamespaceOpen("Xml");
- writer.write("class XmlDocument;");
- writer.writeNamespaceClose("Xml");
- writer.writeNamespaceClose("Utils");
- }
- }
}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java
index 06cb4ee3547..c00ad537abc 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java
@@ -110,7 +110,7 @@ private static java.util.Map renderAll(Protocol p) {
"Example", "AWS_EXAMPLE_API", "Example", "example").render(delegator);
new RequestRenderer(classified.requests(), model, service, resolved,
"Example", "AWS_EXAMPLE_API", "example").render(delegator);
- new ResultRenderer(classified.results(), model, service, resolved,
+ new ResultRenderer(classified.results(), model, service, traits,
"Example", "AWS_EXAMPLE_API", "example").render(delegator);
delegator.flushWriters();
From 9cfda0686ab160f54186f38c4ee61435a255554e Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Mon, 3 Aug 2026 15:02:31 -0400
Subject: [PATCH 24/28] Migrate RequestRenderer to ProtocolTraits
---
.../generators/model/ModelGenerator.java | 2 +-
.../model/renderers/RequestRenderer.java | 22 +++++--------------
.../ProtocolTraitsCharacterizationTest.java | 2 +-
.../generators/model/RequestRendererTest.java | 3 ++-
4 files changed, 10 insertions(+), 19 deletions(-)
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java
index 3809c124825..04999148d30 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java
@@ -71,7 +71,7 @@ private List buildRenderers(ClassifiedShapes classified) {
namespace, exportMacro, serviceName, smithyServiceName));
renderers.add(new RequestRenderer(
- classified.requests(), model, service, protocol,
+ classified.requests(), model, service, protocolTraits,
namespace, exportMacro, smithyServiceName));
renderers.add(new ResultRenderer(
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/RequestRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/RequestRenderer.java
index 44e02264a0c..0aa76dd779d 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/RequestRenderer.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/RequestRenderer.java
@@ -8,7 +8,7 @@
import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriterDelegator;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.CppTypeMapper;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.MemberRenderer;
-import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol.ProtocolTraits;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.ShapeClassifier;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.ShapeClassifier.RequestInfo;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.ShapeRenderer;
@@ -33,18 +33,18 @@ public final class RequestRenderer implements ShapeRenderer {
private final List requests;
private final Model model;
private final ServiceShape service;
- private final Protocol protocol;
+ private final ProtocolTraits protocolTraits;
private final String namespace;
private final String exportMacro;
private final String smithyServiceName;
public RequestRenderer(List requests, Model model, ServiceShape service,
- Protocol protocol, String namespace, String exportMacro,
+ ProtocolTraits protocolTraits, String namespace, String exportMacro,
String smithyServiceName) {
this.requests = requests;
this.model = model;
this.service = service;
- this.protocol = protocol;
+ this.protocolTraits = protocolTraits;
this.namespace = namespace;
this.exportMacro = exportMacro;
this.smithyServiceName = smithyServiceName;
@@ -194,24 +194,14 @@ private void renderSource(CppWriterDelegator writerDelegator,
String fileName = "source/model/" + className + ".cpp";
writerDelegator.useFileWriter(fileName, writer -> {
- if (protocol.isJsonLike()) {
- writer.write("#include ");
- } else {
- writer.write("#include ");
- }
+ protocolTraits.writeSerdeInclude(writer);
writer.write("#include ", smithyServiceName, className);
writer.write("");
writer.write("#include ");
writer.write("");
writer.write("using namespace Aws::$L::Model;", namespace);
- if (protocol.isJsonLike()) {
- writer.write("using namespace Aws::Utils::Json;");
- writer.write("using namespace Aws::Utils;");
- } else {
- writer.write("using namespace Aws::Utils::Xml;");
- writer.write("using namespace Aws::Utils;");
- }
+ protocolTraits.writeSerdeUsingDeclarations(writer);
writer.write("");
writer.write("Aws::String $L::SerializePayload() const { return \"{}\"; }", className);
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java
index c00ad537abc..7a7e8287507 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java
@@ -108,7 +108,7 @@ private static java.util.Map renderAll(Protocol p) {
ProtocolTraits traits = ProtocolResolver.traitsFor(resolved);
new SubObjectRenderer(classified.subObjects(), model, service, traits,
"Example", "AWS_EXAMPLE_API", "Example", "example").render(delegator);
- new RequestRenderer(classified.requests(), model, service, resolved,
+ new RequestRenderer(classified.requests(), model, service, traits,
"Example", "AWS_EXAMPLE_API", "example").render(delegator);
new ResultRenderer(classified.results(), model, service, traits,
"Example", "AWS_EXAMPLE_API", "example").render(delegator);
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/RequestRendererTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/RequestRendererTest.java
index d08fe24fe09..0a79c68e675 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/RequestRendererTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/RequestRendererTest.java
@@ -69,7 +69,8 @@ private static String renderRequestHeaderForStreamingOp(boolean inputStreams, bo
CppWriterDelegator delegator = new CppWriterDelegator(manifest);
RequestRenderer renderer = new RequestRenderer(
ShapeClassifier.classify(model, service).requests(),
- model, service, ProtocolResolver.resolve(service, model),
+ model, service,
+ ProtocolResolver.traitsFor(ProtocolResolver.resolve(service, model)),
"Example", "AWS_EXAMPLE_API", "example");
renderer.render(delegator);
delegator.flushWriters();
From 648ef2ac756f4268f00ecd03d01cf6e3c7f3ccb2 Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Mon, 3 Aug 2026 15:04:45 -0400
Subject: [PATCH 25/28] Migrate EventStreamRenderer to ProtocolTraits
---
.../generators/model/ModelGenerator.java | 2 +-
.../model/renderers/EventStreamRenderer.java | 57 +++++--------------
.../model/EventStreamRendererTest.java | 3 +-
3 files changed, 18 insertions(+), 44 deletions(-)
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java
index 04999148d30..3e175b40da5 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java
@@ -79,7 +79,7 @@ private List buildRenderers(ClassifiedShapes classified) {
namespace, exportMacro, smithyServiceName));
renderers.add(new EventStreamRenderer(
- classified.eventStreamHandlers(), model, service, protocol,
+ classified.eventStreamHandlers(), model, service, protocolTraits,
namespace, exportMacro, smithyServiceName));
return renderers;
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
index 52567d284e7..785a53ed8cc 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
@@ -8,8 +8,7 @@
import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriterDelegator;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.CppTypeMapper;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.MemberRenderer;
-import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
-import com.amazonaws.util.awsclientsmithygenerator.generators.model.SerdeStub;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol.ProtocolTraits;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.ShapeClassifier.EventStreamInfo;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.ShapeRenderer;
import software.amazon.smithy.model.Model;
@@ -33,25 +32,25 @@
* elsewhere (as reachable sub-objects) and only referenced here.
*
* No protocol-specific serialization is emitted; payload (de)serialization points
- * are protocol-agnostic TODO stubs via {@link SerdeStub}.
+ * are protocol-agnostic TODO stubs via {@link ProtocolTraits}.
*/
public final class EventStreamRenderer implements ShapeRenderer {
private final List eventStreams;
private final Model model;
private final ServiceShape service;
- private final Protocol protocol;
+ private final ProtocolTraits protocolTraits;
private final String namespace;
private final String exportMacro;
private final String smithyServiceName;
public EventStreamRenderer(List eventStreams, Model model, ServiceShape service,
- Protocol protocol, String namespace, String exportMacro,
+ ProtocolTraits protocolTraits, String namespace, String exportMacro,
String smithyServiceName) {
this.eventStreams = eventStreams;
this.model = model;
this.service = service;
- this.protocol = protocol;
+ this.protocolTraits = protocolTraits;
this.namespace = namespace;
this.exportMacro = exportMacro;
this.smithyServiceName = smithyServiceName;
@@ -307,7 +306,8 @@ private void renderHandlerSource(CppWriterDelegator writerDelegator, String opNa
});
for (MemberShape event : events) {
writer.openBlock("case $1LEventType::$2L: {", "}", opName, enumConstant(event), () -> {
- SerdeStub.renderEventPayloadDecodeStub(writer, eventShapeName(event), "m_on" + eventShapeName(event));
+ protocolTraits.writeEventPayloadDecode(writer, eventShapeName(event),
+ "m_on" + eventShapeName(event));
writer.write("break;");
});
}
@@ -335,7 +335,7 @@ private void renderHandlerSource(CppWriterDelegator writerDelegator, String opNa
writer.write("errorHeaderIter = headers.find(ERROR_MESSAGE_HEADER);");
writer.openBlock("if (errorHeaderIter == headers.end()) {", "}", () -> {
writer.write("// TODO: read error message from payload once protocol-specific serde lands");
- SerdeStub.renderErrorPayloadParseStub(writer);
+ protocolTraits.writeErrorPayloadParse(writer);
});
writer.openBlock("else {", "}", () -> {
writer.write("errorMessage = errorHeaderIter->second.GetEventHeaderValueAsString();");
@@ -420,14 +420,14 @@ private void renderInitialResponse(CppWriterDelegator writerDelegator, String op
writer.write("#include ", smithyServiceName, namespace);
writer.write("");
writer.writeNamespaceOpen("Aws");
- renderSerdeForwardDeclarations(writer);
+ protocolTraits.writeShapeForwardDeclarations(writer);
writer.writeNamespaceOpen(namespace);
writer.writeNamespaceOpen("Model");
writer.write("");
writer.openBlock("class $L {", "};", className, () -> {
writer.write("public:");
// The header-collection ctor sits before the serialize method (mainline ordering).
- SerdeStub.renderHeaderDeclarations(writer, protocol, exportMacro, className,
+ protocolTraits.writeSerdeMethodDecls(writer, exportMacro, className,
() -> writer.write("$1L $2L(const Http::HeaderValueCollection& responseHeaders);", exportMacro, className));
});
writer.write("");
@@ -438,23 +438,14 @@ private void renderInitialResponse(CppWriterDelegator writerDelegator, String op
String sourceFile = "source/model/" + className + ".cpp";
writerDelegator.useFileWriter(sourceFile, writer -> {
- if (protocol.isJsonLike()) {
- writer.write("#include ");
- } else {
- writer.write("#include ");
- }
+ protocolTraits.writeSerdeInclude(writer);
writer.write("#include ");
writer.write("#include ", smithyServiceName, className);
writer.write("");
writer.write("using namespace Aws::$1L::Model;", namespace);
- if (protocol.isJsonLike()) {
- writer.write("using namespace Aws::Utils::Json;");
- } else {
- writer.write("using namespace Aws::Utils::Xml;");
- }
- writer.write("using namespace Aws::Utils;");
+ protocolTraits.writeSerdeUsingDeclarations(writer);
writer.write("");
- SerdeStub.renderSerdeSourceStub(writer, protocol, className);
+ protocolTraits.writeSerdeMethodImpls(writer, className);
writer.write("");
writer.openBlock("$1L::$1L(const Http::HeaderValueCollection& responseHeaders) {", "}", className, () -> {
writer.write("AWS_UNREFERENCED_PARAM(responseHeaders);");
@@ -462,24 +453,6 @@ private void renderInitialResponse(CppWriterDelegator writerDelegator, String op
});
}
- /** Forward-declares the protocol serde types under Aws:: for header use. */
- private void renderSerdeForwardDeclarations(CppWriter writer) {
- if (protocol.isJsonLike()) {
- writer.writeNamespaceOpen("Utils");
- writer.writeNamespaceOpen("Json");
- writer.write("class JsonValue;");
- writer.write("class JsonView;");
- writer.writeNamespaceClose("Json");
- writer.writeNamespaceClose("Utils");
- } else {
- writer.writeNamespaceOpen("Utils");
- writer.writeNamespaceOpen("Xml");
- writer.write("class XmlNode;");
- writer.writeNamespaceClose("Xml");
- writer.writeNamespaceClose("Utils");
- }
- }
-
private void renderEventStreamUnion(CppWriterDelegator writerDelegator, String opName,
UnionShape union, List events,
List exceptions) {
@@ -498,14 +471,14 @@ private void renderEventStreamUnion(CppWriterDelegator writerDelegator, String o
writer.write("#include ");
writer.write("");
writer.writeNamespaceOpen("Aws");
- renderSerdeForwardDeclarations(writer);
+ protocolTraits.writeShapeForwardDeclarations(writer);
writer.writeNamespaceOpen(namespace);
writer.writeNamespaceOpen("Model");
writer.write("");
MemberRenderer.renderClassDocComment(writer, union, smithyServiceName, service.getVersion());
writer.openBlock("class $L {", "};", className, () -> {
writer.write("public:");
- SerdeStub.renderHeaderDeclarations(writer, protocol, exportMacro, className);
+ protocolTraits.writeSerdeMethodDecls(writer, exportMacro, className, null);
writer.write("");
// Event member accessors, typed as their concrete shape.
for (MemberShape event : events) {
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
index 78d86e3d000..85cf0b0a893 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
@@ -76,7 +76,8 @@ private static String render(String fileSuffix) {
CppWriterDelegator delegator = new CppWriterDelegator(manifest);
EventStreamRenderer renderer = new EventStreamRenderer(
ShapeClassifier.classify(model, service).eventStreamHandlers(),
- model, service, ProtocolResolver.resolve(service, model),
+ model, service,
+ ProtocolResolver.traitsFor(ProtocolResolver.resolve(service, model)),
"Example", "AWS_EXAMPLE_API", "example");
renderer.render(delegator);
delegator.flushWriters();
From 56bcedf394b4617f77172f81f964d207c9395fd7 Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Mon, 3 Aug 2026 15:05:38 -0400
Subject: [PATCH 26/28] Delete SerdeStub; retarget its tests onto
ProtocolTraits
---
.../generators/model/SerdeStub.java | 160 ------------------
.../generators/model/SerdeStubTest.java | 49 ------
.../protocol/ProtocolTraitsSerdeTest.java | 68 ++++++++
3 files changed, 68 insertions(+), 209 deletions(-)
delete mode 100644 tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStub.java
delete mode 100644 tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStubTest.java
create mode 100644 tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/ProtocolTraitsSerdeTest.java
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStub.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStub.java
deleted file mode 100644
index 0efd09b7dc2..00000000000
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStub.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/**
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0.
- */
-package com.amazonaws.util.awsclientsmithygenerator.generators.model;
-
-import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriter;
-import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
-import software.amazon.smithy.model.Model;
-import software.amazon.smithy.model.shapes.StructureShape;
-
-/**
- * Generates protocol-specific serde method declarations (header) and empty stub
- * implementations (source). The stubs will be replaced by schema-based serde.
- */
-public final class SerdeStub {
-
- private SerdeStub() {
- }
-
- public static void renderHeaderDeclarations(CppWriter writer, Protocol protocol,
- String exportMacro, String className) {
- renderHeaderDeclarations(writer, protocol, exportMacro, className, null);
- }
-
- /**
- * Emits the protocol-specific serde method declarations for a class.
- *
- * @param beforeSerializeMethod optional hook run after the (de)serialization constructor
- * and {@code operator=} declarations but before the serialize method
- * ({@code Jsonize}/{@code AddToNode}/{@code OutputToStream}). Used to inject
- * additional constructors (e.g. the event-stream initial-response header ctor)
- * at the mainline position. Ignored when {@code null}.
- */
- public static void renderHeaderDeclarations(CppWriter writer, Protocol protocol,
- String exportMacro, String className,
- Runnable beforeSerializeMethod) {
- if (protocol.isJsonLike()) {
- writer.write("$L $L() = default;", exportMacro, className);
- writer.write("$L $L(Aws::Utils::Json::JsonView jsonValue);", exportMacro, className);
- writer.write("$L $L& operator=(Aws::Utils::Json::JsonView jsonValue);", exportMacro, className);
- if (beforeSerializeMethod != null) {
- beforeSerializeMethod.run();
- }
- writer.write("$L Aws::Utils::Json::JsonValue Jsonize() const;", exportMacro);
- } else if (protocol == Protocol.REST_XML) {
- writer.write("$L $L() = default;", exportMacro, className);
- writer.write("$L $L(const Aws::Utils::Xml::XmlNode& xmlNode);", exportMacro, className);
- writer.write("$L $L& operator=(const Aws::Utils::Xml::XmlNode& xmlNode);", exportMacro, className);
- if (beforeSerializeMethod != null) {
- beforeSerializeMethod.run();
- }
- writer.write("$L void AddToNode(Aws::Utils::Xml::XmlNode& parentNode) const;", exportMacro);
- } else if (protocol == Protocol.QUERY_XML || protocol == Protocol.EC2) {
- writer.write("$L $L() = default;", exportMacro, className);
- writer.write("$L $L(const Aws::Utils::Xml::XmlNode& xmlNode);", exportMacro, className);
- writer.write("$L $L& operator=(const Aws::Utils::Xml::XmlNode& xmlNode);", exportMacro, className);
- if (beforeSerializeMethod != null) {
- beforeSerializeMethod.run();
- }
- writer.write("$L void OutputToStream(Aws::OStream& ostream, const char* location, unsigned index, const char* locationValue) const;", exportMacro);
- writer.write("$L void OutputToStream(Aws::OStream& ostream, const char* location) const;", exportMacro);
- } else {
- throw new UnsupportedOperationException("Unsupported protocol for serde header declarations: " + protocol);
- }
- }
-
- public static void renderSourceImplementation(CppWriter writer, StructureShape shape,
- Model model, Protocol protocol,
- String className, String serviceName,
- String projectName) {
- if (protocol.isJsonLike()) {
- renderJsonStub(writer, className);
- } else if (protocol == Protocol.REST_XML) {
- renderXmlStub(writer, className);
- } else if (protocol == Protocol.QUERY_XML || protocol == Protocol.EC2) {
- renderQueryXmlStub(writer, className);
- } else {
- throw new UnsupportedOperationException("Unsupported protocol for serde source implementation: " + protocol);
- }
- }
-
- /**
- * Emits the empty per-protocol serde source bodies for a class that has no backing
- * StructureShape (event stream union / initial response). Mirrors
- * {@link #renderSourceImplementation} but keyed only on the class name.
- */
- public static void renderSerdeSourceStub(CppWriter writer, Protocol protocol, String className) {
- if (protocol.isJsonLike()) {
- renderJsonStub(writer, className);
- } else if (protocol == Protocol.REST_XML) {
- renderXmlStub(writer, className);
- } else if (protocol == Protocol.QUERY_XML || protocol == Protocol.EC2) {
- renderQueryXmlStub(writer, className);
- } else {
- throw new UnsupportedOperationException("Unsupported protocol for serde source stub: " + protocol);
- }
- }
-
- /**
- * Emits a protocol-agnostic stub for an event-stream event case body: a TODO marker
- * and a minimal, compilable callback invocation with a default-constructed event.
- * No protocol-specific deserialization is emitted.
- *
- * @param writer the CppWriter
- * @param eventShapeName the C++ event shape type (e.g. "SubscribeToShardEvent")
- * @param callbackMember the handler callback member (e.g. "m_onSubscribeToShardEvent")
- */
- public static void renderEventPayloadDecodeStub(CppWriter writer, String eventShapeName,
- String callbackMember) {
- writer.write("// TODO: protocol-specific event payload deserialization");
- writer.write("$L($L{});", callbackMember, eventShapeName);
- }
-
- /**
- * Emits a protocol-agnostic stub in place of the protocol-specific error-payload parse.
- * The header-derived error code/message still flow to MarshallError at the call site.
- */
- public static void renderErrorPayloadParseStub(CppWriter writer) {
- writer.write("// TODO: protocol-specific error payload deserialization");
- }
-
- private static void renderJsonStub(CppWriter writer, String className) {
- writer.write("$L::$L(JsonView jsonValue) { *this = jsonValue; }", className, className);
- writer.write("");
- writer.openBlock("$L& $L::operator=(JsonView jsonValue) {", "}", className, className, () -> {
- writer.write("return *this;");
- });
- writer.write("");
- writer.openBlock("JsonValue $L::Jsonize() const {", "}", className, () -> {
- writer.write("JsonValue payload;");
- writer.write("return payload;");
- });
- }
-
- private static void renderXmlStub(CppWriter writer, String className) {
- writer.write("$L::$L(const XmlNode& xmlNode) { *this = xmlNode; }", className, className);
- writer.write("");
- writer.openBlock("$L& $L::operator=(const XmlNode& xmlNode) {", "}", className, className, () -> {
- writer.write("return *this;");
- });
- writer.write("");
- writer.openBlock("void $L::AddToNode(XmlNode& parentNode) const {", "}", className, () -> {
- });
- }
-
- private static void renderQueryXmlStub(CppWriter writer, String className) {
- writer.write("$L::$L(const XmlNode& xmlNode) { *this = xmlNode; }", className, className);
- writer.write("");
- writer.openBlock("$L& $L::operator=(const XmlNode& xmlNode) {", "}", className, className, () -> {
- writer.write("return *this;");
- });
- writer.write("");
- writer.openBlock("void $L::OutputToStream(Aws::OStream& ostream, const char* location, unsigned index, const char* locationValue) const {", "}", className, () -> {
- });
- writer.write("");
- writer.openBlock("void $L::OutputToStream(Aws::OStream& ostream, const char* location) const {", "}", className, () -> {
- });
- }
-}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStubTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStubTest.java
deleted file mode 100644
index 2335c427e3a..00000000000
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStubTest.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0.
- */
-package com.amazonaws.util.awsclientsmithygenerator.generators.model;
-
-import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriter;
-import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-class SerdeStubTest {
-
- @Test
- void renderEventPayloadDecodeStub_emitsTodoAndNoProtocolTokens() {
- CppWriter writer = new CppWriter();
- SerdeStub.renderEventPayloadDecodeStub(writer, "SubscribeToShardEvent", "m_onSubscribeToShardEvent");
- String out = writer.toString();
- assertTrue(out.contains("// TODO: protocol-specific event payload deserialization"),
- "Missing TODO marker: " + out);
- assertTrue(out.contains("m_onSubscribeToShardEvent(SubscribeToShardEvent{});"),
- "Missing callback invocation: " + out);
- assertFalse(out.contains("JsonValue"), "Should not emit JSON tokens: " + out);
- assertFalse(out.contains("Cbor"), "Should not emit CBOR tokens: " + out);
- }
-
- @Test
- void renderErrorPayloadParseStub_emitsTodoOnly() {
- CppWriter writer = new CppWriter();
- SerdeStub.renderErrorPayloadParseStub(writer);
- String out = writer.toString();
- assertTrue(out.contains("// TODO: protocol-specific error payload deserialization"),
- "Missing TODO marker: " + out);
- assertFalse(out.contains("JsonView"), "Should not emit JSON tokens: " + out);
- }
-
- @Test
- void renderSerdeSourceStub_json_emitsEmptyBodies() {
- CppWriter writer = new CppWriter();
- SerdeStub.renderSerdeSourceStub(writer, Protocol.JSON, "SubscribeToShardEventStream");
- String out = writer.toString();
- assertTrue(out.contains("SubscribeToShardEventStream::SubscribeToShardEventStream(JsonView jsonValue)"),
- "Missing JSON ctor stub: " + out);
- assertTrue(out.contains("JsonValue SubscribeToShardEventStream::Jsonize() const"),
- "Missing Jsonize stub: " + out);
- }
-}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/ProtocolTraitsSerdeTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/ProtocolTraitsSerdeTest.java
new file mode 100644
index 00000000000..3e1f03e2143
--- /dev/null
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/ProtocolTraitsSerdeTest.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+package com.amazonaws.util.awsclientsmithygenerator.generators.model.protocol;
+
+import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriter;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Replaces the former SerdeStubTest. Pins the invariant that the event- and
+ * error-payload stubs stay protocol-agnostic for EVERY protocol -- they are
+ * {@code default} methods on ProtocolTraits and no implementation should override
+ * them with protocol-specific text until schema-based serde lands.
+ */
+class ProtocolTraitsSerdeTest {
+
+ private static String render(java.util.function.Consumer body) {
+ CppWriter writer = new CppWriter();
+ body.accept(writer);
+ return writer.toString();
+ }
+
+ @ParameterizedTest
+ @EnumSource(Protocol.class)
+ void eventPayloadDecode_isProtocolAgnostic(Protocol p) {
+ ProtocolTraits traits = ProtocolResolver.traitsFor(p);
+ String out = render(w -> traits.writeEventPayloadDecode(
+ w, "SubscribeToShardEvent", "m_onSubscribeToShardEvent"));
+ assertTrue(out.contains("// TODO: protocol-specific event payload deserialization"),
+ "Missing TODO marker for " + p + ": " + out);
+ assertTrue(out.contains("m_onSubscribeToShardEvent(SubscribeToShardEvent{});"),
+ "Missing callback invocation for " + p + ": " + out);
+ assertFalse(out.contains("JsonValue"), "Should not emit JSON tokens for " + p + ": " + out);
+ assertFalse(out.contains("Cbor"), "Should not emit CBOR tokens for " + p + ": " + out);
+ assertFalse(out.contains("XmlNode"), "Should not emit XML tokens for " + p + ": " + out);
+ }
+
+ @ParameterizedTest
+ @EnumSource(Protocol.class)
+ void errorPayloadParse_isProtocolAgnostic(Protocol p) {
+ ProtocolTraits traits = ProtocolResolver.traitsFor(p);
+ String out = render(traits::writeErrorPayloadParse);
+ assertTrue(out.contains("// TODO: protocol-specific error payload deserialization"),
+ "Missing TODO marker for " + p + ": " + out);
+ assertFalse(out.contains("JsonView"), "Should not emit JSON tokens for " + p + ": " + out);
+ assertFalse(out.contains("XmlNode"), "Should not emit XML tokens for " + p + ": " + out);
+ }
+
+ /** The JSON stub bodies formerly asserted in SerdeStubTest. */
+ @Test
+ void jsonSerdeMethodImpls_emitEmptyBodies() {
+ ProtocolTraits traits = ProtocolResolver.traitsFor(Protocol.JSON);
+ String out = render(w -> traits.writeSerdeMethodImpls(w, "SubscribeToShardEventStream"));
+ assertTrue(out.contains(
+ "SubscribeToShardEventStream::SubscribeToShardEventStream(JsonView jsonValue)"),
+ "Missing JSON ctor stub: " + out);
+ assertTrue(out.contains("JsonValue SubscribeToShardEventStream::Jsonize() const"),
+ "Missing Jsonize stub: " + out);
+ }
+}
From d104cca3a16e4d93f6325d37c95f70ddc959c222 Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Mon, 3 Aug 2026 15:21:50 -0400
Subject: [PATCH 27/28] Thread resolved protocol into ShapeClassifier for a
single resolution point
---
.../generators/model/ModelGenerator.java | 2 +-
.../generators/model/ShapeClassifier.java | 9 +++++----
.../model/EventStreamRendererTest.java | 7 ++++---
.../ProtocolTraitsCharacterizationTest.java | 2 +-
.../generators/model/RequestRendererTest.java | 7 ++++---
.../generators/model/ShapeClassifierTest.java | 18 +++++++++---------
6 files changed, 24 insertions(+), 21 deletions(-)
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java
index 3e175b40da5..2a70d3e52c9 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java
@@ -52,7 +52,7 @@ public ModelGenerator(Model model, ServiceShape service, CppWriterDelegator writ
}
public void generateAll() {
- ClassifiedShapes classified = ShapeClassifier.classify(model, service);
+ ClassifiedShapes classified = ShapeClassifier.classify(model, service, protocol);
List renderers = buildRenderers(classified);
for (ShapeRenderer renderer : renderers) {
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifier.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifier.java
index df118be1ac0..85c1c6da40a 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifier.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifier.java
@@ -94,12 +94,13 @@ private ShapeClassifier() {}
/**
* Classifies all shapes reachable from the given service into generation buckets.
*
- * @param model the Smithy model
- * @param service the service shape whose operations define the root set
+ * @param model the Smithy model
+ * @param service the service shape whose operations define the root set
+ * @param protocol the already-resolved protocol; passed in rather than re-resolved so
+ * that {@code ModelGenerator} remains the single resolution point
* @return classified shapes grouped by generation bucket
*/
- public static ClassifiedShapes classify(Model model, ServiceShape service) {
- Protocol protocol = ProtocolResolver.resolve(service, model);
+ public static ClassifiedShapes classify(Model model, ServiceShape service, Protocol protocol) {
TopDownIndex index = TopDownIndex.of(model);
Set reachable = GlobalTransforms.computeReachableShapes(model, service);
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
index 85cf0b0a893..5d36380eb4c 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
@@ -5,6 +5,7 @@
package com.amazonaws.util.awsclientsmithygenerator.generators.model;
import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriterDelegator;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers.EventStreamRenderer;
import org.junit.jupiter.api.Test;
import software.amazon.smithy.build.MockManifest;
@@ -74,10 +75,10 @@ private static String render(String fileSuffix) {
ServiceShape service = model.expectShape(ShapeId.from("com.example#Example"), ServiceShape.class);
MockManifest manifest = new MockManifest();
CppWriterDelegator delegator = new CppWriterDelegator(manifest);
+ Protocol protocol = ProtocolResolver.resolve(service, model);
EventStreamRenderer renderer = new EventStreamRenderer(
- ShapeClassifier.classify(model, service).eventStreamHandlers(),
- model, service,
- ProtocolResolver.traitsFor(ProtocolResolver.resolve(service, model)),
+ ShapeClassifier.classify(model, service, protocol).eventStreamHandlers(),
+ model, service, ProtocolResolver.traitsFor(protocol),
"Example", "AWS_EXAMPLE_API", "example");
renderer.render(delegator);
delegator.flushWriters();
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java
index 7a7e8287507..fa55180dd9f 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java
@@ -99,7 +99,7 @@ private static java.util.Map renderAll(Protocol p) {
Protocol resolved = ProtocolResolver.resolve(service, model);
assertEquals(p, resolved, "Fixture trait did not resolve to the intended protocol");
- ShapeClassifier.ClassifiedShapes classified = ShapeClassifier.classify(model, service);
+ ShapeClassifier.ClassifiedShapes classified = ShapeClassifier.classify(model, service, resolved);
MockManifest manifest = new MockManifest();
CppWriterDelegator delegator = new CppWriterDelegator(manifest);
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/RequestRendererTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/RequestRendererTest.java
index 0a79c68e675..d0aefbb6bb6 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/RequestRendererTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/RequestRendererTest.java
@@ -5,6 +5,7 @@
package com.amazonaws.util.awsclientsmithygenerator.generators.model;
import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriterDelegator;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers.RequestRenderer;
import org.junit.jupiter.api.Test;
import software.amazon.smithy.build.MockManifest;
@@ -67,10 +68,10 @@ private static String renderRequestHeaderForStreamingOp(boolean inputStreams, bo
ServiceShape service = model.expectShape(ShapeId.from("com.example#Example"), ServiceShape.class);
MockManifest manifest = new MockManifest();
CppWriterDelegator delegator = new CppWriterDelegator(manifest);
+ Protocol protocol = ProtocolResolver.resolve(service, model);
RequestRenderer renderer = new RequestRenderer(
- ShapeClassifier.classify(model, service).requests(),
- model, service,
- ProtocolResolver.traitsFor(ProtocolResolver.resolve(service, model)),
+ ShapeClassifier.classify(model, service, protocol).requests(),
+ model, service, ProtocolResolver.traitsFor(protocol),
"Example", "AWS_EXAMPLE_API", "example");
renderer.render(delegator);
delegator.flushWriters();
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifierTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifierTest.java
index 6fe308881af..d74f17b29a7 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifierTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifierTest.java
@@ -51,7 +51,7 @@ private Model buildSimpleModel() {
void classifiesRequestShape() {
Model model = buildSimpleModel();
ServiceShape service = model.expectShape(ShapeId.from("com.example#TestService"), ServiceShape.class);
- var classified = ShapeClassifier.classify(model, service);
+ var classified = ShapeClassifier.classify(model, service, ProtocolResolver.resolve(service, model));
assertTrue(classified.requests().stream()
.anyMatch(r -> r.shape().getId().getName().equals("GetItemRequest")));
}
@@ -60,7 +60,7 @@ void classifiesRequestShape() {
void classifiesResultShape() {
Model model = buildSimpleModel();
ServiceShape service = model.expectShape(ShapeId.from("com.example#TestService"), ServiceShape.class);
- var classified = ShapeClassifier.classify(model, service);
+ var classified = ShapeClassifier.classify(model, service, ProtocolResolver.resolve(service, model));
assertTrue(classified.results().stream()
.anyMatch(r -> r.shape().getId().getName().equals("GetItemResponse")));
}
@@ -69,7 +69,7 @@ void classifiesResultShape() {
void classifiesSubObject() {
Model model = buildSimpleModel();
ServiceShape service = model.expectShape(ShapeId.from("com.example#TestService"), ServiceShape.class);
- var classified = ShapeClassifier.classify(model, service);
+ var classified = ShapeClassifier.classify(model, service, ProtocolResolver.resolve(service, model));
assertTrue(classified.subObjects().stream()
.anyMatch(s -> s.getId().getName().equals("ItemData")));
}
@@ -97,7 +97,7 @@ void skipsDocumentShape() {
.build();
StringShape str = StringShape.builder().id("com.example#String").build();
Model model = Model.builder().addShapes(doc, request, response, op, service, str).build();
- var classified = ShapeClassifier.classify(model, service);
+ var classified = ShapeClassifier.classify(model, service, ProtocolResolver.resolve(service, model));
// Document should not appear in any list
assertTrue(classified.subObjects().stream()
.noneMatch(s -> s.getId().getName().equals("Doc")));
@@ -128,7 +128,7 @@ void skipsUnmodeledExceptionJson() {
.addTrait(software.amazon.smithy.aws.traits.protocols.RestJson1Trait.builder().build())
.build();
Model model = Model.builder().addShapes(str, exception, request, response, op, service).build();
- var classified = ShapeClassifier.classify(model, service);
+ var classified = ShapeClassifier.classify(model, service, ProtocolResolver.resolve(service, model));
assertTrue(classified.subObjects().stream()
.noneMatch(s -> s.getId().getName().equals("NotFoundException")));
}
@@ -159,7 +159,7 @@ void classifiesModeledExceptionJson() {
.addTrait(software.amazon.smithy.aws.traits.protocols.RestJson1Trait.builder().build())
.build();
Model model = Model.builder().addShapes(str, exception, request, response, op, service).build();
- var classified = ShapeClassifier.classify(model, service);
+ var classified = ShapeClassifier.classify(model, service, ProtocolResolver.resolve(service, model));
assertTrue(classified.subObjects().stream()
.anyMatch(s -> s.getId().getName().equals("DetailedException")));
}
@@ -190,7 +190,7 @@ void skipsUnmodeledExceptionXml() {
.addTrait(software.amazon.smithy.aws.traits.protocols.RestXmlTrait.builder().build())
.build();
Model model = Model.builder().addShapes(str, exception, request, response, op, service).build();
- var classified = ShapeClassifier.classify(model, service);
+ var classified = ShapeClassifier.classify(model, service, ProtocolResolver.resolve(service, model));
assertTrue(classified.subObjects().stream()
.noneMatch(s -> s.getId().getName().equals("NotFoundException")));
}
@@ -230,7 +230,7 @@ void classifiesEventStreamHandler() {
Model model = Model.builder()
.addShapes(str, eventA, eventStream, request, response, op, service)
.build();
- var classified = ShapeClassifier.classify(model, service);
+ var classified = ShapeClassifier.classify(model, service, ProtocolResolver.resolve(service, model));
// Event stream handler should be recorded
assertEquals(1, classified.eventStreamHandlers().size());
@@ -334,7 +334,7 @@ void classifiesEnumShape() {
Model model = Model.builder()
.addShapes(enumStr, request, response, op, service)
.build();
- var classified = ShapeClassifier.classify(model, service);
+ var classified = ShapeClassifier.classify(model, service, ProtocolResolver.resolve(service, model));
assertTrue(classified.enums().stream()
.anyMatch(s -> s.getId().getName().equals("Status")));
}
From 2334d4d687a98836cd770f3f1b90c4b22fac81fd Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Mon, 3 Aug 2026 15:44:55 -0400
Subject: [PATCH 28/28] Remove stale plan references from codegen comments
---
.../model/renderers/EventStreamRenderer.java | 2 +-
.../generators/model/EventStreamRendererTest.java | 2 +-
.../model/ProtocolTraitsCharacterizationTest.java | 12 ++++++------
.../model/protocol/ProtocolTraitsSerdeTest.java | 10 +++++-----
4 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
index 785a53ed8cc..da3836bd54b 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
@@ -408,7 +408,7 @@ private void renderHandlerSource(CppWriterDelegator writerDelegator, String opNa
});
}
- // ---- Placeholders filled in by Tasks 4 and 5 ----------------------------
+ // ---- Initial response / event stream union ------------------------------
private void renderInitialResponse(CppWriterDelegator writerDelegator, String opName) {
String className = opName + "InitialResponse";
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
index 5d36380eb4c..07423bc434a 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
@@ -159,7 +159,7 @@ void initialResponseHeader_hasHeaderCollectionCtorAndSerdeDecls() {
}
@Test
- void initialResponseSource_hasHeaderCtorAndSerdeStub() {
+ void initialResponseSource_hasHeaderCtorDefinition() {
String c = render("DoStreamInitialResponse.cpp");
assertTrue(c.contains("DoStreamInitialResponse::DoStreamInitialResponse(const Http::HeaderValueCollection&"),
"Missing header ctor definition: " + c);
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java
index fa55180dd9f..82e38cd3221 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ProtocolTraitsCharacterizationTest.java
@@ -27,11 +27,13 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
- * Characterization tests: they assert the generated C++ text produced TODAY, so that
- * the ProtocolTraits refactor can be proven output-preserving.
+ * Characterization tests pinning the exact generated C++ text for every supported
+ * protocol, so that changes to the model renderers or their {@link ProtocolTraits}
+ * strategies cannot silently alter generated output.
*
- * If one of these fails during the refactor, the refactor changed generated output.
- * Fix the production code, NOT the assertion.
+ *
A failure here means generated output changed. Unless the change is intentional,
+ * fix the production code rather than the assertion; if it is intentional, update the
+ * assertion in the same commit that changes the renderer.
*/
class ProtocolTraitsCharacterizationTest {
@@ -103,8 +105,6 @@ private static java.util.Map renderAll(Protocol p) {
MockManifest manifest = new MockManifest();
CppWriterDelegator delegator = new CppWriterDelegator(manifest);
- // NOTE (Tasks 4-7): when a renderer's ctor switches from Protocol to
- // ProtocolTraits, change ONLY the argument below -- never the assertions.
ProtocolTraits traits = ProtocolResolver.traitsFor(resolved);
new SubObjectRenderer(classified.subObjects(), model, service, traits,
"Example", "AWS_EXAMPLE_API", "Example", "example").render(delegator);
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/ProtocolTraitsSerdeTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/ProtocolTraitsSerdeTest.java
index 3e1f03e2143..b6100b4ca77 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/ProtocolTraitsSerdeTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/protocol/ProtocolTraitsSerdeTest.java
@@ -15,10 +15,10 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
- * Replaces the former SerdeStubTest. Pins the invariant that the event- and
- * error-payload stubs stay protocol-agnostic for EVERY protocol -- they are
- * {@code default} methods on ProtocolTraits and no implementation should override
- * them with protocol-specific text until schema-based serde lands.
+ * Pins the invariant that the event- and error-payload stubs stay protocol-agnostic
+ * for EVERY protocol -- they are {@code default} methods on {@link ProtocolTraits} and
+ * no implementation should override them with protocol-specific text until
+ * schema-based serde lands.
*/
class ProtocolTraitsSerdeTest {
@@ -54,7 +54,7 @@ void errorPayloadParse_isProtocolAgnostic(Protocol p) {
assertFalse(out.contains("XmlNode"), "Should not emit XML tokens for " + p + ": " + out);
}
- /** The JSON stub bodies formerly asserted in SerdeStubTest. */
+ /** The JSON serde method bodies are empty stubs pending schema-based serde. */
@Test
void jsonSerdeMethodImpls_emitEmptyBodies() {
ProtocolTraits traits = ProtocolResolver.traitsFor(Protocol.JSON);