/src/CMake/Source/cmJSONState.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | |
4 | | #include "cmJSONState.h" |
5 | | |
6 | | #include <istream> |
7 | | #include <iterator> |
8 | | #include <memory> |
9 | | #include <sstream> |
10 | | |
11 | | #include <cm3p/json/reader.h> |
12 | | #include <cm3p/json/value.h> |
13 | | #include <cm3p/json/version.h> |
14 | | |
15 | | #include "cmsys/FStream.hxx" |
16 | | |
17 | | #include "cmStringAlgorithms.h" |
18 | | #include "cmSystemTools.h" |
19 | | |
20 | | cmJSONState::cmJSONState(std::string jsonFile, Json::Value* root, |
21 | | StrictMode strictMode) |
22 | 64.4k | : Filename(std::move(jsonFile)) |
23 | 64.4k | { |
24 | 64.4k | cmsys::ifstream fin(this->Filename.c_str(), std::ios::in | std::ios::binary); |
25 | 64.4k | if (!fin) { |
26 | 375 | this->AddError(cmStrCat("File not found: ", this->Filename)); |
27 | 375 | return; |
28 | 375 | } |
29 | | |
30 | 64.1k | this->ReadJSONStream(fin, root, strictMode); |
31 | 64.1k | } |
32 | | |
33 | | cmJSONState::cmJSONState(std::istream& jsonIStream, Json::Value* root, |
34 | | StrictMode strictMode) |
35 | 7.15k | { |
36 | 7.15k | this->ReadJSONStream(jsonIStream, root, strictMode); |
37 | 7.15k | } |
38 | | |
39 | | void cmJSONState::AddError(std::string const& errMsg) |
40 | 30.0k | { |
41 | 30.0k | this->errors.emplace_back(errMsg); |
42 | 30.0k | } |
43 | | |
44 | | void cmJSONState::AddErrorAtValue(std::string const& errMsg, |
45 | | Json::Value const* value) |
46 | 923k | { |
47 | 923k | if (value && !value->isNull()) { |
48 | 908k | this->AddErrorAtOffset(errMsg, value->getOffsetStart()); |
49 | 908k | } else { |
50 | 15.3k | this->AddError(errMsg); |
51 | 15.3k | } |
52 | 923k | } |
53 | | |
54 | | void cmJSONState::AddErrorAtOffset(std::string const& errMsg, |
55 | | std::ptrdiff_t offset) |
56 | 928k | { |
57 | 928k | if (doc.empty()) { |
58 | 0 | this->AddError(errMsg); |
59 | 928k | } else { |
60 | 928k | Location loc = LocateInDocument(offset); |
61 | 928k | this->errors.emplace_back(loc, errMsg); |
62 | 928k | } |
63 | 928k | } |
64 | | |
65 | | std::string cmJSONState::GetErrorMessage(bool showContext) |
66 | 0 | { |
67 | 0 | std::string message; |
68 | 0 | std::string filenameName = cmSystemTools::GetFilenameName(this->Filename); |
69 | 0 | for (auto const& error : this->errors) { |
70 | 0 | Location loc = error.GetLocation(); |
71 | 0 | if (!filenameName.empty() && loc.line > 0) { |
72 | 0 | message = |
73 | 0 | cmStrCat(std::move(message), filenameName, ':', loc.line, ": "); |
74 | 0 | } |
75 | 0 | message = cmStrCat(std::move(message), error.GetErrorMessage(), '\n'); |
76 | 0 | if (showContext && loc.line > 0) { |
77 | 0 | message = cmStrCat(std::move(message), GetJsonContext(loc), '\n'); |
78 | 0 | } |
79 | 0 | } |
80 | 0 | if (!message.empty()) { |
81 | 0 | message.pop_back(); |
82 | 0 | } |
83 | 0 | return message; |
84 | 0 | } |
85 | | |
86 | | std::string cmJSONState::key() |
87 | 334k | { |
88 | 334k | if (!this->parseStack.empty()) { |
89 | 334k | return this->parseStack.back().first; |
90 | 334k | } |
91 | 0 | return ""; |
92 | 334k | } |
93 | | |
94 | | std::string cmJSONState::key_after(std::string const& k) |
95 | 51.3k | { |
96 | 197k | for (auto it = this->parseStack.begin(); it != this->parseStack.end(); |
97 | 174k | ++it) { |
98 | 174k | if (it->first == k) { |
99 | 29.5k | auto next = std::next(it); |
100 | 29.5k | if (next != this->parseStack.end()) { |
101 | 28.6k | return next->first; |
102 | 28.6k | } |
103 | 29.5k | } |
104 | 174k | } |
105 | 22.6k | return ""; |
106 | 51.3k | } |
107 | | |
108 | | Json::Value const* cmJSONState::value_after(std::string const& k) |
109 | 0 | { |
110 | 0 | for (auto it = this->parseStack.begin(); it != this->parseStack.end(); |
111 | 0 | ++it) { |
112 | 0 | if (it->first == k) { |
113 | 0 | auto next = std::next(it); |
114 | 0 | if (next != this->parseStack.end()) { |
115 | 0 | return next->second; |
116 | 0 | } |
117 | 0 | } |
118 | 0 | } |
119 | 0 | return nullptr; |
120 | 0 | } |
121 | | |
122 | | void cmJSONState::push_stack(std::string const& k, Json::Value const* value) |
123 | 11.5M | { |
124 | 11.5M | this->parseStack.emplace_back(k, value); |
125 | 11.5M | } |
126 | | |
127 | | void cmJSONState::pop_stack() |
128 | 11.5M | { |
129 | 11.5M | this->parseStack.pop_back(); |
130 | 11.5M | } |
131 | | |
132 | | void cmJSONState::ReadJSONStream(std::istream& jsonIStream, Json::Value* root, |
133 | | StrictMode strictMode) |
134 | 71.2k | { |
135 | | // If there's a BOM, toss it. |
136 | 71.2k | cmsys::FStream::ReadBOM(jsonIStream); |
137 | | |
138 | | // Save the entire document. |
139 | 71.2k | std::streampos inBegin = jsonIStream.tellg(); |
140 | 71.2k | this->doc = std::string(std::istreambuf_iterator<char>(jsonIStream), |
141 | 71.2k | std::istreambuf_iterator<char>()); |
142 | 71.2k | if (this->doc.empty()) { |
143 | 97 | this->AddError("A JSON document cannot be empty"); |
144 | 97 | return; |
145 | 97 | } |
146 | 71.1k | jsonIStream.seekg(inBegin); |
147 | | |
148 | 71.1k | Json::CharReaderBuilder builder; |
149 | 71.1k | if (strictMode == StrictMode::Strict) { |
150 | 49.3k | Json::CharReaderBuilder::strictMode(&builder.settings_); |
151 | 49.3k | } |
152 | 71.1k | std::string errMsg; |
153 | | |
154 | 71.1k | #if JSONCPP_VERSION_HEXA >= 0x01090600 |
155 | | // Has StructuredError |
156 | 71.1k | std::unique_ptr<Json::CharReader> const reader(builder.newCharReader()); |
157 | 71.1k | # if JSON_USE_EXCEPTION |
158 | | // Only this CharReader-based parse() can throw, and only when built with |
159 | | // support for exceptions. Older jsoncpp aborts on bad inputs instead. |
160 | 71.1k | try { |
161 | 71.1k | # endif |
162 | 71.1k | reader->parse(this->doc.data(), this->doc.data() + this->doc.size(), root, |
163 | 71.1k | &errMsg); |
164 | 71.1k | std::vector<Json::CharReader::StructuredError> structuredErrors = |
165 | 71.1k | reader->getStructuredErrors(); |
166 | 71.1k | for (auto const& structuredError : structuredErrors) { |
167 | 20.8k | this->AddErrorAtOffset(structuredError.message, |
168 | 20.8k | structuredError.offset_start); |
169 | 20.8k | } |
170 | 71.1k | # if JSON_USE_EXCEPTION |
171 | 71.1k | } catch (Json::Exception const& e) { |
172 | 165 | if (this->Filename.empty()) { |
173 | 51 | errMsg = cmStrCat("JSON Parse Error:\n ", e.what()); |
174 | 114 | } else { |
175 | 114 | errMsg = cmStrCat("JSON Parse Error: ", this->Filename, ":\n", e.what()); |
176 | 114 | } |
177 | 165 | this->AddError(errMsg); |
178 | 165 | } |
179 | 71.1k | # endif |
180 | | #else |
181 | | // No StructuredError Available, Use error string from jsonCpp |
182 | | if (!Json::parseFromStream(builder, jsonIStream, root, &errMsg)) { |
183 | | if (this->Filename.empty()) { |
184 | | errMsg = cmStrCat("JSON Parse Error:\n ", std::move(errMsg)); |
185 | | } else { |
186 | | errMsg = cmStrCat("JSON Parse Error: ", this->Filename, ":\n", |
187 | | std::move(errMsg)); |
188 | | } |
189 | | this->AddError(errMsg); |
190 | | } |
191 | | #endif |
192 | 71.1k | } |
193 | | |
194 | | std::string cmJSONState::GetJsonContext(Location loc) |
195 | 0 | { |
196 | 0 | std::string line; |
197 | 0 | std::stringstream sstream(doc); |
198 | 0 | for (int i = 0; i < loc.line; ++i) { |
199 | 0 | std::getline(sstream, line, '\n'); |
200 | 0 | } |
201 | 0 | return cmStrCat(line, '\n', std::string(loc.column - 1, ' '), '^'); |
202 | 0 | } |
203 | | |
204 | | cmJSONState::Location cmJSONState::LocateInDocument(ptrdiff_t offset) |
205 | 928k | { |
206 | 928k | int line = 1; |
207 | 928k | int col = 1; |
208 | 928k | char const* beginDoc = doc.data(); |
209 | 928k | char const* last = beginDoc + offset; |
210 | 8.78G | for (; beginDoc != last; ++beginDoc) { |
211 | 8.77G | switch (*beginDoc) { |
212 | 11.7M | case '\r': |
213 | 11.7M | if (beginDoc + 1 != last && beginDoc[1] == '\n') { |
214 | 1.20M | continue; // consume CRLF as a single token. |
215 | 1.20M | } |
216 | 10.5M | CM_FALLTHROUGH; // CR without a following LF is same as LF |
217 | 74.0M | case '\n': |
218 | 74.0M | col = 1; |
219 | 74.0M | ++line; |
220 | 74.0M | break; |
221 | 8.70G | default: |
222 | 8.70G | ++col; |
223 | 8.70G | break; |
224 | 8.77G | } |
225 | 8.77G | } |
226 | 928k | return { line, col }; |
227 | 928k | } |