Line data Source code
1 : //* This file is part of the MOOSE framework
2 : //* https://mooseframework.inl.gov
3 : //*
4 : //* All rights reserved, see COPYRIGHT for full restrictions
5 : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 : //*
7 : //* Licensed under LGPL 2.1, please see LICENSE for details
8 : //* https://www.gnu.org/licenses/lgpl-2.1.html
9 :
10 : #include "JsonSyntaxTree.h"
11 :
12 : // MOOSE includes
13 : #include "MooseEnum.h"
14 : #include "MultiMooseEnum.h"
15 : #include "ExecFlagEnum.h"
16 : #include "Builder.h"
17 : #include "pcrecpp.h"
18 : #include "Action.h"
19 : #include "AppFactory.h"
20 : #include "Registry.h"
21 : #include "MooseUtils.h"
22 :
23 : #include "libmesh/vector_value.h"
24 :
25 : // C++ includes
26 : #include <algorithm>
27 : #include <cctype>
28 :
29 183 : JsonSyntaxTree::JsonSyntaxTree(const std::string & search_string) : _search(search_string)
30 : {
31 : // Registry holds a map with labels (ie MooseApp) as keys and a vector of RegistryEntry
32 : // as values. We need the reverse map: given an action or object name then get the label.
33 183 : auto & actmap = Registry::allActions();
34 549 : for (auto & entry : actmap)
35 38712 : for (auto & act : entry.second)
36 38346 : _action_label_map[act->_classname] = std::make_pair(entry.first, act->_file);
37 :
38 183 : auto & objmap = Registry::allObjects();
39 549 : for (auto & entry : objmap)
40 274086 : for (auto & obj : entry.second)
41 273720 : _object_label_map[obj->name()] = std::make_pair(entry.first, obj->_file);
42 183 : }
43 :
44 : std::vector<std::string>
45 1151936 : JsonSyntaxTree::splitPath(const std::string & path)
46 : {
47 1151936 : std::string s;
48 1151936 : std::istringstream f(path);
49 1151936 : std::vector<std::string> paths;
50 4907315 : while (std::getline(f, s, '/'))
51 2603443 : if (s.size() > 0)
52 2603443 : paths.push_back(s);
53 2303872 : return paths;
54 1151936 : }
55 :
56 : nlohmann::json &
57 419882 : JsonSyntaxTree::getJson(const std::string & path)
58 : {
59 419882 : auto paths = splitPath(path);
60 : mooseAssert(paths.size() > 0, "path is empty");
61 419882 : auto * next = &(_root["blocks"][paths[0]]);
62 :
63 877983 : for (auto pit = paths.begin() + 1; pit != paths.end(); ++pit)
64 : {
65 458101 : if (*pit == "*")
66 : // It has an action syntax as a parent
67 388863 : next = &(*next)["star"];
68 69238 : else if (*pit == "<type>")
69 0 : next = &(*next)["types"];
70 : else
71 69238 : next = &(*next)["subblocks"][*pit];
72 : }
73 419882 : return *next;
74 419882 : }
75 :
76 : nlohmann::json &
77 415592 : JsonSyntaxTree::getJson(const std::string & parent, const std::string & path, bool is_type)
78 : {
79 415592 : if (parent.empty())
80 : {
81 49400 : auto & j = getJson(path);
82 49400 : if (path.back() == '*' && !j.contains("subblock_types"))
83 10812 : j["subblock_types"] = nlohmann::json();
84 38588 : else if (path.back() != '*' && !j.contains("types"))
85 5824 : j["types"] = nlohmann::json();
86 49400 : return j["actions"];
87 : }
88 :
89 366192 : auto & parent_json = getJson(parent);
90 366192 : auto paths = splitPath(path);
91 366192 : std::string key = "subblock_types";
92 366192 : if (is_type)
93 47103 : key = "types";
94 366192 : auto & val = parent_json[key][paths.back()];
95 366192 : return val;
96 366192 : }
97 :
98 : size_t
99 427641 : JsonSyntaxTree::setParams(InputParameters * params, bool search_match, nlohmann::json & all_params)
100 : {
101 427641 : size_t count = 0;
102 14579769 : for (auto & iter : *params)
103 : {
104 : // Make sure we want to see this parameter
105 14152128 : bool param_match = !_search.empty() && MooseUtils::wildCardMatch(iter.first, _search);
106 14152128 : if (params->isPrivate(iter.first) || (!_search.empty() && !search_match && !param_match))
107 7487367 : continue;
108 :
109 6664761 : ++count;
110 6664761 : nlohmann::json param_json;
111 :
112 6664761 : param_json["required"] = params->isParamRequired(iter.first);
113 :
114 : // Only output default if it has one
115 6664761 : if (params->isParamValid(iter.first))
116 4488401 : param_json["default"] = buildOutputString(iter);
117 2176360 : else if (params->hasDefaultCoupledValue(iter.first))
118 5575 : param_json["default"] = params->defaultCoupledValue(iter.first);
119 :
120 6664761 : bool out_of_range_allowed = false;
121 6664761 : std::map<MooseEnumItem, std::string> docs;
122 6664761 : param_json["options"] = buildOptions(iter, out_of_range_allowed, docs);
123 6664761 : if (!nlohmann::to_string(param_json["options"]).empty())
124 : {
125 6664761 : param_json["out_of_range_allowed"] = out_of_range_allowed;
126 6664761 : if (!docs.empty())
127 : {
128 1485 : nlohmann::json jdocs;
129 5445 : for (const auto & doc : docs)
130 3960 : jdocs[doc.first.name()] = doc.second;
131 1485 : param_json["option_docs"] = jdocs;
132 1485 : }
133 : }
134 6664761 : auto reserved_values = params->reservedValues(iter.first);
135 6891779 : for (const auto & reserved : reserved_values)
136 227018 : param_json["reserved_values"].push_back(reserved);
137 :
138 6664761 : std::string t = MooseUtils::prettyCppType(params->type(iter.first));
139 6664761 : param_json["cpp_type"] = t;
140 6664761 : param_json["basic_type"] = basicCppType(t);
141 6664761 : param_json["group_name"] = params->getGroupName(iter.first);
142 6664761 : param_json["name"] = iter.first;
143 :
144 6664761 : std::string doc = params->getDocString(iter.first);
145 6664761 : MooseUtils::escape(doc);
146 6664761 : param_json["description"] = doc;
147 :
148 6664761 : param_json["doc_unit"] = params->getDocUnit(iter.first);
149 :
150 6664761 : param_json["controllable"] = params->isControllable(iter.first);
151 6664761 : param_json["deprecated"] = params->isParamDeprecated(iter.first);
152 6664761 : all_params[iter.first] = param_json;
153 6664761 : }
154 427641 : return count;
155 : }
156 :
157 : void
158 183 : JsonSyntaxTree::addGlobal()
159 : {
160 : // If they are doing a search they probably don't want to see this
161 183 : if (_search.empty())
162 : {
163 165 : auto params = Moose::Builder::validParams();
164 165 : nlohmann::json jparams;
165 165 : setParams(¶ms, true, jparams);
166 165 : _root["global"]["parameters"] = jparams;
167 :
168 : // Just create a list of registered app names
169 165 : nlohmann::json apps;
170 165 : auto & factory = AppFactory::instance();
171 330 : for (const auto & name_bi_pair : factory.registeredObjects())
172 165 : apps.push_back(name_bi_pair.first);
173 :
174 165 : _root["global"]["registered_apps"] = apps;
175 165 : }
176 183 : }
177 :
178 : bool
179 428025 : JsonSyntaxTree::addParameters(const std::string & parent,
180 : const std::string & path,
181 : bool is_type,
182 : const std::string & action,
183 : bool is_action,
184 : InputParameters * params,
185 : const FileLineInfo & lineinfo,
186 : const std::string & classname)
187 : {
188 428025 : if (action == "EmptyAction")
189 549 : return false;
190 :
191 427476 : nlohmann::json all_params;
192 897139 : bool search_match = !_search.empty() && (MooseUtils::wildCardMatch(path, _search) ||
193 469663 : MooseUtils::wildCardMatch(action, _search) ||
194 469663 : MooseUtils::wildCardMatch(parent, _search));
195 427476 : auto count = setParams(params, search_match, all_params);
196 427476 : if (!_search.empty() && count == 0)
197 : // no parameters that matched the search string
198 42173 : return false;
199 :
200 385303 : nlohmann::json & json = getJson(parent, path, is_type);
201 :
202 385303 : if (is_action)
203 : {
204 19111 : json[action]["parameters"] = all_params;
205 19111 : json[action]["description"] = params->getClassDescription();
206 19111 : json[action]["action_path"] = path;
207 19111 : auto label_pair = getActionLabel(action);
208 19111 : json[action]["label"] = label_pair.first;
209 19111 : json[action]["register_file"] = label_pair.second;
210 19111 : if (lineinfo.isValid())
211 15433 : json[action]["file_info"][lineinfo.file()] = lineinfo.line();
212 19111 : }
213 366192 : else if (params)
214 : {
215 366192 : if (params->isParamValid("_moose_base"))
216 366192 : json["moose_base"] = params->get<std::string>("_moose_base");
217 :
218 366192 : json["parameters"] = all_params;
219 366192 : json["syntax_path"] = path;
220 366192 : json["parent_syntax"] = parent;
221 366192 : json["description"] = params->getClassDescription();
222 : // We do this for ActionComponents which are registered as Actions but
223 : // dumped to the syntax tree as Objects
224 366192 : if (params->isParamValid("_moose_base") && json["moose_base"] == "Action")
225 : {
226 330 : auto label_pair = getActionLabel(classname);
227 330 : json["label"] = label_pair.first;
228 330 : json["register_file"] = label_pair.second;
229 330 : }
230 : else
231 : {
232 365862 : auto label_pair = getObjectLabel(path);
233 365862 : json["label"] = label_pair.first;
234 365862 : json["register_file"] = label_pair.second;
235 365862 : }
236 366192 : if (lineinfo.isValid())
237 : {
238 366192 : json["file_info"][lineinfo.file()] = lineinfo.line();
239 366192 : if (!classname.empty())
240 16781 : json["class"] = classname;
241 : }
242 : }
243 385303 : return true;
244 427476 : }
245 :
246 : std::string
247 6664761 : JsonSyntaxTree::buildOptions(const std::iterator_traits<InputParameters::iterator>::value_type & p,
248 : bool & out_of_range_allowed,
249 : std::map<MooseEnumItem, std::string> & docs)
250 : {
251 6664761 : libMesh::Parameters::Value * val = MooseUtils::get(p.second);
252 :
253 6664761 : std::string options;
254 : {
255 6664761 : auto * enum_type = dynamic_cast<InputParameters::Parameter<MooseEnum> *>(val);
256 6664761 : if (enum_type)
257 : {
258 195834 : out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
259 195834 : options = enum_type->get().getRawNames();
260 195834 : docs = enum_type->get().getItemDocumentation();
261 : }
262 : }
263 : {
264 6664761 : auto * enum_type = dynamic_cast<InputParameters::Parameter<MultiMooseEnum> *>(val);
265 6664761 : if (enum_type)
266 : {
267 205171 : out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
268 205171 : options = enum_type->get().getRawNames();
269 205171 : docs = enum_type->get().getItemDocumentation();
270 : }
271 : }
272 : {
273 6664761 : auto * enum_type = dynamic_cast<InputParameters::Parameter<ExecFlagEnum> *>(val);
274 6664761 : if (enum_type)
275 : {
276 179545 : out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
277 179545 : options = enum_type->get().getRawNames();
278 179545 : docs = enum_type->get().getItemDocumentation();
279 : }
280 : }
281 : {
282 6664761 : auto * enum_type = dynamic_cast<InputParameters::Parameter<std::vector<MooseEnum>> *>(val);
283 6664761 : if (enum_type)
284 : {
285 330 : out_of_range_allowed = (enum_type->get())[0].isOutOfRangeAllowed();
286 330 : options = (enum_type->get())[0].getRawNames();
287 330 : docs = enum_type->get()[0].getItemDocumentation();
288 : }
289 : }
290 : {
291 6664761 : auto * enum_type = dynamic_cast<InputParameters::Parameter<std::vector<MultiMooseEnum>> *>(val);
292 6664761 : if (enum_type)
293 : {
294 165 : out_of_range_allowed = (enum_type->get())[0].isOutOfRangeAllowed();
295 165 : options = (enum_type->get())[0].getRawNames();
296 165 : docs = enum_type->get()[0].getItemDocumentation();
297 : }
298 : }
299 6664761 : return options;
300 0 : }
301 :
302 : std::string
303 4488433 : JsonSyntaxTree::buildOutputString(
304 : const std::iterator_traits<InputParameters::iterator>::value_type & p)
305 : {
306 4488433 : libMesh::Parameters::Value * val = MooseUtils::get(p.second);
307 :
308 : // Account for Point
309 4488433 : std::stringstream str;
310 4488433 : InputParameters::Parameter<Point> * ptr0 = dynamic_cast<InputParameters::Parameter<Point> *>(val);
311 :
312 : // Account for RealVectorValues
313 : InputParameters::Parameter<RealVectorValue> * ptr1 =
314 4488433 : dynamic_cast<InputParameters::Parameter<RealVectorValue> *>(val);
315 :
316 : // Output the Point components
317 4488433 : if (ptr0)
318 4564 : str << ptr0->get().operator()(0) << " " << ptr0->get().operator()(1) << " "
319 4564 : << ptr0->get().operator()(2);
320 :
321 : // Output the RealVectorValue components
322 4483869 : else if (ptr1)
323 1980 : str << ptr1->get().operator()(0) << " " << ptr1->get().operator()(1) << " "
324 1980 : << ptr1->get().operator()(2);
325 :
326 : // General case, call the print operator
327 : else
328 4481889 : val->print(str);
329 :
330 : // remove additional '\n' possibly generated in output (breaks JSON parsing)
331 4488433 : std::string tmp_str = str.str();
332 20770738 : for (auto & ch : tmp_str)
333 16282305 : if (ch == '\n')
334 0 : ch = ' ';
335 :
336 8976866 : return tmp_str.substr(0, tmp_str.find("<RESIDUAL>"));
337 4488433 : }
338 :
339 : void
340 4758 : JsonSyntaxTree::addSyntaxType(const std::string & path, const std::string type)
341 : {
342 4758 : if (MooseUtils::wildCardMatch(path, _search))
343 : {
344 4290 : auto & j = getJson(path);
345 4290 : j["associated_types"].push_back(type);
346 : }
347 : // If they are doing a search they probably don't want to see this
348 4758 : if (_search.empty())
349 : {
350 4290 : _root["global"]["associated_types"][type].push_back(path);
351 : }
352 4758 : }
353 :
354 : void
355 30289 : JsonSyntaxTree::addActionTask(const std::string & path,
356 : const std::string & action,
357 : const std::string & task_name,
358 : const FileLineInfo & lineinfo)
359 : {
360 30289 : nlohmann::json & json = getJson("", path, false);
361 30289 : if (lineinfo.isValid())
362 30289 : json[action]["tasks"][task_name]["file_info"][lineinfo.file()] = lineinfo.line();
363 30289 : }
364 :
365 : std::string
366 8530791 : JsonSyntaxTree::basicCppType(const std::string & cpp_type)
367 : {
368 8530791 : std::string s = "String";
369 15212894 : if (cpp_type.find("std::vector") != std::string::npos ||
370 13351012 : cpp_type.find("libMesh::VectorValue") != std::string::npos ||
371 21881803 : cpp_type.find("libMesh::TensorValue") != std::string::npos ||
372 6668586 : cpp_type.find("Eigen::Matrix") != std::string::npos)
373 : {
374 : // Get the template type and use its basic type for the array type
375 1865965 : pcrecpp::RE r("^[^<]+<\\s*(.*)\\s*>$");
376 1865965 : std::string t;
377 1865965 : r.FullMatch(cpp_type, &t);
378 :
379 : // Capture type just to the first comma for Eigen::Matrix<type,V,W,X,Y,Z>
380 1865965 : if (cpp_type.find("Eigen::Matrix") != std::string::npos)
381 3760 : t = t.substr(0, t.find(","));
382 :
383 1865965 : s = "Array:" + basicCppType(t);
384 1865965 : }
385 13124315 : else if (cpp_type.find("MultiMooseEnum") != std::string::npos ||
386 12739433 : cpp_type.find("ExecFlagEnum") != std::string::npos ||
387 19404259 : cpp_type.find("VectorPostprocessorName") != std::string::npos ||
388 6274994 : cpp_type.find("std::map") != std::string::npos)
389 394403 : s = "Array:String";
390 6270423 : else if (cpp_type.find("libMesh::Point") != std::string::npos)
391 26949 : s = "Array:Real";
392 18116606 : else if (cpp_type == "int" || cpp_type == "unsigned int" || cpp_type == "short" ||
393 17209605 : cpp_type == "unsigned short" || cpp_type == "char" || cpp_type == "unsigned char" ||
394 23812682 : cpp_type == "long" || cpp_type == "unsigned long" || cpp_type == "long long" ||
395 5720667 : cpp_type == "unsigned long long")
396 522807 : s = "Integer";
397 5720667 : else if (cpp_type == "double" || cpp_type == "float")
398 381872 : s = "Real";
399 5338795 : else if (cpp_type == "bool")
400 2180720 : s = "Boolean";
401 :
402 8530791 : return s;
403 0 : }
404 :
405 : std::pair<std::string, std::string>
406 365862 : JsonSyntaxTree::getObjectLabel(const std::string & obj) const
407 : {
408 365862 : auto paths = splitPath(obj);
409 365862 : auto it = _object_label_map.find(paths.back());
410 365862 : if (it != _object_label_map.end())
411 365862 : return it->second;
412 : else
413 0 : return std::make_pair("", "");
414 365862 : }
415 :
416 : std::pair<std::string, std::string>
417 19441 : JsonSyntaxTree::getActionLabel(const std::string & action) const
418 : {
419 19441 : auto it = _action_label_map.find(action);
420 19441 : if (it != _action_label_map.end())
421 19441 : return it->second;
422 : else
423 0 : return std::make_pair("", "");
424 : }
|