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.
323 lines
10 KiB
323 lines
10 KiB
#include "xmpmeta/xml/deserializer_impl.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "base/integral_types.h"
|
|
#include "android-base/logging.h"
|
|
#include "strings/numbers.h"
|
|
#include "xmpmeta/base64.h"
|
|
#include "xmpmeta/xml/const.h"
|
|
#include "xmpmeta/xml/search.h"
|
|
#include "xmpmeta/xml/utils.h"
|
|
#include "xmpmeta/xmp_parser.h"
|
|
|
|
namespace dynamic_depth {
|
|
namespace xmpmeta {
|
|
namespace xml {
|
|
namespace {
|
|
|
|
// Converts a string to a boolean value if bool_str is one of "false" or "true",
|
|
// regardless of letter casing.
|
|
bool BoolStringToBool(const string& bool_str, bool* value) {
|
|
string bool_str_lower = bool_str;
|
|
std::transform(bool_str_lower.begin(), bool_str_lower.end(),
|
|
bool_str_lower.begin(), ::tolower);
|
|
if (bool_str_lower == "true") {
|
|
*value = true;
|
|
return true;
|
|
}
|
|
if (bool_str_lower == "false") {
|
|
*value = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Search for an rdf:Seq node, if it hasn't already been set.
|
|
// parent_name is the name of the rdf:Seq node's parent.
|
|
xmlNodePtr FindSeqNode(const xmlNodePtr node, const string& prefix,
|
|
const string& parent_name) {
|
|
xmlNodePtr parent_node =
|
|
DepthFirstSearch(node, prefix.data(), parent_name.data());
|
|
if (parent_node == nullptr) {
|
|
LOG(WARNING) << "Node " << parent_name << " not found";
|
|
return nullptr;
|
|
}
|
|
return GetFirstSeqElement(parent_node);
|
|
}
|
|
|
|
// Extracts the specified string attribute.
|
|
bool GetStringProperty(const xmlNodePtr node, const string& prefix,
|
|
const string& property, string* value) {
|
|
const xmlDocPtr doc = node->doc;
|
|
for (const _xmlAttr* attribute = node->properties; attribute != nullptr;
|
|
attribute = attribute->next) {
|
|
// If prefix is not empty, then the attribute's namespace must not be null.
|
|
if (((attribute->ns && !prefix.empty() &&
|
|
strcmp(FromXmlChar(attribute->ns->prefix), prefix.data()) == 0) ||
|
|
prefix.empty()) &&
|
|
strcmp(FromXmlChar(attribute->name), property.data()) == 0) {
|
|
xmlChar* attribute_string =
|
|
xmlNodeListGetString(doc, attribute->children, 1);
|
|
*value = FromXmlChar(attribute_string);
|
|
xmlFree(attribute_string);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Reads the contents of a node.
|
|
// E.g. <prefix:node_name>Contents Here</prefix:node_name>
|
|
bool ReadNodeContent(const xmlNodePtr node, const string& prefix,
|
|
const string& node_name, string* value) {
|
|
auto* element = DepthFirstSearch(node, prefix.data(), node_name.data());
|
|
if (element == nullptr) {
|
|
return false;
|
|
}
|
|
if (!prefix.empty() &&
|
|
(element->ns == nullptr || element->ns->prefix == nullptr ||
|
|
strcmp(FromXmlChar(element->ns->prefix), prefix.data()) != 0)) {
|
|
return false;
|
|
}
|
|
xmlChar* node_content = xmlNodeGetContent(element);
|
|
*value = FromXmlChar(node_content);
|
|
free(node_content);
|
|
return true;
|
|
}
|
|
|
|
// Reads the string value of a property from the given XML node.
|
|
bool ReadStringProperty(const xmlNodePtr node, const string& prefix,
|
|
const string& property, string* value) {
|
|
if (node == nullptr) {
|
|
return false;
|
|
}
|
|
if (property.empty()) {
|
|
LOG(ERROR) << "Property not given";
|
|
return false;
|
|
}
|
|
|
|
// Try parsing in the format <Node ... Prefix:Property="Value"/>
|
|
bool success = GetStringProperty(node, prefix, property, value);
|
|
if (!success) {
|
|
// Try parsing in the format <Prefix:Property>Value</Prefix:Property>
|
|
success = ReadNodeContent(node, prefix, property, value);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
// Same as ReadStringProperty, but applies base-64 decoding to the output.
|
|
bool ReadBase64Property(const xmlNodePtr node, const string& prefix,
|
|
const string& property, string* value) {
|
|
string base64_data;
|
|
if (!ReadStringProperty(node, prefix, property, &base64_data)) {
|
|
return false;
|
|
}
|
|
return DecodeBase64(base64_data, value);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
DeserializerImpl::DeserializerImpl(const xmlNodePtr node)
|
|
: node_(node), list_node_(nullptr) {}
|
|
|
|
// Public methods.
|
|
std::unique_ptr<Deserializer> DeserializerImpl::CreateDeserializer(
|
|
const string& prefix, const string& child_name) const {
|
|
if (child_name.empty()) {
|
|
LOG(ERROR) << "Child name is empty";
|
|
return nullptr;
|
|
}
|
|
xmlNodePtr child_node =
|
|
DepthFirstSearch(node_, prefix.data(), child_name.data());
|
|
if (child_node == nullptr) {
|
|
return nullptr;
|
|
}
|
|
return std::unique_ptr<Deserializer>(
|
|
new DeserializerImpl(child_node)); // NOLINT
|
|
}
|
|
|
|
std::unique_ptr<Deserializer>
|
|
DeserializerImpl::CreateDeserializerFromListElementAt(const string& prefix,
|
|
const string& list_name,
|
|
int index) const {
|
|
if (index < 0) {
|
|
LOG(ERROR) << "Index must be greater than or equal to zero";
|
|
return nullptr;
|
|
}
|
|
|
|
if (list_name.empty()) {
|
|
LOG(ERROR) << "Parent name cannot be empty";
|
|
return nullptr;
|
|
}
|
|
// Search for an rdf:Seq node, if the name of list_node_ doesn't match
|
|
// the given parent name.
|
|
// Ensures thread safety.
|
|
const xmlNodePtr list_node = [&] {
|
|
std::lock_guard<std::mutex> lock(mtx_);
|
|
if (list_node_ == nullptr ||
|
|
string(FromXmlChar(list_node_->name)) != list_name) {
|
|
list_node_ = DepthFirstSearch(node_, prefix.data(), list_name.data());
|
|
}
|
|
return list_node_;
|
|
}();
|
|
if (list_node == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
xmlNodePtr seq_node = GetFirstSeqElement(list_node);
|
|
if (seq_node == nullptr) {
|
|
LOG(ERROR) << "No rdf:Seq node found on " << list_name;
|
|
return nullptr;
|
|
}
|
|
xmlNodePtr li_node = GetElementAt(seq_node, index);
|
|
if (li_node == nullptr) {
|
|
return nullptr;
|
|
}
|
|
// Return a new Deserializer with the current rdf:li node and the current
|
|
// node name.
|
|
return std::unique_ptr<Deserializer>(
|
|
new DeserializerImpl(li_node)); // NOLINT
|
|
}
|
|
|
|
bool DeserializerImpl::ParseBase64(const string& prefix, const string& name,
|
|
string* value) const {
|
|
return ReadBase64Property(node_, prefix, name, value);
|
|
}
|
|
|
|
bool DeserializerImpl::ParseIntArrayBase64(const string& prefix,
|
|
const string& name,
|
|
std::vector<int>* values) const {
|
|
string base64_data;
|
|
if (!ReadStringProperty(node_, prefix, name, &base64_data)) {
|
|
return false;
|
|
}
|
|
return DecodeIntArrayBase64(base64_data, values);
|
|
}
|
|
|
|
bool DeserializerImpl::ParseFloatArrayBase64(const string& prefix,
|
|
const string& name,
|
|
std::vector<float>* values) const {
|
|
string base64_data;
|
|
if (!ReadStringProperty(node_, prefix, name, &base64_data)) {
|
|
return false;
|
|
}
|
|
return DecodeFloatArrayBase64(base64_data, values);
|
|
}
|
|
|
|
bool DeserializerImpl::ParseDoubleArrayBase64(
|
|
const string& prefix, const string& name,
|
|
std::vector<double>* values) const {
|
|
string base64_data;
|
|
if (!ReadStringProperty(node_, prefix, name, &base64_data)) {
|
|
return false;
|
|
}
|
|
return DecodeDoubleArrayBase64(base64_data, values);
|
|
}
|
|
|
|
bool DeserializerImpl::ParseBoolean(const string& prefix, const string& name,
|
|
bool* value) const {
|
|
string string_value;
|
|
if (!ReadStringProperty(node_, prefix, name, &string_value)) {
|
|
return false;
|
|
}
|
|
return BoolStringToBool(string_value, value);
|
|
}
|
|
|
|
bool DeserializerImpl::ParseDouble(const string& prefix, const string& name,
|
|
double* value) const {
|
|
string string_value;
|
|
if (!ReadStringProperty(node_, prefix, name, &string_value)) {
|
|
return false;
|
|
}
|
|
*value = std::stod(string_value);
|
|
return true;
|
|
}
|
|
|
|
bool DeserializerImpl::ParseInt(const string& prefix, const string& name,
|
|
int* value) const {
|
|
string string_value;
|
|
if (!ReadStringProperty(node_, prefix, name, &string_value)) {
|
|
return false;
|
|
}
|
|
*value = std::stoi(string_value); // NOLINT
|
|
return true;
|
|
}
|
|
|
|
bool DeserializerImpl::ParseFloat(const string& prefix, const string& name,
|
|
float* value) const {
|
|
string string_value;
|
|
if (!ReadStringProperty(node_, prefix, name, &string_value)) {
|
|
return false;
|
|
}
|
|
*value = std::stof(string_value);
|
|
return true;
|
|
}
|
|
|
|
bool DeserializerImpl::ParseLong(const string& prefix, const string& name,
|
|
int64* value) const {
|
|
string string_value;
|
|
if (!ReadStringProperty(node_, prefix, name, &string_value)) {
|
|
return false;
|
|
}
|
|
*value = std::stol(string_value);
|
|
return true;
|
|
}
|
|
|
|
bool DeserializerImpl::ParseString(const string& prefix, const string& name,
|
|
string* value) const {
|
|
return ReadStringProperty(node_, prefix, name, value);
|
|
}
|
|
|
|
bool DeserializerImpl::ParseIntArray(const string& prefix,
|
|
const string& list_name,
|
|
std::vector<int>* values) const {
|
|
xmlNodePtr seq_node = FindSeqNode(node_, prefix, list_name);
|
|
if (seq_node == nullptr) {
|
|
return false;
|
|
}
|
|
values->clear();
|
|
int i = 0;
|
|
for (xmlNodePtr li_node = GetElementAt(seq_node, 0); li_node != nullptr;
|
|
li_node = GetElementAt(seq_node, ++i)) {
|
|
string value = GetLiNodeContent(li_node);
|
|
for (int i = 0; i < value.size(); ++i) {
|
|
if (!isdigit(value[i])) {
|
|
LOG(ERROR) << "Could not parse rdf:li node value to an integer";
|
|
return false;
|
|
}
|
|
}
|
|
int int_value = std::atoi(value.c_str()); // NOLINT
|
|
values->push_back(int_value);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DeserializerImpl::ParseDoubleArray(const string& prefix,
|
|
const string& list_name,
|
|
std::vector<double>* values) const {
|
|
xmlNodePtr seq_node = FindSeqNode(node_, prefix, list_name);
|
|
if (seq_node == nullptr) {
|
|
return false;
|
|
}
|
|
values->clear();
|
|
int i = 0;
|
|
for (xmlNodePtr li_node = GetElementAt(seq_node, 0); li_node != nullptr;
|
|
li_node = GetElementAt(seq_node, ++i)) {
|
|
double double_value;
|
|
if (!dynamic_depth::strings::safe_strtod(GetLiNodeContent(li_node),
|
|
&double_value)) {
|
|
LOG(ERROR) << "Could not parse rdf:li node value to a double";
|
|
return false;
|
|
}
|
|
values->push_back(double_value);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace xml
|
|
} // namespace xmpmeta
|
|
} // namespace dynamic_depth
|