Some comments.
[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 void
161 XMLNode::done ()
162 {
163         xmlpp::Node::NodeList c = _node->get_children ();
164         for (xmlpp::Node::NodeList::iterator i = c.begin(); i != c.end(); ++i) {
165                 if (dynamic_cast<xmlpp::Element *> (*i) && find (_taken.begin(), _taken.end(), (*i)->get_name()) == _taken.end ()) {
166                         throw XMLError ("unexpected XML node " + (*i)->get_name());
167                 }
168         }
169 }
170
171 string
172 XMLNode::content ()
173 {
174         xmlpp::Node::NodeList c = _node->get_children ();
175
176         if (c.size() > 1) {
177                 throw XMLError ("unexpected content in XML node");
178         }
179
180         if (c.empty ()) {
181                 return "";
182         }
183         
184         xmlpp::ContentNode const * v = dynamic_cast<xmlpp::ContentNode const *> (c.front());
185         if (!v) {
186                 throw XMLError ("missing content in XML node");
187         }
188         
189         return v->get_content ();
190 }
191
192 XMLFile::XMLFile (string file, string root_name)
193 {
194         if (!filesystem::exists (file)) {
195                 throw FileError ("XML file does not exist", file);
196         }
197         
198         _parser = new xmlpp::DomParser;
199         _parser->parse_file (file);
200         if (!_parser) {
201                 throw XMLError ("could not parse XML");
202         }
203
204         _node = _parser->get_document()->get_root_node ();
205         if (_node->get_name() != root_name) {
206                 throw XMLError ("unrecognised root node");
207         }
208 }
209
210 XMLFile::~XMLFile ()
211 {
212         delete _parser;
213 }