class
#include <Magnum/OpenDdl/Document.h>
Document OpenDDL document.
Parser for the OpenDDL file format.
The parser loads the file into an in-memory structure, which is just a set of flat arrays. When traversing the parsed document, all Structure and Property objects are just tiny wrappers around references to the internal data of the originating document, thus you must ensure that the document is available for whole lifetime of these instances. On the other hand this allows you to copy and store these instances without worrying about performance.
Usage
To avoid needless allocations and string comparisons when using the parsed document, all structure and property names are represented as integer IDs. To parse a file, you first need to build a list of string names with their corresponding IDs for both structure and property names. The following example is a subset of the OpenGEX file format:
namespace OpenGex { enum: Int { GeometryObject, IndexArray, Mesh, VertexArray }; const std::initializer_list<OpenDdl::CharacterLiteral> structures{ "GeometryObject", "IndexArray", "Mesh", "VertexArray" } enum: Int { attrib, key, motion_blur, primitive, shadow, two_sided, visible }; const std::initializer_list<OpenDdl::CharacterLiteral> properties{ "attrib", "key", "motion_blur", "primitive", "shadow", "two_sided", "visible" }; }
Each enum value has corresponding string representation and the string identifiers are then passed to parse():
OpenDdl::Document d; bool parsed = d.parse(data, OpenGex::structures, OpenGex::properties);
If the file contains structures or properties which are not included in the identifer lists, these are parsed with UnknownIdentifier ID. If the document has syntax errors, the function returns false
and prints detailed diagnostics on Corrade::
for(OpenDdl::Structure geometryObject: d.childrenOf(OpenGex::GeometryObject)) { // Decide about primitive if(Containers::Optional<OpenDdl::Property> primitive = geometryObject.findPropertyOf(OpenGex::primitive)) { if(!primitive->isTypeCompatibleWith(OpenDdl::Type::String)) { // error ... } std::string str = primitive->as<std::string>(); if(str == "triangles") { // ... } else if(str == "lines") { // ... } // ... } else { // ... } // Parse vertex array if(Containers::Optional<OpenDdl::Structure> vertexArray = geometryObject.findFirstChildOf(OpenGex::VertexArray)) { if(!vertexArray->hasChildren() || vertexArray->firstChild().type() != OpenDdl::Type::Float) { // error ... } Containers::ArrayView<const Float> vertexArray = vertexArray->firstChild().asArray<Float>(); // ... } else { // error ... } }
As you can see, the error checking can get pretty tiresome after a while. That's when document validation proves to be useful. The validation is just rough and checks only proper document hierarchy, allowed structure and property types, structure count and presence of required properties, but that's often enough to avoid most of the redundant checks. You define which structures can appear at document level and then for each structure what properties and which substructures it can have. Again a (very stripped down) subset of OpenGEX specification:
namespace OpenGex { using namespace OpenDdl::Validation; // GeometryObject and Metric can be root structures const Structures rootStructures{ {GeometryObject, {}}, {Metric, {}} }; // Info about particular structures const std::initializer_list<Structure> structureInfo{ // Metric structure has required key string property and contains exactly // one float or string primitive substructure with exactly one value {Metric, Properties{{key, PropertyType::String, RequiredProperty}}, Primitives{Type::Float, Type::String}, 1, 1}, // GeometryObject structure has optional visible and shadow boolean // properties and one or more Mesh substructures {GeometryObject, Properties{{visible, OpenDdl::PropertyType::Bool, OptionalProperty}, {shadow, OpenDdl::PropertyType::Bool, OptionalProperty}}, Structures{{Mesh, {1, 0}}}}, // Mesh structure has optional lod and primitive properties, at least one // VertexArray substructure and zero or more IndexArray substructures {Mesh, Properties{{lod, OpenDdl::PropertyType::UnsignedInt, OptionalProperty}, {primitive, OpenDdl::PropertyType::String, OptionalProperty}}, Structures{{VertexArray, {1, 0}}, {IndexArray, {}}}}, // IndexArray structure has exactly one unsigned primitive substructure // with any number of values {IndexArray, Primitives{OpenDdl::Type::UnsignedByte, OpenDdl::Type::UnsignedShort, OpenDdl::Type::UnsignedInt}, 1, 0}, // VertexArray structure has required attrib property and exactly one float // substructure with any number of values {VertexArray, Properties{{attrib, OpenDdl::PropertyType::String, RequiredProperty}}, Primitives{OpenDdl::Type::Float}, 1, 0} }; }
You then pass it to validate() and check the return value. As with parse(), structures with UnknownIdentifier ID are ignored and if the validation fails, detailed diagnostics is printed on Corrade::
bool valid = d.validate(OpenGex::rootStructures, OpenGex::structureInfo);
If the document is valid, you can access child structures and properties directly with e.g. Structure::
// Decide about primitive if(Containers::Optional<OpenDdl::Property> primitive = geometryObject.findPropertyOf(OpenGex::primitive)) { auto&& str = primitive->as<std::string>(); if(str == "triangles") { // ... } else if(str == "lines") { // ... } // ... } else { // ... } // Parse vertex array OpenDdl::Structure vertexArray = geometryObject.firstChildOf(OpenGex::VertexArray); auto&& attrib = vertexArray.propertyOf(OpenGex::attrib).as<std::string>(); if(attrib == "position") { // ... } else if(attrib == "normal") { // ... } // Parse vertex array data Containers::ArrayView<const Float> data = vertexArray.firstChild().asArray<Float>(); // ...
Constructors, destructors, conversion operators
Public functions
- auto operator=(const Document&) -> Document& deleted
- Copying is disabled.
- auto operator=(Document&&) -> Document& deleted
- Moving is disabled.
-
auto parse(Containers::
ArrayView<const char> data, std:: initializer_list<CharacterLiteral> structureIdentifiers, std:: initializer_list<CharacterLiteral> propertyIdentifiers) -> bool - Parse data.
- auto isEmpty() -> bool
- Whether the document is empty.
-
auto findFirstChild() const -> Containers::
Optional<Structure> - Find first top-level structure in the document.
- auto firstChild() const -> Structure
- First top-level structure in the document.
- auto children() const -> Implementation::StructureList
- Top-level structures.
-
auto findFirstChildOf(Type type) const -> Containers::
Optional<Structure> - Find first custom top-level structure of given type.
-
auto findFirstChildOf(Int identifier) const -> Containers::
Optional<Structure> - Find first custom top-level structure of given identifier.
-
auto findFirstChildOf(std::
initializer_list<Int> identifiers) const -> Containers:: Optional<Structure> -
auto findFirstChildOf(Containers::
ArrayView<const Int> identifiers) const -> Containers:: Optional<Structure> - auto firstChildOf(Type type) const -> Structure
- First custom top-level structure of given type.
- auto firstChildOf(Int identifier) const -> Structure
- First custom top-level structure of given identifier.
-
template<class ... T>auto childrenOf(Int identifier, T... identifiers) const -> Implementation::StructureOfList<sizeof...(T)+1>
- Top-level structures of given identifier.
-
auto validate(Validation::
Structures allowedRootStructures, std:: initializer_list<Validation:: Structure> structures) const -> bool - Validate document.
Function documentation
bool Magnum:: OpenDdl:: Document:: parse(Containers:: ArrayView<const char> data,
std:: initializer_list<CharacterLiteral> structureIdentifiers,
std:: initializer_list<CharacterLiteral> propertyIdentifiers)
Parse data.
Parameters | |
---|---|
data | Document data |
structureIdentifiers | Structure identifiers |
propertyIdentifiers | Property identifiers |
Returns | Whether the parsing succeeded |
The data are appended to already parsed data. Each identifier from the lists is converted to ID corresponding to its position in the list. If the parsing results in error, detailed info is printed on error output and the document has undefined contents.
After parsing, all references to structure data are valid until next parse call.
Containers:: Optional<Structure> Magnum:: OpenDdl:: Document:: findFirstChild() const
Find first top-level structure in the document.
Returns Corrade::
Structure Magnum:: OpenDdl:: Document:: firstChild() const
First top-level structure in the document.
The document must not be empty.
Implementation::StructureList Magnum:: OpenDdl:: Document:: children() const
Top-level structures.
The returned list can be traversed using common range-based for:
for(Structure s: document.children()) { // ... }
Containers:: Optional<Structure> Magnum:: OpenDdl:: Document:: findFirstChildOf(Type type) const
Find first custom top-level structure of given type.
Returns Corrade::
Containers:: Optional<Structure> Magnum:: OpenDdl:: Document:: findFirstChildOf(Int identifier) const
Find first custom top-level structure of given identifier.
Returns Corrade::
Containers:: Optional<Structure> Magnum:: OpenDdl:: Document:: findFirstChildOf(std:: initializer_list<Int> identifiers) const
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
Containers:: Optional<Structure> Magnum:: OpenDdl:: Document:: findFirstChildOf(Containers:: ArrayView<const Int> identifiers) const
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
Structure Magnum:: OpenDdl:: Document:: firstChildOf(Type type) const
First custom top-level structure of given type.
Expects that there is such structure.
Structure Magnum:: OpenDdl:: Document:: firstChildOf(Int identifier) const
First custom top-level structure of given identifier.
Expects that there is such structure.
template<class ... T>
Implementation::StructureOfList<sizeof...(T)+1> Magnum:: OpenDdl:: Document:: childrenOf(Int identifier,
T... identifiers) const
Top-level structures of given identifier.
The returned list can be traversed using common range-based for:
for(Structure s: document.childrenOf(...)) { // ... }
bool Magnum:: OpenDdl:: Document:: validate(Validation:: Structures allowedRootStructures,
std:: initializer_list<Validation:: Structure> structures) const
Validate document.
Validates the document according to passed specification. Structures and properties that have unknown names are ignored.
Note that sub-array sizes, reference validity and some other things are not checked, this is just to ensure that the document has expected structure so you can use firstChildOf(), Structure::