27 |
27 |
28 #include <relpipe/reader/typedefs.h> |
28 #include <relpipe/reader/typedefs.h> |
29 #include <relpipe/reader/TypeId.h> |
29 #include <relpipe/reader/TypeId.h> |
30 #include <relpipe/reader/handlers/RelationalReaderStringHandler.h> |
30 #include <relpipe/reader/handlers/RelationalReaderStringHandler.h> |
31 #include <relpipe/reader/handlers/AttributeMetadata.h> |
31 #include <relpipe/reader/handlers/AttributeMetadata.h> |
|
32 #include <relpipe/xmlwriter/XMLWriter.h> |
32 |
33 |
33 namespace relpipe { |
34 namespace relpipe { |
34 namespace out { |
35 namespace out { |
35 namespace xml { |
36 namespace xml { |
36 |
37 |
37 using namespace relpipe::reader; |
38 using namespace relpipe::reader; |
|
39 using namespace relpipe::xmlwriter; |
38 |
40 |
39 class XmlHandler : public handlers::RelationalReaderStringHadler { |
41 class XmlHandler : public handlers::RelationalReaderStringHadler { |
40 private: |
42 private: |
41 std::wstring_convert<std::codecvt_utf8<wchar_t>> convertor; // XML output will be always in UTF-8 |
43 shared_ptr<XMLWriter> xmlWriter; |
42 const char* INDENT = "\t"; |
|
43 |
|
44 std::ostream &output; |
|
45 |
|
46 std::vector<TypeId> columnTypes; |
44 std::vector<TypeId> columnTypes; |
47 std::vector<string_t> columnTypeCodes; |
45 std::vector<string_t> columnTypeCodes; |
48 std::vector<string_t> columnNames; |
46 std::vector<string_t> columnNames; |
49 integer_t valueCount = 0; |
47 integer_t valueCount = 0; |
50 integer_t columnCount = 0; |
48 integer_t columnCount = 0; |
51 integer_t relationCount = 0; |
49 integer_t relationCount = 0; |
52 |
50 |
53 const std::string escapeXmlText(const string_t &value) { |
|
54 // TODO: really bad performance → rewrite |
|
55 // 72 % of whole relpipe-out-xml according to valgrind/callgrind |
|
56 std::wstringstream result; |
|
57 |
|
58 for (auto & ch : value) { |
|
59 switch (ch) { |
|
60 case L'&': result << L"&"; |
|
61 break; |
|
62 case L'<': result << L"<"; |
|
63 break; |
|
64 case L'>': result << L">"; |
|
65 break; |
|
66 case L'\'': result << L"'"; // TODO: escape ' and " only in attributes |
|
67 break; |
|
68 case L'"': result << L"""; // TODO: escape ' and " only in attributes |
|
69 break; |
|
70 default: result << ch; |
|
71 } |
|
72 } |
|
73 |
|
74 return convertor.to_bytes(result.str()); |
|
75 } |
|
76 |
|
77 public: |
51 public: |
78 |
52 |
79 XmlHandler(std::ostream& output) : output(output) { |
53 XmlHandler(std::ostream& output) : xmlWriter(new XMLWriter(output)) { |
80 } |
54 } |
81 |
55 |
82 void startRelation(string_t name, std::vector<handlers::AttributeMetadata> attributes) override { |
56 void startRelation(string_t name, std::vector<handlers::AttributeMetadata> attributes) override { |
83 // TODO: refactor and move common XML functions to relpipe-lib-xml |
|
84 |
|
85 valueCount = 0; |
57 valueCount = 0; |
86 columnCount = 0; |
58 columnCount = 0; |
87 |
59 |
88 if (relationCount == 0) { |
60 if (relationCount == 0) { |
89 output << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl; |
61 xmlWriter->writeStartElement(L"pipe",{L"xmlns", L"tag:globalcode.info,2018:relpipe"}); |
90 output << "<pipe xmlns=\"tag:globalcode.info,2018:relpipe\">" << std::endl; |
|
91 // TODO: xmlns |
|
92 } else { |
62 } else { |
93 output << INDENT << INDENT << "</record>" << std::endl; |
63 xmlWriter->writeEndElement(); |
94 output << INDENT << "</relation>" << std::endl; |
64 xmlWriter->writeEndElement(); |
95 } |
65 } |
96 relationCount++; |
66 relationCount++; |
97 output << INDENT << "<relation>" << std::endl; |
67 xmlWriter->writeStartElement(L"relation"); |
98 |
68 |
99 output << INDENT << INDENT << "<name>" << escapeXmlText(name) << "</name>" << std::endl; |
69 xmlWriter->writeTextElement(L"name",{}, name); |
100 |
70 |
101 |
71 |
102 columnCount = attributes.size(); |
72 columnCount = attributes.size(); |
103 columnTypes.resize(columnCount); |
73 columnTypes.resize(columnCount); |
104 columnTypeCodes.resize(columnCount); |
74 columnTypeCodes.resize(columnCount); |
106 for (int i = 0; i < attributes.size(); i++) { |
76 for (int i = 0; i < attributes.size(); i++) { |
107 columnNames[i] = attributes[i].getAttributeName(); |
77 columnNames[i] = attributes[i].getAttributeName(); |
108 columnTypes[i] = attributes[i].getTypeId(); |
78 columnTypes[i] = attributes[i].getTypeId(); |
109 columnTypeCodes[i] = attributes[i].getTypeName(); |
79 columnTypeCodes[i] = attributes[i].getTypeName(); |
110 } |
80 } |
111 |
81 |
112 // TODO: print attribute metadata |
82 // TODO: print attribute metadata |
113 } |
83 } |
114 |
84 |
115 void attribute(const string_t& value) override { |
85 void attribute(const string_t& value) override { |
116 integer_t i = valueCount % columnCount; |
86 integer_t i = valueCount % columnCount; |
117 |
87 |
118 if (i == 0 && valueCount) output << INDENT << INDENT << "</record>" << std::endl; |
88 if (i == 0 && valueCount) xmlWriter->writeEndElement(); |
119 if (i == 0) output << INDENT << INDENT << "<record>" << std::endl; |
89 if (i == 0) xmlWriter->writeStartElement(L"record"); |
120 |
90 |
121 valueCount++; |
91 valueCount++; |
122 |
92 |
123 // TODO: print attribute metadata (optional) |
93 // TODO: print attribute metadata (optional) |
124 output << INDENT << INDENT << INDENT << "<attribute>"; |
94 xmlWriter->writeTextElement(L"attribute",{}, value); |
125 output << escapeXmlText(value); |
|
126 output << "</attribute>" << std::endl; |
|
127 |
95 |
128 } |
96 } |
129 |
97 |
130 void endOfPipe() { |
98 void endOfPipe() { |
131 if (valueCount) output << INDENT << INDENT << "</record>" << std::endl; |
99 if (valueCount) xmlWriter->writeEndElement(); |
132 if (relationCount) output << INDENT << "</relation>" << std::endl; |
100 if (relationCount) xmlWriter->writeEndElement(); |
133 output << "</pipe>" << std::endl; |
101 xmlWriter->writeEndElement(); |
134 |
|
135 } |
102 } |
136 |
103 |
137 }; |
104 }; |
138 |
105 |
139 } |
106 } |