www.mooseframework.org
SONDefinitionFormatter.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://www.mooseframework.org
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 "SONDefinitionFormatter.h"
11 #include "MooseUtils.h"
12 #include "pcrecpp.h"
13 #include <algorithm>
14 
16 
17 // ******************************** toString ************************************ //
18 // traverse the associated types array of cpp_types and absolute lookup paths and //
19 // transform the paths to work with our parsed hierarchy and store pairs in a map //
20 // of types to paths for use by InputChoices rule and store the global/parameters //
21 // object then add root blocks recursively and return constructed stream's string //
22 // ****************************************************************************** //
23 std::string
24 SONDefinitionFormatter::toString(const nlohmann::json & root)
25 {
26 
27  const std::map<std::string, std::string> json_path_regex_replacement_map = {
28  {"/star/subblock_types/([A-Za-z0-9_]*)/", "/\\1_type/"},
29  {"[A-Za-z0-9_]*/types/([A-Za-z0-9_]*)/", "\\1_type/"},
30  {"/actions/[A-Za-z0-9_]*/parameters/", "/"},
31  {"/parameters/", "/"},
32  {"/subblocks/", "/"}};
33 
34  for (const auto & el : root["global"]["associated_types"].items())
35  {
36  const auto & type = el.key();
37  for (const auto & el_path : el.value().items())
38  {
39  std::string path = el_path.value();
40  for (const auto & map_iter : json_path_regex_replacement_map)
41  pcrecpp::RE(map_iter.first).GlobalReplace(map_iter.second, &path);
42  _assoc_types_map[type].push_back(path);
43  }
44  }
45 
46  _global_params = root["global"]["parameters"];
47  _stream.clear();
48  _stream.str("");
49  for (const auto & el : root["blocks"].items())
50  addBlock(el.key(), el.value());
51  return _stream.str();
52 }
53 
54 // ******************************** addLine ************************************* //
55 // add a single new-line-terminated and indented line to the stream //
56 // ****************************************************************************** //
57 void
58 SONDefinitionFormatter::addLine(const std::string & line)
59 {
60  _stream << line << "\n";
61  return;
62 }
63 
64 // ******************************* addBlock ************************************* //
65 // add parameters and recursively add NormalBlock children and TypeBlock children //
66 // ****************************************************************************** //
67 void
68 SONDefinitionFormatter::addBlock(const std::string & block_name,
69  const nlohmann::json & block,
70  bool is_typeblock,
71  const std::string & parent_name,
72  const nlohmann::json & parameters_in,
73  const nlohmann::json & subblocks_in)
74 {
75  // open block with "_type" appended to the name if this is a TypeBlock because the
76  // parser appends "_type" to the name of blocks with a "type=" parameter specified
77  addLine("'" + block_name + (is_typeblock ? "_type" : "") + "'{");
78  _level++;
79 
80  // decide the actual block [./declarator] name that will be specified later unless
81  // this is a StarBlock and then decide if this is a StarBlock or not for later use
82  // - if TypeBlock : this will be the parent block name
83  // - if NormalBlock : this will be this block name
84  std::string block_decl = (is_typeblock ? parent_name : block_name);
85  bool is_starblock = (block_decl == "*" ? true : false);
86 
87  // - add InputTmpl : the autocomplete template that is used for all block types
88  // - add InputName : if is_typeblock then this will be dropped in after "type="
89  // - add InputType : block type - normal_top / normal_sub / type_top / type_sub
90  // - add InputDefault : block [./declarator] name from above that will be used for
91  // autocompletion of this block unless it is a StarBlock then
92  // [./insert_name_here] will be used because any name is okay
93  addLine("InputTmpl=MooseBlock");
94  addLine("InputName=\"" + block_name + "\"");
95  if (!is_typeblock)
96  addLine(_level == 1 ? "InputType=normal_top" : "InputType=normal_sub");
97  else
98  addLine(_level == 1 ? "InputType=type_top" : "InputType=type_sub");
99  if (!is_starblock)
100  addLine("InputDefault=\"" + block_decl + "\"");
101  else
102  addLine("InputDefault=\"insert_name_here\"");
103 
104  // add Description of block if it exists
105  std::string description = block.contains("description") ? block["description"] : "";
106  pcrecpp::RE("\"").GlobalReplace("'", &description);
107  pcrecpp::RE("[\r\n]").GlobalReplace(" ", &description);
108  if (!description.empty())
109  addLine("Description=\"" + description + "\"");
110 
111  // ensure every block has no more than one string declarator node and if this is a
112  // TypeBlock but not a StarBlock then also ensure that the block [./declarator] is
113  // the expected block_decl from above which should be the name of the parent block
114  addLine("decl{");
115  _level++;
116  addLine("MaxOccurs=1");
117  if (is_typeblock && !is_starblock)
118  addLine("ValEnums=[ \"" + block_decl + "\" ]");
119  _level--;
120  addLine("}");
121 
122  // if this block is the GlobalParams block then add a add "*/value" level
123  if (block_name == "GlobalParams")
124  {
125  addLine("'*'{");
126  _level++;
127  addLine("'value'{");
128  addLine("}");
129  _level--;
130  addLine("}");
131  }
132 
133  // store parameters ---
134  // first : start with global parameters as a base
135  // second : add or overwrite with any parameter inheritance
136  // third : add or overwrite with any local RegularParameters
137  // fourth : add or overwrite with any local ActionParameters
138  nlohmann::json parameters = _global_params;
139  for (const auto & el : parameters_in.items())
140  parameters[el.key()] = el.value();
141  if (block.contains("parameters"))
142  {
143  for (const auto & el : block["parameters"].items())
144  parameters[el.key()] = el.value();
145  }
146 
147  if (block.contains("actions"))
148  {
149  for (const auto & el : block["actions"].items())
150  if (el.value().contains("parameters"))
151  for (const auto & param_el : el.value()["parameters"].items())
152  parameters[param_el.key()] = param_el.value();
153  }
154 
155  // store NormalBlock children ---
156  // first : start with any NormalBlock inheritance passed in as a base
157  // second : add or overwrite these with any local NormalBlock children
158  // third : add star named child block if it exists
159  nlohmann::json subblocks = subblocks_in;
160  if (block.contains("subblocks"))
161  {
162  for (const auto & el : block["subblocks"].items())
163  subblocks[el.key()] = el.value();
164  }
165  if (block.contains("star"))
166  subblocks["*"] = block["star"];
167 
168  // store TypeBlock children ---
169  // first : start with ["types"] child block as a base
170  // second : add ["subblock_types"] child block
171  nlohmann::json typeblocks = block.contains("types") ? block["types"] : nlohmann::json();
172  if (block.contains("subblock_types"))
173  for (const auto & el : block["subblock_types"].items())
174  typeblocks[el.key()] = el.value();
175 
176  // add parameters ---
177  // if this block has a "type=" parameter with a specified default "type=" name and
178  // if that default is also the name of a saved TypeBlock child then the parameters
179  // belonging to that default saved TypeBlock child are added to this block as well
180  // first : start with default saved TypeBlock child's RegularParameters as a base
181  // second : add or overwrite with default saved TypeBlock child's ActionParameters
182  // third : add or overwrite with parameters that were stored above for this block
183  // fourth : either add newly stored parameters or add previously stored parameters
184  if (parameters.contains("type") && parameters["type"].contains("default") &&
185  parameters["type"]["default"].is_string() &&
186  typeblocks.contains(parameters["type"]["default"].get<std::string>()))
187  {
188  std::string type_default = parameters["type"]["default"].get<std::string>();
189  const nlohmann::json & default_block = typeblocks[type_default];
190  if (default_block.contains("parameters"))
191  {
192  nlohmann::json default_child_params = default_block["parameters"];
193  if (default_block.contains("actions"))
194  {
195  const nlohmann::json & default_actions = default_block["actions"];
196  for (const auto & el : default_actions.items())
197  {
198  if (el.value().contains("parameters"))
199  for (const auto & param_el : el.value()["parameters"].items())
200  default_child_params[param_el.key()] = param_el.value();
201  }
202  }
203 
204  // unrequire the 'file' parameter added to the Mesh via the FileMesh TypeBlock
205  // since MeshGenerators internally change the default block type from FileMesh
206  if (block_name == "Mesh" && default_child_params.contains("file") &&
207  default_child_params["file"].contains("required") &&
208  default_child_params["file"]["required"].is_boolean())
209  default_child_params["file"]["required"] = false;
210 
211  for (const auto & el : parameters.items())
212  default_child_params[el.key()] = el.value();
213  addParameters(default_child_params);
214  }
215  }
216  else
217  addParameters(parameters);
218 
219  // add previously stored NormalBlocks children recursively
220  for (const auto & el : subblocks.items())
221  addBlock(el.key(), el.value());
222 
223  // close block now because the parser stores TypeBlock children at this same level
224  _level--;
225  addLine("} % end block " + block_name + (is_typeblock ? "_type" : ""));
226 
227  // add all previously stored TypeBlock children recursively and pass the parameter
228  // and NormalBlock children added at this level in as inheritance to all TypeBlock
229  // children so that they may each also add them and pass in the name of this block
230  // as well so that all TypeBlock children can add a rule ensuring that their block
231  // [./declarator] is the name of this parent block unless this block is named star
232  for (const auto & el : typeblocks.items())
233  addBlock(el.key(), el.value(), true, block_name, parameters, subblocks);
234 }
235 
236 // ***************************** addParameters ********************************** //
237 // add all of the information for each parameter of a block
238 // - parameter :: add ChildAtLeastOne
239 // - parameter :: add InputTmpl
240 // - parameter :: add InputType
241 // - parameter :: add InputName
242 // - parameter :: add Description
243 // - parameter :: add MinOccurs
244 // - parameter :: add MaxOccurs
245 // - parameter's value :: add MinOccurs
246 // - parameter's value :: add MaxOccurs
247 // - parameter's value :: add ValType
248 // - parameter's value :: add ValEnums
249 // - parameter's value :: add InputChoices (options)
250 // - parameter's value :: add InputChoices (lookups)
251 // - parameter's value :: add MinValInc
252 // - parameter's value :: add InputDefault
253 // ****************************************************************************** //
254 void
255 SONDefinitionFormatter::addParameters(const nlohmann::json & params)
256 {
257 
258  // build list of any '_object_params_set_by_action' that are not required in input
259  std::vector<std::string> action_set_params;
260  if (params.contains("_object_params_set_by_action") &&
261  params["_object_params_set_by_action"].contains("default"))
262  {
263  std::string opsba = nlohmann::to_string(params["_object_params_set_by_action"]["default"]);
264  if (opsba.front() == '"' && opsba.back() == '"')
265  {
266  opsba.erase(opsba.begin());
267  opsba.pop_back();
268  }
269  action_set_params = MooseUtils::split(MooseUtils::trim(opsba), " ");
270  }
271 
272  for (const auto & el : params.items())
273  {
274  auto & name = el.key();
275  auto & param = el.value();
276 
277  // skip '_object_params_set_by_action' parameters because they will not be input
278  if (name == "_object_params_set_by_action")
279  continue;
280 
281  // lambda to calculate relative path from the current level to the document root
282  auto backtrack = [](int level)
283  {
284  std::string backtrack_path;
285  for (int i = 0; i < level; ++i)
286  backtrack_path += "../";
287  return backtrack_path;
288  };
289 
290  // capture the cpp_type and basic_type and strip off any unnecessary information
291  std::string cpp_type = param["cpp_type"];
292  std::string basic_type = param["basic_type"];
293  bool is_array = false;
294  if (cpp_type == "FunctionExpression" || cpp_type == "FunctionName" ||
295  basic_type.compare(0, 6, "Array:") == 0 || cpp_type.compare(0, 13, "Eigen::Matrix") == 0)
296  is_array = true;
297  pcrecpp::RE(".+<([A-Za-z0-9_' ':]*)>.*").GlobalReplace("\\1", &cpp_type);
298  pcrecpp::RE("(Array:)*(.*)").GlobalReplace("\\2", &basic_type);
299 
300  // *** ChildAtLeastOne of parameter
301  // if parameter is required, not action set, and no default exists, then specify
302  // ChildAtLeastOne = [ "backtrack/GlobalParams/name/value" "name/value" ]
303  auto def_ptr = param.find("default");
304  std::string def;
305  if (def_ptr != param.end())
306  def = def_ptr->is_string() ? def_ptr->get<std::string>() : nlohmann::to_string(*def_ptr);
307  def = MooseUtils::trim(def);
308  if (param.contains("required") &&
309  std::find(action_set_params.begin(), action_set_params.end(), name) ==
310  action_set_params.end())
311  {
312  bool required = param["required"];
313  if (required && def.empty())
314  addLine("ChildAtLeastOne=[ \"" + backtrack(_level) + "GlobalParams/" + name +
315  "/value\" \"" + name + "\" ]");
316  }
317 
318  // *** open parameter
319  addLine("'" + name + "'" + "{");
320  _level++;
321 
322  // *** InputTmpl of parameter
323  addLine("InputTmpl=MooseParam");
324 
325  // *** InputType of parameter
326  if (is_array)
327  addLine("InputType=key_array");
328  else
329  addLine("InputType=key_value");
330 
331  // *** InputName of parameter
332  addLine("InputName=\"" + name + "\"");
333 
334  // *** Description of parameter
335  if (param.contains("description"))
336  {
337  std::string description = param["description"];
338  pcrecpp::RE("\"").GlobalReplace("'", &description);
339  pcrecpp::RE("[\r\n]").GlobalReplace(" ", &description);
340  if (!description.empty())
341  addLine("Description=\"" + description + "\"");
342  }
343 
344  // *** MaxOccurs=1 for each parameter
345  addLine("MaxOccurs=1");
346 
347  // *** open parameter's value
348  addLine("'value'{");
349  _level++;
350 
351  // *** MinOccurs / MaxOccurs of parameter's value
352  // is_array indicates the parameter value child may occur zero or multiple times
353  if (!is_array)
354  {
355  addLine("MinOccurs=1");
356  addLine("MaxOccurs=1");
357  }
358 
359  // *** ValType of parameter's value
360  if (basic_type == "Integer")
361  addLine("ValType=Int");
362  else if (basic_type == "Real")
363  addLine("ValType=Real");
364 
365  // *** ValEnums / InputChoices of parameter's value
366  if (basic_type.find("Boolean") != std::string::npos)
367  addLine("ValEnums=[ true false 1 0 on off ]");
368  else
369  {
370  std::string options = param["options"];
371  if (!options.empty())
372  {
373  pcrecpp::RE(" ").GlobalReplace("\" \"", &options);
374  if (!param["out_of_range_allowed"])
375  addLine("ValEnums=[ \"" + options + "\" ]");
376  else
377  addLine("InputChoices=[ \"" + options + "\" ]");
378  }
379  }
380 
381  // *** InputChoices (lookups) of parameter's value
382  // add any reserved_values then check if there are any paths associated with the
383  // cpp_type in the assoc_types_map that was built before traversal and add those
384  // paths relative to this node here as well
385  std::string choices;
386  if (param.contains("reserved_values"))
387  {
388  for (const auto & reserved : param["reserved_values"])
389  choices += nlohmann::to_string(reserved) + " ";
390  }
391 
392  for (const auto & path : _assoc_types_map[cpp_type])
393  choices += "PATH:\"" + backtrack(_level) + path + "/decl\" ";
394  if (!choices.empty())
395  addLine("InputChoices=[ " + choices + "]");
396 
397  // *** MinValInc of parameter's value
398  if (cpp_type.compare(0, 8, "unsigned") == 0 && basic_type == "Integer")
399  addLine("MinValInc=0");
400 
401  // *** InputDefault of parameter's value
402  if (!def.empty())
403  addLine("InputDefault=\"" + def + "\"");
404 
405  // *** close parameter's value
406  _level--;
407  addLine("}");
408 
409  // *** close parameter
410  _level--;
411  addLine("}");
412  }
413 }
std::string name(const ElemQuality q)
void addParameters(const nlohmann::json &params)
adds all parameters from a given block
std::vector< std::string > split(const std::string &str, const std::string &delimiter, std::size_t max_count=std::numeric_limits< std::size_t >::max())
Python like split functions for strings.
Definition: MooseUtils.C:1115
std::map< std::string, std::vector< std::string > > _assoc_types_map
std::string trim(const std::string &str, const std::string &white_space=" \\\)
Standard scripting language trim function.
Definition: MooseUtils.C:214
void addLine(const std::string &line)
adds a line to the output with the proper indentation automatically
std::string toString(const nlohmann::json &root)
returns a string representation of the tree in input file format
void addBlock(const std::string &block_name, const nlohmann::json &block, bool is_typeblock=false, const std::string &parent_name="", const nlohmann::json &parameters_in=nlohmann::json(nullptr), const nlohmann::json &subblocks_in=nlohmann::json(nullptr))
adds a new block to the output