7#include <libxml/SAX2.h>
11#include <unordered_set>
26 static const std::map<std::string_view, char> escapes{{
"<",
'<'}, {
">",
'>'}, {
"&",
'&'}, {
""",
'"'}, {
"'",
'\''}};
27 std::string decodedXml(
xml);
30 for (
const auto &kv : escapes) {
31 auto position = decodedXml.find(kv.first);
32 if (position != std::string::npos) {
33 decodedXml.replace(position, kv.first.length(), 1, kv.second);
48 const xmlChar *localname,
49 const xmlChar *prefix,
52 const xmlChar **namespaces,
55 const xmlChar **attributes)
58 unsigned int index = 0;
59 for (
int indexAttribute = 0; indexAttribute < nb_attributes; ++indexAttribute, index += 5) {
60 std::string attributeName(
reinterpret_cast<const char *
>(attributes[index]));
62 auto valueBegin =
reinterpret_cast<const char *
>(attributes[index + 3]);
63 auto valueEnd =
reinterpret_cast<const char *
>(attributes[index + 4]);
64 std::string_view value(valueBegin,
65 valueEnd - valueBegin);
67 attributesMap[attributeName] =
decodeXML(value);
72 std::string_view sPrefix(prefix ==
nullptr ?
"" :
reinterpret_cast<const char *
>(prefix));
74 pParser->OnStartElement(
reinterpret_cast<const char *
>(localname), sPrefix, attributesMap);
79 const xmlChar *localname,
80 const xmlChar *prefix,
90 pParser->
OnTextSection(std::string(
reinterpret_cast<const char *
>(ch), len));
95 const std::string_view message{error->message};
98 if (message.find(
"Namespace") != std::string::npos) {
130 std::vector<std::shared_ptr<XMLTag>> DefTags{
m_pXmlTag};
140 }
catch (
const std::exception &e) {
141 PRECICE_ERROR(
"An unexpected exception occurred during configuration: {}.", e.what());
153 case (XML_ERR_FATAL):
154 case (XML_ERR_ERROR):
157 case (XML_ERR_WARNING):
175 std::ifstream ifs{filePath};
176 PRECICE_CHECK(ifs,
"XML parser was unable to open configuration file \"{}\"", filePath);
180 std::string content{std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>()};
183 auto size = std::filesystem::file_size(filePath, ec);
184 PRECICE_CHECK(!ec,
"XML parser was unable to get the size of the configuration file \"{}\": {}", filePath, ec.message());
186 std::string content(size,
'\0');
187 ifs.read(content.data(), size);
190 PRECICE_CHECK(!content.empty(),
"The configuration file \"{}\" is empty.", filePath);
197 xmlSAXHandler SAXHandler;
199 memset(&SAXHandler, 0,
sizeof(xmlSAXHandler));
201 SAXHandler.initialized = XML_SAX2_MAGIC;
213 auto ctxt = std::unique_ptr<xmlParserCtxt, void (*)(xmlParserCtxtPtr)>(
214 xmlCreatePushParserCtxt(&SAXHandler,
static_cast<void *
>(
this),
215 content.c_str(), content.size(),
nullptr),
218 PRECICE_CHECK(ctxt !=
nullptr,
"XML parser was unable to create a push parser context for file \"{}\"", filePath);
220 xmlParseChunk(ctxt.get(),
nullptr, 0, 1);
227 std::size_t distance;
230 bool operator<(
const Distance &other)
const
232 return distance < other.distance;
235auto gatherCandidates(
const std::vector<std::shared_ptr<XMLTag>> &DefTags, std::string_view prefix)
237 bool validPrefix = std::any_of(DefTags.begin(), DefTags.end(), [prefix](
const auto &tag) { return tag->getNamespace() == prefix; });
239 std::set<std::string> entries;
240 for (
const auto &tag : DefTags) {
241 if (!validPrefix || (tag->getNamespace() == prefix)) {
242 entries.insert(tag->getFullName());
251 std::unordered_set<std::string> usedTags;
253 for (
auto &subtag : SubTags) {
254 std::string expectedName = (subtag->m_Prefix.length() ? subtag->m_Prefix +
":" :
"") + subtag->m_Name;
256 "This configuration contains the tag <solver-interface>, meaning it was created for a preCICE version prior to version 3. "
257 "Are you using the correct version of your simulation case? Has this simulation case been updated to this version of preCICE?");
258 const auto tagPosition = std::find_if(
261 [expectedName](
const std::shared_ptr<XMLTag> &pTag) {
262 return pTag->_fullName == expectedName;
265 if (tagPosition == DefTags.end()) {
267 auto names = gatherCandidates(DefTags, subtag->m_Prefix);
270 if (!matches.empty() && matches.front().distance < 3) {
271 matches.erase(std::remove_if(matches.begin(), matches.end(), [](
auto &m) { return m.distance > 2; }), matches.end());
272 std::vector<std::string> stringMatches;
273 std::transform(matches.begin(), matches.end(), std::back_inserter(stringMatches), [](
auto &m) { return m.name; });
274 PRECICE_ERROR(
"The configuration contains an unknown tag <{}>. Did you mean <{}>?", expectedName, fmt::join(stringMatches,
">,<"));
276 PRECICE_ERROR(
"The configuration contains an unknown tag <{}>. Expected tags are {}.", expectedName, fmt::join(names,
", "));
280 auto pDefSubTag = *tagPosition;
281 pDefSubTag->resetAttributes();
285 "Tag <{}> is not allowed to occur multiple times.", pDefSubTag->_fullName);
286 usedTags.emplace(pDefSubTag->_fullName);
289 pDefSubTag->_configuredNamespaces[pDefSubTag->_namespace] =
true;
290 pDefSubTag->readAttributes(subtag->m_aAttributes);
291 pDefSubTag->_listener.xmlTagCallback(context, *pDefSubTag);
292 pDefSubTag->_configured =
true;
294 connectTags(context, pDefSubTag->_subtags, subtag->m_aSubTags);
296 pDefSubTag->areAllSubtagsConfigured();
297 pDefSubTag->_listener.xmlEndTagCallback(context, *pDefSubTag);
302 std::string_view localname,
303 std::string_view prefix,
306 auto pTag = std::make_shared<CTag>();
308 pTag->m_Prefix = prefix;
309 pTag->m_Name = localname;
310 pTag->m_aAttributes = std::move(attributes);
314 pParentTag->m_aSubTags.push_back(pTag);
#define PRECICE_ERROR(...)
#define PRECICE_WARN(...)
#define PRECICE_INFO(...)
#define PRECICE_CHECK(check,...)
This class provides a lightweight logger.
void OnTextSection(const std::string &ch)
Callback for text sections in xml file.
std::vector< std::shared_ptr< CTag > > CTagPtrVec
void OnEndElement()
Callback for End-Tag.
std::shared_ptr< precice::xml::XMLTag > m_pXmlTag
ConfigParser(std::string_view filePath, const ConfigurationContext &context, std::shared_ptr< XMLTag > pXmlTag)
Parser ctor for Callback init.
void OnStartElement(std::string_view localname, std::string_view prefix, CTag::AttributePair attributes)
Callback for Start-Tag.
void connectTags(const ConfigurationContext &context, std::vector< std::shared_ptr< precice::xml::XMLTag > > &DefTags, CTagPtrVec &SubTags)
Connects the actual tags of an xml layer with the predefined tags.
std::string readFileContent(std::string const &filePath) const
Reads and returns the content of the file.
std::string hash() const
returns the hash of the processed XML file
static void MessageProxy(int level, std::string_view mess)
Proxy for error and warning messages from libxml2.
int readXmlFile(std::string const &filePath)
Reads the xml file.
static precice::logging::Logger _log
std::string _hash
the hash of the last processed config
static constexpr Group Fundamental
Convenience instance of the Cat::Fundamental.
std::string preciceHash(std::string_view s)
creates a portable hash of the given input
std::vector< StringMatch > computeMatches(std::string_view given, const Container &expected)
contains the XML configuration parser.
void OnEndElementNs(void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI)
void OnFatalErrorFunc(void *userData, const char *error,...)
void OnStartElementNs(void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI, int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes)
void OnStructuredErrorFunc(void *userData, const xmlError *error)
void OnCharacters(void *ctx, const xmlChar *ch, int len)
void OnErrorFunc(void *userData, const char *error,...)
std::string decodeXML(std::string_view xml)
Decodes escape sequences of a given xml.
std::map< std::string, std::string > AttributePair
Tightly coupled to the parameters of Participant()