Add support for legacy state/commandDefs to ComponentManager

Added methods to convert legacy state/stateDefs/commandDefs to
the new trait definitions and components and functions to convert
the trait definitions/component tree back to old commandDefs and
state.

Change-Id: Ia03142c53d00bbc4f880389166982167e3c8b1e9
Reviewed-on: https://weave-review.googlesource.com/1787
Reviewed-by: Vitaly Buka <vitalybuka@google.com>
diff --git a/src/component_manager_impl.cc b/src/component_manager_impl.cc
index 8cfa95e..1883739 100644
--- a/src/component_manager_impl.cc
+++ b/src/component_manager_impl.cc
@@ -448,6 +448,142 @@
   return std::string{};
 }
 
+bool ComponentManagerImpl::AddLegacyCommandDefinitions(
+    const base::DictionaryValue& dict,
+    ErrorPtr* error) {
+  bool result = true;
+  bool modified = false;
+  for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
+    const base::DictionaryValue* command_dict = nullptr;
+    if (!it.value().GetAsDictionary(&command_dict)) {
+      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                         errors::commands::kTypeMismatch,
+                         "Package '%s' must be an object", it.key().c_str());
+      result = false;
+      continue;
+    }
+    AddTraitToLegacyComponent(it.key());
+    for (base::DictionaryValue::Iterator it_def(*command_dict);
+         !it_def.IsAtEnd(); it_def.Advance()) {
+      std::string key = base::StringPrintf("%s.commands.%s", it.key().c_str(),
+                                           it_def.key().c_str());
+      if (traits_.GetDictionary(key, nullptr)) {
+        Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                           errors::commands::kInvalidPropValue,
+                           "Redefining command '%s.%s'",
+                           it.key().c_str(), it_def.key().c_str());
+        result = false;
+        continue;
+      }
+      traits_.Set(key, it_def.value().DeepCopy());
+      modified = true;
+    }
+  }
+
+  if (modified) {
+    for (const auto& cb : on_trait_changed_)
+      cb.Run();
+  }
+  return result;
+}
+
+bool ComponentManagerImpl::AddLegacyStateDefinitions(
+    const base::DictionaryValue& dict,
+    ErrorPtr* error) {
+  bool result = true;
+  bool modified = false;
+  for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
+    const base::DictionaryValue* state_dict = nullptr;
+    if (!it.value().GetAsDictionary(&state_dict)) {
+      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                         errors::commands::kTypeMismatch,
+                         "Package '%s' must be an object", it.key().c_str());
+      result = false;
+      continue;
+    }
+    AddTraitToLegacyComponent(it.key());
+    for (base::DictionaryValue::Iterator it_def(*state_dict); !it_def.IsAtEnd();
+         it_def.Advance()) {
+      std::string key = base::StringPrintf("%s.state.%s", it.key().c_str(),
+                                           it_def.key().c_str());
+      if (traits_.GetDictionary(key, nullptr)) {
+        Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                           errors::commands::kInvalidPropValue,
+                           "Redefining state property '%s.%s'",
+                           it.key().c_str(), it_def.key().c_str());
+        result = false;
+        continue;
+      }
+      traits_.Set(key, it_def.value().DeepCopy());
+      modified = true;
+    }
+  }
+
+  if (modified) {
+    for (const auto& cb : on_trait_changed_)
+      cb.Run();
+  }
+  return result;
+}
+
+const base::DictionaryValue& ComponentManagerImpl::GetLegacyState() const {
+  legacy_state_.Clear();
+  // Build state from components.
+  for (base::DictionaryValue::Iterator it(components_); !it.IsAtEnd();
+       it.Advance()) {
+    const base::DictionaryValue* component_dict = nullptr;
+    const base::DictionaryValue* component_state = nullptr;
+    if (it.value().GetAsDictionary(&component_dict) &&
+        component_dict->GetDictionary("state", &component_state)) {
+      legacy_state_.MergeDictionary(component_state);
+    }
+  }
+  return legacy_state_;
+}
+
+const base::DictionaryValue&
+ComponentManagerImpl::GetLegacyCommandDefinitions() const {
+  legacy_command_defs_.Clear();
+  // Build commandDefs from traits.
+  for (base::DictionaryValue::Iterator it(traits_); !it.IsAtEnd();
+       it.Advance()) {
+    const base::DictionaryValue* trait_dict = nullptr;
+    const base::DictionaryValue* trait_commands = nullptr;
+    if (it.value().GetAsDictionary(&trait_dict) &&
+        trait_dict->GetDictionary("commands", &trait_commands)) {
+      base::DictionaryValue dict;
+      dict.Set(it.key(), trait_commands->DeepCopy());
+      legacy_command_defs_.MergeDictionary(&dict);
+    }
+  }
+  return legacy_command_defs_;
+}
+
+void ComponentManagerImpl::AddTraitToLegacyComponent(const std::string& trait) {
+  // First check if we already have a component supporting this trait.
+  if (!FindComponentWithTrait(trait).empty())
+    return;
+
+  // If not, add this trait to the first component available.
+  base::DictionaryValue* component = nullptr;
+  base::DictionaryValue::Iterator it(components_);
+  if (it.IsAtEnd()) {
+    // No components at all. Create a new one with dummy name.
+    // This normally wouldn't happen since libweave creates its own component
+    // at startup.
+    component = new base::DictionaryValue;
+    components_.Set("__weave__", component);
+  } else {
+    CHECK(components_.GetDictionary(it.key(), &component));
+  }
+  base::ListValue* traits = nullptr;
+  if (!component->GetList("traits", &traits)) {
+    traits = new base::ListValue;
+    component->Set("traits", traits);
+  }
+  traits->AppendString(trait);
+}
+
 base::DictionaryValue* ComponentManagerImpl::FindComponentGraftNode(
     const std::string& path, ErrorPtr* error) {
   base::DictionaryValue* root = nullptr;