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 ods { |
36 namespace ods { |
36 |
37 |
37 using namespace relpipe::reader; |
38 using namespace relpipe::reader; |
|
39 using namespace relpipe::xmlwriter; |
38 |
40 |
39 class OdsHandler : public handlers::RelationalReaderStringHadler { |
41 class OdsHandler : 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 |
|
43 // TODO: refactor and move common XML functions to relpipe-lib-xml |
|
44 const char* INDENT_1 = "\t"; |
|
45 const char* INDENT_2 = "\t\t"; |
|
46 const char* INDENT_3 = "\t\t\t"; |
|
47 const char* INDENT_4 = "\t\t\t\t"; |
|
48 const char* INDENT_5 = "\t\t\t\t\t"; |
|
49 const char* INDENT_6 = "\t\t\t\t\t\t"; |
|
50 |
|
51 std::ostream &output; |
|
52 |
|
53 std::vector<TypeId> columnTypes; |
44 std::vector<TypeId> columnTypes; |
54 std::vector<string_t> columnTypeCodes; |
45 std::vector<string_t> columnTypeCodes; |
55 std::vector<string_t> columnNames; |
46 std::vector<string_t> columnNames; |
56 integer_t valueCount = 0; |
47 integer_t valueCount = 0; |
57 integer_t columnCount = 0; |
48 integer_t columnCount = 0; |
58 integer_t relationCount = 0; |
49 integer_t relationCount = 0; |
59 |
50 |
60 // TODO: refactor and move common XML functions to relpipe-lib-xml |
|
61 |
|
62 const std::string escapeXmlText(const string_t &value) { |
|
63 std::wstringstream result; |
|
64 |
|
65 for (auto & ch : value) { |
|
66 switch (ch) { |
|
67 case L'&': result << L"&"; |
|
68 break; |
|
69 case L'<': result << L"<"; |
|
70 break; |
|
71 case L'>': result << L">"; |
|
72 break; |
|
73 case L'\'': result << L"'"; // TODO: escape ' and " only in attributes |
|
74 break; |
|
75 case L'"': result << L"""; // TODO: escape ' and " only in attributes |
|
76 break; |
|
77 default: result << ch; |
|
78 } |
|
79 } |
|
80 |
|
81 return convertor.to_bytes(result.str()); |
|
82 } |
|
83 |
|
84 public: |
51 public: |
85 |
52 |
86 OdsHandler(std::ostream& output) : output(output) { |
53 OdsHandler(std::ostream& output) : xmlWriter(new XMLWriter(output)) { |
87 } |
54 } |
88 |
55 |
89 void startRelation(string_t name, std::vector<handlers::AttributeMetadata> attributes) override { |
56 void startRelation(string_t name, std::vector<handlers::AttributeMetadata> attributes) override { |
90 // TODO: refactor and move common XML functions to relpipe-lib-xml |
|
91 |
|
92 valueCount = 0; |
57 valueCount = 0; |
93 columnCount = 0; |
58 columnCount = 0; |
94 |
59 |
95 if (relationCount == 0) { |
60 if (relationCount == 0) { |
96 output << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl; |
61 xmlWriter->writeStartElement(L"office:document",{ |
97 output << "<office:document" << std::endl; |
62 L"xmlns:office", L"urn:oasis:names:tc:opendocument:xmlns:office:1.0", |
98 output << INDENT_1 << "xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"" << std::endl; |
63 L"xmlns:table", L"urn:oasis:names:tc:opendocument:xmlns:table:1.0", |
99 output << INDENT_1 << "xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"" << std::endl; |
64 L"xmlns:text", L"urn:oasis:names:tc:opendocument:xmlns:text:1.0", |
100 output << INDENT_1 << "xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"" << std::endl; |
65 L"office:mimetype", L"application/vnd.oasis.opendocument.spreadsheet", |
101 output << INDENT_1 << "office:mimetype=\"application/vnd.oasis.opendocument.spreadsheet\"" << std::endl; |
66 L"office:version", L"1.2" |
102 output << INDENT_1 << "office:version=\"1.2\">" << std::endl; |
67 }); |
103 |
68 |
104 output << INDENT_1 << "<office:body>" << std::endl; |
69 xmlWriter->writeStartElement(L"office:body"); |
105 output << INDENT_2 << "<office:spreadsheet>" << std::endl; |
70 xmlWriter->writeStartElement(L"office:spreadsheet"); |
106 |
|
107 } else { |
71 } else { |
108 output << INDENT_4 << "</table:table-row>" << std::endl; |
72 xmlWriter->writeEndElement(); |
109 output << INDENT_3 << "</table:table>" << std::endl; |
73 xmlWriter->writeEndElement(); |
110 } |
74 } |
111 relationCount++; |
75 relationCount++; |
112 // TODO: rename relations with same names |
76 // TODO: rename relations with same names |
113 output << INDENT_3 << "<table:table table:name=\"" << escapeXmlText(name) << "\">" << std::endl; |
77 xmlWriter->writeStartElement(L"table:table",{L"table:name", name}); |
114 |
78 |
115 |
79 |
116 columnCount = attributes.size(); |
80 columnCount = attributes.size(); |
117 columnTypes.resize(columnCount); |
81 columnTypes.resize(columnCount); |
118 columnTypeCodes.resize(columnCount); |
82 columnTypeCodes.resize(columnCount); |
119 columnNames.resize(columnCount); |
83 columnNames.resize(columnCount); |
120 |
84 |
121 output << INDENT_4 << "<table:table-row>" << std::endl; |
85 xmlWriter->writeStartElement(L"table:table-row"); |
122 for (int i = 0; i < attributes.size(); i++) { |
86 for (int i = 0; i < attributes.size(); i++) { |
123 columnNames[i] = attributes[i].getAttributeName(); |
87 columnNames[i] = attributes[i].getAttributeName(); |
124 columnTypes[i] = attributes[i].getTypeId(); |
88 columnTypes[i] = attributes[i].getTypeId(); |
125 columnTypeCodes[i] = attributes[i].getTypeName(); |
89 columnTypeCodes[i] = attributes[i].getTypeName(); |
126 |
90 |
127 output << INDENT_5 << "<table:table-cell>" << std::endl; |
91 xmlWriter->writeStartElement(L"table:table-cell"); |
128 output << INDENT_6 << "<text:p>"; |
92 xmlWriter->writeTextElement(L"text:p",{}, columnNames[i]); |
129 output << escapeXmlText(columnNames[i]); |
93 xmlWriter->writeEndElement(); |
130 output << "</text:p>" << std::endl; |
|
131 output << INDENT_5 << "</table:table-cell>" << std::endl; |
|
132 } |
94 } |
133 output << INDENT_4 << "</table:table-row>" << std::endl; |
95 xmlWriter->writeEndElement(); |
134 |
96 |
135 } |
97 } |
136 |
98 |
137 void attribute(const string_t& value) override { |
99 void attribute(const string_t& value) override { |
138 integer_t i = valueCount % columnCount; |
100 integer_t i = valueCount % columnCount; |
139 |
101 |
140 if (i == 0 && valueCount) output << INDENT_4 << "</table:table-row>" << std::endl; |
102 if (i == 0 && valueCount) xmlWriter->writeEndElement(); |
141 if (i == 0) output << INDENT_4 << "<table:table-row>" << std::endl; |
103 if (i == 0) xmlWriter->writeStartElement(L"table:table-row"); |
142 |
104 |
143 valueCount++; |
105 valueCount++; |
144 |
106 |
145 output << INDENT_5 << "<table:table-cell>" << std::endl; |
107 xmlWriter->writeStartElement(L"table:table-cell"); |
146 output << INDENT_6 << "<text:p>"; |
108 xmlWriter->writeTextElement(L"text:p",{}, value); |
147 output << escapeXmlText(value); |
109 xmlWriter->writeEndElement(); |
148 output << "</text:p>" << std::endl; |
|
149 output << INDENT_5 << "</table:table-cell>" << std::endl; |
|
150 |
|
151 } |
110 } |
152 |
111 |
153 void endOfPipe() { |
112 void endOfPipe() { |
154 if (valueCount) output << INDENT_4 << "</table:table-row>" << std::endl; |
113 if (valueCount) xmlWriter->writeEndElement(); |
155 if (relationCount) output << INDENT_3 << "</table:table>" << std::endl; |
114 if (relationCount) xmlWriter->writeEndElement(); |
156 output << INDENT_2 << "</office:spreadsheet>" << std::endl; |
115 xmlWriter->writeEndElement(); |
157 output << INDENT_1 << "</office:body>" << std::endl; |
116 xmlWriter->writeEndElement(); |
158 output << "</office:document>" << std::endl; |
117 xmlWriter->writeEndElement(); |
159 |
118 |
160 } |
119 } |
161 |
120 |
162 }; |
121 }; |
163 |
122 |