Load the associations definitions

Parse the JSON file to load in the _associations
data structures.  Any failures will cause an exception
to be thrown that will crash the app.

The JSON looks like:

[
    {
        "path": "The relative path of the inventory object to create the
                 org.openbmc.Associations interface on."
        "endpoints":
        [
            {
                "types":
                {
                    "fType": "The forward association type."
                    "rType": "The reverse association type."
                },
                "paths":
                [
                    "The list of association endpoints for this
                     inventory path and association type."
                ]
            }
        ]
    }
]

Change-Id: I098fdc607f0c3ab2861f9b33e3e0d46e4989bd7a
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/association_manager.cpp b/association_manager.cpp
index 41f135d..91bb309 100644
--- a/association_manager.cpp
+++ b/association_manager.cpp
@@ -1,5 +1,10 @@
 #include "association_manager.hpp"
 
+#include <filesystem>
+#include <fstream>
+#include <nlohmann/json.hpp>
+#include <phosphor-logging/log.hpp>
+
 namespace phosphor
 {
 namespace inventory
@@ -8,10 +13,69 @@
 {
 namespace associations
 {
+using namespace phosphor::logging;
+using sdbusplus::exception::SdBusError;
 
 Manager::Manager(sdbusplus::bus::bus& bus, const std::string& jsonPath) :
     _bus(bus), _jsonFile(jsonPath)
 {
+    load();
+}
+
+/**
+ * @brief Throws an exception if 'num' is zero. Used for JSON
+ *        sanity checking.
+ *
+ * @param[in] num - the number to check
+ */
+void throwIfZero(int num)
+{
+    if (!num)
+    {
+        throw std::invalid_argument("Invalid empty field in JSON");
+    }
+}
+
+void Manager::load()
+{
+    // Load the contents of _jsonFile into _associations and throw
+    // an exception on any problem.
+
+    std::ifstream file{_jsonFile};
+
+    auto json = nlohmann::json::parse(file, nullptr, true);
+
+    const std::string root{INVENTORY_ROOT};
+
+    for (const auto& jsonAssoc : json)
+    {
+        // Only add the slash if necessary
+        std::string path = jsonAssoc.at("path");
+        throwIfZero(path.size());
+        if (path.front() != '/')
+        {
+            path = root + "/" + path;
+        }
+        else
+        {
+            path = root + path;
+        }
+
+        auto& assocEndpoints = _associations[path];
+
+        for (const auto& endpoint : jsonAssoc.at("endpoints"))
+        {
+            std::string ftype = endpoint.at("types").at("fType");
+            std::string rtype = endpoint.at("types").at("rType");
+            throwIfZero(ftype.size());
+            throwIfZero(rtype.size());
+            Types types{std::move(ftype), std::move(rtype)};
+
+            Paths paths = endpoint.at("paths");
+            throwIfZero(paths.size());
+            assocEndpoints.emplace_back(std::move(types), std::move(paths));
+        }
+    }
 }
 
 void Manager::createAssociations(const std::string& objectPath)