move common XML code to relpipe-lib-xmlwriter (a header-only library) v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Sun, 06 Jan 2019 22:15:38 +0100
branchv_0
changeset 13 816094aa1fff
parent 12 b8274181b061
child 14 5094a7bf343d
move common XML code to relpipe-lib-xmlwriter (a header-only library)
nbproject/configurations.xml
src/CMakeLists.txt
src/XmlHandler.h
src/relpipe-out-xml.cpp
--- a/nbproject/configurations.xml	Sat Jan 05 18:38:14 2019 +0100
+++ b/nbproject/configurations.xml	Sun Jan 06 22:15:38 2019 +0100
@@ -76,6 +76,14 @@
           <buildCommand>${MAKE} -f Makefile</buildCommand>
           <cleanCommand>${MAKE} -f Makefile clean</cleanCommand>
           <executablePath>build/Debug/src/relpipe-out-xml</executablePath>
+          <ccTool>
+            <incDir>
+              <pElem>../relpipe-lib-reader.cpp/include</pElem>
+              <pElem>../relpipe-lib-cli.cpp/include</pElem>
+              <pElem>../relpipe-lib-xmlwriter.cpp/include</pElem>
+              <pElem>build/Debug/src</pElem>
+            </incDir>
+          </ccTool>
         </makeTool>
         <preBuild>
           <preBuildCommandWorkingDir>build/Debug</preBuildCommandWorkingDir>
@@ -85,11 +93,6 @@
       </makefileType>
       <item path="src/relpipe-out-xml.cpp" ex="false" tool="1" flavor2="0">
         <ccTool flags="0">
-          <incDir>
-            <pElem>../relpipe-lib-reader.cpp/include</pElem>
-            <pElem>../relpipe-lib-cli.cpp/include</pElem>
-            <pElem>build/Debug/src</pElem>
-          </incDir>
         </ccTool>
       </item>
     </conf>
--- a/src/CMakeLists.txt	Sat Jan 05 18:38:14 2019 +0100
+++ b/src/CMakeLists.txt	Sun Jan 06 22:15:38 2019 +0100
@@ -18,7 +18,7 @@
 
 # Relpipe libraries:
 INCLUDE(FindPkgConfig)
-pkg_check_modules (RELPIPE_LIBS relpipe-lib-reader.cpp relpipe-lib-cli.cpp)
+pkg_check_modules (RELPIPE_LIBS relpipe-lib-reader.cpp relpipe-lib-cli.cpp relpipe-lib-xmlwriter.cpp)
 include_directories(${RELPIPE_LIBS_INCLUDE_DIRS})
 link_directories(${RELPIPE_LIBS_LIBRARY_DIRS})
 
--- a/src/XmlHandler.h	Sat Jan 05 18:38:14 2019 +0100
+++ b/src/XmlHandler.h	Sun Jan 06 22:15:38 2019 +0100
@@ -29,20 +29,18 @@
 #include <relpipe/reader/TypeId.h>
 #include <relpipe/reader/handlers/RelationalReaderStringHandler.h>
 #include <relpipe/reader/handlers/AttributeMetadata.h>
