https://mooseframework.inl.gov
JsonSyntaxTree.C
Go to the documentation of this file.
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 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  auto & actmap = Registry::allActions();
34  for (auto & entry : actmap)
35  for (auto & act : entry.second)
36  _action_label_map[act->_classname] = std::make_pair(entry.first, act->_file);
37 
38  auto & objmap = Registry::allObjects();
39  for (auto & entry : objmap)
40  for (auto & obj : entry.second)
41  _object_label_map[obj->name()] = std::make_pair(entry.first, obj->_file);
42 }
43 
44 std::vector<std::string>
45 JsonSyntaxTree::splitPath(const std::string & path)
46 {
47  std::string s;
48  std::istringstream f(path);
49  std::vector<std::string> paths;
50  while (std::getline(f, s, '/'))
51  if (s.size() > 0)
52  paths.push_back(s);
53  return paths;
54 }
55 
56 nlohmann::json &
57 JsonSyntaxTree::getJson(const std::string & path)
58 {
59  auto paths = splitPath(path);
60  mooseAssert(paths.size() > 0, "path is empty");
61  auto * next = &(_root["blocks"][paths[0]]);
62 
63  for (auto pit = paths.begin() + 1; pit != paths.end(); ++pit)
64  {
65  if (*pit == "*")
66  // It has an action syntax as a parent
67  next = &(*next)["star"];
68  else if (*pit == "<type>")
69  next = &(*next)["types"];
70  else
71  next = &(*next)["subblocks"][*pit];
72  }
73  return *next;
74 }
75 
76 nlohmann::json &
77 JsonSyntaxTree::getJson(const std::string & parent, const std::string & path, bool is_type)
78 {
79  if (parent.empty())
80  {
81  auto & j = getJson(path);
82  if (path.back() == '*' && !j.contains("subblock_types"))
83  j["subblock_types"] = nlohmann::json();
84  else if (path.back() != '*' && !j.contains("types"))
85  j["types"] = nlohmann::json();
86  return j["actions"];
87  }
88 
89  auto & parent_json = getJson(parent);
90  auto paths = splitPath(path);
91  std::string key = "subblock_types";
92  if (is_type)
93  key = "types";
94  auto & val = parent_json[key][paths.back()];
95  return val;
96 }
97 
98 size_t
99 JsonSyntaxTree::setParams(InputParameters * params, bool search_match, nlohmann::json & all_params)
100 {
101  size_t count = 0;
102  for (auto & iter : *params)
103  {
104  // Make sure we want to see this parameter
105  bool param_match = !_search.empty() && MooseUtils::wildCardMatch(iter.first, _search);
106  if (params->isPrivate(iter.first) || (!_search.empty() && !search_match && !param_match))
107  continue;
108 
109  ++count;
110  nlohmann::json param_json;
111 
112  param_json["required"] = params->isParamRequired(iter.first);
113 
114  // Only output default if it has one
115  if (params->isParamValid(iter.first))
116  param_json["default"] = buildOutputString(iter);
117  else if (params->hasDefaultCoupledValue(iter.first))
118  param_json["default"] = params->defaultCoupledValue(iter.first);
119 
120  bool out_of_range_allowed = false;
121  std::map<MooseEnumItem, std::string> docs;
122  param_json["options"] = buildOptions(iter, out_of_range_allowed, docs);
123  if (!nlohmann::to_string(param_json["options"]).empty())
124  {
125  param_json["out_of_range_allowed"] = out_of_range_allowed;
126  if (!docs.empty())
127  {
128  nlohmann::json jdocs;
129  for (const auto & doc : docs)
130  jdocs[doc.first.name()] = doc.second;
131  param_json["option_docs"] = jdocs;
132  }
133  }
134  auto reserved_values = params->reservedValues(iter.first);
135  for (const auto & reserved : reserved_values)
136  param_json["reserved_values"].push_back(reserved);
137 
138  std::string t = MooseUtils::prettyCppType(params->type(iter.first));
139  param_json["cpp_type"] = t;
140  param_json["basic_type"] = basicCppType(t);
141  param_json["group_name"] = params->getGroupName(iter.first);
142  param_json["name"] = iter.first;
143 
144  std::string doc = params->getDocString(iter.first);
145  MooseUtils::escape(doc);
146  param_json["description"] = doc;
147 
148  param_json["doc_unit"] = params->getDocUnit(iter.first);
149  param_json["doc_range"] =
150  params->isRangeChecked(iter.first) ? params->rangeCheckedFunction(iter.first) : "";
151 
152  param_json["controllable"] = params->isControllable(iter.first);
153  param_json["deprecated"] = params->isParamDeprecated(iter.first);
154  all_params[iter.first] = param_json;
155  }
156  return count;
157 }
158 
159 void
161 {
162  // If they are doing a search they probably don't want to see this
163  if (_search.empty())
164  {
165  auto params = Moose::Builder::validParams();
166  nlohmann::json jparams;
167  setParams(&params, true, jparams);
168  _root["global"]["parameters"] = jparams;
169 
170  // Just create a list of registered app names
171  nlohmann::json apps;
172  auto & factory = AppFactory::instance();
173  for (const auto & name_bi_pair : factory.registeredObjects())
174  apps.push_back(name_bi_pair.first);
175 
176  _root["global"]["registered_apps"] = apps;
177  }
178 }
179 
180 bool
181 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  if (action == "EmptyAction")
191  return false;
192 
193  nlohmann::json all_params;
194  bool search_match = !_search.empty() && (MooseUtils::wildCardMatch(path, _search) ||
197  auto count = setParams(params, search_match, all_params);
198  if (!_search.empty() && count == 0)
199  // no parameters that matched the search string
200  return false;
201 
202  nlohmann::json & json = getJson(parent, path, is_type);
203 
204  if (is_action)
205  {
206  json[action]["parameters"] = all_params;
207  json[action]["description"] = params->getClassDescription();
208  json[action]["action_path"] = path;
209  auto label_pair = getActionLabel(action);
210  json[action]["label"] = label_pair.first;
211  json[action]["register_file"] = label_pair.second;
212  if (lineinfo.isValid())
213  json[action]["file_info"][lineinfo.file()] = lineinfo.line();
214  }
215  else if (params)
216  {
217  if (params->hasBase())
218  json["moose_base"] = params->getBase();
219 
220  json["parameters"] = all_params;
221  json["syntax_path"] = path;
222  json["parent_syntax"] = parent;
223  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  if (params->hasBase() && json["moose_base"] == "Action")
227  {
228  auto label_pair = getActionLabel(classname);
229  json["label"] = label_pair.first;
230  json["register_file"] = label_pair.second;
231  }
232  else
233  {
234  auto label_pair = getObjectLabel(path);
235  json["label"] = label_pair.first;
236  json["register_file"] = label_pair.second;
237  }
238  if (lineinfo.isValid())
239  {
240  json["file_info"][lineinfo.file()] = lineinfo.line();
241  if (!classname.empty())
242  json["class"] = classname;
243  }
244  }
245  return true;
246 }
247 
248 std::string
249 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  libMesh::Parameters::Value * val = MooseUtils::get(p.second);
254 
255  std::string options;
256  {
257  auto * enum_type = dynamic_cast<InputParameters::Parameter<MooseEnum> *>(val);
258  if (enum_type)
259  {
260  out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
261  options = enum_type->get().getRawNames();
262  docs = enum_type->get().getItemDocumentation();
263  }
264  }
265  {
266  auto * enum_type = dynamic_cast<InputParameters::Parameter<MultiMooseEnum> *>(val);
267  if (enum_type)
268  {
269  out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
270  options = enum_type->get().getRawNames();
271  docs = enum_type->get().getItemDocumentation();
272  }
273  }
274  {
275  auto * enum_type = dynamic_cast<InputParameters::Parameter<ExecFlagEnum> *>(val);
276  if (enum_type)
277  {
278  out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
279  options = enum_type->get().getRawNames();
280  docs = enum_type->get().getItemDocumentation();
281  }
282  }
283  {
284  auto * enum_type = dynamic_cast<InputParameters::Parameter<std::vector<MooseEnum>> *>(val);
285  if (enum_type)
286  {
287  out_of_range_allowed = (enum_type->get())[0].isOutOfRangeAllowed();
288  options = (enum_type->get())[0].getRawNames();
289  docs = enum_type->get()[0].getItemDocumentation();
290  }
291  }
292  {
293  auto * enum_type = dynamic_cast<InputParameters::Parameter<std::vector<MultiMooseEnum>> *>(val);
294  if (enum_type)
295  {
296  out_of_range_allowed = (enum_type->get())[0].isOutOfRangeAllowed();
297  options = (enum_type->get())[0].getRawNames();
298  docs = enum_type->get()[0].getItemDocumentation();
299  }
300  }
301  return options;
302 }
303 
304 std::string
306  const std::iterator_traits<InputParameters::iterator>::value_type & p)
307 {
308  libMesh::Parameters::Value * val = MooseUtils::get(p.second);
309 
310  // Account for Point
311  std::stringstream str;
312  InputParameters::Parameter<Point> * ptr0 = dynamic_cast<InputParameters::Parameter<Point> *>(val);
313 
314  // Account for RealVectorValues
315  InputParameters::Parameter<RealVectorValue> * ptr1 =
316  dynamic_cast<InputParameters::Parameter<RealVectorValue> *>(val);
317 
318  // Output the Point components
319  if (ptr0)
320  str << ptr0->get().operator()(0) << " " << ptr0->get().operator()(1) << " "
321  << ptr0->get().operator()(2);
322 
323  // Output the RealVectorValue components
324  else if (ptr1)
325  str << ptr1->get().operator()(0) << " " << ptr1->get().operator()(1) << " "
326  << ptr1->get().operator()(2);
327 
328  // General case, call the print operator
329  else
330  val->print(str);
331 
332  // remove additional '\n' possibly generated in output (breaks JSON parsing)
333  std::string tmp_str = str.str();
334  for (auto & ch : tmp_str)
335  if (ch == '\n')
336  ch = ' ';
337 
338  return tmp_str.substr(0, tmp_str.find("<RESIDUAL>"));
339 }
340 
341 void
342 JsonSyntaxTree::addSyntaxType(const std::string & path, const std::string type)
343 {
345  {
346  auto & j = getJson(path);
347  j["associated_types"].push_back(type);
348  }
349  // If they are doing a search they probably don't want to see this
350  if (_search.empty())
351  {
352  _root["global"]["associated_types"][type].push_back(path);
353  }
354 }
355 
356 void
357 JsonSyntaxTree::addActionTask(const std::string & path,
358  const std::string & action,
359  const std::string & task_name,
360  const FileLineInfo & lineinfo)
361 {
362  nlohmann::json & json = getJson("", path, false);
363  if (lineinfo.isValid())
364  json[action]["tasks"][task_name]["file_info"][lineinfo.file()] = lineinfo.line();
365 }
366 
367 std::string
368 JsonSyntaxTree::basicCppType(const std::string & cpp_type)
369 {
370  std::string s = "String";
371  if (cpp_type.find("std::vector") != std::string::npos ||
372  cpp_type.find("libMesh::VectorValue") != std::string::npos ||
373  cpp_type.find("libMesh::TensorValue") != std::string::npos ||
374  cpp_type.find("Eigen::Matrix") != std::string::npos)
375  {
376  // Get the template type and use its basic type for the array type
377  pcrecpp::RE r("^[^<]+<\\s*(.*)\\s*>$");
378  std::string t;
379  r.FullMatch(cpp_type, &t);
380 
381  // Capture type just to the first comma for Eigen::Matrix<type,V,W,X,Y,Z>
382  if (cpp_type.find("Eigen::Matrix") != std::string::npos)
383  t = t.substr(0, t.find(","));
384 
385  s = "Array:" + basicCppType(t);
386  }
387  else if (cpp_type.find("std::map") != std::string::npos ||
388  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  "^(?:std::)?(?:unordered_)?map\\s*<\\s*([^,>]+)\\s*,\\s*([^,>]+)(?:\\s*,.*)?\\s*>$");
395 
396  // k and v hold the key and value types
397  std::string k, v;
398  r_map.FullMatch(cpp_type, &k, &v);
399 
400  s = "Map:" + k + "->" + v;
401  }
402  else if (cpp_type.find("MultiMooseEnum") != std::string::npos ||
403  cpp_type.find("ExecFlagEnum") != std::string::npos ||
404  cpp_type.find("VectorPostprocessorName") != std::string::npos)
405  s = "Array:String";
406  else if (cpp_type.find("libMesh::Point") != std::string::npos)
407  s = "Array:Real";
408  else if (cpp_type == "int" || cpp_type == "unsigned int" || cpp_type == "short" ||
409  cpp_type == "unsigned short" || cpp_type == "char" || cpp_type == "unsigned char" ||
410  cpp_type == "long" || cpp_type == "unsigned long" || cpp_type == "long long" ||
411  cpp_type == "unsigned long long")
412  s = "Integer";
413  else if (cpp_type == "double" || cpp_type == "float")
414  s = "Real";
415  else if (cpp_type == "bool")
416  s = "Boolean";
417 
418  return s;
419 }
420 
421 std::pair<std::string, std::string>
422 JsonSyntaxTree::getObjectLabel(const std::string & obj) const
423 {
424  auto paths = splitPath(obj);
425  auto it = _object_label_map.find(paths.back());
426  if (it != _object_label_map.end())
427  return it->second;
428  else
429  return std::make_pair("", "");
430 }
431 
432 std::pair<std::string, std::string>
433 JsonSyntaxTree::getActionLabel(const std::string & action) const
434 {
435  auto it = _action_label_map.find(action);
436  if (it != _action_label_map.end())
437  return it->second;
438  else
439  return std::make_pair("", "");
440 }
void addSyntaxType(const std::string &path, const std::string type)
Add an associated type to a block.
virtual void print(std::ostream &) const=0
std::string buildOptions(const std::iterator_traits< InputParameters::iterator >::value_type &p, bool &out_of_range_allowed, std::map< MooseEnumItem, std::string > &docs)
void addActionTask(const std::string &path, const std::string &action, const std::string &task, const FileLineInfo &lineinfo)
Add a task to the tree.
nlohmann::json & getJson(const std::string &parent, const std::string &path, bool is_type)
static const std::map< std::string, std::vector< std::shared_ptr< RegistryEntryBase > > > & allActions()
Returns a per-label keyed map of all Actions in the registry.
Definition: Registry.h:242
size_t setParams(InputParameters *params, bool search_match, nlohmann::json &all_params)
static std::vector< std::string > splitPath(const std::string &path)
static InputParameters validParams()
Parameters that are processed directly by the Parser and are valid anywhere in the input...
Definition: Builder.C:121
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
bool hasBase() const
static const std::map< std::string, std::vector< std::shared_ptr< RegistryEntryBase > > > & allObjects()
Returns a per-label keyed map of all MooseObjects in the registry.
Definition: Registry.h:237
int line() const
Definition: FileLineInfo.C:23
const std::string & getBase() const
static std::string buildOutputString(const std::iterator_traits< InputParameters::iterator >::value_type &p)
JsonSyntaxTree(const std::string &search_string)
bool addParameters(const std::string &parent_path, const std::string &path, bool is_type, const std::string &action, bool is_action, InputParameters *params, const FileLineInfo &lineinfo, const std::string &classname)
Add parameters to the tree.
std::map< std::string, std::pair< std::string, std::string > > _action_label_map
static std::string basicCppType(const std::string &cpp_type)
Utilities for making sense of c++ types.
Holds file and line information.
Definition: FileLineInfo.h:18
static AppFactory & instance()
Get the instance of the AppFactory.
Definition: AppFactory.C:20
nlohmann::json _root
std::pair< std::string, std::string > getActionLabel(const std::string &action) const
std::pair< std::string, std::string > getObjectLabel(const std::string &obj) const
std::string file() const
Definition: FileLineInfo.C:29
bool wildCardMatch(std::string name, std::string search_string)
Definition: MooseUtils.C:888
bool isValid() const
Definition: FileLineInfo.C:17
std::string _search
void addGlobal()
Add the global section to the output.
std::map< std::string, std::pair< std::string, std::string > > _object_label_map
std::string getClassDescription() const
Returns the class description.
void escape(std::string &str)
Definition: MooseUtils.C:207
std::string prettyCppType(const std::string &cpp_type)
Definition: MooseUtils.C:1140