preCICE v3.1.1
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/String.hpp"
16#include "xml/ConfigParser.hpp"
17#include "xml/XMLTag.hpp"
18
19namespace precice::xml {
20
22{
23 static const std::map<std::string, char> escapes{{"&lt;", '<'}, {"&gt;", '>'}, {"&amp;", '&'}, {"&quot;", '"'}, {"&apos;", '\''}};
24 while (true) {
25 bool changes{false};
26 for (const auto &kv : escapes) {
27 auto position = xml.find(kv.first);
28 if (position != std::string::npos) {
29 xml.replace(position, kv.first.length(), 1, kv.second);
30 changes = true;
31 }
32 }
33 if (!changes) {
34 break;
35 }
36 };
37 return xml;
38}
39
40// ------------------------- Callback functions for libxml2 -------------------------
41
43 void * ctx,
44 const xmlChar * localname,
45 const xmlChar * prefix,
46 const xmlChar * URI,
47 int nb_namespaces,
48 const xmlChar **namespaces,
49 int nb_attributes,
50 int nb_defaulted,
51 const xmlChar **attributes)
52{
54 unsigned int index = 0;
56 std::string attributeName(reinterpret_cast<const char *>(attributes[index]));
57
58 auto valueBegin = reinterpret_cast<const char *>(attributes[index + 3]);
59 auto valueEnd = reinterpret_cast<const char *>(attributes[index + 4]);
61
63 }
64
65 auto pParser = static_cast<ConfigParser *>(ctx);
66
67 std::string sPrefix(prefix == nullptr ? "" : reinterpret_cast<const char *>(prefix));
68
69 pParser->OnStartElement(reinterpret_cast<const char *>(localname), sPrefix, attributesMap);
70}
71
73 void * ctx,
74 const xmlChar *localname,
75 const xmlChar *prefix,
76 const xmlChar *URI)
77{
78 ConfigParser *pParser = static_cast<ConfigParser *>(ctx);
79 pParser->OnEndElement();
80}
81
82void OnCharacters(void *ctx, const xmlChar *ch, int len)
83{
84 ConfigParser *pParser = static_cast<ConfigParser *>(ctx);
85 pParser->OnTextSection(std::string(reinterpret_cast<const char *>(ch), len));
86}
87
88void OnStructuredErrorFunc(void *userData, const xmlError *error)
89{
90 const std::string message{error->message};
91
92 // Ignore all namespace-related messages
93 if (message.find("Namespace") != std::string::npos) {
94 return;
95 }
96
97 ConfigParser::MessageProxy(error->level, message);
98}
99
100// Required for versions before 2.12.0 of libxml
102{
103 OnStructuredErrorFunc(userData, static_cast<const xmlError *>(error));
104}
105
106void OnErrorFunc(void *userData, const char *error, ...)
107{
109}
110
111void OnFatalErrorFunc(void *userData, const char *error, ...)
112{
114}
115
116// ------------------------- ConfigParser implementation -------------------------
117
119
121 : m_pXmlTag(std::move(pXmlTag))
122{
124
127 // Initialize with the root tag, if any.
128 if (not m_AllTags.empty())
129 SubTags.push_back(m_AllTags[0]);
130
131 try {
133 } catch (const std::exception &e) {
134 PRECICE_ERROR("An unexpected exception occurred during configuration: {}.", e.what());
135 }
136}
137
142
144{
145 switch (level) {
146 case (XML_ERR_FATAL):
147 case (XML_ERR_ERROR):
149 break;
150 case (XML_ERR_WARNING):
152 break;
153 default:
155 }
156}
157
159{
161
162 memset(&SAXHandler, 0, sizeof(xmlSAXHandler));
163
164 SAXHandler.initialized = XML_SAX2_MAGIC;
165 SAXHandler.startElementNs = OnStartElementNs;
166 SAXHandler.endElementNs = OnEndElementNs;
167 SAXHandler.characters = OnCharacters;
169 SAXHandler.error = OnErrorFunc;
170 SAXHandler.fatalError = OnFatalErrorFunc;
171
173 PRECICE_CHECK(ifs, "XML parser was unable to open configuration file \"{}\"", filePath);
174
176
177 xmlParserCtxtPtr ctxt = xmlCreatePushParserCtxt(&SAXHandler, static_cast<void *>(this),
178 content.c_str(), content.size(), nullptr);
179
180 xmlParseChunk(ctxt, nullptr, 0, 1);
183
184 return 0;
185}
186
187namespace {
188struct Distance {
191
192 bool operator<(const Distance &other) const
193 {
194 return distance < other.distance;
195 }
196};
197auto gatherCandidates(const std::vector<std::shared_ptr<XMLTag>> &DefTags, std::string_view prefix)
198{
199 bool validPrefix = std::any_of(DefTags.begin(), DefTags.end(), [prefix](const auto &tag) { return tag->getNamespace() == prefix; });
200
201 std::set<std::string> entries;
202 for (const auto &tag : DefTags) {
203 if (!validPrefix || (tag->getNamespace() == prefix)) {
204 entries.insert(tag->getFullName());
205 }
206 }
207 return entries;
208}
209} // namespace
210
212{
214
215 for (auto &subtag : SubTags) {
216 std::string expectedName = (subtag->m_Prefix.length() ? subtag->m_Prefix + ":" : "") + subtag->m_Name;
217 const auto tagPosition = std::find_if(
218 DefTags.begin(),
219 DefTags.end(),
221 return pTag->_fullName == expectedName;
222 });
223
224 if (tagPosition == DefTags.end()) {
225 // Tag not found
226 auto names = gatherCandidates(DefTags, subtag->m_Prefix);
227
229 if (!matches.empty() && matches.front().distance < 3) {
230 matches.erase(std::remove_if(matches.begin(), matches.end(), [](auto &m) { return m.distance > 2; }), matches.end());
232 std::transform(matches.begin(), matches.end(), std::back_inserter(stringMatches), [](auto &m) { return m.name; });
233 PRECICE_ERROR("The configuration contains an unknown tag <{}>. Did you mean <{}>?", expectedName, fmt::join(stringMatches, ">,<"));
234 } else {
235 PRECICE_ERROR("The configuration contains an unknown tag <{}>. Expected tags are {}.", expectedName, fmt::join(names, ", "));
236 }
237 }
238
239 auto pDefSubTag = *tagPosition;
240 pDefSubTag->resetAttributes();
241
242 if ((pDefSubTag->_occurrence == XMLTag::OCCUR_ONCE) || (pDefSubTag->_occurrence == XMLTag::OCCUR_NOT_OR_ONCE)) {
243 PRECICE_CHECK(usedTags.count(pDefSubTag->_fullName) == 0,
244 "Tag <{}> is not allowed to occur multiple times.", pDefSubTag->_fullName);
245 usedTags.emplace(pDefSubTag->_fullName);
246 }
247
248 pDefSubTag->_configuredNamespaces[pDefSubTag->_namespace] = true;
249 pDefSubTag->readAttributes(subtag->m_aAttributes);
250 pDefSubTag->_listener.xmlTagCallback(context, *pDefSubTag);
251 pDefSubTag->_configured = true;
252
253 connectTags(context, pDefSubTag->_subtags, subtag->m_aSubTags);
254
255 pDefSubTag->areAllSubtagsConfigured();
256 pDefSubTag->_listener.xmlEndTagCallback(context, *pDefSubTag);
257 }
258}
259
264{
265 auto pTag = std::make_shared<CTag>();
266
267 pTag->m_Prefix = std::move(prefix);
268 pTag->m_Name = std::move(localname);
269 pTag->m_aAttributes = std::move(attributes);
270
271 if (not m_CurrentTags.empty()) {
273 pParentTag->m_aSubTags.push_back(pTag);
274 }
275
278}
279
284
286{
287 // This page intentionally left blank
288}
289} // namespace precice::xml
std::size_t distance
std::string prefix
#define PRECICE_ERROR(...)
Definition LogMacros.hpp:15
#define PRECICE_WARN(...)
Definition LogMacros.hpp:11
#define PRECICE_INFO(...)
Definition LogMacros.hpp:13
#define PRECICE_CHECK(check,...)
Definition LogMacros.hpp:35
unsigned int index
std::string name
T any_of(T... args)
T back(T... args)
T back_inserter(T... args)
This class provides a lightweight logger.
Definition Logger.hpp:16
void OnTextSection(const std::string &ch)
Callback for text sections in xml file.
ConfigParser(const std::string &filePath, const ConfigurationContext &context, std::shared_ptr< XMLTag > pXmlTag)
Parser ctor for Callback init.
void OnEndElement()
Callback for End-Tag.
std::shared_ptr< precice::xml::XMLTag > m_pXmlTag
void OnStartElement(std::string localname, std::string 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.
int readXmlFile(std::string const &filePath)
Reads the xml file.
static precice::logging::Logger _log
static void MessageProxy(int level, const std::string &mess)
Proxy for error and warning messages from libxml2.
T empty(T... args)
T find(T... args)
T insert(T... args)
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)
std::string decodeXML(std::string xml)
Decodes escape sequences of a given xml.
void OnStructuredErrorFunc(void *userData, const xmlError *error)
void OnCharacters(void *ctx, const xmlChar *ch, int len)
void OnErrorFunc(void *userData, const char *error,...)
STL namespace.
T pop_back(T... args)
T push_back(T... args)
T remove_if(T... args)
T replace(T... args)
Tightly coupled to the parameters of Participant()
Definition XMLTag.hpp:24
T transform(T... args)