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