- add sources.
[platform/framework/web/crosswalk.git] / src / base / json / json_writer.cc
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 "base/json/json_writer.h"
6
7 #include <cmath>
8
9 #include "base/json/string_escape.h"
10 #include "base/logging.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15
16 namespace base {
17
18 #if defined(OS_WIN)
19 static const char kPrettyPrintLineEnding[] = "\r\n";
20 #else
21 static const char kPrettyPrintLineEnding[] = "\n";
22 #endif
23
24 /* static */
25 const char* JSONWriter::kEmptyArray = "[]";
26
27 /* static */
28 void JSONWriter::Write(const Value* const node, std::string* json) {
29   WriteWithOptions(node, 0, json);
30 }
31
32 /* static */
33 void JSONWriter::WriteWithOptions(const Value* const node, int options,
34                                   std::string* json) {
35   json->clear();
36   // Is there a better way to estimate the size of the output?
37   json->reserve(1024);
38
39   bool escape = !(options & OPTIONS_DO_NOT_ESCAPE);
40   bool omit_binary_values = !!(options & OPTIONS_OMIT_BINARY_VALUES);
41   bool omit_double_type_preservation =
42       !!(options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION);
43   bool pretty_print = !!(options & OPTIONS_PRETTY_PRINT);
44
45   JSONWriter writer(escape, omit_binary_values, omit_double_type_preservation,
46                     pretty_print, json);
47   writer.BuildJSONString(node, 0);
48
49   if (pretty_print)
50     json->append(kPrettyPrintLineEnding);
51 }
52
53 JSONWriter::JSONWriter(bool escape, bool omit_binary_values,
54                        bool omit_double_type_preservation, bool pretty_print,
55                        std::string* json)
56     : escape_(escape),
57       omit_binary_values_(omit_binary_values),
58       omit_double_type_preservation_(omit_double_type_preservation),
59       pretty_print_(pretty_print),
60       json_string_(json) {
61   DCHECK(json);
62 }
63
64 void JSONWriter::BuildJSONString(const Value* const node, int depth) {
65   switch (node->GetType()) {
66     case Value::TYPE_NULL:
67       json_string_->append("null");
68       break;
69
70     case Value::TYPE_BOOLEAN:
71       {
72         bool value;
73         bool result = node->GetAsBoolean(&value);
74         DCHECK(result);
75         json_string_->append(value ? "true" : "false");
76         break;
77       }
78
79     case Value::TYPE_INTEGER:
80       {
81         int value;
82         bool result = node->GetAsInteger(&value);
83         DCHECK(result);
84         base::StringAppendF(json_string_, "%d", value);
85         break;
86       }
87
88     case Value::TYPE_DOUBLE:
89       {
90         double value;
91         bool result = node->GetAsDouble(&value);
92         DCHECK(result);
93         if (omit_double_type_preservation_ &&
94             value <= kint64max &&
95             value >= kint64min &&
96             std::floor(value) == value) {
97           json_string_->append(Int64ToString(static_cast<int64>(value)));
98           break;
99         }
100         std::string real = DoubleToString(value);
101         // Ensure that the number has a .0 if there's no decimal or 'e'.  This
102         // makes sure that when we read the JSON back, it's interpreted as a
103         // real rather than an int.
104         if (real.find('.') == std::string::npos &&
105             real.find('e') == std::string::npos &&
106             real.find('E') == std::string::npos) {
107           real.append(".0");
108         }
109         // The JSON spec requires that non-integer values in the range (-1,1)
110         // have a zero before the decimal point - ".52" is not valid, "0.52" is.
111         if (real[0] == '.') {
112           real.insert(0, "0");
113         } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') {
114           // "-.1" bad "-0.1" good
115           real.insert(1, "0");
116         }
117         json_string_->append(real);
118         break;
119       }
120
121     case Value::TYPE_STRING:
122       {
123         std::string value;
124         bool result = node->GetAsString(&value);
125         DCHECK(result);
126         if (escape_) {
127           JsonDoubleQuote(UTF8ToUTF16(value), true, json_string_);
128         } else {
129           JsonDoubleQuote(value, true, json_string_);
130         }
131         break;
132       }
133
134     case Value::TYPE_LIST:
135       {
136         json_string_->append("[");
137         if (pretty_print_)
138           json_string_->append(" ");
139
140         const ListValue* list = static_cast<const ListValue*>(node);
141         for (size_t i = 0; i < list->GetSize(); ++i) {
142           const Value* value = NULL;
143           bool result = list->Get(i, &value);
144           DCHECK(result);
145
146           if (omit_binary_values_ && value->GetType() == Value::TYPE_BINARY) {
147             continue;
148           }
149
150           if (i != 0) {
151             json_string_->append(",");
152             if (pretty_print_)
153               json_string_->append(" ");
154           }
155
156           BuildJSONString(value, depth);
157         }
158
159         if (pretty_print_)
160           json_string_->append(" ");
161         json_string_->append("]");
162         break;
163       }
164
165     case Value::TYPE_DICTIONARY:
166       {
167         json_string_->append("{");
168         if (pretty_print_)
169           json_string_->append(kPrettyPrintLineEnding);
170
171         const DictionaryValue* dict =
172           static_cast<const DictionaryValue*>(node);
173         bool first_entry = true;
174         for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd();
175              itr.Advance(), first_entry = false) {
176           if (omit_binary_values_ &&
177               itr.value().GetType() == Value::TYPE_BINARY) {
178             continue;
179           }
180
181           if (!first_entry) {
182             json_string_->append(",");
183             if (pretty_print_)
184               json_string_->append(kPrettyPrintLineEnding);
185           }
186
187           if (pretty_print_)
188             IndentLine(depth + 1);
189           AppendQuotedString(itr.key());
190           if (pretty_print_) {
191             json_string_->append(": ");
192           } else {
193             json_string_->append(":");
194           }
195           BuildJSONString(&itr.value(), depth + 1);
196         }
197
198         if (pretty_print_) {
199           json_string_->append(kPrettyPrintLineEnding);
200           IndentLine(depth);
201           json_string_->append("}");
202         } else {
203           json_string_->append("}");
204         }
205         break;
206       }
207
208     case Value::TYPE_BINARY:
209       {
210         if (!omit_binary_values_) {
211           NOTREACHED() << "Cannot serialize binary value.";
212         }
213         break;
214       }
215
216     default:
217       NOTREACHED() << "unknown json type";
218   }
219 }
220
221 void JSONWriter::AppendQuotedString(const std::string& str) {
222   // TODO(viettrungluu): |str| is UTF-8, not ASCII, so to properly escape it we
223   // have to convert it to UTF-16. This round-trip is suboptimal.
224   JsonDoubleQuote(UTF8ToUTF16(str), true, json_string_);
225 }
226
227 void JSONWriter::IndentLine(int depth) {
228   // It may be faster to keep an indent string so we don't have to keep
229   // reallocating.
230   json_string_->append(std::string(depth * 3, ' '));
231 }
232
233 }  // namespace base