1 #include "xmpmeta/xml/serializer_impl.h"
2 
3 #include <libxml/tree.h>
4 
5 #include "base/integral_types.h"
6 #include "android-base/logging.h"
7 #include "strings/numbers.h"
8 #include "xmpmeta/xml/const.h"
9 #include "xmpmeta/xml/utils.h"
10 
11 namespace dynamic_depth {
12 namespace xmpmeta {
13 namespace xml {
14 
15 // Methods specific to SerializerImpl.
SerializerImpl(const std::unordered_map<string,xmlNsPtr> & namespaces,xmlNodePtr node)16 SerializerImpl::SerializerImpl(
17     const std::unordered_map<string, xmlNsPtr>& namespaces, xmlNodePtr node)
18     : node_(node), namespaces_(namespaces) {
19   CHECK(node_ != nullptr) << "Node cannot be null";
20   CHECK(node_->name != nullptr) << "Name in the XML node cannot be null";
21 }
22 
SerializeNamespaces()23 bool SerializerImpl::SerializeNamespaces() {
24   if (namespaces_.empty()) {
25     return true;
26   }
27   if (node_->ns == nullptr && !namespaces_.empty()) {
28     return false;
29   }
30   // Check that the namespaces all have hrefs and that there is a value
31   // for the key node_name.
32   // Set the namespaces in the root node.
33   xmlNsPtr node_ns = node_->ns;
34   for (const auto& entry : namespaces_) {
35     CHECK(entry.second->href != nullptr) << "Namespace href cannot be null";
36     if (node_ns != nullptr) {
37       node_ns->next = entry.second;
38     }
39     node_ns = entry.second;
40   }
41   return true;
42 }
43 
FromDataAndSerializeNamespaces(const std::unordered_map<string,xmlNsPtr> & namespaces,xmlNodePtr node)44 std::unique_ptr<SerializerImpl> SerializerImpl::FromDataAndSerializeNamespaces(
45     const std::unordered_map<string, xmlNsPtr>& namespaces, xmlNodePtr node) {
46   std::unique_ptr<SerializerImpl> serializer =
47       std::unique_ptr<SerializerImpl>(            // NOLINT
48           new SerializerImpl(namespaces, node));  // NOLINT
49   if (!serializer->SerializeNamespaces()) {
50     LOG(ERROR) << "Could not serialize namespaces";
51     return nullptr;
52   }
53   return serializer;
54 }
55 
56 // Implemented methods.
CreateSerializer(const string & node_ns_name,const string & node_name) const57 std::unique_ptr<Serializer> SerializerImpl::CreateSerializer(
58     const string& node_ns_name, const string& node_name) const {
59   if (node_name.empty()) {
60     LOG(ERROR) << "Node name is empty";
61     return nullptr;
62   }
63 
64   if (namespaces_.count(node_ns_name) == 0 && !node_ns_name.empty()) {
65     LOG(ERROR) << "Prefix " << node_ns_name << " not found in prefix list";
66     return nullptr;
67   }
68 
69   xmlNodePtr new_node =
70       xmlNewNode(node_ns_name.empty() ? nullptr : namespaces_.at(node_ns_name),
71                  ToXmlChar(node_name.data()));
72   xmlAddChild(node_, new_node);
73   return std::unique_ptr<Serializer>(
74       new SerializerImpl(namespaces_, new_node));  // NOLINT
75 }
76 
CreateItemSerializer(const string & prefix,const string & item_name) const77 std::unique_ptr<Serializer> SerializerImpl::CreateItemSerializer(
78     const string& prefix, const string& item_name) const {
79   if (namespaces_.count(XmlConst::RdfPrefix()) == 0 ||
80       namespaces_.at(XmlConst::RdfPrefix()) == nullptr) {
81     LOG(ERROR) << "No RDF prefix namespace found";
82     return nullptr;
83   }
84   if (!prefix.empty() && !namespaces_.count(prefix)) {
85     LOG(ERROR) << "No namespace found for " << prefix;
86     return nullptr;
87   }
88   if (strcmp(XmlConst::RdfSeq(), FromXmlChar(node_->name)) != 0) {
89     LOG(ERROR) << "No rdf:Seq node for serializing this item";
90     return nullptr;
91   }
92 
93   xmlNsPtr rdf_prefix_ns = namespaces_.at(string(XmlConst::RdfPrefix()));
94   xmlNodePtr li_node = xmlNewNode(nullptr, ToXmlChar(XmlConst::RdfLi()));
95   xmlNodePtr new_node =
96       xmlNewNode(prefix.empty() ? nullptr : namespaces_.at(prefix),
97                  ToXmlChar(item_name.data()));
98   xmlSetNs(li_node, rdf_prefix_ns);
99   xmlAddChild(node_, li_node);
100   xmlAddChild(li_node, new_node);
101   return std::unique_ptr<Serializer>(
102       new SerializerImpl(namespaces_, new_node));  // NOLINT
103 }
104 
CreateListSerializer(const string & prefix,const string & list_name) const105 std::unique_ptr<Serializer> SerializerImpl::CreateListSerializer(
106     const string& prefix, const string& list_name) const {
107   if (namespaces_.count(XmlConst::RdfPrefix()) == 0 ||
108       namespaces_.at(XmlConst::RdfPrefix()) == nullptr) {
109     LOG(ERROR) << "No RDF prefix namespace found";
110     return nullptr;
111   }
112   if (!prefix.empty() && !namespaces_.count(prefix)) {
113     LOG(ERROR) << "No namespace found for " << prefix;
114     return nullptr;
115   }
116 
117   xmlNodePtr list_node =
118       xmlNewNode(prefix.empty() ? nullptr : namespaces_.at(prefix),
119                  ToXmlChar(list_name.data()));
120   xmlNsPtr rdf_prefix_ns = namespaces_.at(string(XmlConst::RdfPrefix()));
121   xmlNodePtr seq_node = xmlNewNode(nullptr, ToXmlChar(XmlConst::RdfSeq()));
122   xmlSetNs(seq_node, rdf_prefix_ns);
123   xmlAddChild(list_node, seq_node);
124   xmlAddChild(node_, list_node);
125   return std::unique_ptr<Serializer>(
126       new SerializerImpl(namespaces_, seq_node));  // NOLINT
127 }
128 
WriteBoolProperty(const string & prefix,const string & name,bool value) const129 bool SerializerImpl::WriteBoolProperty(const string& prefix, const string& name,
130                                        bool value) const {
131   const string& bool_str = (value ? "true" : "false");
132   return WriteProperty(prefix, name, bool_str);
133 }
134 
WriteProperty(const string & prefix,const string & name,const string & value) const135 bool SerializerImpl::WriteProperty(const string& prefix, const string& name,
136                                    const string& value) const {
137   if (!strcmp(XmlConst::RdfSeq(), FromXmlChar(node_->name))) {
138     LOG(ERROR) << "Cannot write a property on an rdf:Seq node";
139     return false;
140   }
141   if (name.empty()) {
142     LOG(ERROR) << "Property name is empty";
143     return false;
144   }
145 
146   // Check that prefix has a corresponding namespace href.
147   if (!prefix.empty() && namespaces_.count(prefix) == 0) {
148     LOG(ERROR) << "No namespace found for prefix " << prefix;
149     return false;
150   }
151 
152   // Serialize the property in the format Prefix:Name="Value".
153   xmlSetNsProp(node_, prefix.empty() ? nullptr : namespaces_.at(prefix),
154                ToXmlChar(name.data()), ToXmlChar(value.data()));
155   return true;
156 }
157 
WriteIntArray(const string & prefix,const string & array_name,const std::vector<int> & values) const158 bool SerializerImpl::WriteIntArray(const string& prefix,
159                                    const string& array_name,
160                                    const std::vector<int>& values) const {
161   if (!strcmp(XmlConst::RdfSeq(), FromXmlChar(node_->name))) {
162     LOG(ERROR) << "Cannot write a property on an rdf:Seq node";
163     return false;
164   }
165   if (values.empty()) {
166     LOG(WARNING) << "No values to write";
167     return false;
168   }
169   if (namespaces_.count(XmlConst::RdfPrefix()) == 0 ||
170       namespaces_.at(XmlConst::RdfPrefix()) == nullptr) {
171     LOG(ERROR) << "No RDF prefix found";
172     return false;
173   }
174   if (!prefix.empty() && !namespaces_.count(prefix)) {
175     LOG(ERROR) << "No namespace found for " << prefix;
176     return false;
177   }
178   if (array_name.empty()) {
179     LOG(ERROR) << "Parent name cannot be empty";
180     return false;
181   }
182 
183   xmlNodePtr array_parent_node =
184       xmlNewNode(prefix.empty() ? nullptr : namespaces_.at(prefix),
185                  ToXmlChar(array_name.data()));
186   xmlAddChild(node_, array_parent_node);
187 
188   xmlNsPtr rdf_prefix_ns = namespaces_.at(XmlConst::RdfPrefix());
189   xmlNodePtr seq_node = xmlNewNode(nullptr, ToXmlChar(XmlConst::RdfSeq()));
190   xmlSetNs(seq_node, rdf_prefix_ns);
191   xmlAddChild(array_parent_node, seq_node);
192   for (int value : values) {
193     xmlNodePtr li_node = xmlNewNode(nullptr, ToXmlChar(XmlConst::RdfLi()));
194     xmlSetNs(li_node, rdf_prefix_ns);
195     xmlAddChild(seq_node, li_node);
196     xmlNodeSetContent(li_node, ToXmlChar(std::to_string(value).c_str()));
197   }
198 
199   return true;
200 }
201 
WriteDoubleArray(const string & prefix,const string & array_name,const std::vector<double> & values) const202 bool SerializerImpl::WriteDoubleArray(const string& prefix,
203                                       const string& array_name,
204                                       const std::vector<double>& values) const {
205   if (!strcmp(XmlConst::RdfSeq(), FromXmlChar(node_->name))) {
206     LOG(ERROR) << "Cannot write a property on an rdf:Seq node";
207     return false;
208   }
209   if (values.empty()) {
210     LOG(WARNING) << "No values to write";
211     return false;
212   }
213   if (namespaces_.count(XmlConst::RdfPrefix()) == 0 ||
214       namespaces_.at(XmlConst::RdfPrefix()) == nullptr) {
215     LOG(ERROR) << "No RDF prefix found";
216     return false;
217   }
218   if (!prefix.empty() && !namespaces_.count(prefix)) {
219     LOG(ERROR) << "No namespace found for " << prefix;
220     return false;
221   }
222   if (array_name.empty()) {
223     LOG(ERROR) << "Parent name cannot be empty";
224     return false;
225   }
226 
227   xmlNodePtr array_parent_node =
228       xmlNewNode(prefix.empty() ? nullptr : namespaces_.at(prefix),
229                  ToXmlChar(array_name.data()));
230   xmlAddChild(node_, array_parent_node);
231 
232   xmlNsPtr rdf_prefix_ns = namespaces_.at(XmlConst::RdfPrefix());
233   xmlNodePtr seq_node = xmlNewNode(nullptr, ToXmlChar(XmlConst::RdfSeq()));
234   xmlSetNs(seq_node, rdf_prefix_ns);
235   xmlAddChild(array_parent_node, seq_node);
236   for (float value : values) {
237     xmlNodePtr li_node = xmlNewNode(nullptr, ToXmlChar(XmlConst::RdfLi()));
238     xmlSetNs(li_node, rdf_prefix_ns);
239     xmlAddChild(seq_node, li_node);
240     xmlNodeSetContent(
241         li_node, ToXmlChar(dynamic_depth::strings::SimpleFtoa(value).c_str()));
242   }
243 
244   return true;
245 }
246 
247 }  // namespace xml
248 }  // namespace xmpmeta
249 }  // namespace dynamic_depth
250