77
88import static java .lang .String .format ;
99
10+ import java .util .HashMap ;
1011import java .util .Locale ;
12+ import java .util .Map ;
1113import java .util .Objects ;
1214import java .util .Optional ;
1315import java .util .logging .Logger ;
1416import software .amazon .smithy .codegen .core .Symbol ;
1517import software .amazon .smithy .codegen .core .SymbolProvider ;
1618import software .amazon .smithy .model .Model ;
19+ import software .amazon .smithy .model .knowledge .OperationIndex ;
1720import software .amazon .smithy .model .shapes .MemberShape ;
21+ import software .amazon .smithy .model .shapes .OperationShape ;
1822import software .amazon .smithy .model .shapes .ServiceShape ;
1923import software .amazon .smithy .model .shapes .Shape ;
24+ import software .amazon .smithy .model .shapes .ShapeId ;
2025import software .amazon .smithy .model .shapes .ShapeVisitor ;
2126import software .amazon .smithy .model .shapes .StructureShape ;
27+ import software .amazon .smithy .model .traits .InputTrait ;
28+ import software .amazon .smithy .model .traits .OutputTrait ;
2229import software .amazon .smithy .model .traits .StringTrait ;
2330import software .amazon .smithy .model .traits .TitleTrait ;
2431import software .amazon .smithy .utils .SmithyUnstableApi ;
3542 * definition section. For services, this defaults to the value of the
3643 * {@code title} trait. For other shapes, it defaults to the shape name including
3744 * any renames from the attached service.
45+ *
3846 * <li>{@code definitionFile}: The file in which the documentation for this shape
3947 * should be written. By default these are all written to a single flat directory.
4048 * If this is empty, the shape does not have its own definition section and cannot
4149 * be linked to.
50+ *
4251 * <li>{@link #SHAPE_PROPERTY}: A named Shape property containing the shape that
4352 * the symbol represents. Decorators provided by
4453 * {@link DocIntegration#decorateSymbolProvider} MUST set or preserve this
4554 * property.
55+ *
56+ * <li>{@link #OPERATION_PROPERTY}: A named OperationShape property containing the
57+ * operation shape that the shape is bound to. This will only be present on
58+ * structure shapes that have the {@code input} or {@code output} traits.
59+ *
4660 * <li>{@link #LINK_ID_PROPERTY}: A named String property containing the string to
4761 * use for the id for links to the shape. In HTML, this would be the {@code id} for
4862 * the tag containing the shape's definition. Given a link id {@code foo}, a link
4963 * to the shape's definition might look like {@code https://example.com/shapes#foo}
5064 * for example. If this or {@code definitionFile} is empty, it is not possible to
5165 * link to the shape.
66+ *
5267 * <li>{@link #ENABLE_DEFAULT_FILE_EXTENSION}: A named boolean property indicating
5368 * whether the symbol's definition file should have the default file extension
5469 * applied. If not present or set to {@code false}, the file extension will not be
@@ -74,6 +89,17 @@ public final class DocSymbolProvider extends ShapeVisitor.Default<Symbol> implem
7489 */
7590 public static final String SHAPE_PROPERTY = "shape" ;
7691
92+ /**
93+ * The operation that the symbol's shape is bound to.
94+ *
95+ * <p>This property will only be present on structures that have either the
96+ * {@code input} or {@code output} trait.
97+ *
98+ * <p>Use {@code symbol.getProperty(OPERATION_PROPERTY, OperationShape.class)} to
99+ * access this property.
100+ */
101+ public static final String OPERATION_PROPERTY = "operation" ;
102+
77103 /**
78104 * The name for a shape symbol's named property containing the string to use for
79105 * the id for links to the shape. In HTML, this would be the {@code id} for the tag
@@ -104,6 +130,7 @@ public final class DocSymbolProvider extends ShapeVisitor.Default<Symbol> implem
104130 private final Model model ;
105131 private final DocSettings docSettings ;
106132 private final ServiceShape serviceShape ;
133+ private final Map <ShapeId , OperationShape > ioToOperation ;
107134
108135 /**
109136 * Constructor.
@@ -115,6 +142,26 @@ public DocSymbolProvider(Model model, DocSettings docSettings) {
115142 this .model = model ;
116143 this .docSettings = docSettings ;
117144 this .serviceShape = model .expectShape (docSettings .service (), ServiceShape .class );
145+ this .ioToOperation = mapIoShapesToOperations (model );
146+ }
147+
148+ private Map <ShapeId , OperationShape > mapIoShapesToOperations (Model model ) {
149+ // Map input and output structures to their containing shapes. These will be
150+ // documented alongside their associated operations, so we need said operations
151+ // when generating symbols for them. Pre-computing this mapping is a bit faster
152+ // than just running a selector every time we hit an IO
153+ // shape.
154+ var operationIoMap = new HashMap <ShapeId , OperationShape >();
155+ var operationIndex = OperationIndex .of (model );
156+ for (var operation : model .getOperationShapes ()) {
157+ operationIndex .getInputShape (operation )
158+ .filter (i -> i .hasTrait (InputTrait .class ))
159+ .ifPresent (i -> operationIoMap .put (i .getId (), operation ));
160+ operationIndex .getOutputShape (operation )
161+ .filter (i -> i .hasTrait (OutputTrait .class ))
162+ .ifPresent (i -> operationIoMap .put (i .getId (), operation ));
163+ }
164+ return Map .copyOf (operationIoMap );
118165 }
119166
120167 @ Override
@@ -132,12 +179,26 @@ public Symbol serviceShape(ServiceShape shape) {
132179 }
133180
134181 @ Override
135- public Symbol structureShape ( StructureShape shape ) {
182+ public Symbol operationShape ( OperationShape shape ) {
136183 return getSymbolBuilder (shape )
137184 .definitionFile (getDefinitionFile (serviceShape , shape ))
138185 .build ();
139186 }
140187
188+ @ Override
189+ public Symbol structureShape (StructureShape shape ) {
190+ var builder = getSymbolBuilder (shape );
191+ if (ioToOperation .containsKey (shape .getId ())) {
192+ // Input and output structures are documented on the operation's definition page.
193+ var operation = ioToOperation .get (shape .getId ());
194+ builder .definitionFile (getDefinitionFile (serviceShape , operation ));
195+ builder .putProperty (OPERATION_PROPERTY , operation );
196+ } else {
197+ builder .definitionFile (getDefinitionFile (serviceShape , shape ));
198+ }
199+ return builder .build ();
200+ }
201+
141202 @ Override
142203 public Symbol memberShape (MemberShape shape ) {
143204 var builder = getSymbolBuilder (shape )
0 commit comments