1// 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|.
24bool PopListElements(MessageReader* reader, base::ListValue* list_value) {
25  while (reader->HasMoreData()) {
26    base::Value* element_value = PopDataAsValue(reader);
27    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,
36                          base::DictionaryValue* dictionary_value) {
37  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.
50      scoped_ptr<base::Value> key(PopDataAsValue(&entry_reader));
51      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.
57    base::Value* value = PopDataAsValue(&entry_reader);
58    if (!value)
59      return false;
60    dictionary_value->SetWithoutPathExpansion(key_string, value);
61  }
62  return true;
63}
64
65// 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}";
80    case base::Value::TYPE_LIST:
81      return "av";
82    default:
83      DLOG(ERROR) << "Unexpected type " << value.GetType();
84      return std::string();
85  }
86}
87
88}  // namespace
89
90base::Value* PopDataAsValue(MessageReader* reader) {
91  base::Value* result = NULL;
92  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))
99        result = new base::FundamentalValue(value);
100      break;
101    }
102    case Message::BOOL: {
103      bool value = false;
104      if (reader->PopBool(&value))
105        result = new base::FundamentalValue(value);
106      break;
107    }
108    case Message::INT16: {
109      int16 value = 0;
110      if (reader->PopInt16(&value))
111        result = new base::FundamentalValue(value);
112      break;
113    }
114    case Message::UINT16: {
115      uint16 value = 0;
116      if (reader->PopUint16(&value))
117        result = new base::FundamentalValue(value);
118      break;
119    }
120    case Message::INT32: {
121      int32 value = 0;
122      if (reader->PopInt32(&value))
123        result = new base::FundamentalValue(value);
124      break;
125    }
126    case Message::UINT32: {
127      uint32 value = 0;
128      if (reader->PopUint32(&value))
129        result = new base::FundamentalValue(static_cast<double>(value));
130      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";
137        result = new base::FundamentalValue(static_cast<double>(value));
138      }
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";
146        result = new base::FundamentalValue(static_cast<double>(value));
147      }
148      break;
149    }
150    case Message::DOUBLE: {
151      double value = 0;
152      if (reader->PopDouble(&value))
153        result = new base::FundamentalValue(value);
154      break;
155    }
156    case Message::STRING: {
157      std::string value;
158      if (reader->PopString(&value))
159        result = new base::StringValue(value);
160      break;
161    }
162    case Message::OBJECT_PATH: {
163      ObjectPath value;
164      if (reader->PopObjectPath(&value))
165        result = new base::StringValue(value.value());
166      break;
167    }
168    case Message::UNIX_FD: {
169      // Cannot distinguish a file descriptor from an int
170      NOTREACHED();
171      break;
172    }
173    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) {
179          scoped_ptr<base::DictionaryValue> dictionary_value(
180              new base::DictionaryValue);
181          if (PopDictionaryEntries(&sub_reader, dictionary_value.get()))
182            result = dictionary_value.release();
183        } else {
184          scoped_ptr<base::ListValue> list_value(new base::ListValue);
185          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)) {
194        scoped_ptr<base::ListValue> list_value(new base::ListValue);
195        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
214void AppendBasicTypeValueData(MessageWriter* writer, const base::Value& value) {
215  switch (value.GetType()) {
216    case base::Value::TYPE_BOOLEAN: {
217      bool bool_value = false;
218      bool success = value.GetAsBoolean(&bool_value);
219      DCHECK(success);
220      writer->AppendBool(bool_value);
221      break;
222    }
223    case base::Value::TYPE_INTEGER: {
224      int int_value = 0;
225      bool success = value.GetAsInteger(&int_value);
226      DCHECK(success);
227      writer->AppendInt32(int_value);
228      break;
229    }
230    case base::Value::TYPE_DOUBLE: {
231      double double_value = 0;
232      bool success = value.GetAsDouble(&double_value);
233      DCHECK(success);
234      writer->AppendDouble(double_value);
235      break;
236    }
237    case base::Value::TYPE_STRING: {
238      std::string string_value;
239      bool success = value.GetAsString(&string_value);
240      DCHECK(success);
241      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
258void 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
307}  // namespace dbus
308