diff --git a/README-CHANGES.xml b/README-CHANGES.xml index ab563d2d..0524b249 100644 --- a/README-CHANGES.xml +++ b/README-CHANGES.xml @@ -6,6 +6,13 @@ 2017-01-09 0.6.0 + + 2017-01-09 + + 29 + Allow redundant use of the metadata command. + + 2017-01-09 diff --git a/io7m-smfj-format-text/src/main/java/com/io7m/smfj/format/text/SMFTV1BodyParser.java b/io7m-smfj-format-text/src/main/java/com/io7m/smfj/format/text/SMFTV1BodyParser.java index 5f0a4b85..0f7fda3a 100644 --- a/io7m-smfj-format-text/src/main/java/com/io7m/smfj/format/text/SMFTV1BodyParser.java +++ b/io7m-smfj-format-text/src/main/java/com/io7m/smfj/format/text/SMFTV1BodyParser.java @@ -51,7 +51,8 @@ final class SMFTV1BodyParser extends SMFTAbstractParser private Map attributes_ok; private Map attributes_attempted; private long parsed_triangles; - private long parsed_metas; + private long metas_parsed_count; + private boolean metas_started; SMFTV1BodyParser( final SMFTAbstractParser in_parent, @@ -131,7 +132,7 @@ private void onEOF() private void failMissedMetadata() { - if (this.parsed_metas != this.header.metaCount()) { + if (this.metas_parsed_count != this.header.metaCount()) { this.fail("Too few metadata elements specified", Optional.empty()); } } @@ -577,13 +578,16 @@ private void parseMetas() { LOG.debug("parsing metadata values"); - if (this.header.metaCount() == 0L) { - super.fail("No metadata was expected.", Optional.empty()); + if (this.metas_started) { + super.fail( + "A metadata command has already been specified.", + Optional.empty()); return; } - this.parsed_metas = 0L; - while (this.parsed_metas != this.header.metaCount()) { + this.metas_started = true; + this.metas_parsed_count = 0L; + while (this.metas_parsed_count != this.header.metaCount()) { if (this.parserHasFailed()) { return; } @@ -614,7 +618,7 @@ private void parseMetas() } } - this.parsed_metas = Math.addExact(this.parsed_metas, 1L); + this.metas_parsed_count = Math.addExact(this.metas_parsed_count, 1L); } } diff --git a/io7m-smfj-specification/src/main/resources/com/io7m/smfj/specification/text.sdi b/io7m-smfj-specification/src/main/resources/com/io7m/smfj/specification/text.sdi index 0c61346c..00e9b784 100644 --- a/io7m-smfj-specification/src/main/resources/com/io7m/smfj/specification/text.sdi +++ b/io7m-smfj-specification/src/main/resources/com/io7m/smfj/specification/text.sdi @@ -278,10 +278,9 @@ The command takes no arguments. [paragraph] The command may only appear in the file after all [link [target smft.data.commands.triangles] triangles] and -[link [target smft.data.commands.attribute] attributes] have been received. - - - +[link [target smft.data.commands.attribute] attributes] have been received. If +the file does not have metadata, the command may be omitted. If the file does +have metadata, the command must be specified exactly once. [subsection [title Data - meta] [id smft.data.commands.meta]] [paragraph] diff --git a/io7m-smfj-tests/src/test/java/com/io7m/smfj/tests/format/text/SMFFormatTextTest.java b/io7m-smfj-tests/src/test/java/com/io7m/smfj/tests/format/text/SMFFormatTextTest.java index 0d572d2d..8d6c2ffc 100644 --- a/io7m-smfj-tests/src/test/java/com/io7m/smfj/tests/format/text/SMFFormatTextTest.java +++ b/io7m-smfj-tests/src/test/java/com/io7m/smfj/tests/format/text/SMFFormatTextTest.java @@ -2896,6 +2896,182 @@ public void testSerializerAttributeNotFinishedSerializingPrevious() serializer.serializeData(SMFAttributeName.of("y")); } + @Test + public void testMetaNoneNotOmitted( + final @Mocked SMFParserEventsType events) + { + final StringBuilder s = new StringBuilder(128); + s.append("smf 1 0"); + s.append(System.lineSeparator()); + s.append("schema 696F376D A0B0C0D0 1 2"); + s.append(System.lineSeparator()); + s.append("coordinates +x +y -z counter-clockwise"); + s.append(System.lineSeparator()); + s.append("attribute a integer-unsigned 1 32"); + s.append(System.lineSeparator()); + s.append("vertices 1"); + s.append(System.lineSeparator()); + s.append("triangles 1 16"); + s.append(System.lineSeparator()); + s.append("data"); + s.append(System.lineSeparator()); + s.append("attribute a"); + s.append(System.lineSeparator()); + s.append("0"); + s.append(System.lineSeparator()); + s.append("triangles"); + s.append(System.lineSeparator()); + s.append("0 0 0"); + s.append(System.lineSeparator()); + s.append("metadata"); + s.append(System.lineSeparator()); + + final SMFAttribute attribute = SMFAttribute.of( + SMFAttributeName.of("a"), + SMFComponentType.ELEMENT_TYPE_INTEGER_UNSIGNED, + 1, + 32); + + final SMFHeader.Builder header_b = baseHeader(List.of(attribute)); + header_b.setTriangleCount(1L); + header_b.setVertexCount(1L); + final SMFHeader h = header_b.build(); + + new StrictExpectations() + {{ + events.onStart(); + events.onVersionReceived(SMFFormatVersion.of(1, 0)); + events.onHeaderParsed(h); + events.onDataAttributeStart(attribute); + events.onDataAttributeValueIntegerUnsigned1(0L); + events.onDataAttributeFinish(attribute); + events.onDataTrianglesStart(); + events.onDataTriangle(0L, 0L, 0L); + events.onDataTrianglesFinish(); + events.onFinish(); + }}; + + runForText(events, true, s); + } + + @Test + public void testMetaNoneOmitted( + final @Mocked SMFParserEventsType events) + { + final StringBuilder s = new StringBuilder(128); + s.append("smf 1 0"); + s.append(System.lineSeparator()); + s.append("schema 696F376D A0B0C0D0 1 2"); + s.append(System.lineSeparator()); + s.append("coordinates +x +y -z counter-clockwise"); + s.append(System.lineSeparator()); + s.append("attribute a integer-unsigned 1 32"); + s.append(System.lineSeparator()); + s.append("vertices 1"); + s.append(System.lineSeparator()); + s.append("triangles 1 16"); + s.append(System.lineSeparator()); + s.append("data"); + s.append(System.lineSeparator()); + s.append("attribute a"); + s.append(System.lineSeparator()); + s.append("0"); + s.append(System.lineSeparator()); + s.append("triangles"); + s.append(System.lineSeparator()); + s.append("0 0 0"); + s.append(System.lineSeparator()); + + final SMFAttribute attribute = SMFAttribute.of( + SMFAttributeName.of("a"), + SMFComponentType.ELEMENT_TYPE_INTEGER_UNSIGNED, + 1, + 32); + + final SMFHeader.Builder header_b = baseHeader(List.of(attribute)); + header_b.setTriangleCount(1L); + header_b.setVertexCount(1L); + final SMFHeader h = header_b.build(); + + new StrictExpectations() + {{ + events.onStart(); + events.onVersionReceived(SMFFormatVersion.of(1, 0)); + events.onHeaderParsed(h); + events.onDataAttributeStart(attribute); + events.onDataAttributeValueIntegerUnsigned1(0L); + events.onDataAttributeFinish(attribute); + events.onDataTrianglesStart(); + events.onDataTriangle(0L, 0L, 0L); + events.onDataTrianglesFinish(); + events.onFinish(); + }}; + + runForText(events, true, s); + } + + @Test + public void testMetaTwice( + final @Mocked SMFParserEventsType events) + { + final StringBuilder s = new StringBuilder(128); + s.append("smf 1 0"); + s.append(System.lineSeparator()); + s.append("schema 696F376D A0B0C0D0 1 2"); + s.append(System.lineSeparator()); + s.append("coordinates +x +y -z counter-clockwise"); + s.append(System.lineSeparator()); + s.append("attribute a integer-unsigned 1 32"); + s.append(System.lineSeparator()); + s.append("vertices 1"); + s.append(System.lineSeparator()); + s.append("triangles 1 16"); + s.append(System.lineSeparator()); + s.append("data"); + s.append(System.lineSeparator()); + s.append("attribute a"); + s.append(System.lineSeparator()); + s.append("0"); + s.append(System.lineSeparator()); + s.append("triangles"); + s.append(System.lineSeparator()); + s.append("0 0 0"); + s.append(System.lineSeparator()); + s.append("metadata"); + s.append(System.lineSeparator()); + s.append("metadata"); + s.append(System.lineSeparator()); + + final SMFAttribute attribute = SMFAttribute.of( + SMFAttributeName.of("a"), + SMFComponentType.ELEMENT_TYPE_INTEGER_UNSIGNED, + 1, + 32); + + final SMFHeader.Builder header_b = baseHeader(List.of(attribute)); + header_b.setTriangleCount(1L); + header_b.setVertexCount(1L); + final SMFHeader h = header_b.build(); + + new StrictExpectations() + {{ + events.onStart(); + events.onVersionReceived(SMFFormatVersion.of(1, 0)); + events.onHeaderParsed(h); + events.onDataAttributeStart(attribute); + events.onDataAttributeValueIntegerUnsigned1(0L); + events.onDataAttributeFinish(attribute); + events.onDataTrianglesStart(); + events.onDataTriangle(0L, 0L, 0L); + events.onDataTrianglesFinish(); + events.onError(this.withArgThat( + new ParseErrorMessageStartsWith("A metadata command has already been specified."))); + events.onFinish(); + }}; + + runForText(events, true, s); + } + private static class ParseErrorMessageStartsWith extends TypeSafeMatcher { private final String message;