Arcane  4.1.12.0
User documentation
Loading...
Searching...
No Matches
DomUtils.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* DomUtils.cc (C) 2000-2018 */
9/* */
10/* Various utility functions concerning the DOM. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/ArcanePrecomp.h"
15
16#include "arcane/utils/Array.h"
17#include "arcane/utils/Iostream.h"
18#include "arcane/utils/Iterator.h"
19#include "arcane/utils/StdHeader.h"
20#include "arcane/utils/String.h"
21#include "arcane/utils/StringBuilder.h"
22#include "arcane/utils/NotImplementedException.h"
23#include "arcane/utils/FatalErrorException.h"
24
25#include "arcane/core/Dom.h"
26#include "arcane/core/DomUtils.h"
27#include "arcane/core/XmlNode.h"
28#include "arcane/core/IXmlDocumentHolder.h"
29
30#include <algorithm>
31
32/*---------------------------------------------------------------------------*/
33/*---------------------------------------------------------------------------*/
34
35namespace Arcane
36{
37
38namespace domutils
39{
40
41 extern "C++" ARCANE_CORE_EXPORT void
42 removeAllChildren(const dom::Node& parent);
43
44 extern "C++" ARCANE_CORE_EXPORT bool
45 writeNode(std::ostream& ostr, const dom::Node&);
46
47 extern "C++" ARCANE_CORE_EXPORT bool
48 writeNodeChildren(std::ostream& ostr, const dom::Node&);
49
50} // namespace domutils
51
52/*---------------------------------------------------------------------------*/
53/*---------------------------------------------------------------------------*/
54
55namespace
56{
57
58 /*---------------------------------------------------------------------------*/
59 /*---------------------------------------------------------------------------*/
60
61 void _notImplemented(const char* reason)
62 {
63 cerr << "* DOMUTILS NOT YET IMPLEMENTED: " << reason << '\n';
64 throw dom::DOMException(dom::NOT_IMPLEMENTED_ERR);
65 }
66
67 /*---------------------------------------------------------------------------*/
68 /*---------------------------------------------------------------------------*/
69
70 void _writeNodeChildren(std::ostream& o, const dom::Node& node)
71 {
72 // Recursively writes child nodes
73 dom::Node next = node.firstChild();
74 while (!next._null()) {
75 domutils::writeNode(o, next);
76 next = next.nextSibling();
77 }
78 }
79
80} // namespace
81
82/*---------------------------------------------------------------------------*/
83/*---------------------------------------------------------------------------*/
84
85String domutils::
86textContent(const dom::Node& node)
87{
88 using namespace dom;
89 StringBuilder str;
90 if (node._null())
91 return str.toString();
92
93 if (node.nodeType() != Node::ELEMENT_NODE)
94 ARCANE_THROW(NotImplementedException, "get text value for non ELEMENT_NODE");
95 for (Node i = node.firstChild(); !i._null(); i = i.nextSibling()) {
96 UShort ntype = i.nodeType();
97 if (ntype == Node::TEXT_NODE)
98 str += i.nodeValue();
99 else if (ntype == Node::CDATA_SECTION_NODE) {
100 str += i.nodeValue();
101 }
102 else if (ntype == Node::ENTITY_REFERENCE_NODE) {
103 ARCANE_THROW(NotImplementedException, "get text value for non ENTITY_REFERENCE_NODE");
104 }
105 }
106 return str.toString();
107}
108
109/*---------------------------------------------------------------------------*/
110/*---------------------------------------------------------------------------*/
111
112void domutils::
113textContent(dom::Node& node, const String& new_value)
114{
115 // Same semantics as Node::textContent of DOM3:
116 // - Removes all child nodes.
117 // - creation of a single text node containing the new value
118 using namespace dom;
119 if (node.nodeType() != Node::ELEMENT_NODE)
120 ARCANE_THROW(NotImplementedException, "set text value for non ELEMENT_NODE");
121 removeAllChildren(node);
122 if (!new_value.null()) {
123 Text text_node = node.ownerDocument().createTextNode(new_value);
124 node.appendChild(text_node);
125 }
126}
127
128/*---------------------------------------------------------------------------*/
129/*---------------------------------------------------------------------------*/
130
131String domutils::
132textValue(const dom::Node& node)
133{
134 return domutils::textContent(node);
135}
136
137/*---------------------------------------------------------------------------*/
138/*---------------------------------------------------------------------------*/
139
140void domutils::
141textValue(dom::Node& node, const String& new_value)
142{
143 textContent(node, new_value);
144}
145
146/*---------------------------------------------------------------------------*/
147/*---------------------------------------------------------------------------*/
148
149dom::Element domutils::
150createElement(const dom::Node& parent, const String& name, const String& value)
151{
152 if (parent._null())
153 return dom::Element();
154 dom::Document doc = parent.ownerDocument();
155 if (doc._null())
156 return dom::Element();
157 dom::Element elem = doc.createElement(name);
158 textContent(elem, value);
159 parent.appendChild(elem);
160 return elem;
161}
162
163/*---------------------------------------------------------------------------*/
164/*---------------------------------------------------------------------------*/
165
166String domutils::
167attrValue(const dom::Node& node, const String& attr_name)
168{
169 // TODO: Use DOM methods directly such as getAttribute()
170 // but for that, namespaces must be handled.
171 String str;
172 if (node._null())
173 return str;
174 const dom::NamedNodeMap& attr = node.attributes();
175 if (attr._null())
176 return str;
177 const dom::Node& n = attr.getNamedItem(attr_name);
178 if (n._null())
179 return str;
180 str = n.nodeValue();
181
182#if 0
183 // To be activated when the implementation via getAttribute() is effective.
184 {
185 dom::Element element{node};
186 String str2 = element.getAttribute(attr_name);
187 if (str2!=str)
188 ARCANE_FATAL("Bad new value for attribute '{0}' new={1} current={2}",attr_name,str2,str);
189 }
190#endif
191
192 return str;
193}
194
195/*---------------------------------------------------------------------------*/
196/*---------------------------------------------------------------------------*/
197
198void domutils::
199setAttr(const dom::Element& elem, const String& name, const String& value)
200{
201 if (elem._null())
202 return;
203 elem.setAttribute(name, value);
204}
205
206/*---------------------------------------------------------------------------*/
207/*---------------------------------------------------------------------------*/
208
209dom::Node domutils::
210childNode(const dom::Node& parent, const String& child_name)
211{
212 dom::DOMString ref_name(child_name);
213 for (dom::Node i = parent.firstChild(); !i._null(); i = i.nextSibling()) {
214 if (i.nodeName() == ref_name)
215 return i;
216 }
217 return dom::Node();
218}
219
220/*---------------------------------------------------------------------------*/
221/*---------------------------------------------------------------------------*/
222
223void domutils::
224removeAllChildren(const dom::Node& parent)
225{
226 if (parent._null())
227 return;
228
229 // Removes child nodes.
230 dom::Node n = parent.firstChild();
231 while (!n._null()) {
232 parent.removeChild(n);
233 // TODO RELEASE MEMORY
234 n = parent.firstChild();
235 }
236}
237
238/*---------------------------------------------------------------------------*/
239/*---------------------------------------------------------------------------*/
240
241/*!
242 * \brief Returns the node corresponding to an XPath expression.
243 * Returns the node corresponding to the expression \a xpath_expr with
244 * \a context_node as context.
245 * The current implementation only supports the following expression type:
246 * - a/b*.
247 */
248dom::Node domutils::
249nodeFromXPath(const dom::Node& context_node, const String& xpath_expr)
250{
251 const char* expr = xpath_expr.localstr();
252 if (context_node._null()) {
253 return dom::Node();
254 }
255 if (!expr) {
256 return dom::Node();
257 }
258 const char* separator = ::strchr(expr, '/');
259 if (separator) {
260 // String of type \a a/b. Searches for the child node named \a a and
261 // recursively applies this function to it with \a b as the expression.
262 std::string_view buf1(expr, (Int64)(separator - expr));
263 String buf(buf1);
264 dom::Node child = childNode(context_node, buf);
265 if (child._null()) {
266 return dom::Node();
267 }
268 return nodeFromXPath(child, String(std::string_view(separator + 1)));
269 }
270 return childNode(context_node, xpath_expr);
271}
272
273/*---------------------------------------------------------------------------*/
274/*---------------------------------------------------------------------------*/
275
276bool domutils::
277writeNodeChildren(std::ostream& ostr, const dom::Node& node)
278{
279 _writeNodeChildren(ostr, node);
280 return true;
281}
282
283/*---------------------------------------------------------------------------*/
284/*---------------------------------------------------------------------------*/
285
286bool domutils::
287writeNode(std::ostream& o, const dom::Node& node)
288{
289 using namespace dom;
290 switch (node.nodeType()) {
291 case Node::ELEMENT_NODE: {
292 o << '<' << node.nodeName();
293 NamedNodeMap attr_list = node.attributes();
294 for (ULong i = 0, s = attr_list.length(); i < s; ++i) {
295 o << ' ';
296 writeNode(o, attr_list.item(i));
297 }
298 o << '>';
299 writeNodeChildren(o, node);
300 o << "</" << node.nodeName() << '>';
301 } break;
302 case Node::ATTRIBUTE_NODE:
303 o << node.nodeName() << '='
304 << '"' << node.nodeValue() << '"';
305 break;
306 case Node::TEXT_NODE:
307 o << node.nodeValue();
308 //_notImplemented("Dom::writeNode() for TEXT_NODE");
309 break;
310 case Node::CDATA_SECTION_NODE:
311 o << node.nodeValue();
312 cerr << "** Dom::writeNode() for CDATA_SECTION_NODE Not fully implemented\n";
313 //_notImplemented("Dom::writeNode() for CDATA_SECTION_NODE");
314 break;
315 case Node::ENTITY_REFERENCE_NODE:
316 _notImplemented("Dom::writeNode() for ENTITY_REFERENCE_NODE");
317 break;
318 case Node::ENTITY_NODE:
319 _notImplemented("Dom::writeNode() for ENTITY_NODE");
320 break;
321 case Node::PROCESSING_INSTRUCTION_NODE:
322 _notImplemented("Dom::writeNode() for PROCESSING_INSTRUCTION_NODE");
323 break;
324 case Node::COMMENT_NODE:
325 o << "<!--" << node.nodeValue() << "-->";
326 break;
327 case Node::DOCUMENT_NODE:
328 _writeNodeChildren(o, node);
329 break;
330 case Node::DOCUMENT_TYPE_NODE:
331 _writeNodeChildren(o, node);
332 break;
333 case Node::DOCUMENT_FRAGMENT_NODE:
334 _notImplemented("Dom::writeNode() for DOCUMENT_FRAGMENT_NODE");
335 break;
336 case Node::NOTATION_NODE:
337 _notImplemented("Dom::writeNode() for NOTATION_NODE");
338 break;
339 default:
340 _notImplemented("Dom::writeNode() for unknown node");
341 break;
342 }
343 return false;
344}
345
346/*---------------------------------------------------------------------------*/
347/*---------------------------------------------------------------------------*/
348
349bool domutils::
350saveDocument(std::ostream& ostr, const dom::Document& doc, int indent_level)
351{
352 ByteUniqueArray bytes;
353 saveDocument(bytes, doc, indent_level);
354 ostr.write((const char*)bytes.data(), bytes.size());
355 return true;
356}
357
358/*---------------------------------------------------------------------------*/
359/*---------------------------------------------------------------------------*/
360
361bool domutils::
362saveDocument(ByteArray& bytes, const dom::Document& doc, int indent_level)
363{
365 domimp._save(bytes, doc, indent_level);
366 Integer nb_byte = bytes.size();
367 if (nb_byte >= 1 && bytes[nb_byte - 1] == '\0') {
368 ARCANE_FATAL("Invalid null charactere at end of XML stream");
369 }
370 return true;
371}
372
373/*---------------------------------------------------------------------------*/
374/*---------------------------------------------------------------------------*/
375
376IXmlDocumentHolder* domutils::
377createXmlDocument()
378{
380 return domimp._newDocument();
381}
382
383/*---------------------------------------------------------------------------*/
384/*---------------------------------------------------------------------------*/
385
386/*---------------------------------------------------------------------------*/
387/*---------------------------------------------------------------------------*/
388
389domutils::NameIterator::
390NameIterator(const dom::Node& from, const String& ref_name)
391: m_parent(from)
392, m_current()
393, m_ref_name(ref_name)
394{
395 _findNextValid(true);
396}
397
398/*---------------------------------------------------------------------------*/
399/*---------------------------------------------------------------------------*/
400
401void domutils::NameIterator::
402_findNextValid(bool is_init)
403{
404 if (is_init)
405 m_current = m_parent.firstChild();
406 else {
407 if (m_current._null())
408 return;
409 m_current = m_current.nextSibling();
410 }
411 while (!m_current._null()) {
412 if (m_current.nodeName() == m_ref_name)
413 break;
414 m_current = m_current.nextSibling();
415 }
416}
417
418/*---------------------------------------------------------------------------*/
419/*---------------------------------------------------------------------------*/
420
421/*---------------------------------------------------------------------------*/
422/*---------------------------------------------------------------------------*/
423
424IXmlDocumentHolder* IXmlDocumentHolder::
425loadFromBuffer(Span<const Byte> buffer, const String& name, ITraceMng* tm)
426{
428 // Reading the file containing internal information.
429 return domimp._load(asBytes(buffer), name, tm);
430}
431
432IXmlDocumentHolder* IXmlDocumentHolder::
433loadFromBuffer(ByteConstSpan buffer, const String& name, ITraceMng* tm)
434{
436 // Reading the file containing internal information.
437 return domimp._load(buffer, name, tm);
438}
439
440/*---------------------------------------------------------------------------*/
441/*---------------------------------------------------------------------------*/
442
443IXmlDocumentHolder* IXmlDocumentHolder::
444loadFromFile(const String& filename, ITraceMng* tm)
445{
446 return loadFromFile(filename, String(), tm);
447}
448
449/*---------------------------------------------------------------------------*/
450/*---------------------------------------------------------------------------*/
451
452IXmlDocumentHolder* IXmlDocumentHolder::
453loadFromFile(const String& filename, const String& schema_filename, ITraceMng* tm)
454{
456 return domimp._load(filename, tm, schema_filename);
457}
458
459/*---------------------------------------------------------------------------*/
460/*---------------------------------------------------------------------------*/
461
462} // namespace Arcane
463
464/*---------------------------------------------------------------------------*/
465/*---------------------------------------------------------------------------*/
#define ARCANE_THROW(exception_class,...)
Macro for throwing an exception with formatting.
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
Manager of a DOM document.
static IXmlDocumentHolder * loadFromFile(const String &filename, ITraceMng *tm)
Loads an XML document.
Definition DomUtils.cc:444
Node of a mesh.
Definition Item.h:598
View of an array of elements of type T.
Definition Span.h:635
Unicode character string constructor.
String toString() const
Returns the constructed character string.
IXmlDocumentHolder * _load(const String &fname, ITraceMng *msg, const String &schemaname)
IXmlDocumentHolder * _newDocument()
The following methods are internal to Arcane.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
std::int64_t Int64
Signed integer type of 64 bits.
Int32 Integer
Type representing an integer.
Array< Byte > ByteArray
Dynamic one-dimensional array of characters.
Definition UtilsTypes.h:121
UniqueArray< Byte > ByteUniqueArray
Dynamic 1D array of characters.
Definition UtilsTypes.h:335
Span< const std::byte > ByteConstSpan
Read-only view of a 1D array of characters.
Definition UtilsTypes.h:548
Impl::SpanTypeFromSize< conststd::byte, SizeType >::SpanType asBytes(const SpanImpl< DataType, SizeType, Extent > &s)
Converts the view into an array of non-modifiable bytes.
Definition Span.h:1032