Use xpath for node_children.
[libcxml.git] / src / cxml.cc
1 #include <sstream>
2 #include <iostream>
3 #include <boost/lexical_cast.hpp>
4 #include <boost/filesystem.hpp>
5 #include <boost/algorithm/string.hpp>
6 #include <libxml++/libxml++.h>
7 #include "cxml.h"
8
9 using namespace std;
10 using namespace boost;
11
12 cxml::Node::Node ()
13         : _node (0)
14 {
15
16 }
17
18 cxml::Node::Node (xmlpp::Node* node)
19         : _node (node)
20 {
21
22 }
23
24 string
25 cxml::Node::name () const
26 {
27         assert (_node);
28         return _node->get_name ();
29 }
30
31 shared_ptr<cxml::Node>
32 cxml::Node::node_child (string name) const
33 {
34         list<shared_ptr<cxml::Node> > n = node_children (name);
35         if (n.size() > 1) {
36                 throw cxml::Error ("duplicate XML tag " + name);
37         } else if (n.empty ()) {
38                 throw cxml::Error ("missing XML tag " + name + " in " + _node->get_name());
39         }
40         
41         return n.front ();
42 }
43
44 shared_ptr<cxml::Node>
45 cxml::Node::optional_node_child (string name) const
46 {
47         list<shared_ptr<cxml::Node> > n = node_children (name);
48         if (n.size() > 1) {
49                 throw cxml::Error ("duplicate XML tag " + name);
50         } else if (n.empty ()) {
51                 return shared_ptr<cxml::Node> ();
52         }
53         
54         return n.front ();
55 }
56
57 list<shared_ptr<cxml::Node> >
58 cxml::Node::node_children (string name) const
59 {
60         xmlpp::NodeSet c = _node->find (name);
61         
62         list<shared_ptr<cxml::Node> > n;
63         for (xmlpp::NodeSet::iterator i = c.begin (); i != c.end(); ++i) {
64                 n.push_back (shared_ptr<Node> (new Node (*i)));
65         }
66         
67         _taken.push_back (name);
68         return n;
69 }
70
71 string
72 cxml::Node::string_child (string c) const
73 {
74         return node_child(c)->content ();
75 }
76
77 optional<string>
78 cxml::Node::optional_string_child (string c) const
79 {
80         list<shared_ptr<Node> > nodes = node_children (c);
81         if (nodes.size() > 1) {
82                 throw cxml::Error ("duplicate XML tag " + c);
83         }
84
85         if (nodes.empty ()) {
86                 return optional<string> ();
87         }
88
89         return nodes.front()->content();
90 }
91
92 bool
93 cxml::Node::bool_child (string c) const
94 {
95         string const s = string_child (c);
96         return (s == "1" || s == "yes");
97 }
98
99 optional<bool>
100 cxml::Node::optional_bool_child (string c) const
101 {
102         optional<string> s = optional_string_child (c);
103         if (!s) {
104                 return optional<bool> ();
105         }
106         
107         return (s.get() == "1" || s.get() == "yes");
108 }
109
110 void
111 cxml::Node::ignore_child (string name) const
112 {
113         _taken.push_back (name);
114 }
115
116 string
117 cxml::Node::string_attribute (string name) const
118 {
119         xmlpp::Element const * e = dynamic_cast<const xmlpp::Element *> (_node);
120         if (!e) {
121                 throw cxml::Error ("missing attribute");
122         }
123         
124         xmlpp::Attribute* a = e->get_attribute (name);
125         if (!a) {
126                 throw cxml::Error ("missing attribute");
127         }
128
129         return a->get_value ();
130 }
131
132 optional<string>
133 cxml::Node::optional_string_attribute (string name) const
134 {
135         xmlpp::Element const * e = dynamic_cast<const xmlpp::Element *> (_node);
136         if (!e) {
137                 return optional<string> ();
138         }
139         
140         xmlpp::Attribute* a = e->get_attribute (name);
141         if (!a) {
142                 return optional<string> ();
143         }
144
145         return string (a->get_value ());
146 }
147
148 bool
149 cxml::Node::bool_attribute (string name) const
150 {
151         string const s = string_attribute (name);
152         return (s == "1" || s == "yes");
153 }
154
155 optional<bool>
156 cxml::Node::optional_bool_attribute (string name) const
157 {
158         optional<string> s = optional_string_attribute (name);
159         if (!s) {
160                 return optional<bool> ();
161         }
162
163         return (s.get() == "1" || s.get() == "yes");
164 }
165
166 void
167 cxml::Node::done () const
168 {
169         xmlpp::Node::NodeList c = _node->get_children ();
170         for (xmlpp::Node::NodeList::iterator i = c.begin(); i != c.end(); ++i) {
171                 if (dynamic_cast<xmlpp::Element *> (*i) && find (_taken.begin(), _taken.end(), (*i)->get_name()) == _taken.end ()) {
172                         throw cxml::Error ("unexpected XML node " + (*i)->get_name());
173                 }
174         }
175 }
176
177 string
178 cxml::Node::content () const
179 {
180         string content;
181         
182         xmlpp::Node::NodeList c = _node->get_children ();
183         for (xmlpp::Node::NodeList::const_iterator i = c.begin(); i != c.end(); ++i) {
184                 xmlpp::ContentNode const * v = dynamic_cast<xmlpp::ContentNode const *> (*i);
185                 if (v) {
186                         content += v->get_content ();
187                 }
188         }
189
190         return content;
191 }
192
193 string
194 cxml::Node::namespace_uri () const
195 {
196         return _node->get_namespace_uri ();
197 }
198
199 string
200 cxml::Node::namespace_prefix () const
201 {
202         return _node->get_namespace_prefix ();
203 }
204
205 cxml::Document::Document (string root_name)
206         : _root_name (root_name)
207 {
208         _parser = new xmlpp::DomParser;
209 }
210
211 cxml::Document::~Document ()
212 {
213         delete _parser;
214 }
215
216 void
217 cxml::Document::read_file (filesystem::path file)
218 {
219         if (!filesystem::exists (file)) {
220                 throw cxml::Error ("XML file does not exist");
221         }
222         
223         _parser->parse_file (file.string ());
224         take_root_node ();
225 }
226
227 void
228 cxml::Document::read_stream (istream& stream)
229 {
230         _parser->parse_stream (stream);
231         take_root_node ();
232 }
233
234 void
235 cxml::Document::take_root_node ()
236 {
237         if (!_parser) {
238                 throw cxml::Error ("could not parse XML");
239         }
240
241         _node = _parser->get_document()->get_root_node ();
242         if (_node->get_name() != _root_name) {
243                 throw cxml::Error ("unrecognised root node");
244         }
245 }
246