Coverage for /Syzygy/core/json_file_writer.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
86.6%2522910.C++source

Line-by-line coverage:

   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

Coverage information generated Thu Jan 14 17:40:38 2016.