preCICE v3.2.0
Loading...
Searching...
No Matches
ConfigParser.cpp
Go to the documentation of this file.
1#include <algorithm>
2#include <cstddef>
3#include <exception>
4#include <fstream>
5#include <iterator>
6#include <libxml/SAX.h>
7#include <memory>
8#include <sstream>
9#include <string>
10#include <unordered_set>
11#include <utility>
12
13#include "logging/LogMacros.hpp"
14#include "logging/Logger.hpp"
15#include "utils/Hash.hpp"
16#include "utils/String.hpp"
17#include "xml/ConfigParser.hpp"
18#include "xml/XMLTag.hpp"
19
20namespace precice::xml {
21
23{
24 static const std::map<std::string_view, char> escapes{{"&lt;", '<'}, {"&gt;", '>'}, {"&amp;", '&'}, {"&quot;", '"'}, {"&apos;", '\''}};
25 std::string decodedXml(xml);
26 while (true) {
27 bool changes{false};
28 for (const auto &kv : escapes) {
29 auto position = decodedXml.find(kv.first);
30 if (position != std::string::npos) {
31 decodedXml.replace(position, kv.first.length(), 1, kv.second);
32 changes = true;
33 }
34 }
35 if (!changes) {
36 break;
37 }
38 };
39 return decodedXml;
40}
41
42// ------------------------- Callback functions for libxml2 -------------------------
43
45 void *ctx,
46 const xmlChar *localname,
47 const xmlChar *prefix,
48 const xmlChar *URI,
49 int nb_namespaces,
50 const xmlChar **namespaces,
51 int nb_attributes,
52 int nb_defaulted,
53 const xmlChar **attributes)
54{
56 unsigned int index = 0;
57 for (int indexAttribute = 0; indexAttribute < nb_attributes; ++indexAttribute, index += 5) {
58 std::string attributeName(reinterpret_cast<const char *>(attributes[index]));
59
60 auto valueBegin = reinterpret_cast<const char *>(attributes[index + 3]);
61 auto valueEnd = reinterpret_cast<const char *>(attributes[index + 4]);
62 std::string_view value(valueBegin,
63 valueEnd - valueBegin);
64
65 attributesMap[attributeName] = decodeXML(value);
66 }
67
68 auto pParser = static_cast<ConfigParser *>(ctx);
69
70 std::string_view sPrefix(prefix == nullptr ? "" : reinterpret_cast<const char *>(prefix));
71
72 pParser->OnStartElement(reinterpret_cast<const char *>(localname), sPrefix, attributesMap);
73}
74
76 void *ctx,
77 const xmlChar *localname,
78 const xmlChar *prefix,
79 const xmlChar *URI)
80{
81 ConfigParser *pParser = static_cast<ConfigParser *>(ctx);
82 pParser->OnEndElement();
83}
84
85void OnCharacters(void *ctx, const xmlChar *ch, int len)
86{
87 ConfigParser *pParser = static_cast<ConfigParser *>(ctx);
88 pParser->OnTextSection(std::string(reinterpret_cast<const char *>(ch), len));
89}
90
91void OnStructuredErrorFunc(void *userData, const xmlError *error)
92{
93 const std::string_view message{error->message};
94
95 // Ignore all namespace-related messages
96 if (message.find("Namespace") != std::string::npos) {
97 return;
98 }
99
100 ConfigParser::MessageProxy(error->level, message);
101}
102
103// Required for versions before 2.12.0 of libxml
104void OnStructuredErrorFunc(void *userData, xmlError *error)
105{
106 OnStructuredErrorFunc(userData, static_cast<const xmlError *>(error));
107}
108
109void OnErrorFunc(void *userData, const char *error, ...)
110{
111 ConfigParser::MessageProxy(XML_ERR_ERROR, error);
112}
113
114void OnFatalErrorFunc(void *userData, const char *error, ...)
115{
116 ConfigParser::MessageProxy(XML_ERR_FATAL, error);
117}
118
119// ------------------------- ConfigParser implementation -------------------------
120
122
124 : m_pXmlTag(std::move(pXmlTag))
125{
126 readXmlFile(std::string(filePath));
127
129 CTagPtrVec SubTags;
130 // Initialize with the root tag, if any.
131 if (not m_AllTags.empty())
132 SubTags.push_back(m_AllTags[0]);
133
134 try {
135 connectTags(context, DefTags, SubTags);
136 } catch (::precice::Error &) {
137 throw;
138 } catch (const std::exception &e) {
139 PRECICE_ERROR("An unexpected exception occurred during configuration: {}.", e.what());
140 }
141}
142
147
149{
150 switch (level) {
151 case (XML_ERR_FATAL):
152 case (XML_ERR_ERROR):
153 PRECICE_ERROR(mess);
154 break;
155 case (XML_ERR_WARNING):
156 PRECICE_WARN(mess);
157 break;
158 default:
159 PRECICE_INFO(mess);
160 }
161}
162
164{
165 return _hash;
166}
167
169{
170 xmlSAXHandler SAXHandler;
171
172 memset(&SAXHandler, 0, sizeof(xmlSAXHandler));
173
174 SAXHandler.initialized = XML_SAX2_MAGIC;
175 SAXHandler.startElementNs = OnStartElementNs;
176 SAXHandler.endElementNs = OnEndElementNs;
177 SAXHandler.characters = OnCharacters;
178 SAXHandler.serror = OnStructuredErrorFunc;
179 SAXHandler.error = OnErrorFunc;
180 SAXHandler.fatalError = OnFatalErrorFunc;
181
182 std::ifstream ifs{filePath};
183 PRECICE_CHECK(ifs, "XML parser was unable to open configuration file \"{}\"", filePath);
184
186
187 PRECICE_CHECK(!content.empty(), "The configuration file \"{}\" is empty.", filePath);
188
189 _hash = utils::preciceHash(content);
190
191 xmlParserCtxtPtr ctxt = xmlCreatePushParserCtxt(&SAXHandler, static_cast<void *>(this),
192 content.c_str(), content.size(), nullptr);
193
194 xmlParseChunk(ctxt, nullptr, 0, 1);
195 xmlFreeParserCtxt(ctxt);
196 xmlCleanupParser();
197
198 return 0;
199}
200
201namespace {
202struct Distance {
203 std::size_t distance;
204 std::string name;
205
206 bool operator<(const Distance &other) const
207 {
208 return distance < other.distance;
209 }
210};
211auto gatherCandidates(const std::vector<std::shared_ptr<XMLTag>> &DefTags, std::string_view prefix)
212{
213 bool validPrefix = std::any_of(DefTags.begin(), DefTags.end(), [prefix](const auto &tag) { return tag->getNamespace() == prefix; });
214
215 std::set<std::string> entries;
216 for (const auto &tag : DefTags) {
217 if (!validPrefix || (tag->getNamespace() == prefix)) {
218 entries.insert(tag->getFullName());
219 }
220 }
221 return entries;
222}
223} // namespace
224
226{
228
229 for (auto &subtag : SubTags) {
230 std::string expectedName = (subtag->m_Prefix.length() ? subtag->m_Prefix + ":" : "") + subtag->m_Name;
231 PRECICE_CHECK(expectedName != "solver-interface",
232 "This configuration contains the tag <solver-interface>, meaning it was created for a preCICE version prior to version 3. "
233 "Are you using the correct version of your simulation case? Has this simulation case been updated to this version of preCICE?");
234 const auto tagPosition = std::find_if(
235 DefTags.begin(),
236 DefTags.end(),
237 [expectedName](const std::shared_ptr<XMLTag> &pTag) {
238 return pTag->_fullName == expectedName;
239 });
240
241 if (tagPosition == DefTags.end()) {
242 // Tag not found
243 auto names = gatherCandidates(DefTags, subtag->m_Prefix);
244
245 auto matches = utils::computeMatches(expectedName, names);
246 if (!matches.empty() && matches.front().distance < 3) {
247 matches.erase(std::remove_if(matches.begin(), matches.end(), [](auto &m) { return m.distance > 2; }), matches.end());
248 std::vector<std::string> stringMatches;
249 std::transform(matches.begin(), matches.end(), std::back_inserter(stringMatches), [](auto &m) { return m.name; });
250 PRECICE_ERROR("The configuration contains an unknown tag <{}>. Did you mean <{}>?", expectedName, fmt::join(stringMatches, ">,<"));
251 } else {
252 PRECICE_ERROR("The configuration contains an unknown tag <{}>. Expected tags are {}.", expectedName, fmt::join(names, ", "));
253 }
254 }
255
256 auto pDefSubTag = *tagPosition;
257 pDefSubTag->resetAttributes();
258
259 if ((pDefSubTag->_occurrence == XMLTag::OCCUR_ONCE) || (pDefSubTag->_occurrence == XMLTag::OCCUR_NOT_OR_ONCE)) {
260 PRECICE_CHECK(usedTags.count(pDefSubTag->_fullName) == 0,
261 "Tag <{}> is not allowed to occur multiple times.", pDefSubTag->_fullName);
262 usedTags.emplace(pDefSubTag->_fullName);
263 }
264
265 pDefSubTag->_configuredNamespaces[pDefSubTag->_namespace] = true;
266 pDefSubTag->readAttributes(subtag->m_aAttributes);
267 pDefSubTag->_listener.xmlTagCallback(context, *pDefSubTag);
268 pDefSubTag->_configured = true;
269
270 connectTags(context, pDefSubTag->_subtags, subtag->m_aSubTags);
271
272 pDefSubTag->areAllSubtagsConfigured();
273 pDefSubTag->_listener.xmlEndTagCallback(context, *pDefSubTag);
274 }
275}
276
278 std::string_view localname,
279 std::string_view prefix,
280 CTag::AttributePair attributes)
281{
282 auto pTag = std::make_shared<CTag>();
283
284 pTag->m_Prefix = prefix;
285 pTag->m_Name = localname;
286 pTag->m_aAttributes = std::move(attributes);
287
288 if (not m_CurrentTags.empty()) {
289 auto pParentTag = m_CurrentTags.back();
290 pParentTag->m_aSubTags.push_back(pTag);
291 }
292
293 m_AllTags.push_back(pTag);
294 m_CurrentTags.push_back(pTag);
295}
296
298{
299 m_CurrentTags.pop_back();
300}
301
303{
304 // This page intentionally left blank
305}
306} // namespace precice::xml
#define PRECICE_ERROR(...)
Definition LogMacros.hpp:16
#define PRECICE_WARN(...)
Definition LogMacros.hpp:12
#define PRECICE_INFO(...)
Definition LogMacros.hpp:14
#define PRECICE_CHECK(check,...)
Definition LogMacros.hpp:32
T any_of(T... args)
T back_inserter(T... args)
T c_str(T... args)
This class provides a lightweight logger.
Definition Logger.hpp:17
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 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
T count(T... args)
T emplace(T... args)
T empty(T... args)
T find(T... args)
T insert(T... args)
T make_shared(T... args)
std::string preciceHash(std::string_view s)
creates a portable hash of the given input
Definition Hash.cpp:10
std::vector< StringMatch > computeMatches(std::string_view given, const Container &expected)
Definition String.hpp:95
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.
STL namespace.
T push_back(T... args)
T remove_if(T... args)
T replace(T... args)
T size(T... args)
std::map< std::string, std::string > AttributePair
Tightly coupled to the parameters of Participant()
Definition XMLTag.hpp:21
T transform(T... args)
T what(T... args)