Clean up and fix subtitle parsing a bit.
[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 + " in " + _node->get_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         string s = string_node (name);
97         erase_all (s, " ");
98         return lexical_cast<int64_t> (s);
99 }
100
101 int64_t
102 XMLNode::optional_int64_node (string name)
103 {
104         list<xmlpp::Node*> nodes = xml_nodes (name);
105         if (nodes.size() > 2) {
106                 throw XMLError ("duplicate XML tag " + name);
107         }
108
109         if (nodes.empty ()) {
110                 return 0;
111         }
112
113         return int64_node (name);
114 }
115
116 float
117 XMLNode::float_node (string name)
118 {
119         return lexical_cast<float> (string_node (name));
120 }
121
122 void
123 XMLNode::ignore_node (string name)
124 {
125         _taken.push_back (name);
126 }
127
128 Time
129 XMLNode::time_attribute (string name)
130 {
131         return Time (string_attribute (name));
132 }
133
134 string
135 XMLNode::string_attribute (string name)
136 {
137         xmlpp::Element const * e = dynamic_cast<const xmlpp::Element *> (_node);
138         if (!e) {
139                 throw XMLError ("missing attribute");
140         }
141         
142         xmlpp::Attribute* a = e->get_attribute (name);
143         if (!a) {
144                 throw XMLError ("missing attribute");
145         }
146
147         return a->get_value ();
148 }
149
150 string
151 XMLNode::optional_string_attribute (string name)
152 {
153         xmlpp::Element const * e = dynamic_cast<const xmlpp::Element *> (_node);
154         if (!e) {
155                 return "";
156         }
157         
158         xmlpp::Attribute* a = e->get_attribute (name);
159         if (!a) {
160                 return "";
161         }
162
163         return a->get_value ();
164 }
165
166 float
167 XMLNode::float_attribute (string name)
168 {
169         return lexical_cast<float> (string_attribute (name));
170 }
171
172 int64_t
173 XMLNode::int64_attribute (string name)
174 {
175         return lexical_cast<int64_t> (string_attribute (name));
176 }
177
178 int64_t
179 XMLNode::optional_int64_attribute (string name)
180 {
181         string const s = optional_string_attribute (name);
182         if (s.empty ()) {
183                 return 0;
184         }
185         
186         return lexical_cast<int64_t> (s);
187 }
188
189 optional<bool>
190 XMLNode::optional_bool_attribute (string name)
191 {
192         string const s = optional_string_attribute (name);
193         if (s.empty ()) {
194                 return optional<bool> ();
195         }
196
197         if (s == "1" || s == "yes") {
198                 return optional<bool> (true);
199         }
200
201         return optional<bool> (false);
202 }
203
204 optional<Color>
205 XMLNode::optional_color_attribute (string name)
206 {
207         string const s = optional_string_attribute (name);
208         if (s.empty ()) {
209                 return optional<Color> ();
210         }
211
212         return optional<Color> (Color (s));
213 }
214
215 void
216 XMLNode::done ()
217 {
218         xmlpp::Node::NodeList c = _node->get_children ();
219         for (xmlpp::Node::NodeList::iterator i = c.begin(); i != c.end(); ++i) {
220                 if (dynamic_cast<xmlpp::Element *> (*i) && find (_taken.begin(), _taken.end(), (*i)->get_name()) == _taken.end ()) {
221                         throw XMLError ("unexpected XML node " + (*i)->get_name());
222                 }
223         }
224 }
225
226 string
227 XMLNode::content ()
228 {
229         string content;
230         
231         xmlpp::Node::NodeList c = _node->get_children ();
232         for (xmlpp::Node::NodeList::const_iterator i = c.begin(); i != c.end(); ++i) {
233                 xmlpp::ContentNode const * v = dynamic_cast<xmlpp::ContentNode const *> (*i);
234                 if (v) {
235                         content += v->get_content ();
236                 }
237         }
238
239         return content;
240 }
241
242 XMLFile::XMLFile (string file, string root_name)
243 {
244         if (!filesystem::exists (file)) {
245                 throw FileError ("XML file does not exist", file);
246         }
247         
248         _parser = new xmlpp::DomParser;
249         _parser->parse_file (file);
250         if (!_parser) {
251                 throw XMLError ("could not parse XML");
252         }
253
254         _node = _parser->get_document()->get_root_node ();
255         if (_node->get_name() != root_name) {
256                 throw XMLError ("unrecognised root node");
257         }
258 }
259
260 XMLFile::~XMLFile ()
261 {
262         delete _parser;
263 }