LCOV - code coverage report
Current view: top level - src/utils - JsonSyntaxTree.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 248 254 97.6 %
Date: 2026-05-29 20:35:17 Functions: 14 14 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 "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        5368 :     for (auto & act : entry.second)
      36        5320 :       _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       39960 :     for (auto & obj : entry.second)
      41       39912 :       _object_label_map[obj->name()] = std::make_pair(entry.first, obj->_file);
      42          24 : }
      43             : 
      44             : std::vector<std::string>
      45      122805 : JsonSyntaxTree::splitPath(const std::string & path)
      46             : {
      47      122805 :   std::string s;
      48      122805 :   std::istringstream f(path);
      49      122805 :   std::vector<std::string> paths;
      50      529950 :   while (std::getline(f, s, '/'))
      51      284340 :     if (s.size() > 0)
      52      284340 :       paths.push_back(s);
      53      245610 :   return paths;
      54      122805 : }
      55             : 
      56             : nlohmann::json &
      57       44216 : JsonSyntaxTree::getJson(const std::string & path)
      58             : {
      59       44216 :   auto paths = splitPath(path);
      60             :   mooseAssert(paths.size() > 0, "path is empty");
      61       44216 :   auto * next = &(_root["blocks"][paths[0]]);
      62             : 
      63       95212 :   for (auto pit = paths.begin() + 1; pit != paths.end(); ++pit)
      64             :   {
      65       50996 :     if (*pit == "*")
      66             :       // It has an action syntax as a parent
      67       44347 :       next = &(*next)["star"];
      68        6649 :     else if (*pit == "<type>")
      69           0 :       next = &(*next)["types"];
      70             :     else
      71        6649 :       next = &(*next)["subblocks"][*pit];
      72             :   }
      73       44216 :   return *next;
      74       44216 : }
      75             : 
      76             : nlohmann::json &
      77       43811 : JsonSyntaxTree::getJson(const std::string & parent, const std::string & path, bool is_type)
      78             : {
      79       43811 :   if (parent.empty())
      80             :   {
      81        4494 :     auto & j = getJson(path);
      82        4494 :     if (path.back() == '*' && !j.contains("subblock_types"))
      83        1007 :       j["subblock_types"] = nlohmann::json();
      84        3487 :     else if (path.back() != '*' && !j.contains("types"))
      85         514 :       j["types"] = nlohmann::json();
      86        4494 :     return j["actions"];
      87             :   }
      88             : 
      89       39317 :   auto & parent_json = getJson(parent);
      90       39317 :   auto paths = splitPath(path);
      91       39317 :   std::string key = "subblock_types";
      92       39317 :   if (is_type)
      93        4690 :     key = "types";
      94       39317 :   auto & val = parent_json[key][paths.back()];
      95       39317 :   return val;
      96       39317 : }
      97             : 
      98             : size_t
      99       66299 : JsonSyntaxTree::setParams(InputParameters * params, bool search_match, nlohmann::json & all_params)
     100             : {
     101       66299 :   size_t count = 0;
     102     2279774 :   for (auto & iter : *params)
     103             :   {
     104             :     // Make sure we want to see this parameter
     105     2213475 :     bool param_match = !_search.empty() && MooseUtils::wildCardMatch(iter.first, _search);
     106     2213475 :     if (params->isPrivate(iter.first) || (!_search.empty() && !search_match && !param_match))
     107     1495263 :       continue;
     108             : 
     109      718212 :     ++count;
     110      718212 :     nlohmann::json param_json;
     111             : 
     112      718212 :     param_json["required"] = params->isParamRequired(iter.first);
     113             : 
     114             :     // Only output default if it has one
     115      718212 :     if (params->isParamValid(iter.first))
     116      483701 :       param_json["default"] = buildOutputString(iter);
     117      234511 :     else if (params->hasDefaultCoupledValue(iter.first))
     118         605 :       param_json["default"] = params->defaultCoupledValue(iter.first);
     119             : 
     120      718212 :     bool out_of_range_allowed = false;
     121      718212 :     std::map<MooseEnumItem, std::string> docs;
     122      718212 :     param_json["options"] = buildOptions(iter, out_of_range_allowed, docs);
     123      718212 :     if (!nlohmann::to_string(param_json["options"]).empty())
     124             :     {
     125      718212 :       param_json["out_of_range_allowed"] = out_of_range_allowed;
     126      718212 :       if (!docs.empty())
     127             :       {
     128         195 :         nlohmann::json jdocs;
     129         735 :         for (const auto & doc : docs)
     130         540 :           jdocs[doc.first.name()] = doc.second;
     131         195 :         param_json["option_docs"] = jdocs;
     132         195 :       }
     133             :     }
     134      718212 :     auto reserved_values = params->reservedValues(iter.first);
     135      740566 :     for (const auto & reserved : reserved_values)
     136       22354 :       param_json["reserved_values"].push_back(reserved);
     137             : 
     138      718212 :     std::string t = MooseUtils::prettyCppType(params->type(iter.first));
     139      718212 :     param_json["cpp_type"] = t;
     140      718212 :     param_json["basic_type"] = basicCppType(t);
     141      718212 :     param_json["group_name"] = params->getGroupName(iter.first);
     142      718212 :     param_json["name"] = iter.first;
     143             : 
     144      718212 :     std::string doc = params->getDocString(iter.first);
     145      718212 :     MooseUtils::escape(doc);
     146      718212 :     param_json["description"] = doc;
     147             : 
     148      718212 :     param_json["doc_unit"] = params->getDocUnit(iter.first);
     149      718212 :     param_json["doc_range"] =
     150     2145558 :         params->isRangeChecked(iter.first) ? params->rangeCheckedFunction(iter.first) : "";
     151             : 
     152      718212 :     param_json["controllable"] = params->isControllable(iter.first);
     153      718212 :     param_json["deprecated"] = params->isParamDeprecated(iter.first);
     154      718212 :     all_params[iter.first] = param_json;
     155      718212 :   }
     156       66299 :   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(&params, 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       66356 : 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       66356 :   if (action == "EmptyAction")
     191          72 :     return false;
     192             : 
     193       66284 :   nlohmann::json all_params;
     194      157795 :   bool search_match = !_search.empty() && (MooseUtils::wildCardMatch(path, _search) ||
     195       91511 :                                            MooseUtils::wildCardMatch(action, _search) ||
     196       91511 :                                            MooseUtils::wildCardMatch(parent, _search));
     197       66284 :   auto count = setParams(params, search_match, all_params);
     198       66284 :   if (!_search.empty() && count == 0)
     199             :     // no parameters that matched the search string
     200       25221 :     return false;
     201             : 
     202       41063 :   nlohmann::json & json = getJson(parent, path, is_type);
     203             : 
     204       41063 :   if (is_action)
     205             :   {
     206        1746 :     json[action]["parameters"] = all_params;
     207        1746 :     json[action]["description"] = params->getClassDescription();
     208        1746 :     json[action]["action_path"] = path;
     209        1746 :     auto label_pair = getActionLabel(action);
     210        1746 :     json[action]["label"] = label_pair.first;
     211        1746 :     json[action]["register_file"] = label_pair.second;
     212        1746 :     if (lineinfo.isValid())
     213        1392 :       json[action]["file_info"][lineinfo.file()] = lineinfo.line();
     214        1746 :   }
     215       39317 :   else if (params)
     216             :   {
     217       39317 :     if (params->hasBase())
     218       39317 :       json["moose_base"] = params->getBase();
     219             : 
     220       39317 :     json["parameters"] = all_params;
     221       39317 :     json["syntax_path"] = path;
     222       39317 :     json["parent_syntax"] = parent;
     223       39317 :     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       39317 :     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       39272 :       auto label_pair = getObjectLabel(path);
     235       39272 :       json["label"] = label_pair.first;
     236       39272 :       json["register_file"] = label_pair.second;
     237       39272 :     }
     238       39317 :     if (lineinfo.isValid())
     239             :     {
     240       39317 :       json["file_info"][lineinfo.file()] = lineinfo.line();
     241       39317 :       if (!classname.empty())
     242        1631 :         json["class"] = classname;
     243             :     }
     244             :   }
     245       41063 :   return true;
     246       66284 : }
     247             : 
     248             : std::string
     249      718212 : 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      718212 :   libMesh::Parameters::Value * val = MooseUtils::get(p.second);
     254             : 
     255      718212 :   std::string options;
     256             :   {
     257      718212 :     auto * enum_type = dynamic_cast<InputParameters::Parameter<MooseEnum> *>(val);
     258      718212 :     if (enum_type)
     259             :     {
     260       36058 :       out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
     261       36058 :       options = enum_type->get().getRawNames();
     262       36058 :       docs = enum_type->get().getItemDocumentation();
     263             :     }
     264             :   }
     265             :   {
     266      718212 :     auto * enum_type = dynamic_cast<InputParameters::Parameter<MultiMooseEnum> *>(val);
     267      718212 :     if (enum_type)
     268             :     {
     269       24693 :       out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
     270       24693 :       options = enum_type->get().getRawNames();
     271       24693 :       docs = enum_type->get().getItemDocumentation();
     272             :     }
     273             :   }
     274             :   {
     275      718212 :     auto * enum_type = dynamic_cast<InputParameters::Parameter<ExecFlagEnum> *>(val);
     276      718212 :     if (enum_type)
     277             :     {
     278       17045 :       out_of_range_allowed = enum_type->get().isOutOfRangeAllowed();
     279       17045 :       options = enum_type->get().getRawNames();
     280       17045 :       docs = enum_type->get().getItemDocumentation();
     281             :     }
     282             :   }
     283             :   {
     284      718212 :     auto * enum_type = dynamic_cast<InputParameters::Parameter<std::vector<MooseEnum>> *>(val);
     285      718212 :     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      718212 :     auto * enum_type = dynamic_cast<InputParameters::Parameter<std::vector<MultiMooseEnum>> *>(val);
     294      718212 :     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      718212 :   return options;
     302           0 : }
     303             : 
     304             : std::string
     305      483765 : JsonSyntaxTree::buildOutputString(
     306             :     const std::iterator_traits<InputParameters::iterator>::value_type & p)
     307             : {
     308      483765 :   libMesh::Parameters::Value * val = MooseUtils::get(p.second);
     309             : 
     310             :   // Account for Point
     311      483765 :   std::stringstream str;
     312      483765 :   InputParameters::Parameter<Point> * ptr0 = dynamic_cast<InputParameters::Parameter<Point> *>(val);
     313             : 
     314             :   // Account for RealVectorValues
     315             :   InputParameters::Parameter<RealVectorValue> * ptr1 =
     316      483765 :       dynamic_cast<InputParameters::Parameter<RealVectorValue> *>(val);
     317             : 
     318             :   // Output the Point components
     319      483765 :   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      483270 :   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      482930 :     val->print(str);
     331             : 
     332             :   // remove additional '\n' possibly generated in output (breaks JSON parsing)
     333      483765 :   std::string tmp_str = str.str();
     334     2674499 :   for (auto & ch : tmp_str)
     335     2190734 :     if (ch == '\n')
     336           0 :       ch = ' ';
     337             : 
     338      967530 :   return tmp_str.substr(0, tmp_str.find("<RESIDUAL>"));
     339      483765 : }
     340             : 
     341             : void
     342         648 : JsonSyntaxTree::addSyntaxType(const std::string & path, const std::string type)
     343             : {
     344         648 :   if (MooseUtils::wildCardMatch(path, _search))
     345             :   {
     346         405 :     auto & j = getJson(path);
     347         405 :     j["associated_types"].push_back(type);
     348             :   }
     349             :   // If they are doing a search they probably don't want to see this
     350         648 :   if (_search.empty())
     351             :   {
     352         405 :     _root["global"]["associated_types"][type].push_back(path);
     353             :   }
     354         648 : }
     355             : 
     356             : void
     357        2748 : JsonSyntaxTree::addActionTask(const std::string & path,
     358             :                               const std::string & action,
     359             :                               const std::string & task_name,
     360             :                               const FileLineInfo & lineinfo)
     361             : {
     362        2748 :   nlohmann::json & json = getJson("", path, false);
     363        2748 :   if (lineinfo.isValid())
     364        2748 :     json[action]["tasks"][task_name]["file_info"][lineinfo.file()] = lineinfo.line();
     365        2748 : }
     366             : 
     367             : std::string
     368      921566 : JsonSyntaxTree::basicCppType(const std::string & cpp_type)
     369             : {
     370      921566 :   std::string s = "String";
     371     1642140 :   if (cpp_type.find("std::vector") != std::string::npos ||
     372     1439521 :       cpp_type.find("libMesh::VectorValue") != std::string::npos ||
     373     2361087 :       cpp_type.find("libMesh::TensorValue") != std::string::npos ||
     374      718883 :       cpp_type.find("Eigen::Matrix") != std::string::npos)
     375             :   {
     376             :     // Get the template type and use its basic type for the array type
     377      203208 :     pcrecpp::RE r("^[^<]+<\\s*(.*)\\s*>$");
     378      203208 :     std::string t;
     379      203208 :     r.FullMatch(cpp_type, &t);
     380             : 
     381             :     // Capture type just to the first comma for Eigen::Matrix<type,V,W,X,Y,Z>
     382      203208 :     if (cpp_type.find("Eigen::Matrix") != std::string::npos)
     383         525 :       t = t.substr(0, t.find(","));
     384             : 
     385      203208 :     s = "Array:" + basicCppType(t);
     386      203208 :   }
     387     1436257 :   else if (cpp_type.find("std::map") != std::string::npos ||
     388      717899 :            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     1411088 :   else if (cpp_type.find("MultiMooseEnum") != std::string::npos ||
     403     1411088 :            cpp_type.find("ExecFlagEnum") != std::string::npos ||
     404      676144 :            cpp_type.find("VectorPostprocessorName") != std::string::npos)
     405       42230 :     s = "Array:String";
     406      675669 :   else if (cpp_type.find("libMesh::Point") != std::string::npos)
     407        2676 :     s = "Array:Real";
     408     1334677 :   else if (cpp_type == "int" || cpp_type == "unsigned int" || cpp_type == "short" ||
     409      622409 :            cpp_type == "unsigned short" || cpp_type == "char" || cpp_type == "unsigned char" ||
     410     1954144 :            cpp_type == "long" || cpp_type == "unsigned long" || cpp_type == "long long" ||
     411      619467 :            cpp_type == "unsigned long long")
     412       53526 :     s = "Integer";
     413      619467 :   else if (cpp_type == "double" || cpp_type == "float")
     414       39716 :     s = "Real";
     415      579751 :   else if (cpp_type == "bool")
     416      223427 :     s = "Boolean";
     417             : 
     418      921566 :   return s;
     419           0 : }
     420             : 
     421             : std::pair<std::string, std::string>
     422       39272 : JsonSyntaxTree::getObjectLabel(const std::string & obj) const
     423             : {
     424       39272 :   auto paths = splitPath(obj);
     425       39272 :   auto it = _object_label_map.find(paths.back());
     426       39272 :   if (it != _object_label_map.end())
     427       39272 :     return it->second;
     428             :   else
     429           0 :     return std::make_pair("", "");
     430       39272 : }
     431             : 
     432             : std::pair<std::string, std::string>
     433        1791 : JsonSyntaxTree::getActionLabel(const std::string & action) const
     434             : {
     435        1791 :   auto it = _action_label_map.find(action);
     436        1791 :   if (it != _action_label_map.end())
     437        1791 :     return it->second;
     438             :   else
     439           0 :     return std::make_pair("", "");
     440             : }

Generated by: LCOV version 1.14