+#include <relpipe/xmlwriter/XMLWriter.h>
 
 namespace relpipe {
 namespace out {
 namespace xml {
 
 using namespace relpipe::reader;
+using namespace relpipe::xmlwriter;
 
 class XmlHandler : public handlers::RelationalReaderStringHadler {
 private:
-	std::wstring_convert<std::codecvt_utf8<wchar_t>> convertor; // XML output will be always in UTF-8
-	const char* INDENT = "\t";
-
-	std::ostream &output;
-
+	shared_ptr<XMLWriter> xmlWriter;
 	std::vector<TypeId> columnTypes;
 	std::vector<string_t> columnTypeCodes;
 	std::vector<string_t> columnNames;
@@ -50,53 +48,25 @@
 	integer_t columnCount = 0;
 	integer_t relationCount = 0;
 
-	const std::string escapeXmlText(const string_t &value) {
-		// TODO: really bad performance → rewrite
-		// 72 % of whole relpipe-out-xml according to valgrind/callgrind
-		std::wstringstream result;
-
-		for (auto & ch : value) {
-			switch (ch) {
-				case L'&': result << L"&amp;";
-					break;
-				case L'<': result << L"&lt;";
-					break;
-				case L'>': result << L"&gt;";
-					break;
-				case L'\'': result << L"&apos;"; // TODO: escape ' and " only in attributes
-					break;
-				case L'"': result << L"&quot;"; // TODO: escape ' and " only in attributes
-					break;
-				default: result << ch;
-			}
-		}
-
-		return convertor.to_bytes(result.str());
-	}
-
 public:
 
-	XmlHandler(std::ostream& output) : output(output) {
+	XmlHandler(std::ostream& output) : xmlWriter(new XMLWriter(output)) {
 	}
 
 	void startRelation(string_t name, std::vector<handlers::AttributeMetadata> attributes) override {
-		// TODO: refactor and move common XML functions to relpipe-lib-xml
-
 		valueCount = 0;
 		columnCount = 0;
 
 		if (relationCount == 0) {
-			output << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
-			output << "<pipe xmlns=\"tag:globalcode.info,2018:relpipe\">" << std::endl;
-			// TODO: xmlns
+			xmlWriter->writeStartElement(L"pipe",{L"xmlns", L"tag:globalcode.info,2018:relpipe"});
 		} else {
-			output << INDENT << INDENT << "</record>" << std::endl;
-			output << INDENT << "</relation>" << std::endl;
+			xmlWriter->writeEndElement();
+			xmlWriter->writeEndElement();
 		}
 		relationCount++;
-		output << INDENT << "<relation>" << std::endl;
+		xmlWriter->writeStartElement(L"relation");
 
-		output << INDENT << INDENT << "<name>" << escapeXmlText(name) << "</name>" << std::endl;
+		xmlWriter->writeTextElement(L"name",{}, name);
 
 
 		columnCount = attributes.size();
@@ -108,30 +78,27 @@
 			columnTypes[i] = attributes[i].getTypeId();
 			columnTypeCodes[i] = attributes[i].getTypeName();
 		}
-		
+
 		// TODO: print attribute metadata
 	}
 
 	void attribute(const string_t& value) override {
 		integer_t i = valueCount % columnCount;
 
-		if (i == 0 && valueCount) output << INDENT << INDENT << "</record>" << std::endl;
-		if (i == 0) output << INDENT << INDENT << "<record>" << std::endl;
+		if (i == 0 && valueCount) xmlWriter->writeEndElement();
+		if (i == 0) xmlWriter->writeStartElement(L"record");
 
 		valueCount++;
 
 		// TODO: print attribute metadata (optional)
-		output << INDENT << INDENT << INDENT << "<attribute>";
-		output << escapeXmlText(value);
-		output << "</attribute>" << std::endl;
+		xmlWriter->writeTextElement(L"attribute",{}, value);
 
 	}
 
 	void endOfPipe() {
-		if (valueCount) output << INDENT << INDENT << "</record>" << std::endl;
-		if (relationCount) output << INDENT << "</relation>" << std::endl;
-		output << "</pipe>" << std::endl;
-
+		if (valueCount) xmlWriter->writeEndElement();
+		if (relationCount) xmlWriter->writeEndElement();
+		xmlWriter->writeEndElement();
 	}
 
 };
--- a/src/relpipe-out-xml.cpp	Sat Jan 05 18:38:14 2019 +0100
+++ b/src/relpipe-out-xml.cpp	Sun Jan 06 22:15:38 2019 +0100
@@ -24,12 +24,14 @@
 #include <relpipe/reader/Factory.h>
 #include <relpipe/reader/RelationalReader.h>
 #include <relpipe/reader/RelpipeReaderException.h>
+#include <relpipe/xmlwriter/RelpipeXMLWriterException.h>
 
 #include "XmlHandler.h"
 
 using namespace relpipe::cli;
 using namespace relpipe::reader;
 using namespace relpipe::out::xml;
+using namespace relpipe::xmlwriter;
 
 int main(int argc, char** argv) {
 	setlocale(LC_ALL, "");
@@ -57,6 +59,9 @@
 		fwprintf(stderr, L"Caught Reader exception: %ls\n", e.getMessge().c_str());
 		fwprintf(stderr, L"Debug: Input stream: eof=%ls, lastRead=%d\n", (cin.eof() ? L"true" : L"false"), cin.gcount());
 		resultCode = CLI::EXIT_CODE_DATA_ERROR;
+	} catch (RelpipeXMLWriterException e) {
+		fwprintf(stderr, L"Error while writing XML: %ls\n", e.getMessge().c_str());
+		resultCode = CLI::EXIT_CODE_UNEXPECTED_ERROR;
 	}
 
 	return resultCode;