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