blob: c27c2723b84cb9f42820d22ffe6cb8fc96ff1cd6 [file] [log] [blame]
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +09001// Copyright (c) 2012 The Chromium 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 "dbus/values_util.h"
6
7#include "base/json/json_writer.h"
8#include "base/logging.h"
9#include "base/memory/scoped_ptr.h"
10#include "base/values.h"
11#include "dbus/message.h"
12
13namespace dbus {
14
15namespace {
16
17// Returns whether |value| is exactly representable by double or not.
18template<typename T>
19bool IsExactlyRepresentableByDouble(T value) {
20 return value == static_cast<T>(static_cast<double>(value));
21}
22
23// Pops values from |reader| and appends them to |list_value|.
thestig@chromium.orge1acdf82013-02-13 05:06:15 +090024bool PopListElements(MessageReader* reader, base::ListValue* list_value) {
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +090025 while (reader->HasMoreData()) {
thestig@chromium.orge1acdf82013-02-13 05:06:15 +090026 base::Value* element_value = PopDataAsValue(reader);
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +090027 if (!element_value)
28 return false;
29 list_value->Append(element_value);
30 }
31 return true;
32}
33
34// Pops dict-entries from |reader| and sets them to |dictionary_value|
35bool PopDictionaryEntries(MessageReader* reader,
thestig@chromium.orge1acdf82013-02-13 05:06:15 +090036 base::DictionaryValue* dictionary_value) {
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +090037 while (reader->HasMoreData()) {
38 DCHECK_EQ(Message::DICT_ENTRY, reader->GetDataType());
39 MessageReader entry_reader(NULL);
40 if (!reader->PopDictEntry(&entry_reader))
41 return false;
42 // Get key as a string.
43 std::string key_string;
44 if (entry_reader.GetDataType() == Message::STRING) {
45 // If the type of keys is STRING, pop it directly.
46 if (!entry_reader.PopString(&key_string))
47 return false;
48 } else {
49 // If the type of keys is not STRING, convert it to string.
thestig@chromium.orge1acdf82013-02-13 05:06:15 +090050 scoped_ptr<base::Value> key(PopDataAsValue(&entry_reader));
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +090051 if (!key.get())
52 return false;
53 // Use JSONWriter to convert an arbitrary value to a string.
54 base::JSONWriter::Write(key.get(), &key_string);
55 }
56 // Get the value and set the key-value pair.
thestig@chromium.orge1acdf82013-02-13 05:06:15 +090057 base::Value* value = PopDataAsValue(&entry_reader);
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +090058 if (!value)
59 return false;
hashimoto@chromium.org9a576b02012-03-21 03:18:57 +090060 dictionary_value->SetWithoutPathExpansion(key_string, value);
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +090061 }
62 return true;
63}
64
hashimoto@chromium.orgea599822012-03-25 05:37:18 +090065// Gets the D-Bus type signature for the value.
66std::string GetTypeSignature(const base::Value& value) {
67 switch (value.GetType()) {
68 case base::Value::TYPE_BOOLEAN:
69 return "b";
70 case base::Value::TYPE_INTEGER:
71 return "i";
72 case base::Value::TYPE_DOUBLE:
73 return "d";
74 case base::Value::TYPE_STRING:
75 return "s";
76 case base::Value::TYPE_BINARY:
77 return "ay";
78 case base::Value::TYPE_DICTIONARY:
79 return "a{sv}";
armansito@chromium.org39d51b92014-05-22 20:52:55 +090080 case base::Value::TYPE_LIST:
81 return "av";
hashimoto@chromium.orgea599822012-03-25 05:37:18 +090082 default:
83 DLOG(ERROR) << "Unexpected type " << value.GetType();
dcheng@chromium.org8164c2c2013-04-09 17:46:45 +090084 return std::string();
hashimoto@chromium.orgea599822012-03-25 05:37:18 +090085 }
86}
87
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +090088} // namespace
89
thestig@chromium.orge1acdf82013-02-13 05:06:15 +090090base::Value* PopDataAsValue(MessageReader* reader) {
91 base::Value* result = NULL;
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +090092 switch (reader->GetDataType()) {
93 case Message::INVALID_DATA:
94 // Do nothing.
95 break;
96 case Message::BYTE: {
97 uint8 value = 0;
98 if (reader->PopByte(&value))
thestig@chromium.orge1acdf82013-02-13 05:06:15 +090099 result = new base::FundamentalValue(value);
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +0900100 break;
101 }
102 case Message::BOOL: {
103 bool value = false;
104 if (reader->PopBool(&value))
thestig@chromium.orge1acdf82013-02-13 05:06:15 +0900105 result = new base::FundamentalValue(value);
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +0900106 break;
107 }
108 case Message::INT16: {
109 int16 value = 0;
110 if (reader->PopInt16(&value))
thestig@chromium.orge1acdf82013-02-13 05:06:15 +0900111 result = new base::FundamentalValue(value);
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +0900112 break;
113 }
114 case Message::UINT16: {
115 uint16 value = 0;
116 if (reader->PopUint16(&value))
thestig@chromium.orge1acdf82013-02-13 05:06:15 +0900117 result = new base::FundamentalValue(value);
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +0900118 break;
119 }
120 case Message::INT32: {
121 int32 value = 0;
122 if (reader->PopInt32(&value))
thestig@chromium.orge1acdf82013-02-13 05:06:15 +0900123 result = new base::FundamentalValue(value);
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +0900124 break;
125 }
126 case Message::UINT32: {
127 uint32 value = 0;
128 if (reader->PopUint32(&value))
thestig@chromium.orge1acdf82013-02-13 05:06:15 +0900129 result = new base::FundamentalValue(static_cast<double>(value));
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +0900130 break;
131 }
132 case Message::INT64: {
133 int64 value = 0;
134 if (reader->PopInt64(&value)) {
135 DLOG_IF(WARNING, !IsExactlyRepresentableByDouble(value)) <<
136 value << " is not exactly representable by double";
thestig@chromium.orge1acdf82013-02-13 05:06:15 +0900137 result = new base::FundamentalValue(static_cast<double>(value));
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +0900138 }
139 break;
140 }
141 case Message::UINT64: {
142 uint64 value = 0;
143 if (reader->PopUint64(&value)) {
144 DLOG_IF(WARNING, !IsExactlyRepresentableByDouble(value)) <<
145 value << " is not exactly representable by double";
thestig@chromium.orge1acdf82013-02-13 05:06:15 +0900146 result = new base::FundamentalValue(static_cast<double>(value));
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +0900147 }
148 break;
149 }
150 case Message::DOUBLE: {
151 double value = 0;
152 if (reader->PopDouble(&value))
thestig@chromium.orge1acdf82013-02-13 05:06:15 +0900153 result = new base::FundamentalValue(value);
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +0900154 break;
155 }
156 case Message::STRING: {
157 std::string value;
158 if (reader->PopString(&value))
thestig@chromium.orge1acdf82013-02-13 05:06:15 +0900159 result = new base::StringValue(value);
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +0900160 break;
161 }
162 case Message::OBJECT_PATH: {
163 ObjectPath value;
164 if (reader->PopObjectPath(&value))
thestig@chromium.orge1acdf82013-02-13 05:06:15 +0900165 result = new base::StringValue(value.value());
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +0900166 break;
167 }
sleffler@chromium.org22fab402012-03-30 15:46:20 +0900168 case Message::UNIX_FD: {
169 // Cannot distinguish a file descriptor from an int
170 NOTREACHED();
171 break;
172 }
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +0900173 case Message::ARRAY: {
174 MessageReader sub_reader(NULL);
175 if (reader->PopArray(&sub_reader)) {
176 // If the type of the array's element is DICT_ENTRY, create a
177 // DictionaryValue, otherwise create a ListValue.
178 if (sub_reader.GetDataType() == Message::DICT_ENTRY) {
thestig@chromium.orge1acdf82013-02-13 05:06:15 +0900179 scoped_ptr<base::DictionaryValue> dictionary_value(
180 new base::DictionaryValue);
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +0900181 if (PopDictionaryEntries(&sub_reader, dictionary_value.get()))
182 result = dictionary_value.release();
183 } else {
thestig@chromium.orge1acdf82013-02-13 05:06:15 +0900184 scoped_ptr<base::ListValue> list_value(new base::ListValue);
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +0900185 if (PopListElements(&sub_reader, list_value.get()))
186 result = list_value.release();
187 }
188 }
189 break;
190 }
191 case Message::STRUCT: {
192 MessageReader sub_reader(NULL);
193 if (reader->PopStruct(&sub_reader)) {
thestig@chromium.orge1acdf82013-02-13 05:06:15 +0900194 scoped_ptr<base::ListValue> list_value(new base::ListValue);
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +0900195 if (PopListElements(&sub_reader, list_value.get()))
196 result = list_value.release();
197 }
198 break;
199 }
200 case Message::DICT_ENTRY:
201 // DICT_ENTRY must be popped as an element of an array.
202 NOTREACHED();
203 break;
204 case Message::VARIANT: {
205 MessageReader sub_reader(NULL);
206 if (reader->PopVariant(&sub_reader))
207 result = PopDataAsValue(&sub_reader);
208 break;
209 }
210 }
211 return result;
212}
213
hashimoto@chromium.orgea599822012-03-25 05:37:18 +0900214void AppendBasicTypeValueData(MessageWriter* writer, const base::Value& value) {
215 switch (value.GetType()) {
216 case base::Value::TYPE_BOOLEAN: {
217 bool bool_value = false;
khorimoto@chromium.org3a567792012-03-29 04:00:06 +0900218 bool success = value.GetAsBoolean(&bool_value);
219 DCHECK(success);
hashimoto@chromium.orgea599822012-03-25 05:37:18 +0900220 writer->AppendBool(bool_value);
221 break;
222 }
223 case base::Value::TYPE_INTEGER: {
224 int int_value = 0;
khorimoto@chromium.org3a567792012-03-29 04:00:06 +0900225 bool success = value.GetAsInteger(&int_value);
226 DCHECK(success);
hashimoto@chromium.orgea599822012-03-25 05:37:18 +0900227 writer->AppendInt32(int_value);
228 break;
229 }
230 case base::Value::TYPE_DOUBLE: {
231 double double_value = 0;
khorimoto@chromium.org3a567792012-03-29 04:00:06 +0900232 bool success = value.GetAsDouble(&double_value);
233 DCHECK(success);
hashimoto@chromium.orgea599822012-03-25 05:37:18 +0900234 writer->AppendDouble(double_value);
235 break;
236 }
237 case base::Value::TYPE_STRING: {
238 std::string string_value;
khorimoto@chromium.org3a567792012-03-29 04:00:06 +0900239 bool success = value.GetAsString(&string_value);
240 DCHECK(success);
hashimoto@chromium.orgea599822012-03-25 05:37:18 +0900241 writer->AppendString(string_value);
242 break;
243 }
244 default:
245 DLOG(ERROR) << "Unexpected type " << value.GetType();
246 break;
247 }
248}
249
250void AppendBasicTypeValueDataAsVariant(MessageWriter* writer,
251 const base::Value& value) {
252 MessageWriter sub_writer(NULL);
253 writer->OpenVariant(GetTypeSignature(value), &sub_writer);
254 AppendBasicTypeValueData(&sub_writer, value);
255 writer->CloseContainer(&sub_writer);
256}
257
armansito@chromium.org39d51b92014-05-22 20:52:55 +0900258void AppendValueData(MessageWriter* writer, const base::Value& value) {
259 switch (value.GetType()) {
260 case base::Value::TYPE_DICTIONARY: {
261 const base::DictionaryValue* dictionary = NULL;
262 value.GetAsDictionary(&dictionary);
263 dbus::MessageWriter array_writer(NULL);
264 writer->OpenArray("{sv}", &array_writer);
265 for (base::DictionaryValue::Iterator iter(*dictionary);
266 !iter.IsAtEnd(); iter.Advance()) {
267 dbus::MessageWriter dict_entry_writer(NULL);
268 array_writer.OpenDictEntry(&dict_entry_writer);
269 dict_entry_writer.AppendString(iter.key());
270 AppendValueDataAsVariant(&dict_entry_writer, iter.value());
271 array_writer.CloseContainer(&dict_entry_writer);
272 }
273 writer->CloseContainer(&array_writer);
274 break;
275 }
276 case base::Value::TYPE_LIST: {
277 const base::ListValue* list = NULL;
278 value.GetAsList(&list);
279 dbus::MessageWriter array_writer(NULL);
280 writer->OpenArray("v", &array_writer);
281 for (base::ListValue::const_iterator iter = list->begin();
282 iter != list->end(); ++iter) {
283 const base::Value* value = *iter;
284 AppendValueDataAsVariant(&array_writer, *value);
285 }
286 writer->CloseContainer(&array_writer);
287 break;
288 }
289 case base::Value::TYPE_BOOLEAN:
290 case base::Value::TYPE_INTEGER:
291 case base::Value::TYPE_DOUBLE:
292 case base::Value::TYPE_STRING:
293 AppendBasicTypeValueData(writer, value);
294 break;
295 default:
296 DLOG(ERROR) << "Unexpected type: " << value.GetType();
297 }
298}
299
300void AppendValueDataAsVariant(MessageWriter* writer, const base::Value& value) {
301 MessageWriter variant_writer(NULL);
302 writer->OpenVariant(GetTypeSignature(value), &variant_writer);
303 AppendValueData(&variant_writer, value);
304 writer->CloseContainer(&variant_writer);
305}
306
hashimoto@chromium.org8fb5a2b2012-03-17 08:08:42 +0900307} // namespace dbus