streamlet examples: xpath: support multiple modes (string, boolean, line-number, xpath), TODO: raw-xml v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Tue, 28 Jan 2020 23:18:21 +0100
branchv_0
changeset 68 5d3d57d9c323
parent 67 0766d298eb1c
child 69 52f837fbb216
streamlet examples: xpath: support multiple modes (string, boolean, line-number, xpath), TODO: raw-xml
streamlet-examples/xpath.cpp
--- a/streamlet-examples/xpath.cpp	Tue Jan 28 14:26:39 2020 +0100
+++ b/streamlet-examples/xpath.cpp	Tue Jan 28 23:18:21 2020 +0100
@@ -36,11 +36,45 @@
 		for (Option o : getOptions(std::wregex(L"xmlns"), std::wregex(L"([^:]+):(.*)"))) ns[convertor.to_bytes(o.valueMatch[1])] = convertor.to_bytes(o.valueMatch[2]);
 	}
 
+	// Modes should share the logic of relpipe-in-xmltable
+
+	enum class Mode {
+		STRING,
+		BOOLEAN,
+		// TODO: support also XML number, when we have a rational or decimal numbers in Relational pipes
+		RAW_XML,
+		LINE_NUMBER,
+		XPATH
+	};
+
+	Mode toMode(std::wstring modeName) {
+		if (modeName == L"string") return Mode::STRING;
+		else if (modeName == L"boolean") return Mode::BOOLEAN;
+		else if (modeName == L"raw-xml") return Mode::RAW_XML;
+		else if (modeName == L"line-number") return Mode::LINE_NUMBER;
+		else if (modeName == L"xpath") return Mode::XPATH;
+		else throw std::invalid_argument("Unsupported mode: " + convertor.to_bytes(modeName));
+	}
+
+	std::wstring toType(Mode mode) {
+		if (mode == Mode::BOOLEAN) return BOOLEAN;
+		else if (mode == Mode::LINE_NUMBER) return INTEGER;
+		else return STRING;
+	}
+
+	// TODO: should not be done in particular streamlets but in the worker
+	std::wstring toNullValue(std::wstring type) {
+		if (type == BOOLEAN) return L"false";
+		else if (type == INTEGER) return L"0";
+		else return L"";
+	}
+
 	class XPathAttribute {
 	public:
 
 		std::wstring name;
 		std::wstring xpath;
+		Mode mode = Mode::STRING;
 	};
 
 	std::vector<XPathAttribute> xpathAttributes;
@@ -53,11 +87,13 @@
 
 		std::vector<AttributeMetadata> oam;
 
+		std::vector<Option> modeOptions = getOptions(L"mode");
 		std::vector<Option> attributeOptions = getOptions(L"attribute");
 		for (int i = 0, limit = attributeOptions.size(); i < limit; i++) {
+			Mode mode = i < modeOptions.size() ? toMode(modeOptions[i].value) : Mode::STRING;
 			std::wstring alias = getAlias(i, attributeOptions[i].value);
-			xpathAttributes.push_back({alias, attributeOptions[i].value});
-			oam.push_back({alias, STRING});
+			xpathAttributes.push_back({alias, attributeOptions[i].value, mode});
+			oam.push_back({alias, toType(mode)});
 		}
 
 		return oam;
@@ -72,12 +108,40 @@
 			xmlpp::Element* root = parser.get_document()->get_root_node();
 
 			for (XPathAttribute xpathAttribute : xpathAttributes) {
-				// TODO: support various modes like in XMLTableCommand
-				std::wstring result = convertor.from_bytes(root->eval_to_string(convertor.to_bytes(xpathAttribute.xpath), ns));
+				std::string xpath = convertor.to_bytes(xpathAttribute.xpath);
+				std::wstring result;
+				bool isNull = false;
+
+				if (xpathAttribute.mode == Mode::STRING) {
+					result = convertor.from_bytes(root->eval_to_string(xpath, ns));
+				} else if (xpathAttribute.mode == Mode::BOOLEAN) {
+					result = root->eval_to_boolean(xpath, ns) ? L"true" : L"false";
+				} else if (xpathAttribute.mode == Mode::LINE_NUMBER) {
+					xmlpp::NodeSet attributeNodes = root->find(xpath, ns);
+					if (attributeNodes.size()) {
+						result = std::to_wstring(attributeNodes[0]->get_line());
+					} else {
+						result = L"0";
+						isNull = true;
+					}
+				} else if (xpathAttribute.mode == Mode::XPATH) {
+					xmlpp::NodeSet attributeNodes = root->find(xpath, ns);
+					if (attributeNodes.size()) {
+						result = convertor.from_bytes(attributeNodes[0]->get_path());
+					} else {
+						result = L"";
+						isNull = true;
+					}
+				} else if (xpathAttribute.mode == Mode::RAW_XML) {
+					throw std::logic_error("Raw XML mode is not yet implemented."); // TODO: implement also RAW_XML
+				} else {
+					throw std::logic_error("Unsupported mode."); // should never happer
+				}
+
 				oa.push_back({result, false});
 			}
 		} catch (xmlpp::parse_error& e) {
-			for (XPathAttribute xpathAttribute : xpathAttributes) oa.push_back({L"", true});
+			for (XPathAttribute xpathAttribute : xpathAttributes) oa.push_back({toNullValue(toType(xpathAttribute.mode)), true});
 			// invalid XML → xmlpp::parse_error → just skip this file
 			// invalid XPath → xmlpp::exception → failure
 		}