Coverage for /Syzygy/core/json_file_writer.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
86.7%2552940.C++source

Line-by-line coverage:

   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

Coverage information generated Thu Sep 06 11:30:46 2012.