| #include "xmpmeta/xml/serializer_impl.h" |
| |
| #include <libxml/tree.h> |
| |
| #include "base/integral_types.h" |
| #include "android-base/logging.h" |
| #include "strings/numbers.h" |
| #include "xmpmeta/xml/const.h" |
| #include "xmpmeta/xml/utils.h" |
| |
| namespace dynamic_depth { |
| namespace xmpmeta { |
| namespace xml { |
| |
| // Methods specific to SerializerImpl. |
| SerializerImpl::SerializerImpl( |
| const std::unordered_map<string, xmlNsPtr>& namespaces, xmlNodePtr node) |
| : node_(node), namespaces_(namespaces) { |
| CHECK(node_ != nullptr) << "Node cannot be null"; |
| CHECK(node_->name != nullptr) << "Name in the XML node cannot be null"; |
| } |
| |
| bool SerializerImpl::SerializeNamespaces() { |
| if (namespaces_.empty()) { |
| return true; |
| } |
| if (node_->ns == nullptr && !namespaces_.empty()) { |
| return false; |
| } |
| // Check that the namespaces all have hrefs and that there is a value |
| // for the key node_name. |
| // Set the namespaces in the root node. |
| xmlNsPtr node_ns = node_->ns; |
| for (const auto& entry : namespaces_) { |
| CHECK(entry.second->href != nullptr) << "Namespace href cannot be null"; |
| if (node_ns != nullptr) { |
| node_ns->next = entry.second; |
| } |
| node_ns = entry.second; |
| } |
| return true; |
| } |
| |
| std::unique_ptr<SerializerImpl> SerializerImpl::FromDataAndSerializeNamespaces( |
| const std::unordered_map<string, xmlNsPtr>& namespaces, xmlNodePtr node) { |
| std::unique_ptr<SerializerImpl> serializer = |
| std::unique_ptr<SerializerImpl>( // NOLINT |
| new SerializerImpl(namespaces, node)); // NOLINT |
| if (!serializer->SerializeNamespaces()) { |
| LOG(ERROR) << "Could not serialize namespaces"; |
| return nullptr; |
| } |
| return serializer; |
| } |
| |
| // Implemented methods. |
| std::unique_ptr<Serializer> SerializerImpl::CreateSerializer( |
| const string& node_ns_name, const string& node_name) const { |
| if (node_name.empty()) { |
| LOG(ERROR) << "Node name is empty"; |
| return nullptr; |
| } |
| |
| if (namespaces_.count(node_ns_name) == 0 && !node_ns_name.empty()) { |
| LOG(ERROR) << "Prefix " << node_ns_name << " not found in prefix list"; |
| return nullptr; |
| } |
| |
| xmlNodePtr new_node = |
| xmlNewNode(node_ns_name.empty() ? nullptr : namespaces_.at(node_ns_name), |
| ToXmlChar(node_name.data())); |
| xmlAddChild(node_, new_node); |
| return std::unique_ptr<Serializer>( |
| new SerializerImpl(namespaces_, new_node)); // NOLINT |
| } |
| |
| std::unique_ptr<Serializer> SerializerImpl::CreateItemSerializer( |
| const string& prefix, const string& item_name) const { |
| if (namespaces_.count(XmlConst::RdfPrefix()) == 0 || |
| namespaces_.at(XmlConst::RdfPrefix()) == nullptr) { |
| LOG(ERROR) << "No RDF prefix namespace found"; |
| return nullptr; |
| } |
| if (!prefix.empty() && !namespaces_.count(prefix)) { |
| LOG(ERROR) << "No namespace found for " << prefix; |
| return nullptr; |
| } |
| if (strcmp(XmlConst::RdfSeq(), FromXmlChar(node_->name)) != 0) { |
| LOG(ERROR) << "No rdf:Seq node for serializing this item"; |
| return nullptr; |
| } |
| |
| xmlNsPtr rdf_prefix_ns = namespaces_.at(string(XmlConst::RdfPrefix())); |
| xmlNodePtr li_node = xmlNewNode(nullptr, ToXmlChar(XmlConst::RdfLi())); |
| xmlNodePtr new_node = |
| xmlNewNode(prefix.empty() ? nullptr : namespaces_.at(prefix), |
| ToXmlChar(item_name.data())); |
| xmlSetNs(li_node, rdf_prefix_ns); |
| xmlAddChild(node_, li_node); |
| xmlAddChild(li_node, new_node); |
| return std::unique_ptr<Serializer>( |
| new SerializerImpl(namespaces_, new_node)); // NOLINT |
| } |
| |
| std::unique_ptr<Serializer> SerializerImpl::CreateListSerializer( |
| const string& prefix, const string& list_name) const { |
| if (namespaces_.count(XmlConst::RdfPrefix()) == 0 || |
| namespaces_.at(XmlConst::RdfPrefix()) == nullptr) { |
| LOG(ERROR) << "No RDF prefix namespace found"; |
| return nullptr; |
| } |
| if (!prefix.empty() && !namespaces_.count(prefix)) { |
| LOG(ERROR) << "No namespace found for " << prefix; |
| return nullptr; |
| } |
| |
| xmlNodePtr list_node = |
| xmlNewNode(prefix.empty() ? nullptr : namespaces_.at(prefix), |
| ToXmlChar(list_name.data())); |
| xmlNsPtr rdf_prefix_ns = namespaces_.at(string(XmlConst::RdfPrefix())); |
| xmlNodePtr seq_node = xmlNewNode(nullptr, ToXmlChar(XmlConst::RdfSeq())); |
| xmlSetNs(seq_node, rdf_prefix_ns); |
| xmlAddChild(list_node, seq_node); |
| xmlAddChild(node_, list_node); |
| return std::unique_ptr<Serializer>( |
| new SerializerImpl(namespaces_, seq_node)); // NOLINT |
| } |
| |
| bool SerializerImpl::WriteBoolProperty(const string& prefix, const string& name, |
| bool value) const { |
| const string& bool_str = (value ? "true" : "false"); |
| return WriteProperty(prefix, name, bool_str); |
| } |
| |
| bool SerializerImpl::WriteProperty(const string& prefix, const string& name, |
| const string& value) const { |
| if (!strcmp(XmlConst::RdfSeq(), FromXmlChar(node_->name))) { |
| LOG(ERROR) << "Cannot write a property on an rdf:Seq node"; |
| return false; |
| } |
| if (name.empty()) { |
| LOG(ERROR) << "Property name is empty"; |
| return false; |
| } |
| |
| // Check that prefix has a corresponding namespace href. |
| if (!prefix.empty() && namespaces_.count(prefix) == 0) { |
| LOG(ERROR) << "No namespace found for prefix " << prefix; |
| return false; |
| } |
| |
| // Serialize the property in the format Prefix:Name="Value". |
| xmlSetNsProp(node_, prefix.empty() ? nullptr : namespaces_.at(prefix), |
| ToXmlChar(name.data()), ToXmlChar(value.data())); |
| return true; |
| } |
| |
| bool SerializerImpl::WriteIntArray(const string& prefix, |
| const string& array_name, |
| const std::vector<int>& values) const { |
| if (!strcmp(XmlConst::RdfSeq(), FromXmlChar(node_->name))) { |
| LOG(ERROR) << "Cannot write a property on an rdf:Seq node"; |
| return false; |
| } |
| if (values.empty()) { |
| LOG(WARNING) << "No values to write"; |
| return false; |
| } |
| if (namespaces_.count(XmlConst::RdfPrefix()) == 0 || |
| namespaces_.at(XmlConst::RdfPrefix()) == nullptr) { |
| LOG(ERROR) << "No RDF prefix found"; |
| return false; |
| } |
| if (!prefix.empty() && !namespaces_.count(prefix)) { |
| LOG(ERROR) << "No namespace found for " << prefix; |
| return false; |
| } |
| if (array_name.empty()) { |
| LOG(ERROR) << "Parent name cannot be empty"; |
| return false; |
| } |
| |
| xmlNodePtr array_parent_node = |
| xmlNewNode(prefix.empty() ? nullptr : namespaces_.at(prefix), |
| ToXmlChar(array_name.data())); |
| xmlAddChild(node_, array_parent_node); |
| |
| xmlNsPtr rdf_prefix_ns = namespaces_.at(XmlConst::RdfPrefix()); |
| xmlNodePtr seq_node = xmlNewNode(nullptr, ToXmlChar(XmlConst::RdfSeq())); |
| xmlSetNs(seq_node, rdf_prefix_ns); |
| xmlAddChild(array_parent_node, seq_node); |
| for (int value : values) { |
| xmlNodePtr li_node = xmlNewNode(nullptr, ToXmlChar(XmlConst::RdfLi())); |
| xmlSetNs(li_node, rdf_prefix_ns); |
| xmlAddChild(seq_node, li_node); |
| xmlNodeSetContent(li_node, ToXmlChar(std::to_string(value).c_str())); |
| } |
| |
| return true; |
| } |
| |
| bool SerializerImpl::WriteDoubleArray(const string& prefix, |
| const string& array_name, |
| const std::vector<double>& values) const { |
| if (!strcmp(XmlConst::RdfSeq(), FromXmlChar(node_->name))) { |
| LOG(ERROR) << "Cannot write a property on an rdf:Seq node"; |
| return false; |
| } |
| if (values.empty()) { |
| LOG(WARNING) << "No values to write"; |
| return false; |
| } |
| if (namespaces_.count(XmlConst::RdfPrefix()) == 0 || |
| namespaces_.at(XmlConst::RdfPrefix()) == nullptr) { |
| LOG(ERROR) << "No RDF prefix found"; |
| return false; |
| } |
| if (!prefix.empty() && !namespaces_.count(prefix)) { |
| LOG(ERROR) << "No namespace found for " << prefix; |
| return false; |
| } |
| if (array_name.empty()) { |
| LOG(ERROR) << "Parent name cannot be empty"; |
| return false; |
| } |
| |
| xmlNodePtr array_parent_node = |
| xmlNewNode(prefix.empty() ? nullptr : namespaces_.at(prefix), |
| ToXmlChar(array_name.data())); |
| xmlAddChild(node_, array_parent_node); |
| |
| xmlNsPtr rdf_prefix_ns = namespaces_.at(XmlConst::RdfPrefix()); |
| xmlNodePtr seq_node = xmlNewNode(nullptr, ToXmlChar(XmlConst::RdfSeq())); |
| xmlSetNs(seq_node, rdf_prefix_ns); |
| xmlAddChild(array_parent_node, seq_node); |
| for (float value : values) { |
| xmlNodePtr li_node = xmlNewNode(nullptr, ToXmlChar(XmlConst::RdfLi())); |
| xmlSetNs(li_node, rdf_prefix_ns); |
| xmlAddChild(seq_node, li_node); |
| xmlNodeSetContent( |
| li_node, ToXmlChar(dynamic_depth::strings::SimpleFtoa(value).c_str())); |
| } |
| |
| return true; |
| } |
| |
| } // namespace xml |
| } // namespace xmpmeta |
| } // namespace dynamic_depth |