# HG changeset patch # User František Kučera # Date 1607192241 -3600 # Node ID 64f8f87924702f8d41b06ec9002a62faec7b1f0b # Parent d68192f0e96095218e39ba272bddf1b8d75a918b support also attribute metadata (i.e. also different attribute types than mere strings) and empty relations (no records) diff -r d68192f0e960 -r 64f8f8792470 src/YAMLCommand.h --- a/src/YAMLCommand.h Thu Dec 03 22:31:03 2020 +0100 +++ b/src/YAMLCommand.h Sat Dec 05 19:17:21 2020 +0100 @@ -50,6 +50,26 @@ return event.type; } + const relpipe::writer::string_t getTypeName() const { + return findTypeName(event.type); + } + + /** Just for debugging and error handling */ + static const relpipe::writer::string_t findTypeName(const yaml_event_type_t eventType) { + if (eventType == YAML_NO_EVENT) return L"NO"; + else if (eventType == YAML_STREAM_START_EVENT) return L"STREAM_START"; + else if (eventType == YAML_STREAM_END_EVENT) return L"STREAM_END"; + else if (eventType == YAML_DOCUMENT_START_EVENT) return L"DOCUMENT_START"; + else if (eventType == YAML_DOCUMENT_END_EVENT) return L"DOCUMENT_END"; + else if (eventType == YAML_ALIAS_EVENT) return L"ALIAS"; + else if (eventType == YAML_SCALAR_EVENT) return L"SCALAR"; + else if (eventType == YAML_SEQUENCE_START_EVENT) return L"SEQUENCE_START"; + else if (eventType == YAML_SEQUENCE_END_EVENT) return L"SEQUENCE_END"; + else if (eventType == YAML_MAPPING_START_EVENT) return L"MAPPING_START"; + else if (eventType == YAML_MAPPING_END_EVENT) return L"MAPPING_END"; + else return L"UNKNOWN"; + } + const yaml_event_t* getEvent() const { return &event; } @@ -94,31 +114,113 @@ } }; + using YAMLEvent_p = std::shared_ptr; + YAMLParser parser; - - using YAMLEvent_p = std::shared_ptr; + std::shared_ptr writer; - enum class State { - START, - RELATIONS, - ATTRIBUTE, - RECORDS - }; + relpipe::writer::string_t relationName; + std::vector record; + std::vector attributesMetadata; - State state; - - relpipe::writer::string_t y2s(yaml_char_t* value) { + relpipe::writer::string_t y2s(const yaml_char_t* value) { return value ? convertor.from_bytes((const char*) value) : L""; } - relpipe::writer::string_t findValue(std::vector record, relpipe::writer::string_t key) { - if (record.size() % 2) relpipe::writer::RelpipeWriterException(L"Invalid count of values in the record vector"); - for (size_t i = 0; i < record.size(); i += 2) { - if (record[i] == key) return record[i + 1]; + relpipe::writer::string_t fetchScalarValue(YAMLEvent_p event) { + if (event->getType() == YAML_SCALAR_EVENT) return y2s(event->getEvent()->data.scalar.value); + else throw relpipe::writer::RelpipeWriterException(L"Invalid YAML structure: expected SCALAR but got: " + event->getTypeName()); + } + + void consumeEvent(const yaml_event_type_t expectedEventType, relpipe::writer::string_t expectedScalarValue = L"") { + YAMLEvent_p event = YAMLEvent_p(parser.next()); + if (!event) throw relpipe::writer::RelpipeWriterException(L"Invalid YAML structure: missing event: " + YAMLEvent::findTypeName(expectedEventType)); + if (event->getType() != expectedEventType) throw relpipe::writer::RelpipeWriterException(L"Invalid YAML structure: expected event: " + YAMLEvent::findTypeName(expectedEventType) + L", but got: " + event->getTypeName()); + if (expectedEventType == YAML_SCALAR_EVENT && expectedScalarValue.size() && expectedScalarValue != fetchScalarValue(event)) throw relpipe::writer::RelpipeWriterException(L"Invalid YAML structure: expected scalar value: " + expectedScalarValue + L", but got " + fetchScalarValue(event)); + } + + relpipe::writer::string_t consumeScalarEvent() { + YAMLEvent_p event = YAMLEvent_p(parser.next()); + if (event && event->getType() == YAML_SCALAR_EVENT) return fetchScalarValue(event); + else throw relpipe::writer::RelpipeWriterException(L"Invalid YAML structure: expected SCALAR, but got: " + event->getTypeName()); + } + + void processRelation() { + YAMLEvent_p event = YAMLEvent_p(parser.next()); + if (event->getType() == YAML_MAPPING_START_EVENT) processRelationWithMetadata(); + else if (event->getType() == YAML_SEQUENCE_START_EVENT)processRelationWithoutMetadata(); + else throw relpipe::writer::RelpipeWriterException(L"Invalid YAML structure: expected MAPPING or SEQUENCE, but got: " + event->getTypeName()); + } + + void processRelationWithMetadata() { + consumeEvent(YAML_SCALAR_EVENT, L"attribute-metadata"); + consumeEvent(YAML_SEQUENCE_START_EVENT); + + for (YAMLEvent_p event = YAMLEvent_p(parser.next()); event && event->getType() != YAML_SEQUENCE_END_EVENT; event = YAMLEvent_p(parser.next())) { + if (event->getType() != YAML_MAPPING_START_EVENT) throw relpipe::writer::RelpipeWriterException(L"Invalid YAML structure: expected MAPPING (attribute-metadata), but got: " + event->getTypeName()); + relpipe::writer::string_t name; + relpipe::writer::string_t type = L"string"; + for (YAMLEvent_p event = YAMLEvent_p(parser.next()); event && event->getType() != YAML_MAPPING_END_EVENT; event = YAMLEvent_p(parser.next())) { + auto key = fetchScalarValue(event); + auto value = consumeScalarEvent(); + if (key == L"name") name = value; + else if (key == L"type") type = value; + else; // unsupported metadata, later there might be something useful + } + attributesMetadata.push_back({name, writer->toTypeId(type)}); } - return L""; + + writer->startRelation(relationName, attributesMetadata, true); + + YAMLEvent_p event = YAMLEvent_p(parser.next()); + if (event->getType() == YAML_SCALAR_EVENT && fetchScalarValue(event) == L"record") { + consumeEvent(YAML_SEQUENCE_START_EVENT); + processRecords(); + consumeEvent(YAML_MAPPING_END_EVENT); + } else if (event->getType() == YAML_MAPPING_END_EVENT) { + // empty relation, no records + } else { + relpipe::writer::RelpipeWriterException(L"Invalid YAML structure: expected 'record' or MAPPING_END, but got: " + event->getTypeName()); + } } + void processRelationWithoutMetadata() { + // First record: + consumeEvent(YAML_MAPPING_START_EVENT); + record.clear(); + for (YAMLEvent_p event = YAMLEvent_p(parser.next()); event && event->getType() != YAML_MAPPING_END_EVENT; event = YAMLEvent_p(parser.next())) { + auto name = fetchScalarValue(event); + auto value = consumeScalarEvent(); + attributesMetadata.push_back({name, relpipe::writer::TypeId::STRING}); + record.push_back(value); + } + writer->startRelation(relationName, attributesMetadata, true); + for (auto value : record) writer->writeAttribute(value); + + // Following records: + processRecords(); + } + + void processRecords() { + for (YAMLEvent_p event = YAMLEvent_p(parser.next()); event && event->getType() != YAML_SEQUENCE_END_EVENT; event = YAMLEvent_p(parser.next())) { + if (event->getType() != YAML_MAPPING_START_EVENT) throw relpipe::writer::RelpipeWriterException(L"Invalid YAML structure: expected MAPPING (record), but got: " + event->getTypeName()); + record.clear(); + record.resize(attributesMetadata.size()); + for (YAMLEvent_p event = YAMLEvent_p(parser.next()); event && event->getType() != YAML_MAPPING_END_EVENT; event = YAMLEvent_p(parser.next())) { + auto name = fetchScalarValue(event); + auto value = consumeScalarEvent(); + for (int i = 0; i < attributesMetadata.size(); i++) { + if (name == attributesMetadata[i].attributeName) { + record[i] = value; + break; + } + } + } + for (auto value : record) writer->writeAttribute(value); + } + } + + public: YAMLCommand() { @@ -129,61 +231,20 @@ void process(std::istream& input, std::shared_ptr writer) { parser.setInput(&input); + this->writer = writer; - relpipe::writer::string_t relationName; - std::vector record; - std::vector attributesMetadata; - - state = State::START; + consumeEvent(YAML_STREAM_START_EVENT); + consumeEvent(YAML_DOCUMENT_START_EVENT); + consumeEvent(YAML_MAPPING_START_EVENT); - for (YAMLEvent_p event = YAMLEvent_p(parser.next()); event; event = YAMLEvent_p(parser.next())) { - if (event->getType() == YAML_NO_EVENT) { - throw relpipe::writer::RelpipeWriterException(L"Invalid YAML structure: YAML_NO_EVENT"); - } else if (event->getType() == YAML_STREAM_START_EVENT) { - } else if (event->getType() == YAML_STREAM_END_EVENT) { - } else if (event->getType() == YAML_DOCUMENT_START_EVENT) { - } else if (event->getType() == YAML_DOCUMENT_END_EVENT) { - } else if (event->getType() == YAML_ALIAS_EVENT) { - } else if (event->getType() == YAML_SCALAR_EVENT) { - relpipe::writer::string_t scalarValue = y2s(event->getEvent()->data.scalar.value); - if (state == State::RELATIONS) relationName = scalarValue; - else if (state == State::ATTRIBUTE) record.push_back(scalarValue); - else throw relpipe::writer::RelpipeWriterException(L"Invalid YAML structure: unexpected YAML_SCALAR_EVENT"); - } else if (event->getType() == YAML_SEQUENCE_START_EVENT) { - if (state == State::RELATIONS) state = State::RECORDS; - else throw relpipe::writer::RelpipeWriterException(L"Invalid YAML structure: unexpected YAML_SEQUENCE_START_EVENT"); - } else if (event->getType() == YAML_SEQUENCE_END_EVENT) { - if (state == State::RECORDS) { - state = State::RELATIONS; - relationName.clear(); - attributesMetadata.clear(); - } else { - throw relpipe::writer::RelpipeWriterException(L"Invalid YAML structure: unexpected YAML_SEQUENCE_END_EVENT"); - } - } else if (event->getType() == YAML_MAPPING_START_EVENT) { - if (state == State::START) state = State::RELATIONS; - else if (state == State::RECORDS) state = State::ATTRIBUTE; - else if (state == State::RELATIONS) throw relpipe::writer::RelpipeWriterException(L"Not yet implemented"); // TODO: there might be also a map in the relation value (not only a sequence) that would contain metadata (i.e. data types + support for relation containing no records) - else throw relpipe::writer::RelpipeWriterException(L"Invalid YAML structure: unexpected YAML_MAPPING_START_EVENT"); - } else if (event->getType() == YAML_MAPPING_END_EVENT) { - if (state == State::ATTRIBUTE) { - if (attributesMetadata.size() == 0) { - if (record.size() % 2) relpipe::writer::RelpipeWriterException(L"Invalid count of values in the record vector"); - for (size_t i = 0; i < record.size(); i += 2) attributesMetadata.push_back(relpipe::writer::AttributeMetadata{record[i], relpipe::writer::TypeId::STRING}); - writer->startRelation(relationName, attributesMetadata, true); - } - for (auto m : attributesMetadata) writer->writeAttribute(findValue(record, m.attributeName)); - record.clear(); - state = State::RECORDS; - } else if (state == State::RELATIONS) { - break; // map of the relations ends (root) - } else { - throw relpipe::writer::RelpipeWriterException(L"Invalid YAML structure: unexpected YAML_MAPPING_END_EVENT"); - } - } else { - throw relpipe::writer::RelpipeWriterException(L"Invalid YAML structure: unknown event: " + std::to_wstring(event->getType())); - } + for (YAMLEvent_p event = YAMLEvent_p(parser.next()); event && event->getType() != YAML_MAPPING_END_EVENT; event = YAMLEvent_p(parser.next())) { + relationName = fetchScalarValue(event); + attributesMetadata.clear(); + processRelation(); } + + consumeEvent(YAML_DOCUMENT_END_EVENT); + consumeEvent(YAML_STREAM_END_EVENT); } };