13 #include <libxml/parser.h>
14 #include <libxml/tree.h>
15 #include <spdlog/spdlog.h>
16 #include <xml_patch.h>
18 #include <boost/property_tree/xml_parser.hpp>
29 const bool be_ruthless,
31 : ptree_(std::move(ptree)),
56 boost::property_tree::ptree& parent,
57 boost::property_tree::ptree::path_type
const& child_path,
58 boost::property_tree::ptree& child,
59 fs::path
const& bench_dir,
62 using boost::property_tree::ptree;
64 method(parent, child_path, child, bench_dir);
65 for (
auto& [key, tree] : child)
67 ptree::path_type
const cur_path = child_path / ptree::path_type(key);
73 void traverse(boost::property_tree::ptree& parent,
const fs::path bench_dir,
80 [[maybe_unused]] boost::property_tree::ptree
const& parent,
81 [[maybe_unused]] boost::property_tree::ptree::path_type
const& child_path,
82 boost::property_tree::ptree& child,
83 fs::path
const& bench_dir)
85 using boost::property_tree::ptree;
86 for (
auto& [key, tree] : child)
90 auto filename = tree.get<std::string>(
"<xmlattr>.file");
91 if (
auto const filepath = fs::path(filename);
92 filepath.is_relative())
94 filename = (bench_dir / filepath).
string();
96 INFO(
"Including {:s} into project file.", filename);
99 read_xml(filename, include_tree,
100 boost::property_tree::xml_parser::no_comments |
101 boost::property_tree::xml_parser::trim_whitespace);
104 auto& tmp_tree = child.put_child(
"include", include_tree);
107 std::move(tmp_tree.begin(), tmp_tree.end(), back_inserter(child));
110 child.erase(
"include");
119 void patchStream(std::string patch_file, std::stringstream& prj_stream)
121 auto patch = xmlParseFile(patch_file.c_str());
124 OGS_FATAL(
"Error reading XML diff file {:s}.", patch_file);
128 xmlParseMemory(prj_stream.str().c_str(), prj_stream.str().size());
131 OGS_FATAL(
"Error reading project file from memory.");
134 auto node = xmlDocGetRootElement(patch);
136 for (node = node ? node->children : NULL; node; node = node->next)
138 if (node->type != XML_ELEMENT_NODE)
143 if (!strcmp(
reinterpret_cast<const char*
>(node->name),
"add"))
145 rc = xml_patch_add(doc, node);
147 else if (!strcmp(
reinterpret_cast<const char*
>(node->name),
"replace"))
149 rc = xml_patch_replace(doc, node);
151 else if (!strcmp(
reinterpret_cast<const char*
>(node->name),
"remove"))
153 rc = xml_patch_remove(doc, node);
158 "Error while patching prj file with patch file {:}. Only "
159 "'add', 'replace' and 'remove' elements are allowed! Got an "
160 "element '{:s}' on line {:d}.",
161 patch_file, node->name, node->line);
167 "Error while patching prj file with patch file {:}. Error in "
168 "element '{:s}' on line {:d}.",
169 patch_file, node->name, node->line);
175 xmlDocDumpMemory(doc, &xmlbuff, &buffersize);
177 prj_stream << xmlbuff;
187 std::vector<std::string> patch_files)
193 if (!patch_files.empty())
196 "It is not allowed to specify additional patch files "
197 "if a patch file was already specified as the "
200 auto patch = xmlParseFile(prj_file.c_str());
201 auto node = xmlDocGetRootElement(patch);
202 auto base_file = xmlGetProp(node, (
const xmlChar*)
"base_file");
203 if (base_file ==
nullptr)
206 "Error reading base prj file (base_file attribute) in given "
210 patch_files = {prj_file};
211 std::stringstream ss;
217 if (std::ifstream file(prj_file); file)
219 prj_stream << file.rdbuf();
225 ERR(
"File {:s} does not exist.", prj_file);
227 DBUG(
"Stream state flags: {:b}.", file.rdstate());
228 OGS_FATAL(
"Could not open project file '{:s}' for reading.", prj_file);
232 if (!patch_files.empty())
234 for (
const auto& patch_file : patch_files)
243 const bool be_ruthless,
244 const std::string& toplevel_tag,
245 const std::vector<std::string>& patch_files)
247 std::string prj_file = filepath;
248 std::stringstream prj_stream;
257 read_xml(prj_stream, ptree,
258 boost::property_tree::xml_parser::no_comments |
259 boost::property_tree::xml_parser::trim_whitespace);
261 if (toplevel_tag ==
"OpenGeoSysProject")
266 catch (boost::property_tree::xml_parser_error
const& e)
268 OGS_FATAL(
"Error while parsing XML file `{:s}' at line {:d}: {:s}.",
269 e.filename(), e.line(), e.message());
272 DBUG(
"Project configuration from file '{:s}' read.", filepath);
274 if (
auto child = ptree.get_child_optional(toplevel_tag))
278 OGS_FATAL(
"Tag <{:s}> has not been found in file `{:s}'.", toplevel_tag,
void INFO(char const *fmt, Args const &... args)
void ERR(char const *fmt, Args const &... args)
void DBUG(char const *fmt, Args const &... args)
ConfigTree ctree_
ConfigTree depending on ptree_.
ConfigTree const & operator*() const
ConfigTree const * operator->() const
void checkAndInvalidate()
ConfigTreeTopLevel(std::string const &filepath, bool const be_ruthless, ConfigTree::PTree &&ptree)
boost::property_tree::ptree PTree
The tree being wrapped by this class.
void patchStream(std::string patch_file, std::stringstream &prj_stream)
void replaceIncludes([[maybe_unused]] boost::property_tree::ptree const &parent, [[maybe_unused]] boost::property_tree::ptree::path_type const &child_path, boost::property_tree::ptree &child, fs::path const &bench_dir)
void traverse(boost::property_tree::ptree &parent, const fs::path bench_dir, T &method)
void checkAndInvalidate(ConfigTree &conf)
This is an overloaded member function, provided for convenience. It differs from the above function o...
std::string getFileExtension(const std::string &path)
std::string extractPath(std::string const &pathname)
void readAndPatchPrj(std::stringstream &prj_stream, std::string &prj_file, std::vector< std::string > patch_files)
void traverse_recursive(boost::property_tree::ptree &parent, boost::property_tree::ptree::path_type const &child_path, boost::property_tree::ptree &child, fs::path const &bench_dir, T &method)
bool IsFileExisting(const std::string &strFilename)
Returns true if given file exists.
std::string joinPaths(std::string const &pathA, std::string const &pathB)
ConfigTreeTopLevel makeConfigTree(const std::string &filepath, const bool be_ruthless, const std::string &toplevel_tag, const std::vector< std::string > &patch_files)