Allow optional elements and attributes.

Per the document, allowed optional elements and attributes are:

* compatibility-matrix.hal.format
* compatibility-matrix.hal.optional
* manifest.hal.format
* manifest.hal.impl

This change is needed to support system/manifest.xml because
manifest.hal.impl is missing.

Test: `adb shell vintf` correctly shows system/manifest.xml
Test: libvintf
Change-Id: Ia54842d128ba1bc614c29f3b364642393d800086
diff --git a/parse_xml.cpp b/parse_xml.cpp
index c054350..8a3f69a 100644
--- a/parse_xml.cpp
+++ b/parse_xml.cpp
@@ -111,6 +111,19 @@
 
 // --------------- tinyxml2 details end.
 
+// Helper functions for XmlConverter
+static bool parse(const std::string &attrText, bool *attr) {
+    if (attrText == "true" || attrText == "1") {
+        *attr = true;
+        return true;
+    }
+    if (attrText == "false" || attrText == "0") {
+        *attr = false;
+        return true;
+    }
+    return false;
+}
+
 // ---------------------- XmlNodeConverter definitions
 
 template<typename Object>
@@ -164,6 +177,8 @@
     }
 
     // convenience methods for implementor.
+
+    // All append* functions helps mutateNode() to serialize the object into XML.
     template <typename T>
     inline void appendAttr(NodeType *e, const std::string &attrName, const T &attr) const {
         return appendStrAttr(e, attrName, ::android::vintf::to_string(attr));
@@ -200,6 +215,21 @@
         }
     }
 
+    // All parse* functions helps buildObject() to deserialize XML to the object. Returns
+    // true if deserialization is successful, false if any error, and mLastError will be
+    // set to error message.
+    template <typename T>
+    inline bool parseOptionalAttr(NodeType *root, const std::string &attrName,
+            T &&defaultValue, T *attr) const {
+        std::string attrText;
+        bool success = getAttr(root, attrName, &attrText) &&
+                       ::android::vintf::parse(attrText, attr);
+        if (!success) {
+            *attr = std::move(defaultValue);
+        }
+        return true;
+    }
+
     template <typename T>
     inline bool parseAttr(NodeType *root, const std::string &attrName, T *attr) const {
         std::string attrText;
@@ -218,25 +248,6 @@
         return ret;
     }
 
-    inline bool parseAttr(NodeType *root, const std::string &attrName, bool *attr) const {
-        std::string attrText;
-        if (!getAttr(root, attrName, &attrText)) {
-            mLastError = "Could not find attr with name " + attrName;
-            return false;
-        }
-        if (attrText == "true" || attrText == "1") {
-            *attr = true;
-            return true;
-        }
-        if (attrText == "false" || attrText == "0") {
-            *attr = false;
-            return true;
-        }
-        mLastError = "Could not parse attr with name \"" + attrName
-                + "\" and value \"" + attrText + "\"";
-        return false;
-    }
-
     inline bool parseTextElement(NodeType *root,
             const std::string &elementName, std::string *s) const {
         NodeType *child = getChild(root, elementName);
@@ -273,6 +284,21 @@
     }
 
     template <typename T>
+    inline bool parseOptionalChild(NodeType *root, const XmlNodeConverter<T> &conv,
+            T &&defaultValue, T *t) const {
+        NodeType *child = getChild(root, conv.elementName());
+        if (child == nullptr) {
+            *t = std::move(defaultValue);
+            return true;
+        }
+        bool success = conv.deserialize(t, child);
+        if (!success) {
+            mLastError = conv.lastError();
+        }
+        return success;
+    }
+
+    template <typename T>
     inline bool parseChildren(NodeType *root, const XmlNodeConverter<T> &conv, std::vector<T> *v) const {
         auto nodes = getChildren(root, conv.elementName());
         v->resize(nodes.size());
@@ -373,8 +399,8 @@
         appendChildren(root, versionRangeConverter, hal.versionRanges, d);
     }
     bool buildObject(MatrixHal *object, NodeType *root) const override {
-        if (!parseAttr(root, "format", &object->format) ||
-            !parseAttr(root, "optional", &object->optional) ||
+        if (!parseOptionalAttr(root, "format", HalFormat::HIDL, &object->format) ||
+            !parseOptionalAttr(root, "optional", false /* defaultValue */, &object->optional) ||
             !parseTextElement(root, "name", &object->name) ||
             !parseChildren(root, versionRangeConverter, &object->versionRanges)) {
             return false;
@@ -455,10 +481,10 @@
     }
     bool buildObject(ManifestHal *object, NodeType *root) const override {
         std::vector<ManifestHalInterface> interfaces;
-        if (!parseAttr(root, "format", &object->format) ||
+        if (!parseOptionalAttr(root, "format", HalFormat::HIDL, &object->format) ||
             !parseTextElement(root, "name", &object->name) ||
             !parseChild(root, transportConverter, &object->transport) ||
-            !parseChild(root, halImplementationConverter, &object->impl) ||
+            !parseOptionalChild(root, halImplementationConverter, {}, &object->impl) ||
             !parseChildren(root, versionConverter, &object->versions) ||
             !parseChildren(root, manfiestHalInterfaceConverter, &interfaces)) {
             return false;