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 39960 : for (auto & obj : entry.second)
41 39912 : _object_label_map[obj->name()] = std::make_pair(entry.first, obj->_file);
42 24 : }
43 :
44 : std::vector<std::string>
45 122805 : JsonSyntaxTree::splitPath(const std::string & path)
46 : {
47 122805 : std::string s;
48 122805 : std::istringstream f(path);
49 122805 : std::vector<std::string> paths;
50 529950 : while (std::getline(f, s, '/'))
51 284340 : if (s.size() > 0)
52 284340 : paths.push_back(s);
53 245610 : return paths;
54 122805 : }
55 :
56 : nlohmann::json &
57 44216 : JsonSyntaxTree::getJson(const std::string & path)
58 : {
59 44216 : auto paths = splitPath(path);
60 : mooseAssert(paths.size() > 0, "path is empty");
61 44216 : auto * next = &(_root["blocks"][paths[0]]);
62 :
63 95212 : for (auto pit = paths.begin() + 1; pit != paths.end(); ++pit)
64 : {
65 50996 : if (*pit == "*")
66 : // It has an action syntax as a parent
67 44347 : next = &(*next)["star"];
68 6649 : else if (*pit == "<type>")
69 0 : next = &(*next)["types"];
70 : else
71 6649 : next = &(*next)["subblocks"][*pit];
72 : }
73 44216 : return *next;
74 44216 : }
75 :
76 : nlohmann::json &
77 43811 : JsonSyntaxTree::getJson(const std::string & parent, const std::string & path, bool is_type)
78 : {
79 43811 : if (parent.empty())
80 : {
81 4494 : auto & j = getJson(path);
82 4494 : if (path.back() == '*' && !j.contains("subblock_types"))
83 1007 : j["subblock_types"] = nlohmann::json();
84 3487 : else if (path.back() != '*' && !j.contains("types"))
85 514 : j["types"] = nlohmann::json();
86 4494 : return j["actions"];
87 : }
88 :
89 39317 : auto & parent_json = getJson(parent);
90 39317 : auto paths = splitPath(path);
91 39317 : std::string key = "subblock_types";
92 39317 : if (is_type)
93 4690 : key = "types";
94 39317 : auto & val = parent_json[key][paths.back()];
95 39317 : return val;
96 39317 : }
97 :
98 : size_t
99 66299 : JsonSyntaxTree::setParams(InputParameters * params, bool search_match, nlohmann::json & all_params)
100 : {
101 66299 : size_t count = 0;
102 2279774 : for (auto & iter : *params)
103 : {
104 : // Make sure we want to see this parameter
105 2213475 : bool param_match = !_search.empty() && MooseUtils::wildCardMatch(iter.first, _search);
106 2213475 : if (params->isPrivate(iter.first) || (!_search.empty() && !search_match && !param_match))
107 1495263 : continue;
108 :
109 718212 : ++count;
110 718212 : nlohmann::json param_json;
111 :
112 718212 : param_json["required"] = params->isParamRequired(iter.first);
113 :
114 : // Only output default if it has one
115 718212 : if (params->isParamValid(iter.first))
116 483701 : param_json["default"] = buildOutputString(iter);
117 234511 : else if (params->hasDefaultCoupledValue(iter.first))
118 605 : param_json["default"] = params->defaultCoupledValue(iter.first);
119 :
120 718212 : bool out_of_range_allowed = false;
121 718212 : std::map<MooseEnumItem, std::string> docs;
122 718212 : param_json["options"] = buildOptions(iter, out_of_range_allowed, docs);
123 718212 : if (!nlohmann::to_string(param_json["options"]).empty())
124 : {
125 718212 : param_json["out_of_range_allowed"] = out_of_range_allowed;
126 718212 : if (!docs.empty())
127 : {
128 195 : nlohmann::json jdocs;
129 735 : for (const auto & doc : docs)
130 540 : jdocs[doc.first.name()] = doc.second;
131 195 : param_json["option_docs"] = jdocs;
132 195 : }
133 : }
134 718212 : auto reserved_values = params->reservedValues(iter.first);
135 740566 : for (const auto & reserved : reserved_values)
136 22354 : param_json["reserved_values"].push_back(reserved);
137 :
138 718212 : std::string t = MooseUtils::prettyCppType(params->type(iter.first));
139 718212 : param_json["cpp_type"] = t;
140 718212 : param_json["basic_type"] = basicCppType(t);
141 718212 : param_json["group_name"] = params->getGroupName(iter.first);
142 718212 : param_json["name"] = iter.first;
143 :
144 718212 : std::string doc = params->getDocString(iter.first);
145 718212 : MooseUtils::escape(doc);
146 718212 : param_json["description"] = doc;
147 :
148 718212 : param_json["doc_unit"] = params->getDocUnit(iter.first);
149 718212 : param_json["doc_range"] =
150 2145558 : params->isRangeChecked(iter.first) ? params->rangeCheckedFunction(iter.first) : "";
151 :
152 718212 : param_json["controllable"] = params->isControllable(iter.first);
153 718212 : param_json["deprecated"] = params->isParamDeprecated(iter.first);
154 718212 : all_params[iter.first] = param_json;
155 718212 : }
156 66299 : 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 66356 : 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 66356 : if (action == "EmptyAction")
191 72 : return false;
192 :
193 66284 : nlohmann::json all_params;
194 157795 : bool search_match = !_search.empty() && (MooseUtils::wildCardMatch(path, _search) ||
195 91511 : MooseUtils::wildCardMatch(action, _search) ||
196 91511 : MooseUtils::wildCardMatch(parent, _search));
197 66284 : auto count = setParams(params, search_match, all_params);
198 66284 : if (!_search.empty() && count == 0)
199 : // no parameters that matched the search string
200 25221 : return false;
201 :
202 41063 : nlohmann::json & json = getJson(parent, path, is_type);
203 :
204 41063 : 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 39317 : else if (params)
216 : {
217 39317 : if (params->hasBase())
218 39317 : json["moose_base"] = params->getBase();
219 :
220 39317 : json["parameters"] = all_params;
221 39317 : json["syntax_path"] = path;
222 39317 : json["parent_syntax"] = parent;
223 39317 : 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 39317 : 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 39272 : auto label_pair = getObjectLabel(path);
235 39272 : json["label"] = label_pair.first;
236 39272 : json["register_file"] = label_pair.second;
237 39272 : }
238 39317 : if (lineinfo.isValid())
239 : {
240 39317 : json["file_info"][lineinfo.file()] = lineinfo.line();
241 39317 : if (!classname.empty())
242 1631 : json["class"] = classname;
243 : }
244 : }
245 41063 : return true;
246 66284 : }
247 :
248 : std::string
249 718212 : 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 718212 : libMesh::Parameters::Value * val = MooseUtils::get(p.second);
254 :
255 718212 : std::string options;
256 : {
257 718212 : auto * enum_type = dynamic_cast<InputParameters::Parameter<MooseEnum> *>(val);
258 718212 : if (enum_type)
259 : {
260 36058 : out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
261 36058 : options = enum_type->get().getRawNames();
262 36058 : docs = enum_type->get().getItemDocumentation();
263 : }
264 : }
265 : {
266 718212 : auto * enum_type = dynamic_cast<InputParameters::Parameter<MultiMooseEnum> *>(val);
267 718212 : 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 718212 : auto * enum_type = dynamic_cast<InputParameters::Parameter<ExecFlagEnum> *>(val);
276 718212 : if (enum_type)
277 : {
278 17045 : out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
279 17045 : options = enum_type->get().getRawNames();
280 17045 : docs = enum_type->get().getItemDocumentation();
281 : }
282 : }
283 : {
284 718212 : auto * enum_type = dynamic_cast<InputParameters::Parameter<std::vector<MooseEnum>> *>(val);
285 718212 : 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 718212 : auto * enum_type = dynamic_cast<InputParameters::Parameter<std::vector<MultiMooseEnum>> *>(val);
294 718212 : 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 718212 : return options;
302 0 : }
303 :
304 : std::string
305 483765 : JsonSyntaxTree::buildOutputString(
306 : const std::iterator_traits<InputParameters::iterator>::value_type & p)
307 : {
308 483765 : libMesh::Parameters::Value * val = MooseUtils::get(p.second);
309 :
310 : // Account for Point
311 483765 : std::stringstream str;
312 483765 : InputParameters::Parameter<Point> * ptr0 = dynamic_cast<InputParameters::Parameter<Point> *>(val);
313 :
314 : // Account for RealVectorValues
315 : InputParameters::Parameter<RealVectorValue> * ptr1 =
316 483765 : dynamic_cast<InputParameters::Parameter<RealVectorValue> *>(val);
317 :
318 : // Output the Point components
319 483765 : 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 483270 : 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 482930 : val->print(str);
331 :
332 : // remove additional '\n' possibly generated in output (breaks JSON parsing)
333 483765 : std::string tmp_str = str.str();
334 2674499 : for (auto & ch : tmp_str)
335 2190734 : if (ch == '\n')
336 0 : ch = ' ';
337 :
338 967530 : return tmp_str.substr(0, tmp_str.find("<RESIDUAL>"));
339 483765 : }
340 :
341 : void
342 648 : JsonSyntaxTree::addSyntaxType(const std::string & path, const std::string type)
343 : {
344 648 : if (MooseUtils::wildCardMatch(path, _search))
345 : {
346 405 : auto & j = getJson(path);
347 405 : j["associated_types"].push_back(type);
348 : }
349 : // If they are doing a search they probably don't want to see this
350 648 : if (_search.empty())
351 : {
352 405 : _root["global"]["associated_types"][type].push_back(path);
353 : }
354 648 : }
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 921566 : JsonSyntaxTree::basicCppType(const std::string & cpp_type)
369 : {
370 921566 : std::string s = "String";
371 1642140 : if (cpp_type.find("std::vector") != std::string::npos ||
372 1439521 : cpp_type.find("libMesh::VectorValue") != std::string::npos ||
373 2361087 : cpp_type.find("libMesh::TensorValue") != std::string::npos ||
374 718883 : cpp_type.find("Eigen::Matrix") != std::string::npos)
375 : {
376 : // Get the template type and use its basic type for the array type
377 203208 : pcrecpp::RE r("^[^<]+<\\s*(.*)\\s*>$");
378 203208 : std::string t;
379 203208 : r.FullMatch(cpp_type, &t);
380 :
381 : // Capture type just to the first comma for Eigen::Matrix<type,V,W,X,Y,Z>
382 203208 : if (cpp_type.find("Eigen::Matrix") != std::string::npos)
383 525 : t = t.substr(0, t.find(","));
384 :
385 203208 : s = "Array:" + basicCppType(t);
386 203208 : }
387 1436257 : else if (cpp_type.find("std::map") != std::string::npos ||
388 717899 : 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 1411088 : else if (cpp_type.find("MultiMooseEnum") != std::string::npos ||
403 1411088 : cpp_type.find("ExecFlagEnum") != std::string::npos ||
404 676144 : cpp_type.find("VectorPostprocessorName") != std::string::npos)
405 42230 : s = "Array:String";
406 675669 : else if (cpp_type.find("libMesh::Point") != std::string::npos)
407 2676 : s = "Array:Real";
408 1334677 : else if (cpp_type == "int" || cpp_type == "unsigned int" || cpp_type == "short" ||
409 622409 : cpp_type == "unsigned short" || cpp_type == "char" || cpp_type == "unsigned char" ||
410 1954144 : cpp_type == "long" || cpp_type == "unsigned long" || cpp_type == "long long" ||
411 619467 : cpp_type == "unsigned long long")
412 53526 : s = "Integer";
413 619467 : else if (cpp_type == "double" || cpp_type == "float")
414 39716 : s = "Real";
415 579751 : else if (cpp_type == "bool")
416 223427 : s = "Boolean";
417 :
418 921566 : return s;
419 0 : }
420 :
421 : std::pair<std::string, std::string>
422 39272 : JsonSyntaxTree::getObjectLabel(const std::string & obj) const
423 : {
424 39272 : auto paths = splitPath(obj);
425 39272 : auto it = _object_label_map.find(paths.back());
426 39272 : if (it != _object_label_map.end())
427 39272 : return it->second;
428 : else
429 0 : return std::make_pair("", "");
430 39272 : }
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 : }
|