1 : // Copyright 2011 Google Inc.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 : //
15 : // JSONFileWriter works as a simple state machine. Rather than using an
16 : // exhaustive set of states and a big switch, its encoded via a few state
17 : // variables, and a handful of state determination functions. The general rule
18 : // of thumb is that when output is produced we write as much as is possible.
19 : #include "syzygy/core/json_file_writer.h"
20 :
21 : #include <stdarg.h>
22 : #include "base/json/json_writer.h"
23 : #include "base/json/string_escape.h"
24 : #include "base/logging.h"
25 : #include "base/memory/scoped_ptr.h"
26 : #include "base/utf_string_conversions.h"
27 : #include "base/values.h"
28 :
29 : namespace core {
30 :
31 : namespace {
32 :
33 : static const char kNewline[] = "\n";
34 : static const char kIndent[] = " ";
35 : static const char kNull[] = "null";
36 : static const char kTrue[] = "true";
37 : static const char kFalse[] = "false";
38 : static const char kCommentPrefix[] = "//";
39 :
40 : static const char* kStructureOpenings[] = { "[", "{", NULL };
41 : static const char* kStructureClosings[] = { "]", "}", NULL };
42 :
43 : } // namespace
44 :
45 : struct JSONFileWriter::Helper {
46 : template<typename KeyType>
47 E : static bool OutputKey(KeyType key, JSONFileWriter* json_file_writer) {
48 E : DCHECK(json_file_writer != NULL);
49 :
50 E : if (!json_file_writer->ReadyForKey())
51 E : return false;
52 :
53 E : if (!json_file_writer->AlignForValueOrKey())
54 i : return false;
55 :
56 E : std::string formatted_key = base::GetDoubleQuotedJson(key);
57 E : if (!json_file_writer->Printf("%s:", formatted_key.c_str()))
58 i : return false;
59 :
60 : // If we're pretty printing, then also output a space between the key and
61 : // the value.
62 E : if (json_file_writer->pretty_print_ && !json_file_writer->PutChar(' '))
63 i : return false;
64 :
65 : // Indicate that we've output a key and require a value.
66 E : json_file_writer->stack_.push_back(StackElement(kDictKey));
67 E : return true;
68 E : }
69 :
70 : template<typename ValueType, typename PrintFunctionPointer>
71 : static bool OutputValue(ValueType value,
72 : PrintFunctionPointer print_function,
73 E : JSONFileWriter* json_file_writer) {
74 E : DCHECK(print_function != NULL);
75 E : DCHECK(json_file_writer != NULL);
76 :
77 E : if (!json_file_writer->ReadyForValue())
78 E : return false;
79 E : if (!json_file_writer->AlignForValueOrKey())
80 i : return false;
81 E : if (!(json_file_writer->*print_function)(value))
82 i : return false;
83 E : json_file_writer->FlushValue(true);
84 E : return true;
85 E : }
86 : };
87 :
88 : JSONFileWriter::JSONFileWriter(FILE* file, bool pretty_print)
89 : : file_(file),
90 : pretty_print_(pretty_print),
91 : finished_(false),
92 : at_col_zero_(true),
93 E : indent_depth_(0) {
94 E : DCHECK(file != NULL);
95 E : }
96 :
97 E : JSONFileWriter::~JSONFileWriter() {
98 E : Flush();
99 E : }
100 :
101 E : bool JSONFileWriter::OutputComment(const char* comment) {
102 E : DCHECK(comment != NULL);
103 :
104 : // If we are in the middle of writing a dictionary key/value pair
105 : // (have the key, not the value), then we can't write a comment.
106 E : if (RequireKeyValue())
107 E : return false;
108 :
109 : // If we're not pretty-printing, this is a no-op.
110 E : if (!pretty_print_)
111 E : return true;
112 :
113 : // Trailing comments can be written directly.
114 E : if (finished_) {
115 E : if (!OutputNewline() || !Printf("%s", kCommentPrefix))
116 i : return false;
117 E : if (comment[0] != 0 && !Printf(" %s", comment))
118 i : return false;
119 E : return true;
120 : }
121 :
122 : // Store the comment for output before the next value.
123 E : comments_.push_back(std::string(comment));
124 :
125 E : return true;
126 E : }
127 :
128 E : bool JSONFileWriter::OutputComment(const wchar_t* comment) {
129 E : DCHECK(comment != NULL);
130 E : std::string utf8;
131 E : if (!WideToUTF8(comment, wcslen(comment), &utf8))
132 i : return false;
133 E : return OutputComment(utf8.c_str());
134 E : }
135 :
136 E : bool JSONFileWriter::OutputTrailingComment(const char* comment) {
137 E : DCHECK(comment != NULL);
138 :
139 : // A trailing comment can only go out after a value has been written.
140 E : if (!stack_.empty()) {
141 E : if (stack_.back().type_ == kDictKey)
142 i : return false;
143 :
144 E : if (stack_.back().has_entries_ == false)
145 i : return false;
146 E : } else {
147 : // If the stack is empty, then a value has only been written if we are
148 : // finished.
149 E : if (!finished_)
150 i : return false;
151 : }
152 :
153 : // No comment? Do nothing!
154 E : if (comment[0] == 0)
155 i : return true;
156 :
157 : // If we already have a trailing comment, bail!
158 E : if (!trailing_comment_.empty())
159 E : return false;
160 :
161 : // Save the comment for output when we're ready. We do this even when not
162 : // pretty-printing so that the state machine functions identically in either
163 : // case.
164 E : trailing_comment_ = comment;
165 :
166 : // Are we finished? Immediately write the comment, but leave
167 : // trailing_comment_ populated so that repeated calls will fail.
168 : if (finished_ &&
169 E : !Printf(" %s %s", kCommentPrefix, trailing_comment_.c_str())) {
170 i : return false;
171 : }
172 :
173 E : return true;
174 E : }
175 :
176 E : bool JSONFileWriter::OutputTrailingComment(const wchar_t* comment) {
177 E : DCHECK(comment != NULL);
178 E : std::string utf8;
179 E : if (!WideToUTF8(comment, wcslen(comment), &utf8))
180 i : return false;
181 E : return OutputTrailingComment(utf8.c_str());
182 E : }
183 :
184 E : bool JSONFileWriter::PrintBoolean(bool value) {
185 E : return Printf("%s", value ? kTrue : kFalse);
186 E : }
187 :
188 E : bool JSONFileWriter::PrintInteger(int value) {
189 E : return Printf("%d", value);
190 E : }
191 :
192 E : bool JSONFileWriter::PrintDouble(double value) {
193 E : base::FundamentalValue fundamental_value(value);
194 E : return PrintValue(&fundamental_value);
195 E : }
196 :
197 E : bool JSONFileWriter::PrintString(const char* value) {
198 E : return Printf("%s", base::GetDoubleQuotedJson(value).c_str());
199 E : }
200 :
201 E : bool JSONFileWriter::PrintNull(int value_unused) {
202 E : return Printf("%s", kNull);
203 E : }
204 :
205 E : bool JSONFileWriter::PrintValue(const base::Value* value) {
206 E : DCHECK(value != NULL);
207 :
208 E : switch (value->GetType()) {
209 : case Value::TYPE_LIST:
210 : case Value::TYPE_DICTIONARY: {
211 : // TODO(chrisha): Eventually, these should be implemented.
212 i : LOG(ERROR) << "JSON Lists and Dictionaries are currently unsupported.";
213 i : return false;
214 : }
215 :
216 : // All simple types.
217 : case Value::TYPE_BOOLEAN:
218 : case Value::TYPE_INTEGER:
219 : case Value::TYPE_DOUBLE:
220 : case Value::TYPE_NULL:
221 : case Value::TYPE_STRING:
222 : case Value::TYPE_BINARY: {
223 E : std::string str;
224 E : base::JSONWriter::Write(value, false, &str);
225 E : return Printf("%s", str.c_str());
226 : }
227 :
228 : default: {
229 i : NOTREACHED() << "Unexpected JSON type: " << value->GetType();
230 i : return false;
231 : }
232 : }
233 E : }
234 :
235 E : bool JSONFileWriter::Printf(const char* format, ...) {
236 : va_list args;
237 E : va_start(args, format);
238 E : int chars_written = vfprintf(file_, format, args);
239 E : va_end(args);
240 E : if (chars_written > 0)
241 E : at_col_zero_ = false;
242 E : return chars_written >= 0;
243 E : }
244 :
245 E : bool JSONFileWriter::PutChar(char c) {
246 E : if (fputc(c, file_) != c)
247 i : return false;
248 E : at_col_zero_ = false;
249 E : return true;
250 E : }
251 :
252 E : bool JSONFileWriter::OpenList() {
253 E : return OpenStructure(kList);
254 E : }
255 :
256 E : bool JSONFileWriter::CloseList() {
257 E : return CloseStructure(kList);
258 E : }
259 :
260 E : bool JSONFileWriter::OpenDict() {
261 E : return OpenStructure(kDict);
262 E : }
263 :
264 E : bool JSONFileWriter::CloseDict() {
265 E : return CloseStructure(kDict);
266 E : }
267 :
268 E : bool JSONFileWriter::OutputKey(const char* key) {
269 E : return Helper::OutputKey(key, this);
270 E : }
271 :
272 : bool JSONFileWriter::OutputKey(const wchar_t* key) {
273 : return Helper::OutputKey(key, this);
274 : }
275 :
276 E : bool JSONFileWriter::Flush() {
277 : // Already finished? This is a no-op.
278 E : if (finished_)
279 E : return true;
280 :
281 : // Are we waiting on a required value?
282 E : if (RequireKeyValue())
283 E : return false;
284 :
285 : // Otherwise, simply close off the structures one by one.
286 E : while (!stack_.empty()) {
287 E : if (!CloseStructure(stack_.back().type_))
288 i : return false;
289 E : }
290 :
291 E : return true;
292 E : }
293 :
294 E : bool JSONFileWriter::OutputBoolean(bool value) {
295 : return Helper::OutputValue(
296 E : value, &JSONFileWriter::PrintBoolean, this);
297 E : }
298 :
299 E : bool JSONFileWriter::OutputInteger(int value) {
300 : return Helper::OutputValue(
301 E : value, &JSONFileWriter::PrintInteger, this);
302 E : }
303 :
304 E : bool JSONFileWriter::OutputDouble(double value) {
305 : return Helper::OutputValue(
306 E : value, &JSONFileWriter::PrintDouble, this);
307 E : }
308 :
309 E : bool JSONFileWriter::OutputString(const char* value) {
310 E : DCHECK(value != NULL);
311 : return Helper::OutputValue(
312 E : value, &JSONFileWriter::PrintString, this);
313 E : }
314 :
315 E : bool JSONFileWriter::OutputString(const wchar_t* value) {
316 E : DCHECK(value != NULL);
317 E : std::string utf8;
318 E : if (!WideToUTF8(value, wcslen(value), &utf8))
319 i : return false;
320 E : return OutputString(utf8.c_str());
321 E : }
322 :
323 E : bool JSONFileWriter::OutputNull() {
324 E : int unused = 0;
325 : return Helper::OutputValue(
326 E : unused, &JSONFileWriter::PrintNull, this);
327 E : }
328 :
329 : bool JSONFileWriter::OutputValue(const Value* value) {
330 : return Helper::OutputValue(
331 : value, &JSONFileWriter::PrintValue, this);
332 : }
333 :
334 E : bool JSONFileWriter::OutputIndent() {
335 E : if (!pretty_print_)
336 i : return true;
337 :
338 : // We bypass Printf and manually update at_col_zero_ here for efficiency.
339 E : if (indent_depth_ > 0)
340 E : at_col_zero_ = false;
341 E : for (size_t i = 0; i < indent_depth_; ++i) {
342 E : if (fprintf(file_, "%s", kIndent) < 0)
343 i : return false;
344 E : }
345 E : return true;
346 E : }
347 :
348 E : bool JSONFileWriter::OutputNewline() {
349 E : if (!pretty_print_ || at_col_zero_)
350 E : return true;
351 :
352 : // Bypass Printf and manually at_col_zero_ for efficiency.
353 E : if (fprintf(file_, "%s", kNewline) < 0)
354 i : return false;
355 E : at_col_zero_ = true;
356 :
357 E : return true;
358 E : }
359 :
360 E : bool JSONFileWriter::OutputComments() {
361 E : if (comments_.empty())
362 E : return true;
363 :
364 : // Comments are only stored if we're pretty-printing.
365 E : DCHECK(pretty_print_);
366 :
367 E : bool indented = at_col_zero_ == false;
368 :
369 E : for (size_t i = 0; i < comments_.size(); ++i) {
370 : // Indent if need be.
371 E : if (at_col_zero_ && !OutputIndent())
372 i : return false;
373 :
374 : // Output the comment prefix.
375 E : if (!Printf("%s", kCommentPrefix))
376 i : return false;
377 :
378 : // Output the comment if there's any content.
379 : if (!comments_[i].empty() &&
380 E : !Printf(" %s", comments_[i].c_str()))
381 i : return false;
382 :
383 E : if (!OutputNewline())
384 i : return false;
385 E : }
386 :
387 : // If we were indented when entering, indent on the way out.
388 E : if (indented && !OutputIndent())
389 i : return false;
390 :
391 : // Clear the comments.
392 E : comments_.clear();
393 :
394 E : return true;
395 E : }
396 :
397 E : bool JSONFileWriter::OutputTrailingComment() {
398 E : if (trailing_comment_.empty())
399 E : return true;
400 :
401 : // If we're pretty-printing, output the comment.
402 : if (pretty_print_ &&
403 E : !Printf(" %s %s", kCommentPrefix, trailing_comment_.c_str())) {
404 i : return false;
405 : }
406 :
407 E : trailing_comment_.clear();
408 :
409 E : return true;
410 E : }
411 :
412 E : bool JSONFileWriter::AlignForValueOrKey() {
413 : // Are we a dictionary key waiting for a value? If so, there's nothing to
414 : // do as the alignment was taken care of when the key was written.
415 E : if (RequireKeyValue())
416 E : return true;
417 :
418 : // Are we in a structure, and not the first entry? Then we need to
419 : // output a trailing comma.
420 E : if (!stack_.empty() && !FirstEntry() && !PutChar(','))
421 i : return false;
422 :
423 : // Are we not pretty-printing? Then we're done!
424 E : if (!pretty_print_)
425 E : return true;
426 :
427 E : if (!OutputTrailingComment())
428 i : return false;
429 :
430 : // Go to a new line if need be.
431 E : if (!OutputNewline())
432 i : return false;
433 :
434 E : if (!OutputIndent())
435 i : return false;
436 :
437 E : return OutputComments();
438 E : }
439 :
440 E : bool JSONFileWriter::FirstEntry() const {
441 E : if (stack_.empty())
442 E : return true;
443 E : return !stack_.back().has_entries_;
444 E : }
445 :
446 E : bool JSONFileWriter::ReadyForKey() const {
447 E : if (stack_.empty())
448 i : return false;
449 E : return stack_.back().type_ == kDict;
450 E : }
451 :
452 E : bool JSONFileWriter::ReadyForValue() const {
453 E : if (finished_)
454 E : return false;
455 E : if (stack_.empty())
456 E : return true;
457 E : return stack_.back().type_ != kDict;
458 E : }
459 :
460 E : bool JSONFileWriter::RequireKeyValue() const {
461 E : if (stack_.empty())
462 E : return false;
463 E : return stack_.back().type_ == kDictKey;
464 E : }
465 :
466 E : bool JSONFileWriter::CanClose(StructureType type) const {
467 : // You can never 'close' a dict key.
468 E : if (stack_.empty() || type == kDictKey)
469 i : return false;
470 E : return stack_.back().type_ == type;
471 E : }
472 :
473 E : bool JSONFileWriter::OpenStructure(StructureType type) {
474 E : DCHECK_GT(arraysize(kStructureOpenings), static_cast<size_t>(type));
475 E : DCHECK(kStructureOpenings[type] != NULL);
476 :
477 : if (!ReadyForValue() ||
478 : !AlignForValueOrKey() ||
479 E : !Printf("%s", kStructureOpenings[type])) {
480 i : return false;
481 : }
482 :
483 : // Opening a new structure is like writing a new value, but the value has
484 : // not been *finished*.
485 E : FlushValue(false);
486 :
487 E : stack_.push_back(StackElement(type));
488 E : ++indent_depth_;
489 :
490 E : return true;
491 E : }
492 :
493 E : bool JSONFileWriter::CloseStructure(StructureType type) {
494 E : DCHECK_GT(arraysize(kStructureClosings), static_cast<size_t>(type));
495 E : DCHECK(kStructureClosings[type] != NULL);
496 :
497 : if (!CanClose(type) ||
498 : !OutputTrailingComment() ||
499 : !OutputNewline() ||
500 E : !OutputComments()) {
501 E : return false;
502 : }
503 :
504 E : stack_.pop_back();
505 E : --indent_depth_;
506 E : if (pretty_print_ && !OutputIndent())
507 i : return false;
508 :
509 E : if (!Printf("%s", kStructureClosings[type])) {
510 i : return false;
511 : }
512 :
513 : // If this closed the last open structure, then the JSON file is finished.
514 E : if (stack_.empty())
515 E : finished_ = true;
516 :
517 E : return true;
518 E : }
519 :
520 E : void JSONFileWriter::FlushValue(bool value_completed) {
521 : // The value was successfully written, so if we were in a dictionary waiting
522 : // for a value, pop the kDictKey entry off the stack.
523 E : if (RequireKeyValue())
524 E : stack_.pop_back();
525 :
526 : // If the stack is not empty, indicate that a value has been written to
527 : // the open structure.
528 E : if (!stack_.empty()) {
529 E : stack_.back().has_entries_ = true;
530 E : } else {
531 E : if (value_completed) {
532 : // If the stack is empty then having a written a single value means the
533 : // JSON file is finished.
534 E : finished_ = true;
535 : }
536 : }
537 E : }
538 :
539 : void JSONFileWriter::CompileAsserts() {
540 : COMPILE_ASSERT(
541 : arraysize(kStructureOpenings) == JSONFileWriter::kMaxStructureType,
542 : StructureOpenings_not_in_sync_with_StructureType_enum);
543 : COMPILE_ASSERT(
544 : arraysize(kStructureClosings) == JSONFileWriter::kMaxStructureType,
545 : StructureClosings_not_in_sync_with_StructureType_enum);
546 : }
547 :
548 : } // namespace core
|