shapePlacer = SmithyIdlModelSerializer::placeShapesByNamespace;
private Path basePath = null;
private boolean serializePrelude = false;
+ private SmithyIdlComponentOrder componentOrder = SmithyIdlComponentOrder.PREFERRED;
public Builder() {}
@@ -381,6 +336,20 @@ public Builder serializePrelude() {
return this;
}
+ /**
+ * Defines how components are sorted in the model, changing the default behavior of sorting alphabetically.
+ *
+ * You can serialize metadata, shapes, and traits in the original order they were defined by setting
+ * this to {@link SmithyIdlComponentOrder#SOURCE_LOCATION}.
+ *
+ * @param componentOrder Change how components are sorted.
+ * @return Returns the builder.
+ */
+ public Builder componentOrder(SmithyIdlComponentOrder componentOrder) {
+ this.componentOrder = Objects.requireNonNull(componentOrder);
+ return this;
+ }
+
@Override
public SmithyIdlModelSerializer build() {
return new SmithyIdlModelSerializer(this);
@@ -396,19 +365,22 @@ private static final class ShapeSerializer extends ShapeVisitor.Default {
private final Predicate traitFilter;
private final Model model;
private final Set inlineableShapes;
+ private final SmithyIdlComponentOrder componentOrder;
ShapeSerializer(
SmithyCodeWriter codeWriter,
NodeSerializer nodeSerializer,
Predicate traitFilter,
Model model,
- Set inlineableShapes
+ Set inlineableShapes,
+ SmithyIdlComponentOrder componentOrder
) {
this.codeWriter = codeWriter;
this.nodeSerializer = nodeSerializer;
this.traitFilter = traitFilter;
this.model = model;
this.inlineableShapes = inlineableShapes;
+ this.componentOrder = componentOrder;
}
@Override
@@ -535,6 +507,8 @@ private void serializeTraits(Map traits, TraitFeature... traitFe
}
}
+ Comparator traitComparator = componentOrder.toShapeIdComparator();
+
traits.values().stream()
.filter(trait -> noSpecialDocsSyntax || !(trait instanceof DocumentationTrait))
// The default and enumValue traits are serialized using the assignment syntactic sugar.
@@ -547,7 +521,7 @@ private void serializeTraits(Map traits, TraitFeature... traitFe
}
})
.filter(traitFilter)
- .sorted(Comparator.comparing(Trait::toShapeId))
+ .sorted(traitComparator)
.forEach(this::serializeTrait);
}
@@ -560,9 +534,10 @@ private void serializeDocumentation(String documentation) {
private void serializeTrait(Trait trait) {
Node node = trait.toNode();
- Shape shape = model.expectShape(trait.toShapeId());
+ // We won't fail if the trait can't be found.
+ Shape shape = model.getShape(trait.toShapeId()).orElse(null);
- if (trait instanceof AnnotationTrait || isEmptyStructure(node, shape)) {
+ if (shape != null && (trait instanceof AnnotationTrait || isEmptyStructure(node, shape))) {
// Traits that inherit from AnnotationTrait specifically can omit a value.
// Traits that are simply boolean shapes which don't implement AnnotationTrait cannot.
// Additionally, empty structure traits can omit a value.
diff --git a/smithy-model/src/test/java/software/amazon/smithy/model/shapes/SmithyIdlModelSerializerTest.java b/smithy-model/src/test/java/software/amazon/smithy/model/shapes/SmithyIdlModelSerializerTest.java
index 32ec26bb34f..8986502963a 100644
--- a/smithy-model/src/test/java/software/amazon/smithy/model/shapes/SmithyIdlModelSerializerTest.java
+++ b/smithy-model/src/test/java/software/amazon/smithy/model/shapes/SmithyIdlModelSerializerTest.java
@@ -6,12 +6,12 @@
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasKey;
-import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import java.io.IOException;
import java.net.URISyntaxException;
+import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -217,4 +217,30 @@ public void serializesRootLevelDefaults() {
is(true));
assertThat(model2, equalTo(model2));
}
+
+ @Test
+ public void usesOriginalSourceLocation() {
+ URL resource = getClass().getResource("idl-serialization/out-of-order.smithy");
+ Model model = Model.assembler().addImport(resource).assemble().unwrap();
+ Map reserialized = SmithyIdlModelSerializer.builder()
+ .componentOrder(SmithyIdlComponentOrder.SOURCE_LOCATION)
+ .build()
+ .serialize(model);
+ String modelResult = reserialized.values().iterator().next();
+
+ assertThat(modelResult, equalTo(IoUtils.readUtf8Url(resource)));
+ }
+
+ @Test
+ public void sortsAlphabetically() {
+ URL resource = getClass().getResource("idl-serialization/alphabetical.smithy");
+ Model model = Model.assembler().addImport(resource).assemble().unwrap();
+ Map reserialized = SmithyIdlModelSerializer.builder()
+ .componentOrder(SmithyIdlComponentOrder.ALPHA_NUMERIC)
+ .build()
+ .serialize(model);
+ String modelResult = reserialized.values().iterator().next();
+
+ assertThat(modelResult, equalTo(IoUtils.readUtf8Url(resource)));
+ }
}
diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/alphabetical.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/alphabetical.smithy
new file mode 100644
index 00000000000..bbf96fd5abd
--- /dev/null
+++ b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/alphabetical.smithy
@@ -0,0 +1,21 @@
+$version: "2.0"
+
+metadata foo = "hi"
+metadata zoo = "test"
+
+namespace com.example
+
+structure Abc {
+ bar: String
+ @length(
+ min: 1
+ )
+ @required
+ baz: String
+}
+
+string Def
+
+service Hij {
+ version: "2006-03-01"
+}
diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/out-of-order.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/out-of-order.smithy
new file mode 100644
index 00000000000..3947bc3f2d3
--- /dev/null
+++ b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/out-of-order.smithy
@@ -0,0 +1,21 @@
+$version: "2.0"
+
+metadata zoo = "test"
+metadata foo = "hi"
+
+namespace com.example
+
+string MyString
+
+structure Hello {
+ bar: String
+ @required
+ @length(
+ min: 1
+ )
+ baz: MyString
+}
+
+service Foo {
+ version: "2006-03-01"
+}