VendorManifest and CompatibilityMatrix from and to XML.

Usage:

    #include <vintf/VendorManifest.h>
    #include <vintf/parse_xml.h>
    VendorManifest vm;
    // ...
    std::string xml = gVendorManifestConverter(vm);
    VendorManifest vm2;
    bool success = gVendorManifestConverter(&vm2, xml);
    if (!success)
        ALOGW("%s", gVendorManifestConverter.lastError());

See Vendor Interface Object and Compatibility Matrix Design
document and libvintf_test (test/main.cpp) for details.
(Up to commit 4468534)

Test: libvintf_test

Bug: 32648352

Change-Id: I7e8ee8d2b9e61bf036e77a390712480cbbd452b4
diff --git a/parse_xml.cpp b/parse_xml.cpp
new file mode 100644
index 0000000..a8be3bb
--- /dev/null
+++ b/parse_xml.cpp
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Convert objects from and to xml.
+
+#include <tinyxml2.h>
+
+#include "parse_string.h"
+#include "parse_xml.h"
+
+namespace android {
+namespace vintf {
+
+// --------------- tinyxml2 details
+
+using NodeType = tinyxml2::XMLElement;
+using DocType = tinyxml2::XMLDocument;
+
+// caller is responsible for deleteDocument() call
+inline DocType *createDocument() {
+    return new tinyxml2::XMLDocument();
+}
+
+// caller is responsible for deleteDocument() call
+inline DocType *createDocument(const std::string &xml) {
+    DocType *doc = new tinyxml2::XMLDocument();
+    if (doc->Parse(xml.c_str()) == tinyxml2::XML_NO_ERROR) {
+        return doc;
+    }
+    delete doc;
+    return nullptr;
+}
+
+inline void deleteDocument(DocType *d) {
+    delete d;
+}
+
+inline std::string printDocument(DocType *d) {
+    tinyxml2::XMLPrinter p;
+    d->Print(&p);
+    return std::string{p.CStr()};
+}
+
+inline NodeType *createNode(const std::string &name, DocType *d) {
+    return d->NewElement(name.c_str());
+}
+
+inline void appendChild(NodeType *parent, NodeType *child) {
+    parent->InsertEndChild(child);
+}
+
+inline void appendChild(DocType *parent, NodeType *child) {
+    parent->InsertEndChild(child);
+}
+
+inline void appendStrAttr(NodeType *e, const std::string &attrName, const std::string &attr) {
+    e->SetAttribute(attrName.c_str(), attr.c_str());
+}
+
+// text -> text
+inline void appendText(NodeType *parent, const std::string &text, DocType *d) {
+    parent->InsertEndChild(d->NewText(text.c_str()));
+}
+
+// text -> <name>text</name>
+inline void appendTextElement(NodeType *parent, const std::string &name,
+            const std::string &text, DocType *d) {
+    NodeType *c = createNode(name, d);
+    appendText(c, text, d);
+    appendChild(parent, c);
+}
+
+inline std::string nameOf(NodeType *root) {
+    return root->Name();
+}
+
+inline std::string getText(NodeType *root) {
+    return root->GetText();
+}
+
+inline NodeType *getChild(NodeType *parent, const std::string &name) {
+    return parent->FirstChildElement(name.c_str());
+}
+
+inline NodeType *getRootChild(DocType *parent) {
+    return parent->FirstChildElement();
+}
+
+inline std::vector<NodeType *> getChildren(NodeType *parent, const std::string &name) {
+    std::vector<NodeType *> v;
+    for (NodeType *child = parent->FirstChildElement(name.c_str());
+         child != nullptr;
+         child = child->NextSiblingElement(name.c_str())) {
+        v.push_back(child);
+    }
+    return v;
+}
+
+inline bool getAttr(NodeType *root, const std::string &attrName, std::string *s) {
+    const char *c = root->Attribute(attrName.c_str());
+    if (c == NULL)
+        return false;
+    *s = c;
+    return true;
+}
+
+// --------------- tinyxml2 details end.
+
+// ---------------------- XmlNodeConverter definitions
+
+template<typename Object>
+struct XmlNodeConverter : public XmlConverter<Object> {
+    XmlNodeConverter() {}
+    virtual ~XmlNodeConverter() {}
+
+    // sub-types should implement these.
+    virtual void mutateNode(const Object &o, NodeType *n, DocType *d) const = 0;
+    virtual bool buildObject(Object *o, NodeType *n) const = 0;
+    virtual std::string elementName() const = 0;
+
+    // convenience methods for user
+    inline const std::string &lastError() const { return mLastError; }
+    inline NodeType *serialize(const Object &o, DocType *d) const {
+        NodeType *root = createNode(this->elementName(), d);
+        this->mutateNode(o, root, d);
+        return root;
+    }
+    inline std::string serialize(const Object &o) const {
+        DocType *doc = createDocument();
+        appendChild(doc, serialize(o, doc));
+        std::string s = printDocument(doc);
+        deleteDocument(doc);
+        return s;
+    }
+    inline bool deserialize(Object *object, NodeType *root) const {
+        if (nameOf(root) != this->elementName()) {
+            return false;
+        }
+        return this->buildObject(object, root);
+    }
+    inline bool deserialize(Object *o, const std::string &xml) const {
+        DocType *doc = createDocument(xml);
+        bool ret = doc != nullptr
+            && deserialize(o, getRootChild(doc));
+        deleteDocument(doc);
+        return ret;
+    }
+    inline NodeType *operator()(const Object &o, DocType *d) const {
+        return serialize(o, d);
+    }
+    inline std::string operator()(const Object &o) const {
+        return serialize(o);
+    }
+    inline bool operator()(Object *o, NodeType *node) const {
+        return deserialize(o, node);
+    }
+    inline bool operator()(Object *o, const std::string &xml) const {
+        return deserialize(o, xml);
+    }
+
+    // convenience methods for implementor.
+    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));
+    }
+
+    inline void appendAttr(NodeType *e, const std::string &attrName, bool attr) const {
+        return appendStrAttr(e, attrName, attr ? "true" : "false");
+    }
+
+    template <typename T>
+    inline bool parseAttr(NodeType *root, const std::string &attrName, T *attr) const {
+        std::string attrText;
+        bool ret = getAttr(root, attrName, &attrText) && ::android::vintf::parse(attrText, attr);
+        if (!ret) {
+            mLastError = "Could not parse attr with name " + attrName;
+        }
+        return ret;
+    }
+
+    inline bool parseAttr(NodeType *root, const std::string &attrName, std::string *attr) const {
+        bool ret = getAttr(root, attrName, attr);
+        if (!ret) {
+            mLastError = "Could not find attr with name " + attrName;
+        }
+        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);
+        if (child == nullptr) {
+            mLastError = "Could not find element with name " + elementName;
+            return false;
+        }
+        *s = getText(child);
+        return true;
+    }
+
+    template <typename T>
+    inline bool parseChild(NodeType *root, const XmlNodeConverter<T> &conv, T *t) const {
+        NodeType *child = getChild(root, conv.elementName());
+        if (child == nullptr) {
+            mLastError = "Could not find element with name " + conv.elementName();
+            return false;
+        }
+        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());
+        for (size_t i = 0; i < nodes.size(); ++i) {
+            if (!conv.deserialize(&v->at(i), nodes[i])) {
+                mLastError = "Could not parse element with name " + conv.elementName();
+                return false;
+            }
+        }
+        return true;
+    }
+
+    inline bool parseText(NodeType *node, std::string *s) const {
+        *s = getText(node);
+        return true;
+    }
+
+protected:
+    mutable std::string mLastError;
+};
+
+template<typename Object>
+struct XmlTextConverter : public XmlNodeConverter<Object> {
+    XmlTextConverter(const std::string &elementName)
+        : mElementName(elementName) {}
+
+    virtual void mutateNode(const Object &object, NodeType *root, DocType *d) const override {
+        appendText(root, ::android::vintf::to_string(object), d);
+    }
+    virtual bool buildObject(Object *object, NodeType *root) const override {
+        std::string text = getText(root);
+        bool ret = ::android::vintf::parse(text, object);
+        if (!ret) {
+            this->mLastError = "Could not parse " + text;
+        }
+        return ret;
+    }
+    virtual std::string elementName() const { return mElementName; };
+private:
+    std::string mElementName;
+};
+
+// ---------------------- XmlNodeConverter definitions end
+
+const XmlTextConverter<Version> versionConverter{"version"};
+const XmlConverter<Version> &gVersionConverter = versionConverter;
+
+const XmlTextConverter<VersionRange> versionRangeConverter{"version"};
+const XmlConverter<VersionRange> &gVersionRangeConverter = versionRangeConverter;
+
+const XmlTextConverter<KernelConfig> kernelConfigConverter{"config"};
+const XmlConverter<KernelConfig> &gKernelConfigConverter = kernelConfigConverter;
+
+const XmlTextConverter<Transport> transportConverter{"transport"};
+const XmlConverter<Transport> &gTransportConverter = transportConverter;
+
+class MatrixHalConverter : public XmlNodeConverter<MatrixHal> {
+    std::string elementName() const override { return "hal"; }
+    void mutateNode(const MatrixHal &hal, NodeType *root, DocType *d) const override {
+        appendAttr(root, "format", hal.format);
+        appendAttr(root, "optional", hal.optional);
+        appendTextElement(root, "name", hal.name, d);
+        for (const auto &version : hal.versionRanges) {
+            appendChild(root, versionRangeConverter(version, d));
+        }
+    }
+    bool buildObject(MatrixHal *object, NodeType *root) const override {
+        if (   !parseAttr(root, "format", &object->format)
+            || !parseAttr(root, "optional", &object->optional)
+            || !parseTextElement(root, "name", &object->name)
+            || !parseChildren(root, versionRangeConverter, &object->versionRanges)) {
+            return false;
+        }
+        return true;
+    }
+};
+
+const MatrixHalConverter matrixHalConverter{};
+const XmlConverter<MatrixHal> &gMatrixHalConverter = matrixHalConverter;
+
+class MatrixKernelConverter : public XmlNodeConverter<MatrixKernel> {
+    std::string elementName() const override { return "kernel"; }
+    void mutateNode(const MatrixKernel &kernel, NodeType *root, DocType *d) const override {
+        appendAttr(root, "version", kernel.version);
+        for (const auto &config : kernel.configs) {
+            appendChild(root, kernelConfigConverter(config, d));
+        }
+    }
+    bool buildObject(MatrixKernel *object, NodeType *root) const override {
+        if (   !parseAttr(root, "version", &object->version)
+            || !parseChildren(root, kernelConfigConverter, &object->configs)) {
+            return false;
+        }
+        return true;
+    }
+};
+
+const MatrixKernelConverter matrixKernelConverter{};
+const XmlConverter<MatrixKernel> &gMatrixKernelConverter = matrixKernelConverter;
+
+class HalImplementationConverter : public XmlNodeConverter<HalImplementation> {
+    std::string elementName() const override { return "impl"; }
+    void mutateNode(const HalImplementation &impl, NodeType *root, DocType *d) const override {
+        appendAttr(root, "level", impl.implLevel);
+        appendText(root, impl.impl, d);
+    }
+    bool buildObject(HalImplementation *object, NodeType *root) const override {
+        if (   !parseAttr(root, "level", &object->implLevel)
+            || !parseText(root, &object->impl)) {
+            return false;
+        }
+        return true;
+    }
+};
+
+const HalImplementationConverter halImplementationConverter{};
+const XmlConverter<HalImplementation> &gHalImplementationConverter = halImplementationConverter;
+
+class ManifestHalConverter : public XmlNodeConverter<ManifestHal> {
+    std::string elementName() const override { return "hal"; }
+    void mutateNode(const ManifestHal &hal, NodeType *root, DocType *d) const override {
+        appendAttr(root, "format", hal.format);
+        appendTextElement(root, "name", hal.name, d);
+        appendChild(root, transportConverter(hal.transport, d));
+        appendChild(root,
+            halImplementationConverter(hal.impl, d));
+        for (const auto &version : hal.versions) {
+            appendChild(root, versionConverter(version, d));
+        }
+    }
+    bool buildObject(ManifestHal *object, NodeType *root) const override {
+        if (   !parseAttr(root, "format", &object->format)
+            || !parseTextElement(root, "name", &object->name)
+            || !parseChild(root, transportConverter, &object->transport)
+            || !parseChild(root, halImplementationConverter, &object->impl)
+            || !parseChildren(root, versionConverter, &object->versions)) {
+            return false;
+        }
+        return true;
+    }
+};
+
+const ManifestHalConverter manifestHalConverter{};
+const XmlConverter<ManifestHal> &gManifestHalConverter = manifestHalConverter;
+
+class SepolicyConverter : public XmlNodeConverter<Sepolicy> {
+    std::string elementName() const override { return "sepolicy"; }
+    void mutateNode(const Sepolicy &, NodeType *, DocType *) const override {
+        // TODO after writing sepolicy
+    }
+    bool buildObject(Sepolicy *, NodeType *) const override {
+        // TODO after writing sepolicy
+        return true;
+    }
+};
+const SepolicyConverter sepolicyConverter{};
+const XmlConverter<Sepolicy> &gSepolicyConverter = sepolicyConverter;
+
+class VendorManifestConverter : public XmlNodeConverter<VendorManifest> {
+    std::string elementName() const override { return "manifest"; }
+    void mutateNode(const VendorManifest &m, NodeType *root, DocType *d) const override {
+        appendAttr(root, "version", VendorManifest::kVersion);
+        for (const auto &pair : m.hals) {
+            appendChild(root, manifestHalConverter(pair.second, d));
+        }
+    }
+    bool buildObject(VendorManifest *object, NodeType *root) const override {
+        std::vector<ManifestHal> hals;
+        if (!parseChildren(root, manifestHalConverter, &hals)) {
+            return false;
+        }
+        for (auto &&hal : hals) {
+            object->add(std::move(hal));
+        }
+        return true;
+    }
+};
+
+const VendorManifestConverter vendorManifestConverter{};
+const XmlConverter<VendorManifest> &gVendorManifestConverter = vendorManifestConverter;
+
+class CompatibilityMatrixConverter : public XmlNodeConverter<CompatibilityMatrix> {
+    std::string elementName() const override { return "compatibility-matrix"; }
+    void mutateNode(const CompatibilityMatrix &m, NodeType *root, DocType *d) const override {
+        appendAttr(root, "version", CompatibilityMatrix::kVersion);
+        for (const auto &pair : m.hals) {
+            appendChild(root, matrixHalConverter(pair.second, d));
+        }
+        for (const auto &kernel : m.kernels) {
+            appendChild(root, matrixKernelConverter(kernel, d));
+        }
+        appendChild(root, sepolicyConverter(m.sepolicy, d));
+    }
+    bool buildObject(CompatibilityMatrix *object, NodeType *root) const override {
+        std::vector<MatrixHal> hals;
+        if (   !parseChildren(root, matrixHalConverter, &hals)
+            || !parseChildren(root, matrixKernelConverter, &object->kernels)
+            || !parseChild(root, sepolicyConverter, &object->sepolicy)) {
+            return false;
+        }
+        for (auto &&hal : hals) {
+            object->add(std::move(hal));
+        }
+        return true;
+    }
+};
+
+const CompatibilityMatrixConverter compatibilityMatrixConverter{};
+const XmlConverter<CompatibilityMatrix> &gCompatibilityMatrixConverter
+        = compatibilityMatrixConverter;
+
+} // namespace vintf
+} // namespace android