Chaste Commit::29c3c4ab2bee00ddd311a035a1e522c03cbe928a
XmlTools.cpp
1/*
2
3Copyright (c) 2005-2026, University of Oxford.
4All rights reserved.
5
6University of Oxford means the Chancellor, Masters and Scholars of the
7University of Oxford, having an administrative office at Wellington
8Square, Oxford OX1 2JD, UK.
9
10This file is part of Chaste.
11
12Redistribution and use in source and binary forms, with or without
13modification, are permitted provided that the following conditions are met:
14 * Redistributions of source code must retain the above copyright notice,
15 this list of conditions and the following disclaimer.
16 * Redistributions in binary form must reproduce the above copyright notice,
17 this list of conditions and the following disclaimer in the documentation
18 and/or other materials provided with the distribution.
19 * Neither the name of the University of Oxford nor the names of its
20 contributors may be used to endorse or promote products derived from this
21 software without specific prior written permission.
22
23THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34*/
35
36#include "XmlTools.hpp"
37
38#include <iostream>
39
40#include <xercesc/dom/DOM.hpp>
41#include <xercesc/util/PlatformUtils.hpp>
42#include <xercesc/util/QName.hpp>
43#include <xercesc/util/XMLUniDefs.hpp> // chLatin_*
44#include <xercesc/framework/Wrapper4InputSource.hpp>
45#include <xercesc/validators/common/Grammar.hpp>
46
47#include <xsd/cxx/xml/sax/std-input-source.hxx>
48#include <xsd/cxx/xml/dom/bits/error-handler-proxy.hxx>
49#include <xsd/cxx/tree/exceptions.hxx>
50
51#include "Exception.hpp"
52
53XSD_DOM_AUTO_PTR<xercesc::DOMDocument> XmlTools::ReadXmlFile(
54 const std::string& rFileName,
55 const ::xsd::cxx::tree::properties<char>& rProps,
56 bool validate)
57{
58 XSD_DOM_AUTO_PTR<xercesc::DOMDocument> p_doc;
59 try
60 {
61 // Initialise Xerces
62 xercesc::XMLPlatformUtils::Initialize();
63 // Set up an error handler
64 ::xsd::cxx::tree::error_handler<char> error_handler;
65 // Parse XML to DOM
66 p_doc = XmlTools::ReadFileToDomDocument(rFileName, error_handler, rProps, validate);
67 // Any errors?
68 error_handler.throw_if_failed< ::xsd::cxx::tree::parsing<char> >();
69 }
70 catch (const ::xsd::cxx::tree::parsing<char>& e)
71 {
72 Finalize();
73 // Test for missing schema/xml file
74 const ::xsd::cxx::tree::diagnostics<char>& diags = e.diagnostics();
75 const ::xsd::cxx::tree::error<char>& first_error = diags[0];
76 if (first_error.line() == 0u)
77 {
78 std::cerr << first_error << std::endl;
79 EXCEPTION("Missing file parsing configuration file: " + rFileName);
80 }
81 else
82 {
83 std::cerr << e << std::endl;
84 EXCEPTION("XML parsing error in configuration file: " + rFileName);
85 }
86 }
87// LCOV_EXCL_START
88 catch (...)
89 { // This shouldn't happen, but just in case...
90 Finalize();
91 throw;
92 }
93// LCOV_EXCL_STOP
94 return p_doc;
95}
96
97
99{
100 xercesc::XMLPlatformUtils::Terminate();
101}
102
104{
105 // The init=true case will very rarely be used, but a parameter to the constructor is needed
106 // to stop some compilers complaining about an unused variable!
107 if (init)
108 {
109// LCOV_EXCL_START
110 xercesc::XMLPlatformUtils::Initialize();
111// LCOV_EXCL_STOP
112 }
113}
114
119
120XSD_DOM_AUTO_PTR<xercesc::DOMDocument> XmlTools::ReadFileToDomDocument(
121 const std::string& rFileName,
122 ::xsd::cxx::xml::error_handler<char>& rErrorHandler,
123 const ::xsd::cxx::tree::properties<char>& rProps,
124 bool validate)
125{
126 using namespace xercesc;
127 namespace xml = xsd::cxx::xml;
128
129 // Get an implementation of the Load-Store (LS) interface.
130 const XMLCh ls_id [] = {chLatin_L, chLatin_S, chNull};
131 DOMImplementation* p_impl(DOMImplementationRegistry::getDOMImplementation(ls_id));
132
133 XSD_DOM_AUTO_PTR<DOMLSParser> p_parser(p_impl->createLSParser(DOMImplementationLS::MODE_SYNCHRONOUS, 0));
134 DOMConfiguration* p_conf(p_parser->getDomConfig());
135
136 // Discard comment nodes in the document.
137 p_conf->setParameter(XMLUni::fgDOMComments, false);
138
139 // Enable datatype normalization.
140 p_conf->setParameter(XMLUni::fgDOMDatatypeNormalization, true);
141
142 // Do not create EntityReference nodes in the DOM tree. No
143 // EntityReference nodes will be created, only the nodes
144 // corresponding to their fully expanded substitution text
145 // will be created.
146 p_conf->setParameter(XMLUni::fgDOMEntities, false);
147
148 // Perform namespace processing.
149 p_conf->setParameter(XMLUni::fgDOMNamespaces, true);
150
151 // Do not include ignorable whitespace in the DOM tree.
152 p_conf->setParameter(XMLUni::fgDOMElementContentWhitespace, false);
153
154 // Enable validation.
155 if (validate)
156 {
157 p_conf->setParameter(XMLUni::fgDOMValidate, true);
158 p_conf->setParameter(XMLUni::fgXercesSchema, true);
159 p_conf->setParameter(XMLUni::fgXercesSchemaFullChecking, false);
160 // Code taken from xsd/cxx/xml/dom/parsing-source.txx
161 if (!rProps.schema_location().empty())
162 {
163 xml::string locn(rProps.schema_location());
164 const void* p_locn(locn.c_str());
165 p_conf->setParameter(XMLUni::fgXercesSchemaExternalSchemaLocation,
166 const_cast<void*>(p_locn));
167 }
168 if (!rProps.no_namespace_schema_location().empty())
169 {
170 xml::string locn(rProps.no_namespace_schema_location());
171 const void* p_locn(locn.c_str());
172
173 p_conf->setParameter(XMLUni::fgXercesSchemaExternalNoNameSpaceSchemaLocation,
174 const_cast<void*>(p_locn));
175 }
176 }
177 else
178 {
179 // This branch is only used by projects
180// LCOV_EXCL_START
181 p_conf->setParameter(XMLUni::fgDOMValidate, false);
182 p_conf->setParameter(XMLUni::fgXercesSchema, false);
183 p_conf->setParameter(XMLUni::fgXercesSchemaFullChecking, false);
184// LCOV_EXCL_STOP
185 }
186
187 // We will release the DOM document ourselves.
188 p_conf->setParameter(XMLUni::fgXercesUserAdoptsDOMDocument, true);
189
190 // Set error handler.
191 xml::dom::bits::error_handler_proxy<char> ehp(rErrorHandler);
192 p_conf->setParameter(XMLUni::fgDOMErrorHandler, &ehp);
193
194 // Do the parse
195 XSD_DOM_AUTO_PTR<DOMDocument> p_doc(p_parser->parseURI(rFileName.c_str()));
196
197 if (ehp.failed())
198 {
199 p_doc.reset();
200 }
201
202 return p_doc;
203}
204
205// LCOV_EXCL_START
206void XmlTools::PrintNode(const std::string& rMsg, xercesc::DOMNode* pNode, bool showChildren)
207{
208 std::string prefix = X2C(pNode->getPrefix());
209 std::string name = X2C(pNode->getLocalName());
210 std::string nsuri = X2C(pNode->getNamespaceURI());
211 std::cout << rMsg << " " << pNode << " " << prefix << ":" << name << " in " << nsuri << std::endl;
212 if (showChildren)
213 {
214 for (xercesc::DOMNode* p_node = pNode->getFirstChild();
215 p_node != NULL;
216 p_node = p_node->getNextSibling())
217 {
218 std::cout << " child type " << p_node->getNodeType();
219 PrintNode("", p_node, false);
220 }
221 xercesc::DOMNamedNodeMap* p_attrs = pNode->getAttributes();
222 if (p_attrs)
223 {
224 for (XMLSize_t i=0; i<p_attrs->getLength(); i++)
225 {
226 xercesc::DOMNode* p_attr = p_attrs->item(i);
227 std::string value = X2C(p_attr->getNodeValue());
228 PrintNode(" attr (" + value + ")", p_attr, false);
229 }
230 }
231 }
232}
233// LCOV_EXCL_STOP
234
235xercesc::DOMElement* XmlTools::SetNamespace(xercesc::DOMDocument* pDocument,
236 xercesc::DOMElement* pElement,
237 const XMLCh* pNamespace)
238{
239 using namespace xercesc;
240
241 //PrintNode("Renaming", pElement, true);
242 DOMNamedNodeMap* p_orig_attrs = pElement->getAttributes();
243 std::vector<std::string> attr_values;
244 if (p_orig_attrs)
245 {
246 for (XMLSize_t i=0; i<p_orig_attrs->getLength(); i++)
247 {
248 DOMNode* p_attr = p_orig_attrs->item(i);
249 attr_values.push_back(X2C(p_attr->getNodeValue()));
250 }
251 }
252 DOMElement* p_new_elt = static_cast<DOMElement*>(
253 pDocument->renameNode(pElement, pNamespace, pElement->getLocalName()));
254 //PrintNode(" to", p_new_elt, true);
255 // Fix attributes - some get broken by the rename!
256 if (p_orig_attrs)
257 {
258 DOMNamedNodeMap* p_new_attrs = p_new_elt->getAttributes();
259 assert(p_new_attrs);
260 assert(p_new_attrs == p_orig_attrs);
261 assert(p_new_attrs->getLength() == attr_values.size());
262 for (XMLSize_t i=0; i<p_new_attrs->getLength(); i++)
263 {
264 DOMNode* p_attr = p_new_attrs->item(i);
265 p_attr->setNodeValue(X(attr_values[i]));
266 }
267 }
268 //PrintNode(" after attr fix", p_new_elt, true);
269
270 std::vector<DOMElement*> children = GetChildElements(p_new_elt);
271 for (std::vector<DOMElement*>::iterator it = children.begin(); it != children.end(); ++it)
272 {
273 SetNamespace(pDocument, *it, pNamespace);
274 }
275
276 return p_new_elt;
277}
278
279xercesc::DOMElement* XmlTools::SetNamespace(xercesc::DOMDocument* pDocument,
280 xercesc::DOMElement* pElement,
281 const std::string& rNamespace)
282{
283 return SetNamespace(pDocument, pElement, X(rNamespace));
284}
285
286
287std::vector<xercesc::DOMElement*> XmlTools::GetChildElements(const xercesc::DOMElement* pElement)
288{
289 std::vector<xercesc::DOMElement*> children;
290 for (xercesc::DOMNode* p_node = pElement->getFirstChild();
291 p_node != NULL;
292 p_node = p_node->getNextSibling())
293 {
294 if (p_node->getNodeType() == xercesc::DOMNode::ELEMENT_NODE)
295 {
296 children.push_back(static_cast<xercesc::DOMElement*>(p_node));
297 }
298 }
299 return children;
300}
301
302
303void XmlTools::FindElements(const xercesc::DOMElement* pContextElement,
304 const std::vector<std::string>& rNames,
305 std::vector<xercesc::DOMElement*>& rResults,
306 unsigned depth)
307{
308 for (xercesc::DOMNode* p_node = pContextElement->getFirstChild();
309 p_node != NULL;
310 p_node = p_node->getNextSibling())
311 {
312 if (p_node->getNodeType() == xercesc::DOMNode::ELEMENT_NODE &&
313 X2C(p_node->getLocalName()) == rNames[depth])
314 {
315 xercesc::DOMElement* p_child_elt = static_cast<xercesc::DOMElement*>(p_node);
316 if (depth == rNames.size() - 1)
317 {
318 rResults.push_back(p_child_elt);
319 }
320 else
321 {
322 FindElements(p_child_elt, rNames, rResults, depth+1);
323 }
324 }
325 }
326}
327
328std::vector<xercesc::DOMElement*> XmlTools::FindElements(const xercesc::DOMElement* pContextElement,
329 const std::string& rPath)
330{
331 std::vector<xercesc::DOMElement*> results;
332 std::vector<std::string> path;
333 size_t start_pos = 0;
334 size_t slash_pos = 0;
335 while (slash_pos != std::string::npos)
336 {
337 slash_pos = rPath.find('/', start_pos);
338 if (slash_pos == std::string::npos)
339 {
340 path.push_back(rPath.substr(start_pos));
341 }
342 else
343 {
344 path.push_back(rPath.substr(start_pos, slash_pos-start_pos));
345 }
346 start_pos = slash_pos + 1;
347 }
348 FindElements(pContextElement, path, results);
349 return results;
350}
351
352
353void XmlTools::WrapContentInElement(xercesc::DOMDocument* pDocument,
354 xercesc::DOMElement* pElement,
355 const XMLCh* pNewElementLocalName)
356{
357 const XMLCh* p_namespace_uri = pElement->getNamespaceURI();
358 const XMLCh* p_prefix = pElement->getPrefix();
359 const XMLCh* p_qualified_name;
360 if (p_prefix)
361 {
362// LCOV_EXCL_START
363 // We can't actually cover this code, since versions of the parameters file which need this
364 // transform didn't use a namespace, so can't have a namespace prefix!
365 xercesc::QName qname(p_prefix, pNewElementLocalName, 0);
366 p_qualified_name = qname.getRawName();
367// LCOV_EXCL_STOP
368 }
369 else
370 {
371 p_qualified_name = pNewElementLocalName;
372 }
373 xercesc::DOMElement* p_wrapper_elt = pDocument->createElementNS(p_namespace_uri, p_qualified_name);
374 // Move all child nodes of pElement to be children of p_wrapper_elt
375 xercesc::DOMNodeList* p_children = pElement->getChildNodes();
376 for (unsigned i=0; i<p_children->getLength(); i++)
377 {
378 xercesc::DOMNode* p_child = pElement->removeChild(p_children->item(i));
379 p_wrapper_elt->appendChild(p_child);
380 }
381 // Add the wrapper as the sole child of pElement
382 pElement->appendChild(p_wrapper_elt);
383}
384
385
386std::string XmlTools::EscapeSpaces(const std::string& rPath)
387{
388 std::string escaped_path;
389 for (std::string::const_iterator it = rPath.begin(); it != rPath.end(); ++it)
390 {
391 if (*it == ' ')
392 {
393 escaped_path += "%20";
394 }
395 else
396 {
397 escaped_path += *it;
398 }
399 }
400 return escaped_path;
401}
#define EXCEPTION(message)
Finalizer(bool init)
Definition XmlTools.cpp:103
static xercesc::DOMElement * SetNamespace(xercesc::DOMDocument *pDocument, xercesc::DOMElement *pElement, const std::string &rNamespace)
Definition XmlTools.cpp:279
static std::string EscapeSpaces(const std::string &rPath)
Definition XmlTools.cpp:386
static XSD_DOM_AUTO_PTR< xercesc::DOMDocument > ReadFileToDomDocument(const std::string &rFileName, ::xsd::cxx::xml::error_handler< char > &rErrorHandler, const ::xsd::cxx::tree::properties< char > &rProps, bool validate=true)
Definition XmlTools.cpp:120
static void PrintNode(const std::string &rMsg, xercesc::DOMNode *pNode, bool showChildren=false)
Definition XmlTools.cpp:206
static std::vector< xercesc::DOMElement * > FindElements(const xercesc::DOMElement *pContextElement, const std::string &rPath)
Definition XmlTools.cpp:328
static XSD_DOM_AUTO_PTR< xercesc::DOMDocument > ReadXmlFile(const std::string &rFileName, const ::xsd::cxx::tree::properties< char > &rProps, bool validate=true)
Definition XmlTools.cpp:53
static std::vector< xercesc::DOMElement * > GetChildElements(const xercesc::DOMElement *pElement)
Definition XmlTools.cpp:287
static void WrapContentInElement(xercesc::DOMDocument *pDocument, xercesc::DOMElement *pElement, const XMLCh *pNewElementLocalName)
Definition XmlTools.cpp:353
static void Finalize()
Definition XmlTools.cpp:98