123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing for details. */
- #include "cmJSONState.h"
- #include <iterator>
- #include <sstream>
- #include <cm3p/json/reader.h>
- #include <cm3p/json/value.h>
- #include "cmsys/FStream.hxx"
- #include "cmStringAlgorithms.h"
- cmJSONState::cmJSONState(const std::string& filename, Json::Value* root)
- {
- cmsys::ifstream fin(filename.c_str(), std::ios::in | std::ios::binary);
- if (!fin) {
- this->AddError(cmStrCat("File not found: ", filename));
- return;
- }
- // If there's a BOM, toss it.
- cmsys::FStream::ReadBOM(fin);
- // Save the entire document.
- std::streampos finBegin = fin.tellg();
- this->doc = std::string(std::istreambuf_iterator<char>(fin),
- std::istreambuf_iterator<char>());
- if (this->doc.empty()) {
- this->AddError("A JSON document cannot be empty");
- return;
- }
- fin.seekg(finBegin);
- // Parse the document.
- Json::CharReaderBuilder builder;
- Json::CharReaderBuilder::strictMode(&builder.settings_);
- std::string errMsg;
- if (!Json::parseFromStream(builder, fin, root, &errMsg)) {
- errMsg = cmStrCat("JSON Parse Error: ", filename, ":\n", errMsg);
- this->AddError(errMsg);
- }
- }
- void cmJSONState::AddError(std::string const& errMsg)
- {
- this->errors.push_back(Error(errMsg));
- }
- void cmJSONState::AddErrorAtValue(std::string const& errMsg,
- const Json::Value* value)
- {
- if (value && !value->isNull()) {
- this->AddErrorAtOffset(errMsg, value->getOffsetStart());
- } else {
- this->AddError(errMsg);
- }
- }
- void cmJSONState::AddErrorAtOffset(std::string const& errMsg,
- std::ptrdiff_t offset)
- {
- if (doc.empty()) {
- this->AddError(errMsg);
- } else {
- Location loc = LocateInDocument(offset);
- this->errors.push_back(Error(loc, errMsg));
- }
- }
- std::string cmJSONState::GetErrorMessage(bool showContext)
- {
- std::string message;
- for (auto const& error : this->errors) {
- message = cmStrCat(message, error.GetErrorMessage(), "\n");
- if (showContext) {
- Location loc = error.GetLocation();
- if (loc.column > 0) {
- message = cmStrCat(message, GetJsonContext(loc), "\n");
- }
- }
- }
- message = cmStrCat("\n", message);
- message.pop_back();
- return message;
- }
- std::string cmJSONState::key()
- {
- if (!this->parseStack.empty()) {
- return this->parseStack.back().first;
- }
- return "";
- }
- std::string cmJSONState::key_after(std::string const& k)
- {
- for (auto it = this->parseStack.begin(); it != this->parseStack.end();
- ++it) {
- if (it->first == k && (++it) != this->parseStack.end()) {
- return it->first;
- }
- }
- return "";
- }
- const Json::Value* cmJSONState::value_after(std::string const& k)
- {
- for (auto it = this->parseStack.begin(); it != this->parseStack.end();
- ++it) {
- if (it->first == k && (++it) != this->parseStack.end()) {
- return it->second;
- }
- }
- return nullptr;
- }
- void cmJSONState::push_stack(std::string const& k, const Json::Value* value)
- {
- this->parseStack.push_back(JsonPair(k, value));
- }
- void cmJSONState::pop_stack()
- {
- this->parseStack.pop_back();
- }
- std::string cmJSONState::GetJsonContext(Location loc)
- {
- std::string line;
- std::stringstream sstream(doc);
- for (int i = 0; i < loc.line; ++i) {
- std::getline(sstream, line, '\n');
- }
- return cmStrCat(line, '\n', std::string(loc.column - 1, ' '), '^');
- }
- cmJSONState::Location cmJSONState::LocateInDocument(ptrdiff_t offset)
- {
- int line = 1;
- int col = 1;
- const char* beginDoc = doc.data();
- const char* last = beginDoc + offset;
- for (; beginDoc != last; ++beginDoc) {
- switch (*beginDoc) {
- case '\r':
- if (beginDoc + 1 != last && beginDoc[1] == '\n') {
- continue; // consume CRLF as a single token.
- }
- CM_FALLTHROUGH; // CR without a following LF is same as LF
- case '\n':
- col = 1;
- ++line;
- break;
- default:
- ++col;
- break;
- }
- }
- return { line, col };
- }
|