#include "xmpmeta/xml/deserializer_impl.h" #include #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. Contents Here 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 bool success = GetStringProperty(node, prefix, property, value); if (!success) { // Try parsing in the format Value 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 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( new DeserializerImpl(child_node)); // NOLINT } std::unique_ptr 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 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( 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* 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* 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* 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* 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* 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