blob: 51d35f113e204395fcfaf88fbb8b65a7bf1d96f3 [file] [log] [blame]
// Copyright 2015 The Chromium OS 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 "buffet/dbus_conversion.h"
#include <set>
#include <string>
#include <vector>
#include <chromeos/type_name_undecorate.h>
namespace buffet {
namespace {
// Helpers for JsonToAny().
template <typename T>
chromeos::Any ValueToAny(const base::Value& json,
bool (base::Value::*fnc)(T*) const) {
T val;
CHECK((json.*fnc)(&val));
return val;
}
chromeos::Any ValueToAny(const base::Value& json);
template <typename T>
chromeos::Any ListToAny(const base::ListValue& list,
bool (base::Value::*fnc)(T*) const) {
std::vector<T> result;
result.reserve(list.GetSize());
for (const base::Value* v : list) {
T val;
CHECK((v->*fnc)(&val));
result.push_back(val);
}
return result;
}
chromeos::Any DictListToAny(const base::ListValue& list) {
std::vector<chromeos::VariantDictionary> result;
result.reserve(list.GetSize());
for (const base::Value* v : list) {
const base::DictionaryValue* dict = nullptr;
CHECK(v->GetAsDictionary(&dict));
result.push_back(DictionaryToDBusVariantDictionary(*dict));
}
return result;
}
chromeos::Any ListListToAny(const base::ListValue& list) {
std::vector<chromeos::Any> result;
result.reserve(list.GetSize());
for (const base::Value* v : list)
result.push_back(ValueToAny(*v));
return result;
}
// Converts a JSON value into an Any so it can be sent over D-Bus using
// UpdateState D-Bus method from Buffet.
chromeos::Any ValueToAny(const base::Value& json) {
chromeos::Any prop_value;
switch (json.GetType()) {
case base::Value::TYPE_BOOLEAN:
prop_value = ValueToAny<bool>(json, &base::Value::GetAsBoolean);
break;
case base::Value::TYPE_INTEGER:
prop_value = ValueToAny<int>(json, &base::Value::GetAsInteger);
break;
case base::Value::TYPE_DOUBLE:
prop_value = ValueToAny<double>(json, &base::Value::GetAsDouble);
break;
case base::Value::TYPE_STRING:
prop_value = ValueToAny<std::string>(json, &base::Value::GetAsString);
break;
case base::Value::TYPE_DICTIONARY: {
const base::DictionaryValue* dict = nullptr;
CHECK(json.GetAsDictionary(&dict));
prop_value = DictionaryToDBusVariantDictionary(*dict);
break;
}
case base::Value::TYPE_LIST: {
const base::ListValue* list = nullptr;
CHECK(json.GetAsList(&list));
if (list->empty()) {
// We don't know type of objects this list intended for, so we just use
// vector<chromeos::Any>.
prop_value = ListListToAny(*list);
break;
}
auto type = (*list->begin())->GetType();
for (const base::Value* v : *list)
CHECK_EQ(v->GetType(), type) << "Unsupported different type elements";
switch (type) {
case base::Value::TYPE_BOOLEAN:
prop_value = ListToAny<bool>(*list, &base::Value::GetAsBoolean);
break;
case base::Value::TYPE_INTEGER:
prop_value = ListToAny<int>(*list, &base::Value::GetAsInteger);
break;
case base::Value::TYPE_DOUBLE:
prop_value = ListToAny<double>(*list, &base::Value::GetAsDouble);
break;
case base::Value::TYPE_STRING:
prop_value = ListToAny<std::string>(*list, &base::Value::GetAsString);
break;
case base::Value::TYPE_DICTIONARY:
prop_value = DictListToAny(*list);
break;
case base::Value::TYPE_LIST:
// We can't support Any{vector<vector<>>} as the type is only known
// in runtime when we need to instantiate templates in compile time.
// We can use Any{vector<Any>} instead.
prop_value = ListListToAny(*list);
break;
default:
LOG(FATAL) << "Unsupported JSON value type for list element: "
<< (*list->begin())->GetType();
}
break;
}
default:
LOG(FATAL) << "Unexpected JSON value type: " << json.GetType();
break;
}
return prop_value;
}
template <typename T>
std::unique_ptr<base::Value> CreateValue(const T& value,
chromeos::ErrorPtr* error) {
return std::unique_ptr<base::Value>{new base::FundamentalValue{value}};
}
template <>
std::unique_ptr<base::Value> CreateValue<std::string>(
const std::string& value,
chromeos::ErrorPtr* error) {
return std::unique_ptr<base::Value>{new base::StringValue{value}};
}
template <>
std::unique_ptr<base::Value> CreateValue<chromeos::VariantDictionary>(
const chromeos::VariantDictionary& value,
chromeos::ErrorPtr* error) {
return DictionaryFromDBusVariantDictionary(value, error);
}
template <typename T>
std::unique_ptr<base::ListValue> CreateListValue(const std::vector<T>& value,
chromeos::ErrorPtr* error) {
std::unique_ptr<base::ListValue> list{new base::ListValue};
for (const T& i : value) {
auto item = CreateValue(i, error);
if (!item)
return nullptr;
list->Append(item.release());
}
return list;
}
// Returns false only in case of error. True can be returned if type is not
// matched.
template <typename T>
bool TryCreateValue(const chromeos::Any& any,
std::unique_ptr<base::Value>* value,
chromeos::ErrorPtr* error) {
if (any.IsTypeCompatible<T>()) {
*value = CreateValue(any.Get<T>(), error);
return *value != nullptr;
}
if (any.IsTypeCompatible<std::vector<T>>()) {
*value = CreateListValue(any.Get<std::vector<T>>(), error);
return *value != nullptr;
}
return true; // Not an error, we will try different type.
}
template <>
std::unique_ptr<base::Value> CreateValue<chromeos::Any>(
const chromeos::Any& any,
chromeos::ErrorPtr* error) {
std::unique_ptr<base::Value> result;
if (!TryCreateValue<bool>(any, &result, error) || result)
return result;
if (!TryCreateValue<int>(any, &result, error) || result)
return result;
if (!TryCreateValue<double>(any, &result, error) || result)
return result;
if (!TryCreateValue<std::string>(any, &result, error) || result)
return result;
if (!TryCreateValue<chromeos::VariantDictionary>(any, &result, error) ||
result) {
return result;
}
// This will collapse Any{Any{T}} and vector{Any{T}}.
if (!TryCreateValue<chromeos::Any>(any, &result, error) || result)
return result;
chromeos::Error::AddToPrintf(
error, FROM_HERE, "buffet", "unknown_type", "Type '%s' is not supported.",
chromeos::UndecorateTypeName(any.GetType().name()).c_str());
return nullptr;
}
} // namespace
// TODO(vitalybuka): Use in buffet_client.
chromeos::VariantDictionary DictionaryToDBusVariantDictionary(
const base::DictionaryValue& object) {
chromeos::VariantDictionary result;
for (base::DictionaryValue::Iterator it(object); !it.IsAtEnd(); it.Advance())
result.emplace(it.key(), ValueToAny(it.value()));
return result;
}
std::unique_ptr<base::DictionaryValue> DictionaryFromDBusVariantDictionary(
const chromeos::VariantDictionary& object,
chromeos::ErrorPtr* error) {
std::unique_ptr<base::DictionaryValue> result{new base::DictionaryValue};
for (const auto& pair : object) {
auto value = CreateValue(pair.second, error);
if (!value)
return nullptr;
result->SetWithoutPathExpansion(pair.first, value.release());
}
return result;
}
} // namespace buffet