1//
2// AbstractContainerNode.cpp
3//
4// Library: XML
5// Package: DOM
6// Module: DOM
7//
8// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/DOM/AbstractContainerNode.h"
16#include "Poco/DOM/Document.h"
17#include "Poco/DOM/Element.h"
18#include "Poco/DOM/Attr.h"
19#include "Poco/DOM/DOMException.h"
20#include "Poco/DOM/ElementsByTagNameList.h"
21#include "Poco/DOM/AutoPtr.h"
22#include "Poco/NumberParser.h"
23#include "Poco/UnicodeConverter.h"
24
25
26namespace Poco {
27namespace XML {
28
29
30const XMLString AbstractContainerNode::WILDCARD(toXMLString("*"));
31
32
33AbstractContainerNode::AbstractContainerNode(Document* pOwnerDocument):
34 AbstractNode(pOwnerDocument),
35 _pFirstChild(0)
36{
37}
38
39
40AbstractContainerNode::AbstractContainerNode(Document* pOwnerDocument, const AbstractContainerNode& node):
41 AbstractNode(pOwnerDocument, node),
42 _pFirstChild(0)
43{
44}
45
46
47AbstractContainerNode::~AbstractContainerNode()
48{
49 AbstractNode* pChild = static_cast<AbstractNode*>(_pFirstChild);
50 while (pChild)
51 {
52 AbstractNode* pDelNode = pChild;
53 pChild = pChild->_pNext;
54 pDelNode->_pNext = 0;
55 pDelNode->_pParent = 0;
56 pDelNode->release();
57 }
58}
59
60
61Node* AbstractContainerNode::firstChild() const
62{
63 return _pFirstChild;
64}
65
66
67Node* AbstractContainerNode::lastChild() const
68{
69 AbstractNode* pChild = _pFirstChild;
70 if (pChild)
71 {
72 while (pChild->_pNext) pChild = pChild->_pNext;
73 return pChild;
74 }
75 return 0;
76}
77
78
79Node* AbstractContainerNode::insertBefore(Node* newChild, Node* refChild)
80{
81 poco_check_ptr (newChild);
82
83 if (static_cast<AbstractNode*>(newChild)->_pOwner != _pOwner && static_cast<AbstractNode*>(newChild)->_pOwner != this)
84 throw DOMException(DOMException::WRONG_DOCUMENT_ERR);
85 if (refChild && static_cast<AbstractNode*>(refChild)->_pParent != this)
86 throw DOMException(DOMException::NOT_FOUND_ERR);
87 if (newChild == refChild)
88 return newChild;
89 if (this == newChild)
90 throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
91
92 AbstractNode* pFirst = 0;
93 AbstractNode* pLast = 0;
94 if (newChild->nodeType() == Node::DOCUMENT_FRAGMENT_NODE)
95 {
96 AbstractContainerNode* pFrag = static_cast<AbstractContainerNode*>(newChild);
97 pFirst = pFrag->_pFirstChild;
98 pLast = pFirst;
99 if (pFirst)
100 {
101 while (pLast->_pNext)
102 {
103 pLast->_pParent = this;
104 pLast = pLast->_pNext;
105 }
106 pLast->_pParent = this;
107 }
108 pFrag->_pFirstChild = 0;
109 }
110 else
111 {
112 newChild->duplicate();
113 AbstractContainerNode* pParent = static_cast<AbstractNode*>(newChild)->_pParent;
114 if (pParent) pParent->removeChild(newChild);
115 pFirst = static_cast<AbstractNode*>(newChild);
116 pLast = pFirst;
117 pFirst->_pParent = this;
118 }
119 if (_pFirstChild && pFirst)
120 {
121 AbstractNode* pCur = _pFirstChild;
122 if (pCur == refChild)
123 {
124 pLast->_pNext = _pFirstChild;
125 _pFirstChild = pFirst;
126 }
127 else
128 {
129 while (pCur && pCur->_pNext != refChild) pCur = pCur->_pNext;
130 if (pCur)
131 {
132 pLast->_pNext = pCur->_pNext;
133 pCur->_pNext = pFirst;
134 }
135 else throw DOMException(DOMException::NOT_FOUND_ERR);
136 }
137 }
138 else _pFirstChild = pFirst;
139
140 if (events())
141 {
142 while (pFirst && pFirst != pLast->_pNext)
143 {
144 pFirst->dispatchNodeInserted();
145 pFirst->dispatchNodeInsertedIntoDocument();
146 pFirst = pFirst->_pNext;
147 }
148 dispatchSubtreeModified();
149 }
150 return newChild;
151}
152
153
154Node* AbstractContainerNode::replaceChild(Node* newChild, Node* oldChild)
155{
156 poco_check_ptr (newChild);
157 poco_check_ptr (oldChild);
158
159 if (static_cast<AbstractNode*>(newChild)->_pOwner != _pOwner && static_cast<AbstractNode*>(newChild)->_pOwner != this)
160 throw DOMException(DOMException::WRONG_DOCUMENT_ERR);
161 if (static_cast<AbstractNode*>(oldChild)->_pParent != this)
162 throw DOMException(DOMException::NOT_FOUND_ERR);
163 if (newChild == oldChild)
164 return newChild;
165 if (this == newChild)
166 throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
167
168 bool doEvents = events();
169 if (newChild->nodeType() == Node::DOCUMENT_FRAGMENT_NODE)
170 {
171 insertBefore(newChild, oldChild);
172 removeChild(oldChild);
173 }
174 else
175 {
176 AbstractContainerNode* pParent = static_cast<AbstractNode*>(newChild)->_pParent;
177 if (pParent) pParent->removeChild(newChild);
178
179 if (oldChild == _pFirstChild)
180 {
181 if (doEvents)
182 {
183 _pFirstChild->dispatchNodeRemoved();
184 _pFirstChild->dispatchNodeRemovedFromDocument();
185 }
186 static_cast<AbstractNode*>(newChild)->_pNext = static_cast<AbstractNode*>(oldChild)->_pNext;
187 static_cast<AbstractNode*>(newChild)->_pParent = this;
188 _pFirstChild->_pNext = 0;
189 _pFirstChild->_pParent = 0;
190 _pFirstChild = static_cast<AbstractNode*>(newChild);
191 if (doEvents)
192 {
193 static_cast<AbstractNode*>(newChild)->dispatchNodeInserted();
194 static_cast<AbstractNode*>(newChild)->dispatchNodeInsertedIntoDocument();
195 }
196 }
197 else
198 {
199 AbstractNode* pCur = _pFirstChild;
200 while (pCur && pCur->_pNext != oldChild) pCur = pCur->_pNext;
201 if (pCur)
202 {
203 poco_assert_dbg (pCur->_pNext == oldChild);
204
205 if (doEvents)
206 {
207 static_cast<AbstractNode*>(oldChild)->dispatchNodeRemoved();
208 static_cast<AbstractNode*>(oldChild)->dispatchNodeRemovedFromDocument();
209 }
210 static_cast<AbstractNode*>(newChild)->_pNext = static_cast<AbstractNode*>(oldChild)->_pNext;
211 static_cast<AbstractNode*>(newChild)->_pParent = this;
212 static_cast<AbstractNode*>(oldChild)->_pNext = 0;
213 static_cast<AbstractNode*>(oldChild)->_pParent = 0;
214 pCur->_pNext = static_cast<AbstractNode*>(newChild);
215 if (doEvents)
216 {
217 static_cast<AbstractNode*>(newChild)->dispatchNodeInserted();
218 static_cast<AbstractNode*>(newChild)->dispatchNodeInsertedIntoDocument();
219 }
220 }
221 else throw DOMException(DOMException::NOT_FOUND_ERR);
222 }
223 newChild->duplicate();
224 oldChild->autoRelease();
225 }
226 if (doEvents) dispatchSubtreeModified();
227 return oldChild;
228}
229
230
231Node* AbstractContainerNode::removeChild(Node* oldChild)
232{
233 poco_check_ptr (oldChild);
234
235 bool doEvents = events();
236 if (oldChild == _pFirstChild)
237 {
238 if (doEvents)
239 {
240 static_cast<AbstractNode*>(oldChild)->dispatchNodeRemoved();
241 static_cast<AbstractNode*>(oldChild)->dispatchNodeRemovedFromDocument();
242 }
243 _pFirstChild = _pFirstChild->_pNext;
244 static_cast<AbstractNode*>(oldChild)->_pNext = 0;
245 static_cast<AbstractNode*>(oldChild)->_pParent = 0;
246 }
247 else
248 {
249 AbstractNode* pCur = _pFirstChild;
250 while (pCur && pCur->_pNext != oldChild) pCur = pCur->_pNext;
251 if (pCur)
252 {
253 if (doEvents)
254 {
255 static_cast<AbstractNode*>(oldChild)->dispatchNodeRemoved();
256 static_cast<AbstractNode*>(oldChild)->dispatchNodeRemovedFromDocument();
257 }
258 pCur->_pNext = pCur->_pNext->_pNext;
259 static_cast<AbstractNode*>(oldChild)->_pNext = 0;
260 static_cast<AbstractNode*>(oldChild)->_pParent = 0;
261 }
262 else throw DOMException(DOMException::NOT_FOUND_ERR);
263 }
264 oldChild->autoRelease();
265 if (doEvents) dispatchSubtreeModified();
266 return oldChild;
267}
268
269
270Node* AbstractContainerNode::appendChild(Node* newChild)
271{
272 return insertBefore(newChild, 0);
273}
274
275
276void AbstractContainerNode::dispatchNodeRemovedFromDocument()
277{
278 AbstractNode::dispatchNodeRemovedFromDocument();
279 Node* pChild = firstChild();
280 while (pChild)
281 {
282 static_cast<AbstractNode*>(pChild)->dispatchNodeRemovedFromDocument();
283 pChild = pChild->nextSibling();
284 }
285}
286
287
288void AbstractContainerNode::dispatchNodeInsertedIntoDocument()
289{
290 AbstractNode::dispatchNodeInsertedIntoDocument();
291 Node* pChild = firstChild();
292 while (pChild)
293 {
294 static_cast<AbstractNode*>(pChild)->dispatchNodeInsertedIntoDocument();
295 pChild = pChild->nextSibling();
296 }
297}
298
299
300bool AbstractContainerNode::hasChildNodes() const
301{
302 return _pFirstChild != 0;
303}
304
305
306bool AbstractContainerNode::hasAttributes() const
307{
308 return false;
309}
310
311
312Node* AbstractContainerNode::getNodeByPath(const XMLString& path) const
313{
314 XMLString::const_iterator it = path.begin();
315 if (it != path.end() && *it == '/')
316 {
317 ++it;
318 if (it != path.end() && *it == '/')
319 {
320 ++it;
321 XMLString name;
322 while (it != path.end() && *it != '/' && *it != '@' && *it != '[') name += *it++;
323 if (it != path.end() && *it == '/') ++it;
324 if (name.empty()) name = WILDCARD;
325 AutoPtr<ElementsByTagNameList> pList = new ElementsByTagNameList(this, name);
326 unsigned long length = pList->length();
327 for (unsigned long i = 0; i < length; i++)
328 {
329 XMLString::const_iterator beg = it;
330 const Node* pNode = findNode(beg, path.end(), pList->item(i), 0);
331 if (pNode) return const_cast<Node*>(pNode);
332 }
333 return 0;
334 }
335 }
336 return const_cast<Node*>(findNode(it, path.end(), this, 0));
337}
338
339
340Node* AbstractContainerNode::getNodeByPathNS(const XMLString& path, const NSMap& nsMap) const
341{
342 XMLString::const_iterator it = path.begin();
343 if (it != path.end() && *it == '/')
344 {
345 ++it;
346 if (it != path.end() && *it == '/')
347 {
348 ++it;
349 XMLString name;
350 while (it != path.end() && *it != '/' && *it != '@' && *it != '[') name += *it++;
351 if (it != path.end() && *it == '/') ++it;
352 XMLString namespaceURI;
353 XMLString localName;
354 bool nameOK = true;
355 if (name.empty())
356 {
357 namespaceURI = WILDCARD;
358 localName = WILDCARD;
359 }
360 else
361 {
362 nameOK = nsMap.processName(name, namespaceURI, localName, false);
363 }
364 if (nameOK)
365 {
366 AutoPtr<ElementsByTagNameListNS> pList = new ElementsByTagNameListNS(this, namespaceURI, localName);
367 unsigned long length = pList->length();
368 for (unsigned long i = 0; i < length; i++)
369 {
370 XMLString::const_iterator beg = it;
371 const Node* pNode = findNode(beg, path.end(), pList->item(i), &nsMap);
372 if (pNode) return const_cast<Node*>(pNode);
373 }
374 }
375 return 0;
376 }
377 }
378 return const_cast<Node*>(findNode(it, path.end(), this, &nsMap));
379}
380
381
382const Node* AbstractContainerNode::findNode(XMLString::const_iterator& it, const XMLString::const_iterator& end, const Node* pNode, const NSMap* pNSMap)
383{
384 if (pNode && it != end)
385 {
386 if (*it == '[')
387 {
388 ++it;
389 if (it != end && *it == '@')
390 {
391 ++it;
392 XMLString attr;
393 while (it != end && *it != ']' && *it != '=') attr += *it++;
394 if (it != end && *it == '=')
395 {
396 ++it;
397 XMLString value;
398 if (it != end && *it == '\'')
399 {
400 ++it;
401 while (it != end && *it != '\'') value += *it++;
402 if (it != end) ++it;
403 }
404 else
405 {
406 while (it != end && *it != ']') value += *it++;
407 }
408 if (it != end) ++it;
409 return findNode(it, end, findElement(attr, value, pNode, pNSMap), pNSMap);
410 }
411 else
412 {
413 if (it != end) ++it;
414 return findAttribute(attr, pNode, pNSMap);
415 }
416 }
417 else
418 {
419 XMLString index;
420 while (it != end && *it != ']') index += *it++;
421 if (it != end) ++it;
422#ifdef XML_UNICODE_WCHAR_T
423 std::string idx;
424 Poco::UnicodeConverter::convert(index, idx);
425 int i = Poco::NumberParser::parse(idx);
426#else
427 int i = Poco::NumberParser::parse(index);
428#endif
429 return findNode(it, end, findElement(i, pNode, pNSMap), pNSMap);
430 }
431 }
432 else
433 {
434 while (it != end && *it == '/') ++it;
435 XMLString key;
436 while (it != end && *it != '/' && *it != '[') key += *it++;
437
438 XMLString::const_iterator itStart(it);
439 const Node* pFound = 0;
440 const Node* pElem = findElement(key, pNode->firstChild(), pNSMap);
441 while (!pFound && pElem)
442 {
443 pFound = findNode(it, end, pElem, pNSMap);
444 if (!pFound) pElem = findElement(key, pElem->nextSibling(), pNSMap);
445 it = itStart;
446 }
447 return pFound;
448 }
449 }
450 else return pNode;
451}
452
453
454const Node* AbstractContainerNode::findElement(const XMLString& name, const Node* pNode, const NSMap* pNSMap)
455{
456 while (pNode)
457 {
458 if (pNode->nodeType() == Node::ELEMENT_NODE && namesAreEqual(pNode, name, pNSMap))
459 return pNode;
460 pNode = pNode->nextSibling();
461 }
462 return 0;
463}
464
465
466const Node* AbstractContainerNode::findElement(int index, const Node* pNode, const NSMap* pNSMap)
467{
468 const Node* pRefNode = pNode;
469 if (index > 0)
470 {
471 pNode = pNode->nextSibling();
472 while (pNode)
473 {
474 if (namesAreEqual(pNode, pRefNode, pNSMap))
475 {
476 if (--index == 0) break;
477 }
478 pNode = pNode->nextSibling();
479 }
480 }
481 return pNode;
482}
483
484
485const Node* AbstractContainerNode::findElement(const XMLString& attr, const XMLString& value, const Node* pNode, const NSMap* pNSMap)
486{
487 const Node* pRefNode = pNode;
488 const Element* pElem = dynamic_cast<const Element*>(pNode);
489 if (!(pElem && pElem->hasAttributeValue(attr, value, pNSMap)))
490 {
491 pNode = pNode->nextSibling();
492 while (pNode)
493 {
494 if (namesAreEqual(pNode, pRefNode, pNSMap))
495 {
496 pElem = dynamic_cast<const Element*>(pNode);
497 if (pElem && pElem->hasAttributeValue(attr, value, pNSMap)) break;
498 }
499 pNode = pNode->nextSibling();
500 }
501 }
502 return pNode;
503}
504
505
506const Attr* AbstractContainerNode::findAttribute(const XMLString& name, const Node* pNode, const NSMap* pNSMap)
507{
508 const Attr* pResult(0);
509 const Element* pElem = dynamic_cast<const Element*>(pNode);
510 if (pElem)
511 {
512 if (pNSMap)
513 {
514 XMLString namespaceURI;
515 XMLString localName;
516 if (pNSMap->processName(name, namespaceURI, localName, true))
517 {
518 pResult = pElem->getAttributeNodeNS(namespaceURI, localName);
519 }
520 }
521 else
522 {
523 pResult = pElem->getAttributeNode(name);
524 }
525 }
526 return pResult;
527}
528
529
530bool AbstractContainerNode::hasAttributeValue(const XMLString& name, const XMLString& value, const NSMap* pNSMap) const
531{
532 const Attr* pAttr = findAttribute(name, this, pNSMap);
533 return pAttr && pAttr->getValue() == value;
534}
535
536
537bool AbstractContainerNode::namesAreEqual(const Node* pNode1, const Node* pNode2, const NSMap* pNSMap)
538{
539 if (pNSMap)
540 {
541 return pNode1->localName() == pNode2->localName() && pNode1->namespaceURI() == pNode2->namespaceURI();
542 }
543 else
544 {
545 return pNode1->nodeName() == pNode2->nodeName();
546 }
547}
548
549
550bool AbstractContainerNode::namesAreEqual(const Node* pNode, const XMLString& name, const NSMap* pNSMap)
551{
552 if (pNSMap)
553 {
554 XMLString namespaceURI;
555 XMLString localName;
556 if (name == WILDCARD)
557 {
558 return true;
559 }
560 else if (pNSMap->processName(name, namespaceURI, localName, false))
561 {
562 return (pNode->namespaceURI() == namespaceURI || namespaceURI == WILDCARD) && (pNode->localName() == localName || localName == WILDCARD);
563 }
564 else return false;
565 }
566 else
567 {
568 return pNode->nodeName() == name || name == WILDCARD;
569 }
570}
571
572
573} } // namespace Poco::XML
574