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 24 : 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 24 : auto & actmap = Registry::allActions();
34 72 : for (auto & entry : actmap)
35 5368 : for (auto & act : entry.second)
36 5320 : _action_label_map[act->_classname] = std::make_pair(entry.first, act->_file);
37 :
38 24 : auto & objmap = Registry::allObjects();
39 72 : for (auto & entry : objmap)
40 40096 : for (auto & obj : entry.second)
41 40048 : _object_label_map[obj->name()] = std::make_pair(entry.first, obj->_file);
42 24 : }
43 :
44 : std::vector<std::string>
45 123316 : JsonSyntaxTree::splitPath(const std::string & path)
46 : {
47 123316 : std::string s;
48 123316 : std::istringstream f(path);
49 123316 : std::vector<std::string> paths;
50 532049 : while (std::getline(f, s, '/'))
51 285417 : if (s.size() > 0)
52 285417 : paths.push_back(s);
53 246632 : return paths;
54 123316 : }
55 :
56 : nlohmann::json &
57 44393 : JsonSyntaxTree::getJson(const std::string & path)
58 : {
59 44393 : auto paths = splitPath(path);
60 : mooseAssert(paths.size() > 0, "path is empty");
61 44393 : auto * next = &(_root["blocks"][paths[0]]);
62 :
63 95751 : for (auto pit = paths.begin() + 1; pit != paths.end(); ++pit)
64 : {
65 51358 : if (*pit == "*")
66 : // It has an action syntax as a parent
67 44694 : next = &(*next)["star"];
68 6664 : else if (*pit == "<type>")
69 0 : next = &(*next)["types"];
70 : else
71 6664 : next = &(*next)["subblocks"][*pit];
72 : }
73 44393 : return *next;
74 44393 : }
75 :
76 : nlohmann::json &
77 43978 : JsonSyntaxTree::getJson(const std::string & parent, const std::string & path, bool is_type)
78 : {
79 43978 : if (parent.empty())
80 : {
81 4494 : auto & j = getJson(path);
82 4494 : if (path.back() == '*' && !j.contains("subblock_types"))
83 1017 : j["subblock_types"] = nlohmann::json();
84 3477 : else if (path.back() != '*' && !j.contains("types"))
85 504 : j["types"] = nlohmann::json();
86 4494 : return j["actions"];
87 : }
88 :
89 39484 : auto & parent_json = getJson(parent);
90 39484 : auto paths = splitPath(path);
91 39484 : std::string key = "subblock_types";
92 39484 : if (is_type)
93 4575 : key = "types";
94 39484 : auto & val = parent_json[key][paths.back()];
95 39484 : return val;
96 39484 : }
97 :
98 : size_t
99 66571 : JsonSyntaxTree::setParams(InputParameters * params, bool search_match, nlohmann::json & all_params)
100 : {
101 66571 : size_t count = 0;
102 2286403 : for (auto & iter : *params)
103 : {
104 : // Make sure we want to see this parameter
105 2219832 : bool param_match = !_search.empty() && MooseUtils::wildCardMatch(iter.first, _search);
106 2219832 : if (params->isPrivate(iter.first) || (!_search.empty() && !search_match && !param_match))
107 1500405 : continue;
108 :
109 719427 : ++count;
110 719427 : nlohmann::json param_json;
111 :
112 719427 : param_json["required"] = params->isParamRequired(iter.first);
113 :
114 : // Only output default if it has one
115 719427 : if (params->isParamValid(iter.first))
116 486713 : param_json["default"] = buildOutputString(iter);
117 232714 : else if (params->hasDefaultCoupledValue(iter.first))
118 605 : param_json["default"] = params->defaultCoupledValue(iter.first);
119 :
120 719427 : bool out_of_range_allowed = false;
121 719427 : std::map<MooseEnumItem, std::string> docs;
122 719427 : param_json["options"] = buildOptions(iter, out_of_range_allowed, docs);
123 719427 : if (!nlohmann::to_string(param_json["options"]).empty())
124 : {
125 719427 : param_json["out_of_range_allowed"] = out_of_range_allowed;
126 719427 : if (!docs.empty())
127 : {
128 465 : nlohmann::json jdocs;
129 2085 : for (const auto & doc : docs)
130 1620 : jdocs[doc.first.name()] = doc.second;
131 465 : param_json["option_docs"] = jdocs;
132 465 : }
133 : }
134 719427 : auto reserved_values = params->reservedValues(iter.first);
135 741781 : for (const auto & reserved : reserved_values)
136 22354 : param_json["reserved_values"].push_back(reserved);
137 :
138 719427 : std::string t = MooseUtils::prettyCppType(params->type(iter.first));
139 719427 : param_json["cpp_type"] = t;
140 719427 : param_json["basic_type"] = basicCppType(t);
141 719427 : param_json["group_name"] = params->getGroupName(iter.first);
142 719427 : param_json["name"] = iter.first;
143 :
144 719427 : std::string doc = params->getDocString(iter.first);
145 719427 : MooseUtils::escape(doc);
146 719427 : param_json["description"] = doc;
147 :
148 719427 : param_json["doc_unit"] = params->getDocUnit(iter.first);
149 719427 : param_json["doc_range"] =
150 2149068 : params->isRangeChecked(iter.first) ? params->rangeCheckedFunction(iter.first) : "";
151 :
152 719427 : param_json["controllable"] = params->isControllable(iter.first);
153 719427 : param_json["deprecated"] = params->isParamDeprecated(iter.first);
154 719427 : all_params[iter.first] = param_json;
155 719427 : }
156 66571 : return count;
157 : }
158 :
159 : void
160 24 : JsonSyntaxTree::addGlobal()
161 : {
162 : // If they are doing a search they probably don't want to see this
163 24 : if (_search.empty())
164 : {
165 15 : auto params = Moose::Builder::validParams();
166 15 : nlohmann::json jparams;
167 15 : setParams(¶ms, true, jparams);
168 15 : _root["global"]["parameters"] = jparams;
169 :
170 : // Just create a list of registered app names
171 15 : nlohmann::json apps;
172 15 : auto & factory = AppFactory::instance();
173 30 : for (const auto & name_bi_pair : factory.registeredObjects())
174 15 : apps.push_back(name_bi_pair.first);
175 :
176 15 : _root["global"]["registered_apps"] = apps;
177 15 : }
178 24 : }
179 :
180 : bool
181 66628 : JsonSyntaxTree::addParameters(const std::string & parent,
182 : const std::string & path,
183 : bool is_type,
184 : const std::string & action,
185 : bool is_action,
186 : InputParameters * params,
187 : const FileLineInfo & lineinfo,
188 : const std::string & classname)
189 : {
190 66628 : if (action == "EmptyAction")
191 72 : return false;
192 :
193 66556 : nlohmann::json all_params;
194 158444 : bool search_match = !_search.empty() && (MooseUtils::wildCardMatch(path, _search) ||
195 91888 : MooseUtils::wildCardMatch(action, _search) ||
196 91888 : MooseUtils::wildCardMatch(parent, _search));
197 66556 : auto count = setParams(params, search_match, all_params);
198 66556 : if (!_search.empty() && count == 0)
199 : // no parameters that matched the search string
200 25326 : return false;
201 :
202 41230 : nlohmann::json & json = getJson(parent, path, is_type);
203 :
204 41230 : if (is_action)
205 : {
206 1746 : json[action]["parameters"] = all_params;
207 1746 : json[action]["description"] = params->getClassDescription();
208 1746 : json[action]["action_path"] = path;
209 1746 : auto label_pair = getActionLabel(action);
210 1746 : json[action]["label"] = label_pair.first;
211 1746 : json[action]["register_file"] = label_pair.second;
212 1746 : if (lineinfo.isValid())
213 1392 : json[action]["file_info"][lineinfo.file()] = lineinfo.line();
214 1746 : }
215 39484 : else if (params)
216 : {
217 39484 : if (params->hasBase())
218 39484 : json["moose_base"] = params->getBase();
219 :
220 39484 : json["parameters"] = all_params;
221 39484 : json["syntax_path"] = path;
222 39484 : json["parent_syntax"] = parent;
223 39484 : json["description"] = params->getClassDescription();
224 : // We do this for ActionComponents which are registered as Actions but
225 : // dumped to the syntax tree as Objects
226 39484 : if (params->hasBase() && json["moose_base"] == "Action")
227 : {
228 45 : auto label_pair = getActionLabel(classname);
229 45 : json["label"] = label_pair.first;
230 45 : json["register_file"] = label_pair.second;
231 45 : }
232 : else
233 : {
234 39439 : auto label_pair = getObjectLabel(path);
235 39439 : json["label"] = label_pair.first;
236 39439 : json["register_file"] = label_pair.second;
237 39439 : }
238 39484 : if (lineinfo.isValid())
239 : {
240 39484 : json["file_info"][lineinfo.file()] = lineinfo.line();
241 39484 : if (!classname.empty())
242 1631 : json["class"] = classname;
243 : }
244 : }
245 41230 : return true;
246 66556 : }
247 :
248 : std::string
249 719427 : JsonSyntaxTree::buildOptions(const std::iterator_traits<InputParameters::iterator>::value_type & p,
250 : bool & out_of_range_allowed,
251 : std::map<MooseEnumItem, std::string> & docs)
252 : {
253 719427 : libMesh::Parameters::Value * val = MooseUtils::get(p.second);
254 :
255 719427 : std::string options;
256 : {
257 719427 : auto * enum_type = dynamic_cast<InputParameters::Parameter<MooseEnum> *>(val);
258 719427 : if (enum_type)
259 : {
260 36554 : out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
261 36554 : options = enum_type->get().getRawNames();
262 36554 : docs = enum_type->get().getItemDocumentation();
263 : }
264 : }
265 : {
266 719427 : auto * enum_type = dynamic_cast<InputParameters::Parameter<MultiMooseEnum> *>(val);
267 719427 : if (enum_type)
268 : {
269 24693 : out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
270 24693 : options = enum_type->get().getRawNames();
271 24693 : docs = enum_type->get().getItemDocumentation();
272 : }
273 : }
274 : {
275 719427 : auto * enum_type = dynamic_cast<InputParameters::Parameter<ExecFlagEnum> *>(val);
276 719427 : if (enum_type)
277 : {
278 17030 : out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
279 17030 : options = enum_type->get().getRawNames();
280 17030 : docs = enum_type->get().getItemDocumentation();
281 : }
282 : }
283 : {
284 719427 : auto * enum_type = dynamic_cast<InputParameters::Parameter<std::vector<MooseEnum>> *>(val);
285 719427 : if (enum_type)
286 : {
287 120 : out_of_range_allowed = (enum_type->get())[0].isOutOfRangeAllowed();
288 120 : options = (enum_type->get())[0].getRawNames();
289 120 : docs = enum_type->get()[0].getItemDocumentation();
290 : }
291 : }
292 : {
293 719427 : auto * enum_type = dynamic_cast<InputParameters::Parameter<std::vector<MultiMooseEnum>> *>(val);
294 719427 : if (enum_type)
295 : {
296 15 : out_of_range_allowed = (enum_type->get())[0].isOutOfRangeAllowed();
297 15 : options = (enum_type->get())[0].getRawNames();
298 15 : docs = enum_type->get()[0].getItemDocumentation();
299 : }
300 : }
301 719427 : return options;
302 0 : }
303 :
304 : std::string
305 486777 : JsonSyntaxTree::buildOutputString(
306 : const std::iterator_traits<InputParameters::iterator>::value_type & p)
307 : {
308 486777 : libMesh::Parameters::Value * val = MooseUtils::get(p.second);
309 :
310 : // Account for Point
311 486777 : std::stringstream str;
312 486777 : InputParameters::Parameter<Point> * ptr0 = dynamic_cast<InputParameters::Parameter<Point> *>(val);
313 :
314 : // Account for RealVectorValues
315 : InputParameters::Parameter<RealVectorValue> * ptr1 =
316 486777 : dynamic_cast<InputParameters::Parameter<RealVectorValue> *>(val);
317 :
318 : // Output the Point components
319 486777 : if (ptr0)
320 495 : str << ptr0->get().operator()(0) << " " << ptr0->get().operator()(1) << " "
321 495 : << ptr0->get().operator()(2);
322 :
323 : // Output the RealVectorValue components
324 486282 : else if (ptr1)
325 340 : str << ptr1->get().operator()(0) << " " << ptr1->get().operator()(1) << " "
326 340 : << ptr1->get().operator()(2);
327 :
328 : // General case, call the print operator
329 : else
330 485942 : val->print(str);
331 :
332 : // remove additional '\n' possibly generated in output (breaks JSON parsing)
333 486777 : std::string tmp_str = str.str();
334 2686458 : for (auto & ch : tmp_str)
335 2199681 : if (ch == '\n')
336 0 : ch = ' ';
337 :
338 973554 : return tmp_str.substr(0, tmp_str.find("<RESIDUAL>"));
339 486777 : }
340 :
341 : void
342 664 : JsonSyntaxTree::addSyntaxType(const std::string & path, const std::string type)
343 : {
344 664 : if (MooseUtils::wildCardMatch(path, _search))
345 : {
346 415 : auto & j = getJson(path);
347 415 : j["associated_types"].push_back(type);
348 : }
349 : // If they are doing a search they probably don't want to see this
350 664 : if (_search.empty())
351 : {
352 415 : _root["global"]["associated_types"][type].push_back(path);
353 : }
354 664 : }
355 :
356 : void
357 2748 : JsonSyntaxTree::addActionTask(const std::string & path,
358 : const std::string & action,
359 : const std::string & task_name,
360 : const FileLineInfo & lineinfo)
361 : {
362 2748 : nlohmann::json & json = getJson("", path, false);
363 2748 : if (lineinfo.isValid())
364 2748 : json[action]["tasks"][task_name]["file_info"][lineinfo.file()] = lineinfo.line();
365 2748 : }
366 :
367 : std::string
368 920671 : JsonSyntaxTree::basicCppType(const std::string & cpp_type)
369 : {
370 920671 : std::string s = "String";
371 1642458 : if (cpp_type.find("std::vector") != std::string::npos ||
372 1441947 : cpp_type.find("libMesh::VectorValue") != std::string::npos ||
373 2362618 : cpp_type.find("libMesh::TensorValue") != std::string::npos ||
374 720096 : cpp_type.find("Eigen::Matrix") != std::string::npos)
375 : {
376 : // Get the template type and use its basic type for the array type
377 201100 : pcrecpp::RE r("^[^<]+<\\s*(.*)\\s*>$");
378 201100 : std::string t;
379 201100 : r.FullMatch(cpp_type, &t);
380 :
381 : // Capture type just to the first comma for Eigen::Matrix<type,V,W,X,Y,Z>
382 201100 : if (cpp_type.find("Eigen::Matrix") != std::string::npos)
383 525 : t = t.substr(0, t.find(","));
384 :
385 201100 : s = "Array:" + basicCppType(t);
386 201100 : }
387 1438683 : else if (cpp_type.find("std::map") != std::string::npos ||
388 719112 : cpp_type.find("std::unordered_map") != std::string::npos)
389 : {
390 : // Get the template types
391 : // Matches std::map< K , V [, ...] >
392 : // and std::unordered_map< K , V [, ...] >
393 : pcrecpp::RE r_map(
394 459 : "^(?:std::)?(?:unordered_)?map\\s*<\\s*([^,>]+)\\s*,\\s*([^,>]+)(?:\\s*,.*)?\\s*>$");
395 :
396 : // k and v hold the key and value types
397 459 : std::string k, v;
398 459 : r_map.FullMatch(cpp_type, &k, &v);
399 :
400 459 : s = "Map:" + k + "->" + v;
401 459 : }
402 1413514 : else if (cpp_type.find("MultiMooseEnum") != std::string::npos ||
403 1413514 : cpp_type.find("ExecFlagEnum") != std::string::npos ||
404 677372 : cpp_type.find("VectorPostprocessorName") != std::string::npos)
405 42215 : s = "Array:String";
406 676897 : else if (cpp_type.find("libMesh::Point") != std::string::npos)
407 2676 : s = "Array:Real";
408 1337178 : else if (cpp_type == "int" || cpp_type == "unsigned int" || cpp_type == "short" ||
409 622721 : cpp_type == "unsigned short" || cpp_type == "char" || cpp_type == "unsigned char" ||
410 1956930 : cpp_type == "long" || cpp_type == "unsigned long" || cpp_type == "long long" ||
411 619752 : cpp_type == "unsigned long long")
412 54469 : s = "Integer";
413 619752 : else if (cpp_type == "double" || cpp_type == "float")
414 39961 : s = "Real";
415 579791 : else if (cpp_type == "bool")
416 224490 : s = "Boolean";
417 :
418 920671 : return s;
419 0 : }
420 :
421 : std::pair<std::string, std::string>
422 39439 : JsonSyntaxTree::getObjectLabel(const std::string & obj) const
423 : {
424 39439 : auto paths = splitPath(obj);
425 39439 : auto it = _object_label_map.find(paths.back());
426 39439 : if (it != _object_label_map.end())
427 39439 : return it->second;
428 : else
429 0 : return std::make_pair("", "");
430 39439 : }
431 :
432 : std::pair<std::string, std::string>
433 1791 : JsonSyntaxTree::getActionLabel(const std::string & action) const
434 : {
435 1791 : auto it = _action_label_map.find(action);
436 1791 : if (it != _action_label_map.end())
437 1791 : return it->second;
438 : else
439 0 : return std::make_pair("", "");
440 : }
|