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 38529 : for (auto & act : entry.second)
36 38163 : _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 273138 : for (auto & obj : entry.second)
41 272772 : _object_label_map[obj->name()] = std::make_pair(entry.first, obj->_file);
42 183 : }
43 :
44 : std::vector<std::string>
45 1148117 : JsonSyntaxTree::splitPath(const std::string & path)
46 : {
47 1148117 : std::string s;
48 1148117 : std::istringstream f(path);
49 1148117 : std::vector<std::string> paths;
50 4892039 : while (std::getline(f, s, '/'))
51 2595805 : if (s.size() > 0)
52 2595805 : paths.push_back(s);
53 2296234 : return paths;
54 1148117 : }
55 :
56 : nlohmann::json &
57 418609 : JsonSyntaxTree::getJson(const std::string & path)
58 : {
59 418609 : auto paths = splitPath(path);
60 : mooseAssert(paths.size() > 0, "path is empty");
61 418609 : auto * next = &(_root["blocks"][paths[0]]);
62 :
63 875437 : for (auto pit = paths.begin() + 1; pit != paths.end(); ++pit)
64 : {
65 456828 : if (*pit == "*")
66 : // It has an action syntax as a parent
67 387590 : next = &(*next)["star"];
68 69238 : else if (*pit == "<type>")
69 0 : next = &(*next)["types"];
70 : else
71 69238 : next = &(*next)["subblocks"][*pit];
72 : }
73 418609 : return *next;
74 418609 : }
75 :
76 : nlohmann::json &
77 414319 : JsonSyntaxTree::getJson(const std::string & parent, const std::string & path, bool is_type)
78 : {
79 414319 : 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 364919 : auto & parent_json = getJson(parent);
90 364919 : auto paths = splitPath(path);
91 364919 : std::string key = "subblock_types";
92 364919 : if (is_type)
93 47103 : key = "types";
94 364919 : auto & val = parent_json[key][paths.back()];
95 364919 : return val;
96 364919 : }
97 :
98 : size_t
99 426230 : JsonSyntaxTree::setParams(InputParameters * params, bool search_match, nlohmann::json & all_params)
100 : {
101 426230 : size_t count = 0;
102 14521675 : for (auto & iter : *params)
103 : {
104 : // Make sure we want to see this parameter
105 14095445 : bool param_match = !_search.empty() && MooseUtils::wildCardMatch(iter.first, _search);
106 14095445 : if (params->isPrivate(iter.first) || (!_search.empty() && !search_match && !param_match))
107 7455761 : continue;
108 :
109 6639684 : ++count;
110 6639684 : nlohmann::json param_json;
111 :
112 6639684 : param_json["required"] = params->isParamRequired(iter.first);
113 :
114 : // Only output default if it has one
115 6639684 : if (params->isParamValid(iter.first))
116 4474666 : param_json["default"] = buildOutputString(iter);
117 2165018 : else if (params->hasDefaultCoupledValue(iter.first))
118 5575 : param_json["default"] = params->defaultCoupledValue(iter.first);
119 :
120 6639684 : bool out_of_range_allowed = false;
121 6639684 : std::map<MooseEnumItem, std::string> docs;
122 6639684 : param_json["options"] = buildOptions(iter, out_of_range_allowed, docs);
123 6639684 : if (!nlohmann::to_string(param_json["options"]).empty())
124 : {
125 6639684 : param_json["out_of_range_allowed"] = out_of_range_allowed;
126 6639684 : if (!docs.empty())
127 : {
128 495 : nlohmann::json jdocs;
129 2145 : for (const auto & doc : docs)
130 1650 : jdocs[doc.first.name()] = doc.second;
131 495 : param_json["option_docs"] = jdocs;
132 495 : }
133 : }
134 6639684 : auto reserved_values = params->reservedValues(iter.first);
135 6866042 : for (const auto & reserved : reserved_values)
136 226358 : param_json["reserved_values"].push_back(reserved);
137 :
138 6639684 : std::string t = MooseUtils::prettyCppType(params->type(iter.first));
139 6639684 : param_json["cpp_type"] = t;
140 6639684 : param_json["basic_type"] = basicCppType(t);
141 6639684 : param_json["group_name"] = params->getGroupName(iter.first);
142 6639684 : param_json["name"] = iter.first;
143 :
144 6639684 : std::string doc = params->getDocString(iter.first);
145 6639684 : MooseUtils::escape(doc);
146 6639684 : param_json["description"] = doc;
147 :
148 6639684 : param_json["doc_unit"] = params->getDocUnit(iter.first);
149 :
150 6639684 : param_json["controllable"] = params->isControllable(iter.first);
151 6639684 : param_json["deprecated"] = params->isParamDeprecated(iter.first);
152 6639684 : all_params[iter.first] = param_json;
153 6639684 : }
154 426230 : 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 426614 : 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 426614 : if (action == "EmptyAction")
189 549 : return false;
190 :
191 426065 : nlohmann::json all_params;
192 894179 : bool search_match = !_search.empty() && (MooseUtils::wildCardMatch(path, _search) ||
193 468114 : MooseUtils::wildCardMatch(action, _search) ||
194 468114 : MooseUtils::wildCardMatch(parent, _search));
195 426065 : auto count = setParams(params, search_match, all_params);
196 426065 : if (!_search.empty() && count == 0)
197 : // no parameters that matched the search string
198 42035 : return false;
199 :
200 384030 : nlohmann::json & json = getJson(parent, path, is_type);
201 :
202 384030 : 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 364919 : else if (params)
214 : {
215 364919 : if (params->isParamValid("_moose_base"))
216 364919 : json["moose_base"] = params->get<std::string>("_moose_base");
217 :
218 364919 : json["parameters"] = all_params;
219 364919 : json["syntax_path"] = path;
220 364919 : json["parent_syntax"] = parent;
221 364919 : 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 364919 : 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 364589 : auto label_pair = getObjectLabel(path);
233 364589 : json["label"] = label_pair.first;
234 364589 : json["register_file"] = label_pair.second;
235 364589 : }
236 364919 : if (lineinfo.isValid())
237 : {
238 364919 : json["file_info"][lineinfo.file()] = lineinfo.line();
239 364919 : if (!classname.empty())
240 16458 : json["class"] = classname;
241 : }
242 : }
243 384030 : return true;
244 426065 : }
245 :
246 : std::string
247 6639684 : 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 6639684 : libMesh::Parameters::Value * val = MooseUtils::get(p.second);
252 :
253 6639684 : std::string options;
254 : {
255 6639684 : auto * enum_type = dynamic_cast<InputParameters::Parameter<MooseEnum> *>(val);
256 6639684 : if (enum_type)
257 : {
258 194521 : out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
259 194521 : options = enum_type->get().getRawNames();
260 194521 : docs = enum_type->get().getItemDocumentation();
261 : }
262 : }
263 : {
264 6639684 : auto * enum_type = dynamic_cast<InputParameters::Parameter<MultiMooseEnum> *>(val);
265 6639684 : if (enum_type)
266 : {
267 204841 : out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
268 204841 : options = enum_type->get().getRawNames();
269 204841 : docs = enum_type->get().getItemDocumentation();
270 : }
271 : }
272 : {
273 6639684 : auto * enum_type = dynamic_cast<InputParameters::Parameter<ExecFlagEnum> *>(val);
274 6639684 : if (enum_type)
275 : {
276 178925 : out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
277 178925 : options = enum_type->get().getRawNames();
278 178925 : docs = enum_type->get().getItemDocumentation();
279 : }
280 : }
281 : {
282 6639684 : auto * enum_type = dynamic_cast<InputParameters::Parameter<std::vector<MooseEnum>> *>(val);
283 6639684 : 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 6639684 : auto * enum_type = dynamic_cast<InputParameters::Parameter<std::vector<MultiMooseEnum>> *>(val);
292 6639684 : 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 6639684 : return options;
300 0 : }
301 :
302 : std::string
303 4474698 : JsonSyntaxTree::buildOutputString(
304 : const std::iterator_traits<InputParameters::iterator>::value_type & p)
305 : {
306 4474698 : libMesh::Parameters::Value * val = MooseUtils::get(p.second);
307 :
308 : // Account for Point
309 4474698 : std::stringstream str;
310 4474698 : InputParameters::Parameter<Point> * ptr0 = dynamic_cast<InputParameters::Parameter<Point> *>(val);
311 :
312 : // Account for RealVectorValues
313 : InputParameters::Parameter<RealVectorValue> * ptr1 =
314 4474698 : dynamic_cast<InputParameters::Parameter<RealVectorValue> *>(val);
315 :
316 : // Output the Point components
317 4474698 : 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 4470134 : 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 4468154 : val->print(str);
329 :
330 : // remove additional '\n' possibly generated in output (breaks JSON parsing)
331 4474698 : std::string tmp_str = str.str();
332 20695818 : for (auto & ch : tmp_str)
333 16221120 : if (ch == '\n')
334 0 : ch = ' ';
335 :
336 8949396 : return tmp_str.substr(0, tmp_str.find("<RESIDUAL>"));
337 4474698 : }
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 8501539 : JsonSyntaxTree::basicCppType(const std::string & cpp_type)
367 : {
368 8501539 : std::string s = "String";
369 15158565 : if (cpp_type.find("std::vector") != std::string::npos ||
370 13300858 : cpp_type.find("libMesh::VectorValue") != std::string::npos ||
371 21802397 : cpp_type.find("libMesh::TensorValue") != std::string::npos ||
372 6643509 : cpp_type.find("Eigen::Matrix") != std::string::npos)
373 : {
374 : // Get the template type and use its basic type for the array type
375 1861790 : pcrecpp::RE r("^[^<]+<\\s*(.*)\\s*>$");
376 1861790 : std::string t;
377 1861790 : r.FullMatch(cpp_type, &t);
378 :
379 : // Capture type just to the first comma for Eigen::Matrix<type,V,W,X,Y,Z>
380 1861790 : if (cpp_type.find("Eigen::Matrix") != std::string::npos)
381 3760 : t = t.substr(0, t.find(","));
382 :
383 1861790 : s = "Array:" + basicCppType(t);
384 1861790 : }
385 13074491 : else if (cpp_type.find("MultiMooseEnum") != std::string::npos ||
386 12690559 : cpp_type.find("ExecFlagEnum") != std::string::npos ||
387 19330308 : cpp_type.find("VectorPostprocessorName") != std::string::npos ||
388 6250867 : cpp_type.find("std::map") != std::string::npos)
389 393453 : s = "Array:String";
390 6246296 : else if (cpp_type.find("libMesh::Point") != std::string::npos)
391 26949 : s = "Array:Real";
392 18048937 : else if (cpp_type == "int" || cpp_type == "unsigned int" || cpp_type == "short" ||
393 17149500 : cpp_type == "unsigned short" || cpp_type == "char" || cpp_type == "unsigned char" ||
394 23724978 : cpp_type == "long" || cpp_type == "unsigned long" || cpp_type == "long long" ||
395 5700632 : cpp_type == "unsigned long long")
396 518715 : s = "Integer";
397 5700632 : else if (cpp_type == "double" || cpp_type == "float")
398 381570 : s = "Real";
399 5319062 : else if (cpp_type == "bool")
400 2172275 : s = "Boolean";
401 :
402 8501539 : return s;
403 0 : }
404 :
405 : std::pair<std::string, std::string>
406 364589 : JsonSyntaxTree::getObjectLabel(const std::string & obj) const
407 : {
408 364589 : auto paths = splitPath(obj);
409 364589 : auto it = _object_label_map.find(paths.back());
410 364589 : if (it != _object_label_map.end())
411 364589 : return it->second;
412 : else
413 0 : return std::make_pair("", "");
414 364589 : }
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 : }
|