Some maths operations with Time.
[libdcp.git] / src / xml.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 "xml.h"
8 #include "exceptions.h"
9 #include "util.h"
10
11 using namespace std;
12 using namespace boost;
13 using namespace libdcp;
14
15 XMLNode::XMLNode ()
16         : _node (0)
17 {
18
19 }
20
21 XMLNode::XMLNode (xmlpp::Node const * node)
22         : _node (node)
23 {
24         
25 }
26
27 xmlpp::Node *
28 XMLNode::xml_node (string name)
29 {
30         list<xmlpp::Node*> n = xml_nodes (name);
31         if (n.size() > 1) {
32                 throw XMLError ("duplicate XML tag " + name);
33         } else if (n.empty ()) {
34                 throw XMLError ("missing XML tag " + name);
35         }
36         
37         return n.front ();
38 }
39
40 list<xmlpp::Node*>
41 XMLNode::xml_nodes (string name)
42 {
43         /* XXX: using find / get_path should work here, but I can't follow
44            how get_path works.
45         */
46
47         xmlpp::Node::NodeList c = _node->get_children ();
48         
49         list<xmlpp::Node*> n;
50         for (xmlpp::Node::NodeList::iterator i = c.begin (); i != c.end(); ++i) {
51                 if ((*i)->get_name() == name) {
52                         n.push_back (*i);
53                 }
54         }
55         
56         _taken.push_back (name);
57         return n;
58 }
59
60 string
61 XMLNode::string_node (string name)
62 {
63         return XMLNode (xml_node (name)).content ();
64 }
65
66 string
67 XMLNode::optional_string_node (string name)
68 {
69         list<xmlpp::Node*> nodes = xml_nodes (name);
70         if (nodes.size() > 2) {
71                 throw XMLError ("duplicate XML tag " + name);
72         }
73
74         if (nodes.empty ()) {
75                 return "";
76         }
77
78         return string_node (name);
79 }
80
81 ContentKind
82 XMLNode::kind_node (string name)
83 {
84         return content_kind_from_string (string_node (name));
85 }
86
87 Fraction
88 XMLNode::fraction_node (string name)
89 {
90         return Fraction (string_node (name));
91 }
92
93 int64_t
94 XMLNode::int64_node (string name)
95 {
96         return lexical_cast<int64_t> (string_node (name));
97 }
98
99 int64_t
100 XMLNode::optional_int64_node (string name)
101 {
102         list<xmlpp::Node*> nodes = xml_nodes (name);
103         if (nodes.size() > 2) {
104                 throw XMLError ("duplicate XML tag " + name);
105         }
106
107         if (nodes.empty ()) {
108                 return 0;
109         }
110
111         return int64_node (name);
112 }
113
114 float
115 XMLNode::float_node (string name)
116 {
117         return lexical_cast<float> (string_node (name));
118 }
119
120 void
121 XMLNode::ignore_node (string name)
122 {
123         _taken.push_back (name);
124 }
125
126 Time
127 XMLNode::time_attribute (string name)
128 {
129         return Time (string_attribute (name));
130 }
131
132 string
133 XMLNode::string_attribute (string name)
134 {
135         xmlpp::Element const * e = dynamic_cast<const xmlpp::Element *> (_node);
136         if (!e) {
137                 throw XMLError ("missing attribute");
138         }
139         
140         xmlpp::Attribute* a = e->get_attribute (name);
141         if (!a) {
142                 throw XMLError ("missing attribute");
143         }
144
145         return a->get_value ();
146 }
147
148 string
149 XMLNode::optional_string_attribute (string name)
150 {
151         xmlpp::Element const * e = dynamic_cast<const xmlpp::Element *> (_node);
152         if (!e) {
153                 return "";
154         }
155         
156         xmlpp::Attribute* a = e->get_attribute (name);
157         if (!a) {
158                 return "";
159         }
160
161         return a->get_value ();
162 }
163
164 float
165 XMLNode::float_attribute (string name)
166 {
167         return lexical_cast<float> (string_attribute (name));
168 }
169
170 int64_t
171 XMLNode::int64_attribute (string name)
172 {
173         return lexical_cast<int64_t> (string_attribute (name));
174 }
175
176 int64_t
177 XMLNode::optional_int64_attribute (string name)
178 {
179         string const s = optional_string_attribute (name);
180         if (s.empty ()) {
181                 return 0;
182         }
183         
184         return lexical_cast<int64_t> (s);
185 }
186
187 optional<bool>
188 XMLNode::optional_bool_attribute (string name)
189 {
190         string const s = optional_string_attribute (name);
191         if (s.empty ()) {
192                 return optional<bool> ();
193         }
194
195         if (s == "1" || s == "yes") {
196                 return optional<bool> (true);
197         }
198
199         return optional<bool> (false);
200 }
201
202 optional<Color>
203 XMLNode::optional_color_attribute (string name)
204 {
205         string const s = optional_string_attribute (name);
206         if (s.empty ()) {
207                 return optional<Color> ();
208         }
209
210         return optional<Color> (Color (s));
211 }
212
213 void
214 XMLNode::done ()
215 {
216         xmlpp::Node::NodeList c = _node->get_children ();
217         for (xmlpp::Node::NodeList::iterator i = c.begin(); i != c.end(); ++i) {
218                 if (dynamic_cast<xmlpp::Element *> (*i) && find (_taken.begin(), _taken.end(), (*i)->get_name()) == _taken.end ()) {
219                         throw XMLError ("unexpected XML node " + (*i)->get_name());
220                 }
221         }
222 }
223
224 string
225 XMLNode::content ()
226 {
227         xmlpp::Node::NodeList c = _node->get_children ();
228
229         if (c.size() > 1) {
230                 throw XMLError ("unexpected content in XML node");
231         }
232
233         if (c.empty ()) {
234                 return "";
235         }
236         
237         xmlpp::ContentNode const * v = dynamic_cast<xmlpp::ContentNode const *> (c.front());
238         if (!v) {
239                 throw XMLError ("missing content in XML node");
240         }
241         
242         return v->get_content ();
243 }
244
245 XMLFile::XMLFile (string file, string root_name)
246 {
247         if (!filesystem::exists (file)) {
248                 throw FileError ("XML file does not exist", file);
249         }
250         
251         _parser = new xmlpp::DomParser;
252         _parser->parse_file (file);
253         if (!_parser) {
254                 throw XMLError ("could not parse XML");
255         }
256
257         _node = _parser->get_document()->get_root_node ();
258         if (_node->get_name() != root_name) {
259                 throw XMLError ("unrecognised root node");
260         }
261 }
262
263 XMLFile::~XMLFile ()
264 {
265         delete _parser;
266 }