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 5496 : for (auto & act : entry.second)
36 5448 : _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 40280 : for (auto & obj : entry.second)
41 40232 : _object_label_map[obj->name()] = std::make_pair(entry.first, obj->_file);
42 24 : }
43 :
44 : std::vector<std::string>
45 123306 : JsonSyntaxTree::splitPath(const std::string & path)
46 : {
47 123306 : std::string s;
48 123306 : std::istringstream f(path);
49 123306 : std::vector<std::string> paths;
50 532129 : while (std::getline(f, s, '/'))
51 285517 : if (s.size() > 0)
52 285517 : paths.push_back(s);
53 246612 : return paths;
54 123306 : }
55 :
56 : nlohmann::json &
57 44443 : JsonSyntaxTree::getJson(const std::string & path)
58 : {
59 44443 : auto paths = splitPath(path);
60 : mooseAssert(paths.size() > 0, "path is empty");
61 44443 : auto * next = &(_root["blocks"][paths[0]]);
62 :
63 95971 : for (auto pit = paths.begin() + 1; pit != paths.end(); ++pit)
64 : {
65 51528 : if (*pit == "*")
66 : // It has an action syntax as a parent
67 44744 : next = &(*next)["star"];
68 6784 : else if (*pit == "<type>")
69 0 : next = &(*next)["types"];
70 : else
71 6784 : next = &(*next)["subblocks"][*pit];
72 : }
73 44443 : return *next;
74 44443 : }
75 :
76 : nlohmann::json &
77 44028 : JsonSyntaxTree::getJson(const std::string & parent, const std::string & path, bool is_type)
78 : {
79 44028 : if (parent.empty())
80 : {
81 4574 : auto & j = getJson(path);
82 4574 : if (path.back() == '*' && !j.contains("subblock_types"))
83 1039 : j["subblock_types"] = nlohmann::json();
84 3535 : else if (path.back() != '*' && !j.contains("types"))
85 504 : j["types"] = nlohmann::json();
86 4574 : return j["actions"];
87 : }
88 :
89 39454 : auto & parent_json = getJson(parent);
90 39454 : auto paths = splitPath(path);
91 39454 : std::string key = "subblock_types";
92 39454 : if (is_type)
93 4575 : key = "types";
94 39454 : auto & val = parent_json[key][paths.back()];
95 39454 : return val;
96 39454 : }
97 :
98 : size_t
99 66560 : JsonSyntaxTree::setParams(InputParameters * params, bool search_match, nlohmann::json & all_params)
100 : {
101 66560 : size_t count = 0;
102 2287291 : for (auto & iter : *params)
103 : {
104 : // Make sure we want to see this parameter
105 2220731 : bool param_match = !_search.empty() && MooseUtils::wildCardMatch(iter.first, _search);
106 2220731 : if (params->isPrivate(iter.first) || (!_search.empty() && !search_match && !param_match))
107 1500740 : continue;
108 :
109 719991 : ++count;
110 719991 : nlohmann::json param_json;
111 :
112 719991 : param_json["required"] = params->isParamRequired(iter.first);
113 :
114 : // Only output default if it has one
115 719991 : if (params->isParamValid(iter.first))
116 486905 : param_json["default"] = buildOutputString(iter);
117 233086 : else if (params->hasDefaultCoupledValue(iter.first))
118 605 : param_json["default"] = params->defaultCoupledValue(iter.first);
119 :
120 719991 : bool out_of_range_allowed = false;
121 719991 : std::map<MooseEnumItem, std::string> docs;
122 719991 : param_json["options"] = buildOptions(iter, out_of_range_allowed, docs);
123 719991 : if (!nlohmann::to_string(param_json["options"]).empty())
124 : {
125 719991 : param_json["out_of_range_allowed"] = out_of_range_allowed;
126 719991 : 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 719991 : auto reserved_values = params->reservedValues(iter.first);
135 742405 : for (const auto & reserved : reserved_values)
136 22414 : param_json["reserved_values"].push_back(reserved);
137 :
138 719991 : std::string t = MooseUtils::prettyCppType(params->type(iter.first));
139 719991 : param_json["cpp_type"] = t;
140 719991 : param_json["basic_type"] = basicCppType(t);
141 719991 : param_json["group_name"] = params->getGroupName(iter.first);
142 719991 : param_json["name"] = iter.first;
143 :
144 719991 : std::string doc = params->getDocString(iter.first);
145 719991 : MooseUtils::escape(doc);
146 719991 : param_json["description"] = doc;
147 :
148 719991 : param_json["doc_unit"] = params->getDocUnit(iter.first);
149 719991 : param_json["doc_range"] =
150 2150740 : params->isRangeChecked(iter.first) ? params->rangeCheckedFunction(iter.first) : "";
151 :
152 719991 : param_json["controllable"] = params->isControllable(iter.first);
153 719991 : param_json["deprecated"] = params->isParamDeprecated(iter.first);
154 719991 : all_params[iter.first] = param_json;
155 719991 : }
156 66560 : 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 66617 : 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 66617 : if (action == "EmptyAction")
191 72 : return false;
192 :
193 66545 : nlohmann::json all_params;
194 158419 : bool search_match = !_search.empty() && (MooseUtils::wildCardMatch(path, _search) ||
195 91874 : MooseUtils::wildCardMatch(action, _search) ||
196 91874 : MooseUtils::wildCardMatch(parent, _search));
197 66545 : auto count = setParams(params, search_match, all_params);
198 66545 : if (!_search.empty() && count == 0)
199 : // no parameters that matched the search string
200 25323 : return false;
201 :
202 41222 : nlohmann::json & json = getJson(parent, path, is_type);
203 :
204 41222 : if (is_action)
205 : {
206 1768 : json[action]["parameters"] = all_params;
207 1768 : json[action]["description"] = params->getClassDescription();
208 1768 : json[action]["action_path"] = path;
209 1768 : auto label_pair = getActionLabel(action);
210 1768 : json[action]["label"] = label_pair.first;
211 1768 : json[action]["register_file"] = label_pair.second;
212 1768 : if (lineinfo.isValid())
213 1404 : json[action]["file_info"][lineinfo.file()] = lineinfo.line();
214 1768 : }
215 39454 : else if (params)
216 : {
217 39454 : if (params->hasBase())
218 39454 : json["moose_base"] = params->getBase();
219 :
220 39454 : json["parameters"] = all_params;
221 39454 : json["syntax_path"] = path;
222 39454 : json["parent_syntax"] = parent;
223 39454 : 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 39454 : 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 39409 : auto label_pair = getObjectLabel(path);
235 39409 : json["label"] = label_pair.first;
236 39409 : json["register_file"] = label_pair.second;
237 39409 : }
238 39454 : if (lineinfo.isValid())
239 : {
240 39454 : json["file_info"][lineinfo.file()] = lineinfo.line();
241 39454 : if (!classname.empty())
242 1631 : json["class"] = classname;
243 : }
244 : }
245 41222 : return true;
246 66545 : }
247 :
248 : std::string
249 719991 : 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 719991 : libMesh::Parameters::Value * val = MooseUtils::get(p.second);
254 :
255 719991 : std::string options;
256 : {
257 719991 : auto * enum_type = dynamic_cast<InputParameters::Parameter<MooseEnum> *>(val);
258 719991 : if (enum_type)
259 : {
260 36698 : out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
261 36698 : options = enum_type->get().getRawNames();
262 36698 : docs = enum_type->get().getItemDocumentation();
263 : }
264 : }
265 : {
266 719991 : auto * enum_type = dynamic_cast<InputParameters::Parameter<MultiMooseEnum> *>(val);
267 719991 : if (enum_type)
268 : {
269 24733 : out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
270 24733 : options = enum_type->get().getRawNames();
271 24733 : docs = enum_type->get().getItemDocumentation();
272 : }
273 : }
274 : {
275 719991 : auto * enum_type = dynamic_cast<InputParameters::Parameter<ExecFlagEnum> *>(val);
276 719991 : if (enum_type)
277 : {
278 17060 : out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
279 17060 : options = enum_type->get().getRawNames();
280 17060 : docs = enum_type->get().getItemDocumentation();
281 : }
282 : }
283 : {
284 719991 : auto * enum_type = dynamic_cast<InputParameters::Parameter<std::vector<MooseEnum>> *>(val);
285 719991 : 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 719991 : auto * enum_type = dynamic_cast<InputParameters::Parameter<std::vector<MultiMooseEnum>> *>(val);
294 719991 : 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 719991 : return options;
302 0 : }
303 :
304 : std::string
305 486969 : JsonSyntaxTree::buildOutputString(
306 : const std::iterator_traits<InputParameters::iterator>::value_type & p)
307 : {
308 486969 : libMesh::Parameters::Value * val = MooseUtils::get(p.second);
309 :
310 : // Account for Point
311 486969 : std::stringstream str;
312 486969 : InputParameters::Parameter<Point> * ptr0 = dynamic_cast<InputParameters::Parameter<Point> *>(val);
313 :
314 : // Account for RealVectorValues
315 : InputParameters::Parameter<RealVectorValue> * ptr1 =
316 486969 : dynamic_cast<InputParameters::Parameter<RealVectorValue> *>(val);
317 :
318 : // Output the Point components
319 486969 : 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 486474 : 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 486134 : val->print(str);
331 :
332 : // remove additional '\n' possibly generated in output (breaks JSON parsing)
333 486969 : std::string tmp_str = str.str();
334 2689644 : for (auto & ch : tmp_str)
335 2202675 : if (ch == '\n')
336 0 : ch = ' ';
337 :
338 973938 : return tmp_str.substr(0, tmp_str.find("<RESIDUAL>"));
339 486969 : }
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 2806 : JsonSyntaxTree::addActionTask(const std::string & path,
358 : const std::string & action,
359 : const std::string & task_name,
360 : const FileLineInfo & lineinfo)
361 : {
362 2806 : nlohmann::json & json = getJson("", path, false);
363 2806 : if (lineinfo.isValid())
364 2806 : json[action]["tasks"][task_name]["file_info"][lineinfo.file()] = lineinfo.line();
365 2806 : }
366 :
367 : std::string
368 921525 : JsonSyntaxTree::basicCppType(const std::string & cpp_type)
369 : {
370 921525 : std::string s = "String";
371 1643886 : if (cpp_type.find("std::vector") != std::string::npos ||
372 1443085 : cpp_type.find("libMesh::VectorValue") != std::string::npos ||
373 2364610 : cpp_type.find("libMesh::TensorValue") != std::string::npos ||
374 720660 : cpp_type.find("Eigen::Matrix") != std::string::npos)
375 : {
376 : // Get the template type and use its basic type for the array type
377 201390 : pcrecpp::RE r("^[^<]+<\\s*(.*)\\s*>$");
378 201390 : std::string t;
379 201390 : r.FullMatch(cpp_type, &t);
380 :
381 : // Capture type just to the first comma for Eigen::Matrix<type,V,W,X,Y,Z>
382 201390 : if (cpp_type.find("Eigen::Matrix") != std::string::npos)
383 525 : t = t.substr(0, t.find(","));
384 :
385 201390 : s = "Array:" + basicCppType(t);
386 201390 : }
387 1439811 : else if (cpp_type.find("std::map") != std::string::npos ||
388 719676 : 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 1414602 : else if (cpp_type.find("MultiMooseEnum") != std::string::npos ||
403 1414602 : cpp_type.find("ExecFlagEnum") != std::string::npos ||
404 677866 : cpp_type.find("VectorPostprocessorName") != std::string::npos)
405 42285 : s = "Array:String";
406 677391 : else if (cpp_type.find("libMesh::Point") != std::string::npos)
407 2676 : s = "Array:Real";
408 1338326 : else if (cpp_type == "int" || cpp_type == "unsigned int" || cpp_type == "short" ||
409 623375 : cpp_type == "unsigned short" || cpp_type == "char" || cpp_type == "unsigned char" ||
410 1958732 : cpp_type == "long" || cpp_type == "unsigned long" || cpp_type == "long long" ||
411 620406 : cpp_type == "unsigned long long")
412 54309 : s = "Integer";
413 620406 : else if (cpp_type == "double" || cpp_type == "float")
414 39831 : s = "Real";
415 580575 : else if (cpp_type == "bool")
416 224678 : s = "Boolean";
417 :
418 921525 : return s;
419 0 : }
420 :
421 : std::pair<std::string, std::string>
422 39409 : JsonSyntaxTree::getObjectLabel(const std::string & obj) const
423 : {
424 39409 : auto paths = splitPath(obj);
425 39409 : auto it = _object_label_map.find(paths.back());
426 39409 : if (it != _object_label_map.end())
427 39409 : return it->second;
428 : else
429 0 : return std::make_pair("", "");
430 39409 : }
431 :
432 : std::pair<std::string, std::string>
433 1813 : JsonSyntaxTree::getActionLabel(const std::string & action) const
434 : {
435 1813 : auto it = _action_label_map.find(action);
436 1813 : if (it != _action_label_map.end())
437 1813 : return it->second;
438 : else
439 0 : return std::make_pair("", "");
440 : }
|