LCOV - code coverage report
Current view: top level - src/outputs/formatters - SONDefinitionFormatter.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 191 198 96.5 %
Date: 2025-07-17 01:28:37 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          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 "SONDefinitionFormatter.h"
      11             : #include "MooseUtils.h"
      12             : #include "pcrecpp.h"
      13             : #include <algorithm>
      14             : 
      15         130 : SONDefinitionFormatter::SONDefinitionFormatter() : _spaces(2), _level(0) {}
      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         130 : 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         780 :       {"/subblocks/", "/"}};
      33             : 
      34        5590 :   for (const auto & el : root["global"]["associated_types"].items())
      35             :   {
      36        2730 :     const auto & type = el.key();
      37        9490 :     for (const auto & el_path : el.value().items())
      38             :     {
      39        3380 :       std::string path = el_path.value();
      40       20280 :       for (const auto & map_iter : json_path_regex_replacement_map)
      41       16900 :         pcrecpp::RE(map_iter.first).GlobalReplace(map_iter.second, &path);
      42        3380 :       _assoc_types_map[type].push_back(path);
      43        6110 :     }
      44         130 :   }
      45             : 
      46         130 :   _global_params = root["global"]["parameters"];
      47         130 :   _stream.clear();
      48         130 :   _stream.str("");
      49        9672 :   for (const auto & el : root["blocks"].items())
      50        9672 :     addBlock(el.key(), el.value());
      51         260 :   return _stream.str();
      52         260 : }
      53             : 
      54             : // ******************************** addLine ************************************* //
      55             : // add a single new-line-terminated and indented line to the stream               //
      56             : // ****************************************************************************** //
      57             : void
      58   163169734 : SONDefinitionFormatter::addLine(const std::string & line)
      59             : {
      60   163169734 :   _stream << line << "\n";
      61   163169734 :   return;
      62             : }
      63             : 
      64             : // ******************************* addBlock ************************************* //
      65             : // add parameters and recursively add NormalBlock children and TypeBlock children //
      66             : // ****************************************************************************** //
      67             : void
      68      842595 : 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      842595 :   addLine("'" + block_name + (is_typeblock ? "_type" : "") + "'{");
      78      842595 :   _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      842595 :   std::string block_decl = (is_typeblock ? parent_name : block_name);
      85      842595 :   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      842595 :   addLine("InputTmpl=MooseBlock");
      94      842595 :   addLine("InputName=\"" + block_name + "\"");
      95      842595 :   if (!is_typeblock)
      96       49582 :     addLine(_level == 1 ? "InputType=normal_top" : "InputType=normal_sub");
      97             :   else
      98      793013 :     addLine(_level == 1 ? "InputType=type_top" : "InputType=type_sub");
      99      842595 :   if (!is_starblock)
     100      363324 :     addLine("InputDefault=\"" + block_decl + "\"");
     101             :   else
     102      479271 :     addLine("InputDefault=\"insert_name_here\"");
     103             : 
     104             :   // add Description of block if it exists
     105      842595 :   std::string description = block.contains("description") ? block["description"] : "";
     106      842595 :   pcrecpp::RE("\"").GlobalReplace("'", &description);
     107      842595 :   pcrecpp::RE("[\r\n]").GlobalReplace(" ", &description);
     108      842595 :   if (!description.empty())
     109      668278 :     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      842595 :   addLine("decl{");
     115      842595 :   _level++;
     116      842595 :   addLine("MaxOccurs=1");
     117      842595 :   if (is_typeblock && !is_starblock)
     118      329784 :     addLine("ValEnums=[ \"" + block_decl + "\" ]");
     119      842595 :   _level--;
     120      842595 :   addLine("}");
     121             : 
     122             :   // if this block is the GlobalParams block then add a add "*/value" level
     123      842595 :   if (block_name == "GlobalParams")
     124             :   {
     125         130 :     addLine("'*'{");
     126         130 :     _level++;
     127         130 :     addLine("'value'{");
     128         130 :     addLine("}");
     129         130 :     _level--;
     130         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      842595 :   nlohmann::json parameters = _global_params;
     139     4351295 :   for (const auto & el : parameters_in.items())
     140     4351295 :     parameters[el.key()] = el.value();
     141      842595 :   if (block.contains("parameters"))
     142             :   {
     143    12175709 :     for (const auto & el : block["parameters"].items())
     144    12175709 :       parameters[el.key()] = el.value();
     145             :   }
     146             : 
     147      842595 :   if (block.contains("actions"))
     148             :   {
     149      117962 :     for (const auto & el : block["actions"].items())
     150       39884 :       if (el.value().contains("parameters"))
     151      314600 :         for (const auto & param_el : el.value()["parameters"].items())
     152      352794 :           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      842595 :   nlohmann::json subblocks = subblocks_in;
     160      842595 :   if (block.contains("subblocks"))
     161             :   {
     162        4550 :     for (const auto & el : block["subblocks"].items())
     163        4550 :       subblocks[el.key()] = el.value();
     164             :   }
     165      842595 :   if (block.contains("star"))
     166       13624 :     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      842595 :   nlohmann::json typeblocks = block.contains("types") ? block["types"] : nlohmann::json();
     172      842595 :   if (block.contains("subblock_types"))
     173      479271 :     for (const auto & el : block["subblock_types"].items())
     174      479271 :       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     1622192 :   if (parameters.contains("type") && parameters["type"].contains("default") &&
     185     2464787 :       parameters["type"]["default"].is_string() &&
     186     1640912 :       typeblocks.contains(parameters["type"]["default"].get<std::string>()))
     187             :   {
     188        1560 :     std::string type_default = parameters["type"]["default"].get<std::string>();
     189        1560 :     const nlohmann::json & default_block = typeblocks[type_default];
     190        1560 :     if (default_block.contains("parameters"))
     191             :     {
     192        1560 :       nlohmann::json default_child_params = default_block["parameters"];
     193        1560 :       if (default_block.contains("actions"))
     194             :       {
     195           0 :         const nlohmann::json & default_actions = default_block["actions"];
     196           0 :         for (const auto & el : default_actions.items())
     197             :         {
     198           0 :           if (el.value().contains("parameters"))
     199           0 :             for (const auto & param_el : el.value()["parameters"].items())
     200           0 :               default_child_params[param_el.key()] = param_el.value();
     201           0 :         }
     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        1690 :       if (block_name == "Mesh" && default_child_params.contains("file") &&
     207        1820 :           default_child_params["file"].contains("required") &&
     208         130 :           default_child_params["file"]["required"].is_boolean())
     209         130 :         default_child_params["file"]["required"] = false;
     210             : 
     211       17420 :       for (const auto & el : parameters.items())
     212       17420 :         default_child_params[el.key()] = el.value();
     213        1560 :       addParameters(default_child_params);
     214        1560 :     }
     215        1560 :   }
     216             :   else
     217      841035 :     addParameters(parameters);
     218             : 
     219             :   // add previously stored NormalBlocks children recursively
     220      882635 :   for (const auto & el : subblocks.items())
     221      882635 :     addBlock(el.key(), el.value());
     222             : 
     223             :   // close block now because the parser stores TypeBlock children at this same level
     224      842595 :   _level--;
     225      842595 :   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     2428621 :   for (const auto & el : typeblocks.items())
     233     1635608 :     addBlock(el.key(), el.value(), true, block_name, parameters, subblocks);
     234      842595 : }
     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      842595 : 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      842595 :   std::vector<std::string> action_set_params;
     260     1097655 :   if (params.contains("_object_params_set_by_action") &&
     261      255060 :       params["_object_params_set_by_action"].contains("default"))
     262             :   {
     263      255060 :     std::string opsba = nlohmann::to_string(params["_object_params_set_by_action"]["default"]);
     264      255060 :     if (opsba.front() == '"' && opsba.back() == '"')
     265             :     {
     266      255060 :       opsba.erase(opsba.begin());
     267      255060 :       opsba.pop_back();
     268             :     }
     269      255060 :     action_set_params = MooseUtils::split(MooseUtils::trim(opsba), " ");
     270      255060 :   }
     271             : 
     272    28382003 :   for (const auto & el : params.items())
     273             :   {
     274    13769704 :     auto & name = el.key();
     275    13769704 :     auto & param = el.value();
     276             : 
     277             :     // skip '_object_params_set_by_action' parameters because they will not be input
     278    13769704 :     if (name == "_object_params_set_by_action")
     279      255060 :       continue;
     280             : 
     281             :     // lambda to calculate relative path from the current level to the document root
     282     2914964 :     auto backtrack = [](int level)
     283             :     {
     284     2914964 :       std::string backtrack_path;
     285    13283790 :       for (int i = 0; i < level; ++i)
     286    10368826 :         backtrack_path += "../";
     287     2914964 :       return backtrack_path;
     288           0 :     };
     289             : 
     290             :     // capture the cpp_type and basic_type and strip off any unnecessary information
     291    13514644 :     std::string cpp_type = param["cpp_type"];
     292    13514644 :     std::string basic_type = param["basic_type"];
     293    13514644 :     bool is_array = false;
     294    27019148 :     if (cpp_type == "FunctionExpression" || cpp_type == "FunctionName" ||
     295    27019148 :         basic_type.compare(0, 6, "Array:") == 0 || cpp_type.compare(0, 13, "Eigen::Matrix") == 0)
     296     5264220 :       is_array = true;
     297    13514644 :     pcrecpp::RE(".+<([A-Za-z0-9_' ':]*)>.*").GlobalReplace("\\1", &cpp_type);
     298    13514644 :     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    13514644 :     auto def_ptr = param.find("default");
     304    13514644 :     std::string def;
     305    13514644 :     if (def_ptr != param.end())
     306     9479587 :       def = def_ptr->is_string() ? def_ptr->get<std::string>() : nlohmann::to_string(*def_ptr);
     307    13514644 :     def = MooseUtils::trim(def);
     308    27029288 :     if (param.contains("required") &&
     309    13514644 :         std::find(action_set_params.begin(), action_set_params.end(), name) ==
     310    27029288 :             action_set_params.end())
     311             :     {
     312    13267124 :       bool required = param["required"];
     313    13267124 :       if (required && def.empty())
     314     3588273 :         addLine("ChildAtLeastOne=[ \"" + backtrack(_level) + "GlobalParams/" + name +
     315     3588273 :                 "/value\" \"" + name + "\" ]");
     316             :     }
     317             : 
     318             :     // *** open parameter
     319    13514644 :     addLine("'" + name + "'" + "{");
     320    13514644 :     _level++;
     321             : 
     322             :     // *** InputTmpl of parameter
     323    13514644 :     addLine("InputTmpl=MooseParam");
     324             : 
     325             :     // *** InputType of parameter
     326    13514644 :     if (is_array)
     327     5264220 :       addLine("InputType=key_array");
     328             :     else
     329     8250424 :       addLine("InputType=key_value");
     330             : 
     331             :     // *** InputName of parameter
     332    13514644 :     addLine("InputName=\"" + name + "\"");
     333             : 
     334             :     // *** Description of parameter
     335    13514644 :     if (param.contains("description"))
     336             :     {
     337    13514644 :       std::string description = param["description"];
     338    13514644 :       pcrecpp::RE("\"").GlobalReplace("'", &description);
     339    13514644 :       pcrecpp::RE("[\r\n]").GlobalReplace(" ", &description);
     340    13514644 :       if (!description.empty())
     341    12702079 :         addLine("Description=\"" + description + "\"");
     342    13514644 :     }
     343             : 
     344             :     // *** MaxOccurs=1 for each parameter
     345    13514644 :     addLine("MaxOccurs=1");
     346             : 
     347             :     // *** open parameter's value
     348    13514644 :     addLine("'value'{");
     349    13514644 :     _level++;
     350             : 
     351             :     // *** MinOccurs / MaxOccurs of parameter's value
     352             :     // is_array indicates the parameter value child may occur zero or multiple times
     353    13514644 :     if (!is_array)
     354             :     {
     355     8250424 :       addLine("MinOccurs=1");
     356     8250424 :       addLine("MaxOccurs=1");
     357             :     }
     358             : 
     359             :     // *** ValType of parameter's value
     360    13514644 :     if (basic_type == "Integer")
     361      988845 :       addLine("ValType=Int");
     362    12525799 :     else if (basic_type == "Real")
     363      985309 :       addLine("ValType=Real");
     364             : 
     365             :     // *** ValEnums / InputChoices of parameter's value
     366    13514644 :     if (basic_type.find("Boolean") != std::string::npos)
     367     3710395 :       addLine("ValEnums=[ true false 1 0 on off ]");
     368             :     else
     369             :     {
     370     9804249 :       std::string options = param["options"];
     371     9804249 :       if (!options.empty())
     372             :       {
     373      863837 :         pcrecpp::RE(" ").GlobalReplace("\" \"", &options);
     374      863837 :         if (!param["out_of_range_allowed"])
     375      700531 :           addLine("ValEnums=[ \"" + options + "\" ]");
     376             :         else
     377      163306 :           addLine("InputChoices=[ \"" + options + "\" ]");
     378             :       }
     379     9804249 :     }
     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    13514644 :     std::string choices;
     386    13514644 :     if (param.contains("reserved_values"))
     387             :     {
     388      268827 :       for (const auto & reserved : param["reserved_values"])
     389      179218 :         choices += nlohmann::to_string(reserved) + " ";
     390             :     }
     391             : 
     392    15233517 :     for (const auto & path : _assoc_types_map[cpp_type])
     393     1718873 :       choices += "PATH:\"" + backtrack(_level) + path + "/decl\" ";
     394    13514644 :     if (!choices.empty())
     395     1220050 :       addLine("InputChoices=[ " + choices + "]");
     396             : 
     397             :     // *** MinValInc of parameter's value
     398    13514644 :     if (cpp_type.compare(0, 8, "unsigned") == 0 && basic_type == "Integer")
     399      781482 :       addLine("MinValInc=0");
     400             : 
     401             :     // *** InputDefault of parameter's value
     402    13514644 :     if (!def.empty())
     403     7521709 :       addLine("InputDefault=\"" + def + "\"");
     404             : 
     405             :     // *** close parameter's value
     406    13514644 :     _level--;
     407    13514644 :     addLine("}");
     408             : 
     409             :     // *** close parameter
     410    13514644 :     _level--;
     411    13514644 :     addLine("}");
     412    14357239 :   }
     413      842595 : }

Generated by: LCOV version 1.14