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/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

Coverage information generated Tue Jun 25 13:56:24 2013.