convert YAML to relations v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Thu, 03 Dec 2020 22:31:03 +0100
branchv_0
changeset 2 d68192f0e960
parent 1 8d2d8f4077af
child 3 64f8f8792470
convert YAML to relations Expected structure is: map: key = relation name value = sequence of maps (records) where: key = attribute name value = attribute value Example: -------- fruit: - id: 1 name: cherry - id: 2 name: apple - id: 3 name: plum empty: # this relation has no attributes and will be ignored sparse: - a: aaa b: bbb c: ccc - a: AAA b: BBB - c: C d: D e: E a: A f: F - c: CCC -------- Not yet supported: -------- my_relation_with_metadata: attribute-metadata: - name: a type: string - name: b type: boolean record: - a: a - b: true --------
src/YAMLCommand.h
--- a/src/YAMLCommand.h	Thu Dec 03 17:16:09 2020 +0100
+++ b/src/YAMLCommand.h	Thu Dec 03 22:31:03 2020 +0100
@@ -32,7 +32,7 @@
 
 class YAMLCommand {
 private:
-	std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings.
+	std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // YAML strings are in UTF-8
 
 	class YAMLEvent {
 	private:
@@ -98,20 +98,25 @@
 
 	using YAMLEvent_p = std::shared_ptr<YAMLEvent>;
 
-	enum class Mode {
-		ROOT,
-		RELATIONS_SEQUENCE,
-		MAPPING,
-		MAP_KEY
+	enum class State {
+		START,
+		RELATIONS,
+		ATTRIBUTE,
+		RECORDS
 	};
 
-	std::vector<Mode> mode;
+	State state;
+
+	relpipe::writer::string_t y2s(yaml_char_t* value) {
+		return value ? convertor.from_bytes((const char*) value) : L"";
+	}
 
-	/**
-	 * Both YAML and XML strings are in UTF-8.
-	 */
-	const char* y2x(yaml_char_t* value) {
-		return value ? (const char*) value : "";
+	relpipe::writer::string_t findValue(std::vector<relpipe::writer::string_t> 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];
+		}
+		return L"";
 	}
 
 public:
@@ -125,32 +130,60 @@
 	void process(std::istream& input, std::shared_ptr<relpipe::writer::RelationalWriter> writer) {
 		parser.setInput(&input);
 
+		relpipe::writer::string_t relationName;
+		std::vector<relpipe::writer::string_t> record;
 		std::vector<relpipe::writer::AttributeMetadata> attributesMetadata;
-		attributesMetadata.push_back(relpipe::writer::AttributeMetadata{L"todo", relpipe::writer::TypeId::STRING});
-		writer->startRelation(L"YAML", attributesMetadata, true);
 
-		mode.push_back(Mode::ROOT);
-		std::string itemName;
-
-		writer->writeAttribute(L"before cycle");
-		for (YAMLEvent_p event = YAMLEvent_p(parser.next()); event; event = YAMLEvent_p(parser.next())) {
+		state = State::START;
 
-			if (event->getType() == YAML_NO_EVENT) { writer->writeAttribute(L"YAML_NO_EVENT");
-			} else if (event->getType() == YAML_STREAM_START_EVENT) { writer->writeAttribute(L"YAML_STREAM_START_EVENT");
-			} else if (event->getType() == YAML_STREAM_END_EVENT) { writer->writeAttribute(L"YAML_STREAM_END_EVENT");
-			} else if (event->getType() == YAML_DOCUMENT_START_EVENT) { writer->writeAttribute(L"YAML_DOCUMENT_START_EVENT");
-			} else if (event->getType() == YAML_DOCUMENT_END_EVENT) { writer->writeAttribute(L"YAML_DOCUMENT_END_EVENT");
-			} else if (event->getType() == YAML_ALIAS_EVENT) { writer->writeAttribute(L"YAML_ALIAS_EVENT");
-			} else if (event->getType() == YAML_SCALAR_EVENT) { writer->writeAttribute(L"YAML_SCALAR_EVENT");
-			} else if (event->getType() == YAML_SEQUENCE_START_EVENT) { writer->writeAttribute(L"YAML_SEQUENCE_START_EVENT");
-			} else if (event->getType() == YAML_SEQUENCE_END_EVENT) { writer->writeAttribute(L"YAML_SEQUENCE_END_EVENT");
-			} else if (event->getType() == YAML_MAPPING_START_EVENT) { writer->writeAttribute(L"YAML_MAPPING_START_EVENT");
-			} else if (event->getType() == YAML_MAPPING_END_EVENT) { writer->writeAttribute(L"YAML_MAPPING_END_EVENT");
-			} else { writer->writeAttribute(L"else???");
-				// TODO: unsupported type?
+		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()));
 			}
 		}
-		writer->writeAttribute(L"after cycle");
 	}
 };