32 #include <relpipe/writer/Factory.h> |
32 #include <relpipe/writer/Factory.h> |
33 |
33 |
34 #include <relpipe/cli/RelpipeCLIException.h> |
34 #include <relpipe/cli/RelpipeCLIException.h> |
35 |
35 |
36 #include "Configuration.h" |
36 #include "Configuration.h" |
|
37 #include "XMLNameCodec.h" |
37 |
38 |
38 namespace relpipe { |
39 namespace relpipe { |
39 namespace tr { |
40 namespace tr { |
40 namespace xpath { |
41 namespace xpath { |
41 |
42 |
42 class XPathHandler : public relpipe::reader::handlers::RelationalReaderStringHandler { |
43 class XPathHandler : public relpipe::reader::handlers::RelationalReaderStringHandler { |
43 private: |
44 private: |
|
45 std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // XML is in UTF-8 |
44 shared_ptr<relpipe::writer::RelationalWriter> relationalWriter; |
46 shared_ptr<relpipe::writer::RelationalWriter> relationalWriter; |
45 Configuration configuration; |
47 Configuration configuration; |
46 RelationConfiguration* currentRelationConfiguration; |
48 RelationConfiguration* currentRelationConfiguration = nullptr; |
47 std::vector<relpipe::reader::handlers::AttributeMetadata> currentReaderMetadata; |
49 std::vector<relpipe::reader::handlers::AttributeMetadata> currentReaderMetadata; |
48 std::vector<relpipe::writer::AttributeMetadata> currentWriterMetadata; |
50 std::vector<relpipe::writer::AttributeMetadata> currentWriterMetadata; |
49 size_t currentAttributeIndex = 0; |
51 size_t currentAttributeIndex = 0; |
50 |
52 |
|
53 |
51 xmlpp::DomParser dom; |
54 xmlpp::DomParser dom; |
|
55 xmlpp::Element* recordElement = nullptr; |
|
56 relpipe::in::xmltable::XMLNameCodec xmlNameCodec; // TODO: move to a common library |
52 |
57 |
53 void copyInputAttributesToOutput() { |
58 void copyInputAttributesToOutput() { |
54 for (auto rm : currentReaderMetadata) currentWriterMetadata.push_back({rm.getAttributeName(), relationalWriter->toTypeId(rm.getTypeName())}); |
59 for (auto rm : currentReaderMetadata) currentWriterMetadata.push_back({rm.getAttributeName(), relationalWriter->toTypeId(rm.getTypeName())}); |
55 } |
60 } |
56 |
61 |
59 || (currentRelationConfiguration->inputAttributePolicy == InputAttributePolicy::Auto && currentRelationConfiguration->outputAttributes.size() == 0); |
64 || (currentRelationConfiguration->inputAttributePolicy == InputAttributePolicy::Auto && currentRelationConfiguration->outputAttributes.size() == 0); |
60 } |
65 } |
61 |
66 |
62 bool isAppendingInputAttributes() { |
67 bool isAppendingInputAttributes() { |
63 return currentRelationConfiguration->inputAttributePolicy == InputAttributePolicy::Append; |
68 return currentRelationConfiguration->inputAttributePolicy == InputAttributePolicy::Append; |
|
69 } |
|
70 |
|
71 void resetRecordElement() { |
|
72 if (recordElement) dom.get_document()->get_root_node()->remove_child(recordElement); |
|
73 recordElement = dom.get_document()->get_root_node()->add_child("record"); |
|
74 } |
|
75 |
|
76 const Glib::ustring s2x(relpipe::common::type::StringX value) { |
|
77 return Glib::ustring(convertor.to_bytes(value)); |
|
78 } |
|
79 |
|
80 const relpipe::common::type::StringX x2s(const Glib::ustring& value) { |
|
81 return convertor.from_bytes(value); |
|
82 } |
|
83 |
|
84 void writeInputAttributes() { |
|
85 for (xmlpp::Node* attributeNode : recordElement->get_children()) { |
|
86 if (xmlpp::Element * attributeElement = dynamic_cast<xmlpp::Element*> (attributeNode)) { |
|
87 auto value = attributeElement->get_child_text()->get_content(); |
|
88 relationalWriter->writeAttribute(x2s(value)); |
|
89 } |
|
90 } |
|
91 } |
|
92 |
|
93 void writeOutputAttributes() { |
|
94 for (auto oa : currentRelationConfiguration->outputAttributes) { |
|
95 auto value = recordElement->eval_to_string(s2x(oa.xpath)); |
|
96 relationalWriter->writeAttribute(x2s(value)); |
|
97 } |
64 } |
98 } |
65 |
99 |
66 public: |
100 public: |
67 |
101 |
68 XPathHandler(shared_ptr<relpipe::writer::RelationalWriter> relationalWriter, Configuration configuration) : relationalWriter(relationalWriter), configuration(configuration) { |
102 XPathHandler(shared_ptr<relpipe::writer::RelationalWriter> relationalWriter, Configuration configuration) : relationalWriter(relationalWriter), configuration(configuration) { |
90 if (isPrependingInputAttributes()) copyInputAttributesToOutput(); |
124 if (isPrependingInputAttributes()) copyInputAttributesToOutput(); |
91 for (auto oa : currentRelationConfiguration->outputAttributes) currentWriterMetadata.push_back({oa.name, oa.type}); |
125 for (auto oa : currentRelationConfiguration->outputAttributes) currentWriterMetadata.push_back({oa.name, oa.type}); |
92 if (isAppendingInputAttributes()) copyInputAttributesToOutput(); |
126 if (isAppendingInputAttributes()) copyInputAttributesToOutput(); |
93 |
127 |
94 // TODO: prepare DOM |
128 // TODO: prepare DOM |
|
129 dom.get_document()->create_root_node("relpipe-tr-xpath"); |
|
130 resetRecordElement(); |
95 } |
131 } |
96 |
132 |
97 relationalWriter->startRelation(name, currentWriterMetadata, true); |
133 relationalWriter->startRelation(name, currentWriterMetadata, true); |
98 } |
134 } |
99 |
135 |
100 void attribute(const relpipe::common::type::StringX& value) override { |
136 void attribute(const relpipe::common::type::StringX& value) override { |
101 if (currentRelationConfiguration) { |
137 if (currentRelationConfiguration) { |
102 relpipe::reader::handlers::AttributeMetadata attributeMetadata = currentReaderMetadata[currentAttributeIndex]; |
138 relpipe::reader::handlers::AttributeMetadata attributeMetadata = currentReaderMetadata[currentAttributeIndex]; |
103 |
139 |
104 // TODO: add attribute to DOM |
140 xmlpp::Element* attributeElement = recordElement->add_child(xmlNameCodec.encode(s2x(attributeMetadata.getAttributeName()))); |
|
141 attributeElement->set_attribute("name", s2x(attributeMetadata.getAttributeName())); |
|
142 attributeElement->set_attribute("type", s2x(attributeMetadata.getTypeName())); |
|
143 attributeElement->add_child_text(s2x(value)); |
105 |
144 |
106 currentAttributeIndex++; |
145 currentAttributeIndex++; |
107 |
146 |
108 if (currentAttributeIndex == currentReaderMetadata.size()) { |
147 if (currentAttributeIndex == currentReaderMetadata.size()) { |
|
148 if (currentRelationConfiguration->where.empty() || recordElement->eval_to_boolean(s2x(currentRelationConfiguration->where))) { |
|
149 if (isPrependingInputAttributes()) writeInputAttributes(); |
|
150 writeOutputAttributes(); |
|
151 if (isAppendingInputAttributes()) writeInputAttributes(); |
|
152 } |
109 |
153 |
110 // TODO: evaluate XPath expression |
154 resetRecordElement(); |
111 // TODO: write record to output, if the XPath condition was met |
|
112 // TODO: clean record node in DOM |
|
113 currentAttributeIndex = 0; |
155 currentAttributeIndex = 0; |
114 } |
156 } |
115 } else { |
157 } else { |
116 relationalWriter->writeAttribute(value); |
158 relationalWriter->writeAttribute(value); |
117 } |
159 } |