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