diff options
Diffstat (limited to 'Source/cmJSONState.cxx')
-rw-r--r-- | Source/cmJSONState.cxx | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/Source/cmJSONState.cxx b/Source/cmJSONState.cxx new file mode 100644 index 0000000..92bde77 --- /dev/null +++ b/Source/cmJSONState.cxx @@ -0,0 +1,163 @@ +/* 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 <sstream> + +#include <cm/memory> + +#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 }; +} |