普通文本  |  250行  |  8.42 KB

#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