Add object/interface update methods

Add methods that handle both adding/updating
objects/interfaces.

Change-Id: I3c682365aad18f439babfb0413f29af05327eead
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
diff --git a/Makefile.am b/Makefile.am
index 1243e21..1e7ed65 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -16,8 +16,8 @@
 	errors.cpp \
 	functor.cpp \
 	manager.cpp
-libmanagercommon_la_LIBADD = $(SDBUSPLUS_LIBS) $(PHOSPHOR_DBUS_INTERFACES_LIBS)
-libmanagercommon_la_CFLAGS = $(SDBUSPLUS_CFLAGS) $(PHOSPHOR_DBUS_INTERACES_CFLAGS)
+libmanagercommon_la_LIBADD = $(SDBUSPLUS_LIBS) $(PHOSPHOR_DBUS_INTERFACES_LIBS) $(PHOSPHOR_LOGGING_LIBS)
+libmanagercommon_la_CFLAGS = $(SDBUSPLUS_CFLAGS) $(PHOSPHOR_DBUS_INTERACES_CFLAGS) $(PHOSPHOR_LOGGING_CFLAGS)
 
 libmanager_la_SOURCES = \
 	extra_ifaces.cpp \
diff --git a/configure.ac b/configure.ac
index 83d063e..47d70d4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -21,10 +21,9 @@
 
 # Checks for libraries.
 PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221])
-PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus],,
-    AC_MSG_ERROR(["Requires sdbusplus package."]))
-PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [phosphor-dbus-interfaces],,
-    AC_MSG_ERROR(["Requires phosphor-dbus-interfaces package."]))
+PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus],, AC_MSG_ERROR(["Requires sdbusplus package."]))
+PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [phosphor-dbus-interfaces],, AC_MSG_ERROR(["Requires phosphor-dbus-interfaces package."]))
+PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging],, AC_MSG_ERROR(["Requires phosphor-logging."]))
 
 # Checks for typedefs, structures, and compiler characteristics.
 AX_CXX_COMPILE_STDCXX_14([noext])
diff --git a/generated.mako.cpp b/generated.mako.cpp
index 235e58a..7a9a38e 100644
--- a/generated.mako.cpp
+++ b/generated.mako.cpp
@@ -24,7 +24,10 @@
         std::make_tuple(
             MakeInterface<
                 ServerObject<
-                    ${i.namespace()}>>::make
+                    ${i.namespace()}>>::make,
+            MakeInterface<
+                ServerObject<
+                    ${i.namespace()}>>::assign
         )
     },
 % endfor
diff --git a/manager.cpp b/manager.cpp
index 0c6831e..820eb62 100644
--- a/manager.cpp
+++ b/manager.cpp
@@ -16,8 +16,10 @@
 #include <iostream>
 #include <exception>
 #include <chrono>
+#include <algorithm>
 #include <log.hpp>
 #include "manager.hpp"
+#include "errors.hpp"
 
 using namespace std::literals::chrono_literals;
 
@@ -140,6 +142,122 @@
     }
 }
 
