36 class HierarchicalINIContentHandler : public INIContentHandler { |
36 class HierarchicalINIContentHandler : public INIContentHandler { |
37 private: |
37 private: |
38 xmlpp::DomParser* domParser; |
38 xmlpp::DomParser* domParser; |
39 XMLNameCodec nameCodec; |
39 XMLNameCodec nameCodec; |
40 xmlpp::Element* currentSection = nullptr; |
40 xmlpp::Element* currentSection = nullptr; |
|
41 |
|
42 enum class TreeStyle { |
|
43 Standard, |
|
44 Literal, |
|
45 }; |
|
46 |
|
47 TreeStyle parseTreeStyle(const std::string& name) { |
|
48 if (name == "standard") return TreeStyle::Standard; |
|
49 else if (name == "literal") return TreeStyle::Literal; |
|
50 else throw std::invalid_argument(std::string("Unknown tree style: ") + name); |
|
51 } |
|
52 |
|
53 TreeStyle treeStyle = TreeStyle::Literal; |
|
54 |
|
55 /** |
|
56 * TODO: use a common method |
|
57 */ |
|
58 bool parseBoolean(const std::string& value) { |
|
59 if (value == "true") return true; |
|
60 else if (value == "false") return false; |
|
61 else throw std::invalid_argument(std::string("Unable to parse boolean value: ") + value + " (expecting true or false)"); |
|
62 } |
|
63 |
|
64 bool treeWithNamespaces = false; |
|
65 |
41 public: |
66 public: |
42 |
67 |
43 HierarchicalINIContentHandler(xmlpp::DomParser* domParser) : domParser(domParser) { |
68 HierarchicalINIContentHandler(xmlpp::DomParser* domParser) : domParser(domParser) { |
44 } |
69 } |
45 |
70 |
46 virtual ~HierarchicalINIContentHandler() { |
71 virtual ~HierarchicalINIContentHandler() { |
47 } |
72 } |
48 |
73 |
49 void startDocument() override { |
74 void startDocument() override { |
50 if (currentSection) throw std::out_of_range("Lunatic INI parser send us multiple documents."); |
75 if (currentSection) throw std::out_of_range("Lunatic INI parser send us multiple documents."); |
51 currentSection = domParser->get_document()->create_root_node("ini"); |
76 currentSection = domParser->get_document()->create_root_node("ini", treeWithNamespaces ? xml::XMLNS : ""); |
52 }; |
77 }; |
53 |
78 |
54 void endDocument() override { |
79 void endDocument() override { |
55 }; |
80 }; |
56 |
81 |
57 void startSection(const SectionStartEvent& event) override { |
82 void startSection(const SectionStartEvent& event) override { |
58 currentSection = currentSection->add_child(nameCodec.encode(event.name)); |
83 currentSection = currentSection->add_child(treeStyle == TreeStyle::Literal ? nameCodec.encode(event.name) : "section"); |
59 currentSection->set_attribute("type", "section"); |
84 if (treeStyle == TreeStyle::Literal) currentSection->set_attribute("type", "section"); |
60 currentSection->set_attribute("name", event.name); |
85 currentSection->set_attribute("name", event.name); |
61 if (event.comment.size()) currentSection->set_attribute("comment", event.comment); |
86 if (event.comment.size()) currentSection->set_attribute("comment", event.comment); |
62 if (event.lineNumber >= 0) currentSection->set_attribute("line-number", std::to_string(event.lineNumber)); |
87 if (event.lineNumber >= 0) currentSection->set_attribute("line-number", std::to_string(event.lineNumber)); |
63 if (event.eventNumber >= 0) currentSection->set_attribute("event-number", std::to_string(event.eventNumber)); |
88 if (event.eventNumber >= 0) currentSection->set_attribute("event-number", std::to_string(event.eventNumber)); |
64 }; |
89 }; |
67 currentSection = currentSection->get_parent(); |
92 currentSection = currentSection->get_parent(); |
68 if (currentSection == nullptr) throw std::out_of_range("Lunatic INI parser tried to end a section without starting it before."); |
93 if (currentSection == nullptr) throw std::out_of_range("Lunatic INI parser tried to end a section without starting it before."); |
69 }; |
94 }; |
70 |
95 |
71 void entry(const EntryEvent& event) override { |
96 void entry(const EntryEvent& event) override { |
72 xmlpp::Element* entry = currentSection->add_child(nameCodec.encode(event.fullKey)); |
97 xmlpp::Element* entry = currentSection->add_child(treeStyle == TreeStyle::Literal ? nameCodec.encode(event.fullKey) : "entry"); |
73 entry->set_attribute("type", "entry"); |
98 if (treeStyle == TreeStyle::Literal) entry->set_attribute("type", "entry"); |
74 entry->set_attribute("key", event.key); |
99 entry->set_attribute("key", event.key); |
75 entry->set_attribute("full-key", event.fullKey); |
100 entry->set_attribute("full-key", event.fullKey); |
76 if (event.subKey.size()) entry->set_attribute("sub-key", event.subKey); |
101 if (event.subKey.size()) entry->set_attribute("sub-key", event.subKey); |
77 if (event.comment.size()) entry->set_attribute("comment", event.comment); |
102 if (event.comment.size()) entry->set_attribute("comment", event.comment); |
78 if (event.lineNumber >= 0) entry->set_attribute("line-number", std::to_string(event.lineNumber)); |
103 if (event.lineNumber >= 0) entry->set_attribute("line-number", std::to_string(event.lineNumber)); |
80 entry->add_child_text(event.value); |
105 entry->add_child_text(event.value); |
81 }; |
106 }; |
82 |
107 |
83 void comment(const CommentEvent& event) override { |
108 void comment(const CommentEvent& event) override { |
84 xmlpp::Element* comment = currentSection->add_child("comment"); |
109 xmlpp::Element* comment = currentSection->add_child("comment"); |
85 comment->set_attribute("type", "comment"); |
110 if (treeStyle == TreeStyle::Literal) comment->set_attribute("type", "comment"); |
86 if (event.lineNumber >= 0) comment->set_attribute("line-number", std::to_string(event.lineNumber)); |
111 if (event.lineNumber >= 0) comment->set_attribute("line-number", std::to_string(event.lineNumber)); |
87 if (event.eventNumber >= 0) comment->set_attribute("event-number", std::to_string(event.eventNumber)); |
112 if (event.eventNumber >= 0) comment->set_attribute("event-number", std::to_string(event.eventNumber)); |
88 comment->add_child_text(event.comment); |
113 comment->add_child_text(event.comment); |
89 } |
114 } |
90 |
115 |
91 void whitespace(const WhitespaceEvent& event) override { |
116 void whitespace(const WhitespaceEvent& event) override { |
92 xmlpp::Element* comment = currentSection->add_child("whitespace"); |
117 xmlpp::Element* whitespace = currentSection->add_child("whitespace"); |
93 comment->set_attribute("type", "whitespace"); |
118 if (treeStyle == TreeStyle::Literal) whitespace->set_attribute("type", "whitespace"); |
94 if (event.lineNumber >= 0) comment->set_attribute("line-number", std::to_string(event.lineNumber)); |
119 if (event.lineNumber >= 0) whitespace->set_attribute("line-number", std::to_string(event.lineNumber)); |
95 if (event.eventNumber >= 0) comment->set_attribute("event-number", std::to_string(event.eventNumber)); |
120 if (event.eventNumber >= 0) whitespace->set_attribute("event-number", std::to_string(event.eventNumber)); |
96 comment->add_child_text(event.whitespace); |
121 whitespace->add_child_text(event.whitespace); |
|
122 } |
|
123 |
|
124 bool setOption(const std::string& uri, const std::string& value) { |
|
125 if (uri == xml::TreeWithNamespaces) treeWithNamespaces = parseBoolean(value); |
|
126 else if (uri == xml::TreeStyle) treeStyle = parseTreeStyle(value); |
|
127 else return false; |
|
128 return true; |
97 } |
129 } |
98 |
130 |
99 }; |
131 }; |
100 |
132 |
101 // TODO: support also other styles/mappings e.g. <section/> and <entry/> with INI names only in the XML attributes (and thus without @type="section|entry") |
133 // TODO: support also other styles/mappings e.g. <section/> and <entry/> with INI names only in the XML attributes (and thus without @type="section|entry") |
105 class XMLDocumentConstructor { |
137 class XMLDocumentConstructor { |
106 private: |
138 private: |
107 std::istream* input = nullptr; |
139 std::istream* input = nullptr; |
108 xmlpp::DomParser* parser = nullptr; |
140 xmlpp::DomParser* parser = nullptr; |
109 std::shared_ptr<INIReader> reader; |
141 std::shared_ptr<INIReader> reader; |
|
142 std::shared_ptr<HierarchicalINIContentHandler> handler; |
110 public: |
143 public: |
111 |
144 |
112 XMLDocumentConstructor(std::istream* input, xmlpp::DomParser* parser) : input(input), parser(parser) { |
145 XMLDocumentConstructor(std::istream* input, xmlpp::DomParser* parser) : input(input), parser(parser) { |
113 reader.reset(INIReader::create(*input)); |
146 reader.reset(INIReader::create(*input)); |
114 reader->addUnescapingProcessor(std::make_shared<BasicUnescapingProcessor>(), unescaping::Basic, true); |
147 reader->addUnescapingProcessor(std::make_shared<BasicUnescapingProcessor>(), unescaping::Basic, true); |
115 reader->addUnescapingProcessor(std::make_shared<JavaPropertiesUnescapingProcessor>(), unescaping::JavaProperties, false); |
148 reader->addUnescapingProcessor(std::make_shared<JavaPropertiesUnescapingProcessor>(), unescaping::JavaProperties, false); |
116 reader->addUnescapingProcessor(std::make_shared<BackspaceUnescapingProcessor>(), unescaping::Backspace, true); |
149 reader->addUnescapingProcessor(std::make_shared<BackspaceUnescapingProcessor>(), unescaping::Backspace, true); |
117 reader->addDialect(std::make_shared<JavaPropertiesDialect>(), dialect::JavaProperties, false); |
150 reader->addDialect(std::make_shared<JavaPropertiesDialect>(), dialect::JavaProperties, false); |
|
151 handler = std::make_shared<HierarchicalINIContentHandler>(parser); |
118 } |
152 } |
119 |
153 |
120 void setOption(const std::string& uri, const std::string& value) { |
154 void setOption(const std::string& uri, const std::string& value) { |
|
155 if (handler->setOption(uri, value)) return; |
121 reader->setOption(uri, value); |
156 reader->setOption(uri, value); |
122 } |
157 } |
123 |
158 |
124 void process() { |
159 void process() { |
125 HierarchicalINIContentHandler handler(parser); |
160 reader->addHandler(handler.get()); |
126 reader->addHandler(&handler); |
|
127 reader->process(); |
161 reader->process(); |
128 } |
162 } |
129 }; |
163 }; |
130 |
164 |
131 } |
165 } |