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