+void Manager::updateInterfaces(
+    const sdbusplus::message::object_path& path,
+    const Object& interfaces,
+    ObjectReferences::iterator pos,
+    bool newObject)
+{
+    auto& refaces = pos->second;
+    auto ifaceit = interfaces.cbegin();
+    auto opsit = _makers.cbegin();
+    auto refaceit = refaces.begin();
+    std::vector<std::string> signals;
+
+    while (ifaceit != interfaces.cend())
+    {
+        try
+        {
+            // Find the binding ops for this interface.
+            opsit = std::lower_bound(
+                        opsit,
+                        _makers.cend(),
+                        ifaceit->first,
+                        compareFirst(_makers.key_comp()));
+
+            if (opsit == _makers.cend() || opsit->first != ifaceit->first)
+            {
+                // This interface is not supported.
+                throw InterfaceError(
+                    "Encountered unsupported interface.",
+                    ifaceit->first);
+            }
+
+            // Find the binding insertion point or the binding to update.
+            refaceit = std::lower_bound(
+                           refaceit,
+                           refaces.end(),
+                           ifaceit->first,
+                           compareFirst(refaces.key_comp()));
+
+            if (refaceit == refaces.end() || refaceit->first != ifaceit->first)
+            {
+                // Add the new interface.
+                auto& ctor = std::get<MakerType>(opsit->second);
+                refaceit = refaces.insert(
+                               refaceit,
+                               std::make_pair(
+                                   ifaceit->first,
+                                   ctor(
+                                       _bus,
+                                       path.str.c_str(),
+                                       ifaceit->second)));
+                signals.push_back(ifaceit->first);
+            }
+            else
+            {
+                // Set the new property values.
+                auto& assign = std::get<AssignerType>(opsit->second);
+                assign(ifaceit->second, refaceit->second);
+            }
+        }
+        catch (const InterfaceError& e)
+        {
+            // Reset the binding ops iterator since we are
+            // at the end.
+            opsit = _makers.cbegin();
+            e.log();
+        }
+
+        ++ifaceit;
+    }
+
+    if (newObject)
+    {
+        _bus.emit_object_added(path.str.c_str());
+    }
+    else if (!signals.empty())
+    {
+        // TODO: emit an interfaces added signal
+    }
+}
+
+void Manager::updateObjects(
+    const std::map<sdbusplus::message::object_path, Object>& objs)
+{
+    auto objit = objs.cbegin();
+    auto refit = _refs.begin();
+    std::string absPath;
+    bool newObj;
+
+    while (objit != objs.cend())
+    {
+        // Find the insertion point or the object to update.
+        refit = std::lower_bound(
+                    refit,
+                    _refs.end(),
+                    objit->first,
+                    compareFirst(RelPathCompare(_root)));
+
+        absPath.assign(_root);
+        absPath.append(objit->first);
+
+        newObj = false;
+        if (refit == _refs.end() || refit->first != absPath)
+        {
+            refit = _refs.insert(
+                        refit,
+                        std::make_pair(
+                            absPath,
+                            decltype(_refs)::mapped_type()));
+            newObj = true;
+        }
+
+        updateInterfaces(absPath, objit->second, refit, newObj);
+        ++objit;
+    }
+}
+
 void Manager::notify(std::map<sdbusplus::message::object_path, Object> objs)
 {
     updateObjects(objs);
diff --git a/manager.hpp b/manager.hpp
index 797880b..11750b8 100644
--- a/manager.hpp
+++ b/manager.hpp
@@ -23,6 +23,27 @@
 using ManagerIface =
     sdbusplus::xyz::openbmc_project::Inventory::server::Manager;
 
+/** @struct PropertiesVariant
+ *  @brief Wrapper for sdbusplus PropertiesVariant.
+ *
+ *  A wrapper is useful since MakeInterface is instantiated with 'int'
+ *  to deduce the return type of its methods, which does not depend
+ *  on T.
+ *
+ *  @tparam T - The sdbusplus server binding type.
+ */
+template <typename T, typename Enable = void>
+struct PropertiesVariant {};
+
+template <typename T>
+struct PropertiesVariant<T, typename std::enable_if<std::is_object<T>::value>::type>
+{
+    using Type = typename T::PropertiesVariant;
+};
+
+template <typename T>
+using PropertiesVariantType = typename PropertiesVariant<T>::Type;
+
 /** @struct MakeInterface
  *  @brief Adapt an sdbusplus interface proxy.
  *
@@ -31,6 +52,7 @@
  *
  *  @tparam T - The type of the interface being adapted.
  */
+
 template <typename T>
 struct MakeInterface
 {
@@ -39,9 +61,8 @@
         const char* path,
         const Interface& props)
     {
-        using PropertiesVariant = typename T::PropertiesVariant;
         using InterfaceVariant =
-            std::map<std::string, PropertiesVariant>;
+            std::map<std::string, PropertiesVariantType<T>>;
 
         InterfaceVariant v;
 
@@ -49,11 +70,21 @@
         {
             v.emplace(
                 p.first,
-                convertVariant<PropertiesVariant>(p.second));
+                convertVariant<PropertiesVariantType<T>>(p.second));
         }
 
         return any_ns::any(std::make_shared<T>(bus, path, v));
     }
+
+    static void assign(const Interface& props, any_ns::any& holder)
+    {
+        auto& iface = *any_ns::any_cast<std::shared_ptr<T> &>(holder);
+        for (const auto& p : props)
+        {
+            iface.setPropertyByName(
+                p.first, convertVariant<PropertiesVariantType<T>>(p.second));
+        }
+    }
 };
 
 /** @class Manager
@@ -110,6 +141,10 @@
         void createObjects(
             const std::map<sdbusplus::message::object_path, Object>& objs);
 
+        /** @brief Add or update objects on DBus. */
+        void updateObjects(
+            const std::map<sdbusplus::message::object_path, Object>& objs);
+
         /** @brief Invoke an sdbusplus server binding method.
          *
          *  Invoke the requested method with a reference to the requested
@@ -149,11 +184,13 @@
         using ObjectReferences = std::map<std::string, InterfaceComposite>;
         using Events = std::vector<EventInfo>;
 
-        // The int instantiation is safe since the signature of these
+        // The int instantiations are safe since the signature of these
         // functions don't change from one instantiation to the next.
         using MakerType = std::add_pointer_t <
                           decltype(MakeInterface<int>::make) >;
-        using Makers = std::map<std::string, std::tuple<MakerType>>;
+        using AssignerType = std::add_pointer_t <
+                             decltype(MakeInterface<int>::assign) >;
+        using Makers = std::map<std::string, std::tuple<MakerType, AssignerType>>;
 
         /** @brief Provides weak references to interface holders.
          *
@@ -195,6 +232,13 @@
             return *any_ns::any_cast<T>(holder);
         }
 
+        /** @brief Add or update interfaces on DBus. */
+        void updateInterfaces(
+            const sdbusplus::message::object_path& path,
+            const Object& interfaces,
+            ObjectReferences::iterator pos,
+            bool emitSignals = true);
+
         /** @brief Provided for testing only. */
         volatile bool _shutdown;