handle arrays and maps v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Sat, 05 Jun 2021 20:03:13 +0200
branchv_0
changeset 32 82b7f6e41a65
parent 31 273faff8b848
child 33 57cb51fb5212
handle arrays and maps
src/XMLDocumentConstructor.h
--- a/src/XMLDocumentConstructor.h	Tue Jun 01 19:25:12 2021 +0200
+++ b/src/XMLDocumentConstructor.h	Sat Jun 05 20:03:13 2021 +0200
@@ -40,14 +40,23 @@
 
 	enum class Mode {
 		ROOT,
-		SEQUENCE,
-		MAPPING,
-		MAP_KEY
+		ARRAY,
+		MAP_KEY,
+		MAP_VALUE,
+		CHAR_STRING,
+		BYTE_STRING
 	};
 
-	std::string rootName = "cbor";
+
+	Glib::ustring rootName = "cbor";
+	Glib::ustring itemName;
 	xmlpp::Element* current;
 	std::vector<Mode> mode;
+	/**
+	 * number of remainin items in the fixed-size container (map or array) at current tree-level
+	 */
+	std::vector<ssize_t> remainingItems;
+#define INDEFINITE -1
 
 	cbor_callbacks callbacks = cbor_empty_callbacks;
 
@@ -66,11 +75,71 @@
 		return current->get_parent() == nullptr ? current : current->get_parent();
 	}
 
+	void checkRemainingItems() {
+		if (mode.back() == Mode::ARRAY || mode.back() == Mode::MAP_KEY) {
+			if (remainingItems.back() != INDEFINITE) {
+				remainingItems.back()--;
+				if (remainingItems.back() < 1) containerEnd();
+			}
+		}
+	}
+
 	void appendScalarValue(Glib::ustring value, Glib::ustring cborType, bool isNull = false) {
-		// TODO: different behavior depending on mode
-		xmlpp::Element* element = current->add_child("scalar");
-		element->add_child_text(value);
-		element->set_attribute("type", cborType);
+		// TODO: null
+		if (mode.back() == Mode::ARRAY) {
+			current->add_child(itemName)->add_child_text(value);
+		} else if (mode.back() == Mode::MAP_KEY) {
+			current = current->add_child(value);
+			mode.push_back(Mode::MAP_VALUE);
+		} else if (mode.back() == Mode::MAP_VALUE) {
+			current->add_child_text(value);
+			current = parentOrSelf(current);
+			mode.pop_back();
+		} else if (mode.back() == Mode::ROOT) {
+			current->add_child_text(value);
+		} else {
+			// TODO: process YAML_SCALAR_EVENT
+		}
+
+		checkRemainingItems();
+	}
+
+	void arrayStart(ssize_t size) {
+		xmlpp::Element* parent = current->get_parent();
+		if (parent) {
+			itemName = current->get_name();
+			parent->remove_child(current);
+			current = parent;
+		} else {
+			itemName = "item";
+		}
+
+		if (mode.back() == Mode::MAP_KEY) mode.pop_back();
+		mode.push_back(Mode::ARRAY);
+		remainingItems.push_back(size);
+	}
+
+	void mapStart(ssize_t size) {
+		if (mode.back() == Mode::ROOT) {
+		} else if (mode.back() == Mode::ARRAY) {
+			current = current->add_child(itemName);
+		} else if (mode.back() == Mode::MAP_VALUE) {
+			mode.pop_back();
+			// TODO: remainingItems
+		} else {
+			// TODO: map might be a key of another map → wrap/nest
+			// …probably not
+		}
+
+		mode.push_back(Mode::MAP_KEY);
+		remainingItems.push_back(size);
+	}
+
+	void containerEnd() {
+		current->add_child_comment(Glib::ustring::compose("end of a container: mode = %1", (int) mode.back())); // FIXME: remove
+		remainingItems.pop_back();
+		if (mode.back() == Mode::MAP_KEY || mode.back() == Mode::MAP_VALUE) current = parentOrSelf(current);
+		mode.pop_back(); // TODO: assert map/array
 	}
 
 public:
@@ -82,9 +151,7 @@
 
 		callbacks.array_start = [](void* context, size_t size) {
 			CBOR_CALLBACK_START
-			// TODO: implement
-			xmlpp::Element* element = instance->current->add_child("array-start");
-			element->set_attribute("size", std::to_string(size));
+			instance->arrayStart(size);
 			CBOR_CALLBACK_END
 		};
 
@@ -105,6 +172,8 @@
 
 		callbacks.byte_string_start = [](void* context) {
 			CBOR_CALLBACK_START
+			instance->mode.push_back(Mode::BYTE_STRING);
+			instance->remainingItems.push_back(INDEFINITE);
 			// TODO: implement
 			xmlpp::Element* element = instance->current->add_child("byte-string-start");
 			CBOR_CALLBACK_END
@@ -130,30 +199,25 @@
 
 		callbacks.indef_array_start = [](void* context) {
 			CBOR_CALLBACK_START
-			// TODO: implement
-			xmlpp::Element* element = instance->current->add_child("array-start");
+			instance->arrayStart(INDEFINITE);
 			CBOR_CALLBACK_END
 		};
 
 		callbacks.indef_map_start = [](void* context) {
 			CBOR_CALLBACK_START
-			// TODO: implement
-			xmlpp::Element* element = instance->current->add_child("map-start");
+			instance->mapStart(INDEFINITE);
 			CBOR_CALLBACK_END
 		};
 
 		callbacks.indef_break = [](void* context) {
 			CBOR_CALLBACK_START
-			// TODO: implement
-			xmlpp::Element* element = instance->current->add_child("indef-break");
+			instance->containerEnd();
 			CBOR_CALLBACK_END
 		};
 
 		callbacks.map_start = [](void* context, size_t size) {
 			CBOR_CALLBACK_START
-			// TODO: implement
-			xmlpp::Element* element = instance->current->add_child("map-start");
-			element->set_attribute("size", std::to_string(size));
+			instance->mapStart(size);
 			CBOR_CALLBACK_END
 		};
 
@@ -195,6 +259,8 @@
 
 		callbacks.string_start = [](void* context) {
 			CBOR_CALLBACK_START
+			instance->mode.push_back(Mode::CHAR_STRING);
+			instance->remainingItems.push_back(INDEFINITE);
 			// TODO: implement
 			xmlpp::Element* element = instance->current->add_child("string-start");
 			CBOR_CALLBACK_END
@@ -250,7 +316,9 @@
 
 	void process() {
 		current = parser->get_document()->create_root_node(rootName);
+		mode.push_back(Mode::ROOT);
 
+		// TODO: better streaming/buffering? (however, we still have to hold whole document in memory and „infinite“ stream processing is impossible)
 		std::stringstream bufferStream;
 		for (char ch = input->get(); input->good(); ch = input->get()) bufferStream.put(ch);
 		std::string buffer = bufferStream.str();
@@ -262,6 +330,8 @@
 			if (result.status != cbor_decoder_status::CBOR_DECODER_FINISHED) throw relpipe::writer::RelpipeWriterException(L"CBOR parsing failed: status = " + std::to_wstring(result.status));
 		}
 
+		checkRemainingItems();
+
 		parser->get_document()->get_root_node()->set_attribute("bytes-read", std::to_string(bytesRead));
 	}
 };