Various bits of development.
[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 const * 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         /* XXX: using find / get_path should work here, but I can't follow
61            how get_path works.
62         */
63
64         xmlpp::Node::NodeList c = _node->get_children ();
65         
66         list<shared_ptr<cxml::Node> > n;
67         for (xmlpp::Node::NodeList::iterator i = c.begin (); i != c.end(); ++i) {
68                 if ((*i)->get_name() == name) {
69                         n.push_back (shared_ptr<Node> (new Node (*i)));
70                 }
71         }
72         
73         _taken.push_back (name);
74         return n;
75 }
76
77 string
78 cxml::Node::string_child (string c) const
79 {
80         return node_child(c)->content ();
81 }
82
83 optional<string>
84 cxml::Node::optional_string_child (string c) const
85 {
86         list<shared_ptr<Node> > nodes = node_children (c);
87         if (nodes.size() > 1) {
88                 throw cxml::Error ("duplicate XML tag " + c);
89         }
90
91         if (nodes.empty ()) {
92                 return optional<string> ();
93         }
94
95         return nodes.front()->content();
96 }
97
98 bool
99 cxml::Node::bool_child (string c) const
100 {
101         string const s = string_child (c);
102         return (s == "1" || s == "yes");
103 }
104
105 optional<bool>
106 cxml::Node::optional_bool_child (string c) const
107 {
108         optional<string> s = optional_string_child (c);
109         if (!s) {
110                 return optional<bool> ();
111         }
112         
113         return (s.get() == "1" || s.get() == "yes");
114 }
115
116 void
117 cxml::Node::ignore_child (string name) const
118 {
119         _taken.push_back (name);
120 }
121
122 string
123 cxml::Node::string_attribute (string name) const
124 {
125         xmlpp::Element const * e = dynamic_cast<const xmlpp::Element *> (_node);
126         if (!e) {
127                 throw cxml::Error ("missing attribute");
128         }
129         
130         xmlpp::Attribute* a = e->get_attribute (name);
131         if (!a) {
132                 throw cxml::Error ("missing attribute");
133         }
134
135         return a->get_value ();
136 }
137
138 optional<string>
139 cxml::Node::optional_string_attribute (string name) const
140 {
141         xmlpp::Element const * e = dynamic_cast<const xmlpp::Element *> (_node);
142         if (!e) {
143                 return optional<string> ();
144         }
145         
146         xmlpp::Attribute* a = e->get_attribute (name);
147         if (!a) {
148                 return optional<string> ();
149         }
150
151         return string (a->get_value ());
152 }
153
154 bool
155 cxml::Node::bool_attribute (string name) const
156 {
157         string const s = string_attribute (name);
158         return (s == "1" || s == "yes");
159 }
160
161 optional<bool>
162 cxml::Node::optional_bool_attribute (string name) const
163 {
164         optional<string> s = optional_string_attribute (name);
165         if (!s) {
166                 return optional<bool> ();
167         }
168
169         return (s.get() == "1" || s.get() == "yes");
170 }
171
172 void
173 cxml::Node::done () const
174 {
175         xmlpp::Node::NodeList c = _node->get_children ();
176         for (xmlpp::Node::NodeList::iterator i = c.begin(); i != c.end(); ++i) {
177                 if (dynamic_cast<xmlpp::Element *> (*i) && find (_taken.begin(), _taken.end(), (*i)->get_name()) == _taken.end ()) {
178                         throw cxml::Error ("unexpected XML node " + (*i)->get_name());
179                 }
180         }
181 }
182
183 string
184 cxml::Node::content () const
185 {
186         string content;
187         
188         xmlpp::Node::NodeList c = _node->get_children ();
189         for (xmlpp::Node::NodeList::const_iterator i = c.begin(); i != c.end(); ++i) {
190                 xmlpp::ContentNode const * v = dynamic_cast<xmlpp::ContentNode const *> (*i);
191                 if (v) {
192                         content += v->get_content ();
193                 }
194         }
195
196         return content;
197 }
198
199 cxml::File::File (string file, string root_name)
200 {
201         if (!filesystem::exists (file)) {
202                 throw cxml::Error ("XML file does not exist");
203         }
204         
205         _parser = new xmlpp::DomParser;
206         _parser->parse_file (file);
207         if (!_parser) {
208                 throw cxml::Error ("could not parse XML");
209         }
210
211         _node = _parser->get_document()->get_root_node ();
212         if (_node->get_name() != root_name) {
213                 throw cxml::Error ("unrecognised root node");
214         }
215 }
216
217 cxml::File::~File ()
218 {
219         delete _parser;
220 }