Recurse into font nodes and pick up details of italics.
[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         string const t = string_attribute (name);
130
131         vector<string> b;
132         boost::split (b, t, is_any_of (":"));
133         assert (b.size() == 4);
134
135         return Time (lexical_cast<int> (b[0]), lexical_cast<int> (b[1]), lexical_cast<int> (b[2]), lexical_cast<int> (b[3]));
136 }
137
138 string
139 XMLNode::string_attribute (string name)
140 {
141         xmlpp::Element const * e = dynamic_cast<const xmlpp::Element *> (_node);
142         if (!e) {
143                 return "";
144         }
145         
146         xmlpp::Attribute* a = e->get_attribute (name);
147         if (!a) {
148                 return "";
149         }
150
151         return a->get_value ();
152 }
153
154 float
155 XMLNode::float_attribute (string name)
156 {
157         return lexical_cast<float> (string_attribute (name));
158 }
159
160 int64_t
161 XMLNode::int64_attribute (string name)
162 {
163         return lexical_cast<int64_t> (string_attribute (name));
164 }
165
166 int64_t
167 XMLNode::optional_int64_attribute (string name)
168 {
169         string const s = string_attribute (name);
170         if (s.empty ()) {
171                 return 0;
172         }
173         
174         return lexical_cast<int64_t> (s);
175 }
176
177 optional<bool>
178 XMLNode::optional_bool_attribute (string name)
179 {
180         string const s = string_attribute (name);
181         if (s.empty ()) {
182                 return optional<bool> ();
183         }
184
185         if (s == "1" || s == "yes") {
186                 return optional<bool> (true);
187         }
188
189         return optional<bool> (false);
190 }
191
192 void
193 XMLNode::done ()
194 {
195         xmlpp::Node::NodeList c = _node->get_children ();
196         for (xmlpp::Node::NodeList::iterator i = c.begin(); i != c.end(); ++i) {
197                 if (dynamic_cast<xmlpp::Element *> (*i) && find (_taken.begin(), _taken.end(), (*i)->get_name()) == _taken.end ()) {
198                         throw XMLError ("unexpected XML node " + (*i)->get_name());
199                 }
200         }
201 }
202
203 string
204 XMLNode::content ()
205 {
206         xmlpp::Node::NodeList c = _node->get_children ();
207
208         if (c.size() > 1) {
209                 throw XMLError ("unexpected content in XML node");
210         }
211
212         if (c.empty ()) {
213                 return "";
214         }
215         
216         xmlpp::ContentNode const * v = dynamic_cast<xmlpp::ContentNode const *> (c.front());
217         if (!v) {
218                 throw XMLError ("missing content in XML node");
219         }
220         
221         return v->get_content ();
222 }
223
224 XMLFile::XMLFile (string file, string root_name)
225 {
226         if (!filesystem::exists (file)) {
227                 throw FileError ("XML file does not exist", file);
228         }
229         
230         _parser = new xmlpp::DomParser;
231         _parser->parse_file (file);
232         if (!_parser) {
233                 throw XMLError ("could not parse XML");
234         }
235
236         _node = _parser->get_document()->get_root_node ();
237         if (_node->get_name() != root_name) {
238                 throw XMLError ("unrecognised root node");
239         }
240 }
241
242 XMLFile::~XMLFile ()
243 {
244         delete _parser;
245 }