blob: e943ad776abdc2d48c2a2284fb03452efc8ae279 [file] [log] [blame]
Vitaly Bukaa0305d32015-07-27 16:08:51 -07001// Copyright 2015 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "buffet/dbus_conversion.h"
6
7#include <set>
8#include <string>
9#include <vector>
10
11#include <chromeos/type_name_undecorate.h>
12
13namespace buffet {
14
15namespace {
16
17// Helpers for JsonToAny().
18template <typename T>
19chromeos::Any ValueToAny(const base::Value& json,
20 bool (base::Value::*fnc)(T*) const) {
21 T val;
22 CHECK((json.*fnc)(&val));
23 return val;
24}
25
26chromeos::Any ValueToAny(const base::Value& json);
27
28template <typename T>
29chromeos::Any ListToAny(const base::ListValue& list,
30 bool (base::Value::*fnc)(T*) const) {
31 std::vector<T> result;
32 result.reserve(list.GetSize());
33 for (const base::Value* v : list) {
34 T val;
35 CHECK((v->*fnc)(&val));
36 result.push_back(val);
37 }
38 return result;
39}
40
41chromeos::Any DictListToAny(const base::ListValue& list) {
42 std::vector<chromeos::VariantDictionary> result;
43 result.reserve(list.GetSize());
44 for (const base::Value* v : list) {
45 const base::DictionaryValue* dict = nullptr;
46 CHECK(v->GetAsDictionary(&dict));
47 result.push_back(DictionaryToDBusVariantDictionary(*dict));
48 }
49 return result;
50}
51
52chromeos::Any ListListToAny(const base::ListValue& list) {
53 std::vector<chromeos::Any> result;
54 result.reserve(list.GetSize());
55 for (const base::Value* v : list)
56 result.push_back(ValueToAny(*v));
57 return result;
58}
59
60// Converts a JSON value into an Any so it can be sent over D-Bus using
61// UpdateState D-Bus method from Buffet.
62chromeos::Any ValueToAny(const base::Value& json) {
63 chromeos::Any prop_value;
64 switch (json.GetType()) {
65 case base::Value::TYPE_BOOLEAN:
66 prop_value = ValueToAny<bool>(json, &base::Value::GetAsBoolean);
67 break;
68 case base::Value::TYPE_INTEGER:
69 prop_value = ValueToAny<int>(json, &base::Value::GetAsInteger);
70 break;
71 case base::Value::TYPE_DOUBLE:
72 prop_value = ValueToAny<double>(json, &base::Value::GetAsDouble);
73 break;
74 case base::Value::TYPE_STRING:
75 prop_value = ValueToAny<std::string>(json, &base::Value::GetAsString);
76 break;
77 case base::Value::TYPE_DICTIONARY: {
78 const base::DictionaryValue* dict = nullptr;
79 CHECK(json.GetAsDictionary(&dict));
80 prop_value = DictionaryToDBusVariantDictionary(*dict);
81 break;
82 }
83 case base::Value::TYPE_LIST: {
84 const base::ListValue* list = nullptr;
85 CHECK(json.GetAsList(&list));
86 if (list->empty()) {
87 // We don't know type of objects this list intended for, so we just use
88 // vector<chromeos::Any>.
89 prop_value = ListListToAny(*list);
90 break;
91 }
92 auto type = (*list->begin())->GetType();
93 for (const base::Value* v : *list)
94 CHECK_EQ(v->GetType(), type) << "Unsupported different type elements";
95
96 switch (type) {
97 case base::Value::TYPE_BOOLEAN:
98 prop_value = ListToAny<bool>(*list, &base::Value::GetAsBoolean);
99 break;
100 case base::Value::TYPE_INTEGER:
101 prop_value = ListToAny<int>(*list, &base::Value::GetAsInteger);
102 break;
103 case base::Value::TYPE_DOUBLE:
104 prop_value = ListToAny<double>(*list, &base::Value::GetAsDouble);
105 break;
106 case base::Value::TYPE_STRING:
107 prop_value = ListToAny<std::string>(*list, &base::Value::GetAsString);
108 break;
109 case base::Value::TYPE_DICTIONARY:
110 prop_value = DictListToAny(*list);
111 break;
112 case base::Value::TYPE_LIST:
113 // We can't support Any{vector<vector<>>} as the type is only known
114 // in runtime when we need to instantiate templates in compile time.
115 // We can use Any{vector<Any>} instead.
116 prop_value = ListListToAny(*list);
117 break;
118 default:
119 LOG(FATAL) << "Unsupported JSON value type for list element: "
120 << (*list->begin())->GetType();
121 }
122 break;
123 }
124 default:
125 LOG(FATAL) << "Unexpected JSON value type: " << json.GetType();
126 break;
127 }
128 return prop_value;
129}
130
131template <typename T>
132std::unique_ptr<base::Value> CreateValue(const T& value,
133 chromeos::ErrorPtr* error) {
134 return std::unique_ptr<base::Value>{new base::FundamentalValue{value}};
135}
136
137template <>
138std::unique_ptr<base::Value> CreateValue<std::string>(
139 const std::string& value,
140 chromeos::ErrorPtr* error) {
141 return std::unique_ptr<base::Value>{new base::StringValue{value}};
142}
143
144template <>
145std::unique_ptr<base::Value> CreateValue<chromeos::VariantDictionary>(
146 const chromeos::VariantDictionary& value,
147 chromeos::ErrorPtr* error) {
148 return DictionaryFromDBusVariantDictionary(value, error);
149}
150
151template <typename T>
152std::unique_ptr<base::ListValue> CreateListValue(const std::vector<T>& value,
153 chromeos::ErrorPtr* error) {
154 std::unique_ptr<base::ListValue> list{new base::ListValue};
155
156 for (const T& i : value) {
157 auto item = CreateValue(i, error);
158 if (!item)
159 return nullptr;
160 list->Append(item.release());
161 }
162
163 return list;
164}
165
166// Returns false only in case of error. True can be returned if type is not
167// matched.
168template <typename T>
169bool TryCreateValue(const chromeos::Any& any,
170 std::unique_ptr<base::Value>* value,
171 chromeos::ErrorPtr* error) {
172 if (any.IsTypeCompatible<T>()) {
173 *value = CreateValue(any.Get<T>(), error);
174 return *value != nullptr;
175 }
176
177 if (any.IsTypeCompatible<std::vector<T>>()) {
178 *value = CreateListValue(any.Get<std::vector<T>>(), error);
179 return *value != nullptr;
180 }
181
182 return true; // Not an error, we will try different type.
183}
184
185template <>
186std::unique_ptr<base::Value> CreateValue<chromeos::Any>(
187 const chromeos::Any& any,
188 chromeos::ErrorPtr* error) {
189 std::unique_ptr<base::Value> result;
190 if (!TryCreateValue<bool>(any, &result, error) || result)
191 return result;
192
193 if (!TryCreateValue<int>(any, &result, error) || result)
194 return result;
195
196 if (!TryCreateValue<double>(any, &result, error) || result)
197 return result;
198
199 if (!TryCreateValue<std::string>(any, &result, error) || result)
200 return result;
201
202 if (!TryCreateValue<chromeos::VariantDictionary>(any, &result, error) ||
203 result) {
204 return result;
205 }
206
207 // This will collapse Any{Any{T}} and vector{Any{T}}.
208 if (!TryCreateValue<chromeos::Any>(any, &result, error) || result)
209 return result;
210
211 chromeos::Error::AddToPrintf(
212 error, FROM_HERE, "buffet", "unknown_type", "Type '%s' is not supported.",
213 chromeos::UndecorateTypeName(any.GetType().name()).c_str());
214
215 return nullptr;
216}
217
218} // namespace
219
220// TODO(vitalybuka): Use in buffet_client.
221chromeos::VariantDictionary DictionaryToDBusVariantDictionary(
222 const base::DictionaryValue& object) {
223 chromeos::VariantDictionary result;
224
225 for (base::DictionaryValue::Iterator it(object); !it.IsAtEnd(); it.Advance())
226 result.emplace(it.key(), ValueToAny(it.value()));
227
228 return result;
229}
230
231std::unique_ptr<base::DictionaryValue> DictionaryFromDBusVariantDictionary(
232 const chromeos::VariantDictionary& object,
233 chromeos::ErrorPtr* error) {
234 std::unique_ptr<base::DictionaryValue> result{new base::DictionaryValue};
235
236 for (const auto& pair : object) {
237 auto value = CreateValue(pair.second, error);
238 if (!value)
239 return nullptr;
Alex Vakulenko2915a7b2015-10-07 17:04:00 -0700240 result->Set(pair.first, value.release());
Vitaly Bukaa0305d32015-07-27 16:08:51 -0700241 }
242
243 return result;
244}
245
246} // namespace buffet