1 /**
2 * Inspired by: http://dbus-cplusplus.sourceforge.net/
3 */
4
5 #include "xml.h"
6
7 #include <expat.h>
8
9 using namespace sdbuscpp::xml;
10
operator >>(std::istream & in,Document & doc)11 std::istream &operator >> (std::istream& in, Document& doc)
12 {
13 std::stringbuf xmlbuf;
14 in.get(xmlbuf, '\0');
15 doc.from_xml(xmlbuf.str());
16
17 return in;
18 }
19
operator <<(std::ostream & out,const Document & doc)20 std::ostream &operator << (std::ostream& out, const Document& doc)
21 {
22 return out << doc.to_xml();
23 }
24
25
Error(const char * error,int line,int column)26 Error::Error(const char *error, int line, int column)
27 {
28 std::ostringstream estream;
29
30 estream << "line " << line << ", column " << column << ": " << error;
31
32 m_error = estream.str();
33 }
34
Node(const char * n,const char ** a)35 Node::Node(const char *n, const char **a)
36 : name(n)
37 {
38 if (a)
39 {
40 for (int i = 0; a[i]; i += 2)
41 {
42 m_attrs[a[i]] = a[i + 1];
43 }
44 }
45 }
46
operator [](const std::string & key) const47 Nodes Nodes::operator[](const std::string& key) const
48 {
49 Nodes result;
50
51 for (auto it = begin(), endIt = end(); it != endIt; ++it)
52 {
53 Nodes part = (**it)[key];
54
55 result.insert(result.end(), part.begin(), part.end());
56 }
57 return result;
58 }
59
select(const std::string & attr,const std::string & value) const60 Nodes Nodes::select(const std::string& attr, const std::string& value) const
61 {
62 Nodes result;
63
64 for (auto it = begin(), itEnd = end(); it != itEnd; ++it)
65 {
66 if ((*it)->get(attr) == value)
67 {
68 result.insert(result.end(), *it);
69 }
70 }
71 return result;
72 }
73
operator [](const std::string & key)74 Nodes Node::operator[](const std::string& key)
75 {
76 Nodes result;
77
78 if (key.length() == 0)
79 {
80 return result;
81 }
82
83 for (auto it = children.begin(), itEnd = children.end(); it != itEnd; ++it)
84 {
85 if (it->name == key)
86 {
87 result.push_back(&(*it));
88 }
89 }
90 return result;
91 }
92
get(const std::string & attribute) const93 std::string Node::get(const std::string& attribute) const
94 {
95 const auto it = m_attrs.find(attribute);
96 if (it != m_attrs.end())
97 {
98 return it->second;
99 }
100
101 return "";
102 }
103
set(const std::string & attribute,std::string value)104 void Node::set(const std::string& attribute, std::string value)
105 {
106 if (value.length())
107 {
108 m_attrs[attribute] = value;
109 }
110 else
111 {
112 m_attrs.erase(value);
113 }
114 }
115
to_xml() const116 std::string Node::to_xml() const
117 {
118 std::string xml;
119 int depth = 0;
120
121 _raw_xml(xml, depth);
122
123 return xml;
124 }
125
_raw_xml(std::string & xml,int & depth) const126 void Node::_raw_xml(std::string& xml, int& depth) const
127 {
128 xml.append(depth * 2, ' ');
129 xml.append("<" + name);
130
131 for (auto it = m_attrs.begin(), itEnd = m_attrs.end(); it != itEnd; ++it)
132 {
133 xml.append(" " + it->first + "=\"" + it->second + "\"");
134 }
135
136 if (cdata.length() == 0 && children.size() == 0)
137 {
138 xml.append("/>\n");
139 }
140 else
141 {
142 xml.append(">");
143
144 if (cdata.length())
145 {
146 xml.append(cdata);
147 }
148
149 if (children.size())
150 {
151 xml.append("\n");
152 ++depth;
153
154 for (auto it = children.begin(), itEnd = children.end(); it != itEnd; ++it)
155 {
156 it->_raw_xml(xml, depth);
157 }
158
159 --depth;
160 xml.append(depth * 2, ' ');
161 }
162
163 xml.append("</" + name + ">\n");
164 }
165 }
166
Document()167 Document::Document() :
168 root(nullptr),
169 m_depth(0)
170 {
171 }
172
Document(const std::string & xml)173 Document::Document(const std::string &xml) :
174 root(nullptr),
175 m_depth(0)
176 {
177 from_xml(xml);
178 }
179
~Document()180 Document::~Document()
181 {
182 delete root;
183 }
184
185 struct Document::Expat
186 {
187 static void start_doctype_decl_handler(
188 void *data, const XML_Char *name, const XML_Char *sysid, const XML_Char *pubid, int has_internal_subset
189 );
190 static void end_doctype_decl_handler(void *data);
191 static void start_element_handler(void *data, const XML_Char *name, const XML_Char **atts);
192 static void character_data_handler(void *data, const XML_Char *chars, int len);
193 static void end_element_handler(void *data, const XML_Char *name);
194 };
195
from_xml(const std::string & xml)196 void Document::from_xml(const std::string& xml)
197 {
198 m_depth = 0;
199 if (root)
200 {
201 delete root;
202 }
203 root = nullptr;
204
205 XML_Parser parser = XML_ParserCreate("UTF-8");
206
207 XML_SetUserData(parser, this);
208
209 XML_SetDoctypeDeclHandler(
210 parser,
211 Document::Expat::start_doctype_decl_handler,
212 Document::Expat::end_doctype_decl_handler
213 );
214
215 XML_SetElementHandler(
216 parser,
217 Document::Expat::start_element_handler,
218 Document::Expat::end_element_handler
219 );
220
221 XML_SetCharacterDataHandler(
222 parser,
223 Document::Expat::character_data_handler
224 );
225
226 XML_Status status = XML_Parse(parser, xml.c_str(), xml.length(), true);
227
228 if (status == XML_STATUS_ERROR)
229 {
230 const char* error = XML_ErrorString(XML_GetErrorCode(parser));
231 int line = XML_GetCurrentLineNumber(parser);
232 int column = XML_GetCurrentColumnNumber(parser);
233
234 XML_ParserFree(parser);
235
236 throw Error(error, line, column);
237 }
238 else
239 {
240 XML_ParserFree(parser);
241 }
242 }
243
to_xml() const244 std::string Document::to_xml() const
245 {
246 return root->to_xml();
247 }
248
start_doctype_decl_handler(void *,const XML_Char *,const XML_Char *,const XML_Char *,int)249 void Document::Expat::start_doctype_decl_handler(
250 void* /*data*/,
251 const XML_Char* /*name*/,
252 const XML_Char* /*sysid*/,
253 const XML_Char */*pubid*/,
254 int /*has_internal_subset*/)
255 {
256 }
257
end_doctype_decl_handler(void *)258 void Document::Expat::end_doctype_decl_handler(void* /*data*/)
259 {
260 }
261
start_element_handler(void * data,const XML_Char * name,const XML_Char ** atts)262 void Document::Expat::start_element_handler(void* data, const XML_Char* name, const XML_Char** atts)
263 {
264 Document* doc = static_cast<Document*>(data);
265
266 if (!doc->root)
267 {
268 doc->root = new Node(name, atts);
269 }
270 else
271 {
272 Node::Children* cld = &(doc->root->children);
273
274 for (int i = 1; i < doc->m_depth; ++i)
275 {
276 cld = &(cld->back().children);
277 }
278 cld->push_back(Node(name, atts));
279
280 }
281 doc->m_depth++;
282 }
283
character_data_handler(void * data,const XML_Char * chars,int len)284 void Document::Expat::character_data_handler(void* data, const XML_Char* chars, int len)
285 {
286 Document* doc = static_cast<Document*>(data);
287
288 Node* nod = doc->root;
289
290 for (int i = 1; i < doc->m_depth; ++i)
291 {
292 nod = &(nod->children.back());
293 }
294
295 int x = 0, y = len - 1;
296
297 while (isspace(chars[y]) && y > 0) --y;
298 while (isspace(chars[x]) && x < y) ++x;
299
300 nod->cdata = std::string(chars, x, y + 1);
301 }
302
end_element_handler(void * data,const XML_Char *)303 void Document::Expat::end_element_handler(void* data, const XML_Char* /*name*/)
304 {
305 Document* doc = static_cast<Document*>(data);
306
307 doc->m_depth--;
308 }
309
310