summaryrefslogtreecommitdiff
path: root/internal/xmpmeta/xml/deserializer_impl.cc
diff options
context:
space:
mode:
authorEino-Ville Talvala <etalvala@google.com>2018-11-15 15:49:02 -0800
committerEino-Ville Talvala <etalvala@google.com>2018-11-15 16:07:24 -0800
commit09f199a694ef5b956cabc368e40ab5ca11c64044 (patch)
tree456d184a3817c8b6524a90fc2da60893a2fc1895 /internal/xmpmeta/xml/deserializer_impl.cc
parent2d25fc4f6a7e7453f877958a2f3f59b6cc588ca4 (diff)
downloaddynamic_depth-09f199a694ef5b956cabc368e40ab5ca11c64044.tar.gz
Initial commit of libdynamic_depth
Dynamic depth is a standard for embedding depth maps and other similar extensions into standard image files like JPEG. Test: m libdynamic_depth Bug: 109735087 Bug: 119211681 Change-Id: I0103b7d47e60dc8e3a3b277456903d76f727926f
Diffstat (limited to 'internal/xmpmeta/xml/deserializer_impl.cc')
-rw-r--r--internal/xmpmeta/xml/deserializer_impl.cc321
1 files changed, 321 insertions, 0 deletions
diff --git a/internal/xmpmeta/xml/deserializer_impl.cc b/internal/xmpmeta/xml/deserializer_impl.cc
new file mode 100644
index 0000000..6214a0c
--- /dev/null
+++ b/internal/xmpmeta/xml/deserializer_impl.cc
@@ -0,0 +1,321 @@
+#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 photos_editing_formats {
+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;
+ }
+ }
+ LOG(WARNING) << "Could not find string attribute: " << property;
+ 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 photos_editing_formats