You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
250 lines
8.4 KiB
250 lines
8.4 KiB
#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
|