|  | // Copyright 2015 The Weave Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "src/states/state_manager.h" | 
|  |  | 
|  | #include <base/logging.h> | 
|  | #include <base/values.h> | 
|  |  | 
|  | #include "src/json_error_codes.h" | 
|  | #include "src/states/error_codes.h" | 
|  | #include "src/states/state_change_queue_interface.h" | 
|  | #include "src/string_utils.h" | 
|  | #include "src/utils.h" | 
|  |  | 
|  | namespace weave { | 
|  |  | 
|  | StateManager::StateManager(StateChangeQueueInterface* state_change_queue) | 
|  | : state_change_queue_(state_change_queue) { | 
|  | CHECK(state_change_queue_) << "State change queue not specified"; | 
|  | } | 
|  |  | 
|  | StateManager::~StateManager() {} | 
|  |  | 
|  | void StateManager::AddChangedCallback(const base::Closure& callback) { | 
|  | on_changed_.push_back(callback); | 
|  | callback.Run();  // Force to read current state. | 
|  | } | 
|  |  | 
|  | std::unique_ptr<base::DictionaryValue> StateManager::GetState() const { | 
|  | std::unique_ptr<base::DictionaryValue> dict{new base::DictionaryValue}; | 
|  | for (const auto& pair : packages_) { | 
|  | auto pkg_value = pair.second->GetValuesAsJson(); | 
|  | CHECK(pkg_value); | 
|  | dict->SetWithoutPathExpansion(pair.first, pkg_value.release()); | 
|  | } | 
|  | return dict; | 
|  | } | 
|  |  | 
|  | bool StateManager::SetProperty(const std::string& name, | 
|  | const base::Value& value, | 
|  | ErrorPtr* error) { | 
|  | bool result = SetPropertyValue(name, value, base::Time::Now(), error); | 
|  | for (const auto& cb : on_changed_) | 
|  | cb.Run(); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<base::Value> StateManager::GetProperty( | 
|  | const std::string& name) const { | 
|  | auto parts = SplitAtFirst(name, ".", true); | 
|  | const std::string& package_name = parts.first; | 
|  | const std::string& property_name = parts.second; | 
|  | if (package_name.empty() || property_name.empty()) | 
|  | return nullptr; | 
|  |  | 
|  | const StatePackage* package = FindPackage(package_name); | 
|  | if (!package) | 
|  | return nullptr; | 
|  |  | 
|  | return package->GetPropertyValue(property_name, nullptr); | 
|  | } | 
|  |  | 
|  | bool StateManager::SetPropertyValue(const std::string& full_property_name, | 
|  | const base::Value& value, | 
|  | const base::Time& timestamp, | 
|  | ErrorPtr* error) { | 
|  | auto parts = SplitAtFirst(full_property_name, ".", true); | 
|  | const std::string& package_name = parts.first; | 
|  | const std::string& property_name = parts.second; | 
|  | const bool split = (full_property_name.find(".") != std::string::npos); | 
|  |  | 
|  | if (full_property_name.empty() || (split && property_name.empty())) { | 
|  | Error::AddTo(error, FROM_HERE, errors::state::kDomain, | 
|  | errors::state::kPropertyNameMissing, | 
|  | "Property name is missing"); | 
|  | return false; | 
|  | } | 
|  | if (!split || package_name.empty()) { | 
|  | Error::AddTo(error, FROM_HERE, errors::state::kDomain, | 
|  | errors::state::kPackageNameMissing, | 
|  | "Package name is missing in the property name"); | 
|  | return false; | 
|  | } | 
|  | StatePackage* package = FindPackage(package_name); | 
|  | if (package == nullptr) { | 
|  | Error::AddToPrintf(error, FROM_HERE, errors::state::kDomain, | 
|  | errors::state::kPropertyNotDefined, | 
|  | "Unknown state property package '%s'", | 
|  | package_name.c_str()); | 
|  | return false; | 
|  | } | 
|  | if (!package->SetPropertyValue(property_name, value, error)) | 
|  | return false; | 
|  |  | 
|  | ValueMap prop_set{{full_property_name, package->GetProperty(property_name)}}; | 
|  | state_change_queue_->NotifyPropertiesUpdated(timestamp, prop_set); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | std::pair<StateChangeQueueInterface::UpdateID, std::vector<StateChange>> | 
|  | StateManager::GetAndClearRecordedStateChanges() { | 
|  | return std::make_pair(state_change_queue_->GetLastStateChangeId(), | 
|  | state_change_queue_->GetAndClearRecordedStateChanges()); | 
|  | } | 
|  |  | 
|  | void StateManager::NotifyStateUpdatedOnServer( | 
|  | StateChangeQueueInterface::UpdateID id) { | 
|  | state_change_queue_->NotifyStateUpdatedOnServer(id); | 
|  | } | 
|  |  | 
|  | bool StateManager::LoadStateDefinition(const base::DictionaryValue& dict, | 
|  | ErrorPtr* error) { | 
|  | for (base::DictionaryValue::Iterator iter(dict); !iter.IsAtEnd(); | 
|  | iter.Advance()) { | 
|  | std::string package_name = iter.key(); | 
|  | if (package_name.empty()) { | 
|  | Error::AddTo(error, FROM_HERE, errors::kErrorDomain, | 
|  | errors::kInvalidPackageError, "State package name is empty"); | 
|  | return false; | 
|  | } | 
|  | const base::DictionaryValue* package_dict = nullptr; | 
|  | if (!iter.value().GetAsDictionary(&package_dict)) { | 
|  | Error::AddToPrintf(error, FROM_HERE, errors::json::kDomain, | 
|  | errors::json::kObjectExpected, | 
|  | "State package '%s' must be an object", | 
|  | package_name.c_str()); | 
|  | return false; | 
|  | } | 
|  | StatePackage* package = FindOrCreatePackage(package_name); | 
|  | CHECK(package) << "Unable to create state package " << package_name; | 
|  | if (!package->AddSchemaFromJson(package_dict, error)) | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool StateManager::LoadStateDefinitionFromJson(const std::string& json, | 
|  | ErrorPtr* error) { | 
|  | std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error); | 
|  | if (!dict) | 
|  | return false; | 
|  | if (!LoadStateDefinition(*dict, error)) { | 
|  | Error::AddToPrintf(error, FROM_HERE, errors::kErrorDomain, | 
|  | errors::kSchemaError, | 
|  | "Failed to load state definition: '%s'", json.c_str()); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool StateManager::SetProperties(const base::DictionaryValue& dict, | 
|  | ErrorPtr* error) { | 
|  | base::Time timestamp = base::Time::Now(); | 
|  | bool all_success = true; | 
|  | for (base::DictionaryValue::Iterator iter(dict); !iter.IsAtEnd(); | 
|  | iter.Advance()) { | 
|  | if (iter.key().empty()) { | 
|  | Error::AddTo(error, FROM_HERE, errors::kErrorDomain, | 
|  | errors::kInvalidPackageError, "State package name is empty"); | 
|  | all_success = false; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | const base::DictionaryValue* package_dict = nullptr; | 
|  | if (!iter.value().GetAsDictionary(&package_dict)) { | 
|  | Error::AddToPrintf(error, FROM_HERE, errors::json::kDomain, | 
|  | errors::json::kObjectExpected, | 
|  | "State package '%s' must be an object", | 
|  | iter.key().c_str()); | 
|  | all_success = false; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | for (base::DictionaryValue::Iterator it_prop(*package_dict); | 
|  | !it_prop.IsAtEnd(); it_prop.Advance()) { | 
|  | if (!SetPropertyValue(iter.key() + "." + it_prop.key(), it_prop.value(), | 
|  | timestamp, error)) { | 
|  | all_success = false; | 
|  | continue; | 
|  | } | 
|  | } | 
|  | } | 
|  | for (const auto& cb : on_changed_) | 
|  | cb.Run(); | 
|  | return all_success; | 
|  | } | 
|  |  | 
|  | bool StateManager::SetPropertiesFromJson(const std::string& json, | 
|  | ErrorPtr* error) { | 
|  | std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error); | 
|  | if (!dict) | 
|  | return false; | 
|  | if (!SetProperties(*dict, error)) { | 
|  | Error::AddToPrintf(error, FROM_HERE, errors::kErrorDomain, | 
|  | errors::kSchemaError, "Failed to load defaults: '%s'", | 
|  | json.c_str()); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | StatePackage* StateManager::FindPackage(const std::string& package_name) { | 
|  | auto it = packages_.find(package_name); | 
|  | return (it != packages_.end()) ? it->second.get() : nullptr; | 
|  | } | 
|  |  | 
|  | const StatePackage* StateManager::FindPackage( | 
|  | const std::string& package_name) const { | 
|  | auto it = packages_.find(package_name); | 
|  | return (it != packages_.end()) ? it->second.get() : nullptr; | 
|  | } | 
|  |  | 
|  | StatePackage* StateManager::FindOrCreatePackage( | 
|  | const std::string& package_name) { | 
|  | StatePackage* package = FindPackage(package_name); | 
|  | if (package == nullptr) { | 
|  | std::unique_ptr<StatePackage> new_package{new StatePackage(package_name)}; | 
|  | package = packages_.emplace(package_name, std::move(new_package)) | 
|  | .first->second.get(); | 
|  | } | 
|  | return package; | 
|  | } | 
|  |  | 
|  | }  // namespace weave |