Tweaks for more real-life DCPs; beginning of subtitle support.
[libdcp.git] / src / xml.cc
1 #include <sstream>
2 #include <iostream>
3 #include <boost/lexical_cast.hpp>
4 #include <boost/filesystem.hpp>
5 #include <libxml++/libxml++.h>
6 #include "xml.h"
7 #include "exceptions.h"
8 #include "util.h"
9
10 using namespace std;
11 using namespace boost;
12 using namespace libdcp;
13
14 XMLNode::XMLNode ()
15         : _node (0)
16 {
17
18 }
19
20 XMLNode::XMLNode (xmlpp::Node const * node)
21         : _node (node)
22 {
23         
24 }
25
26 xmlpp::Node *
27 XMLNode::xml_node (string name)
28 {
29         list<xmlpp::Node*> n = xml_nodes (name);
30         if (n.size() > 1) {
31                 throw XMLError ("duplicate XML tag " + name);
32         } else if (n.empty ()) {
33                 throw XMLError ("missing XML tag " + name);
34         }
35         
36         return n.front ();
37 }
38
39 list<xmlpp::Node*>
40 XMLNode::xml_nodes (string name)
41 {
42         /* XXX: using find / get_path should work here, but I can't follow
43            how get_path works.
44         */
45
46         xmlpp::Node::NodeList c = _node->get_children ();
47         
48         list<xmlpp::Node*> n;
49         for (xmlpp::Node::NodeList::iterator i = c.begin (); i != c.end(); ++i) {
50                 if ((*i)->get_name() == name) {
51                         n.push_back (*i);
52                 }
53         }
54         
55         _taken.push_back (name);
56         return n;
57 }
58
59 string
60 XMLNode::string_node (string name)
61 {
62         xmlpp::Node* node = xml_node (name);
63         
64         xmlpp::Node::NodeList c = node->get_children ();
65
66         if (c.size() > 1) {
67                 throw XMLError ("unexpected content in XML node");
68         }
69
70         if (c.empty ()) {
71                 return "";
72         }
73         
74         xmlpp::ContentNode const * v = dynamic_cast<xmlpp::ContentNode const *> (c.front());
75         if (!v) {
76                 throw XMLError ("missing content in XML node");
77         }
78         
79         return v->get_content ();
80 }
81
82 string
83 XMLNode::optional_string_node (string name)
84 {
85         list<xmlpp::Node*> nodes = xml_nodes (name);
86         if (nodes.size() > 2) {
87                 throw XMLError ("duplicate XML tag " + name);
88         }
89
90         if (nodes.empty ()) {
91                 return "";
92         }
93
94         return string_node (name);
95 }
96
97 ContentKind
98 XMLNode::kind_node (string name)
99 {
100         return content_kind_from_string (string_node (name));
101 }
102
103 Fraction
104 XMLNode::fraction_node (string name)
105 {
106         return Fraction (string_node (name));
107 }
108
109 int64_t
110 XMLNode::int64_node (string name)
111 {
112         return lexical_cast<int64_t> (string_node (name));
113 }
114
115 int64_t
116 XMLNode::optional_int64_node (string name)
117 {
118         list<xmlpp::Node*> nodes = xml_nodes (name);
119         if (nodes.size() > 2) {
120                 throw XMLError ("duplicate XML tag " + name);
121         }
122
123         if (nodes.empty ()) {
124                 return 0;
125         }
126
127         return int64_node (name);
128 }
129
130 float
131 XMLNode::float_node (string name)
132 {
133         return lexical_cast<float> (string_node (name));
134 }
135
136 void
137 XMLNode::ignore_node (string name)
138 {
139         _taken.push_back (name);
140 }
141
142 void
143 XMLNode::done ()
144 {
145         xmlpp::Node::NodeList c = _node->get_children ();
146         for (xmlpp::Node::NodeList::iterator i = c.begin(); i != c.end(); ++i) {
147                 if (dynamic_cast<xmlpp::Element *> (*i) && find (_taken.begin(), _taken.end(), (*i)->get_name()) == _taken.end ()) {
148                         throw XMLError ("unexpected XML node " + (*i)->get_name());
149                 }
150         }
151 }
152
153 XMLFile::XMLFile (string file, string root_name)
154 {
155         if (!filesystem::exists (file)) {
156                 throw FileError ("XML file does not exist", file);
157         }
158         
159         _parser = new xmlpp::DomParser;
160         _parser->parse_file (file);
161         if (!_parser) {
162                 throw XMLError ("could not parse XML");
163         }
164
165         _node = _parser->get_document()->get_root_node ();
166         if (_node->get_name() != root_name) {
167                 throw XMLError ("unrecognised root node");
168         }
169 }
170
171 XMLFile::~XMLFile ()
172 {
173         delete _parser;
174 }