cmJSONState.cxx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmJSONState.h"
  4. #include <iterator>
  5. #include <sstream>
  6. #include <cm3p/json/reader.h>
  7. #include <cm3p/json/value.h>
  8. #include "cmsys/FStream.hxx"
  9. #include "cmStringAlgorithms.h"
  10. cmJSONState::cmJSONState(const std::string& filename, Json::Value* root)
  11. {
  12. cmsys::ifstream fin(filename.c_str(), std::ios::in | std::ios::binary);
  13. if (!fin) {
  14. this->AddError(cmStrCat("File not found: ", filename));
  15. return;
  16. }
  17. // If there's a BOM, toss it.
  18. cmsys::FStream::ReadBOM(fin);
  19. // Save the entire document.
  20. std::streampos finBegin = fin.tellg();
  21. this->doc = std::string(std::istreambuf_iterator<char>(fin),
  22. std::istreambuf_iterator<char>());
  23. if (this->doc.empty()) {
  24. this->AddError("A JSON document cannot be empty");
  25. return;
  26. }
  27. fin.seekg(finBegin);
  28. // Parse the document.
  29. Json::CharReaderBuilder builder;
  30. Json::CharReaderBuilder::strictMode(&builder.settings_);
  31. std::string errMsg;
  32. if (!Json::parseFromStream(builder, fin, root, &errMsg)) {
  33. errMsg = cmStrCat("JSON Parse Error: ", filename, ":\n", errMsg);
  34. this->AddError(errMsg);
  35. }
  36. }
  37. void cmJSONState::AddError(std::string const& errMsg)
  38. {
  39. this->errors.push_back(Error(errMsg));
  40. }
  41. void cmJSONState::AddErrorAtValue(std::string const& errMsg,
  42. const Json::Value* value)
  43. {
  44. if (value && !value->isNull()) {
  45. this->AddErrorAtOffset(errMsg, value->getOffsetStart());
  46. } else {
  47. this->AddError(errMsg);
  48. }
  49. }
  50. void cmJSONState::AddErrorAtOffset(std::string const& errMsg,
  51. std::ptrdiff_t offset)
  52. {
  53. if (doc.empty()) {
  54. this->AddError(errMsg);
  55. } else {
  56. Location loc = LocateInDocument(offset);
  57. this->errors.push_back(Error(loc, errMsg));
  58. }
  59. }
  60. std::string cmJSONState::GetErrorMessage(bool showContext)
  61. {
  62. std::string message;
  63. for (auto const& error : this->errors) {
  64. message = cmStrCat(message, error.GetErrorMessage(), "\n");
  65. if (showContext) {
  66. Location loc = error.GetLocation();
  67. if (loc.column > 0) {
  68. message = cmStrCat(message, GetJsonContext(loc), "\n");
  69. }
  70. }
  71. }
  72. message = cmStrCat("\n", message);
  73. message.pop_back();
  74. return message;
  75. }
  76. std::string cmJSONState::key()
  77. {
  78. if (!this->parseStack.empty()) {
  79. return this->parseStack.back().first;
  80. }
  81. return "";
  82. }
  83. std::string cmJSONState::key_after(std::string const& k)
  84. {
  85. for (auto it = this->parseStack.begin(); it != this->parseStack.end();
  86. ++it) {
  87. if (it->first == k && (++it) != this->parseStack.end()) {
  88. return it->first;
  89. }
  90. }
  91. return "";
  92. }
  93. const Json::Value* cmJSONState::value_after(std::string const& k)
  94. {
  95. for (auto it = this->parseStack.begin(); it != this->parseStack.end();
  96. ++it) {
  97. if (it->first == k && (++it) != this->parseStack.end()) {
  98. return it->second;
  99. }
  100. }
  101. return nullptr;
  102. }
  103. void cmJSONState::push_stack(std::string const& k, const Json::Value* value)
  104. {
  105. this->parseStack.push_back(JsonPair(k, value));
  106. }
  107. void cmJSONState::pop_stack()
  108. {
  109. this->parseStack.pop_back();
  110. }
  111. std::string cmJSONState::GetJsonContext(Location loc)
  112. {
  113. std::string line;
  114. std::stringstream sstream(doc);
  115. for (int i = 0; i < loc.line; ++i) {
  116. std::getline(sstream, line, '\n');
  117. }
  118. return cmStrCat(line, '\n', std::string(loc.column - 1, ' '), '^');
  119. }
  120. cmJSONState::Location cmJSONState::LocateInDocument(ptrdiff_t offset)
  121. {
  122. int line = 1;
  123. int col = 1;
  124. const char* beginDoc = doc.data();
  125. const char* last = beginDoc + offset;
  126. for (; beginDoc != last; ++beginDoc) {
  127. switch (*beginDoc) {
  128. case '\r':
  129. if (beginDoc + 1 != last && beginDoc[1] == '\n') {
  130. continue; // consume CRLF as a single token.
  131. }
  132. CM_FALLTHROUGH; // CR without a following LF is same as LF
  133. case '\n':
  134. col = 1;
  135. ++line;
  136. break;
  137. default:
  138. ++col;
  139. break;
  140. }
  141. }
  142. return { line, col };
  143. }