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 | |
26 | namespace Poco { |
27 | namespace XML { |
28 | |
29 | |
30 | const XMLString AbstractContainerNode::WILDCARD(toXMLString("*" )); |
31 | |
32 | |
33 | AbstractContainerNode::AbstractContainerNode(Document* pOwnerDocument): |
34 | AbstractNode(pOwnerDocument), |
35 | _pFirstChild(0) |
36 | { |
37 | } |
38 | |
39 | |
40 | AbstractContainerNode::AbstractContainerNode(Document* pOwnerDocument, const AbstractContainerNode& node): |
41 | AbstractNode(pOwnerDocument, node), |
42 | _pFirstChild(0) |
43 | { |
44 | } |
45 | |
46 | |
47 | AbstractContainerNode::~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 | |
61 | Node* AbstractContainerNode::firstChild() const |
62 | { |
63 | return _pFirstChild; |
64 | } |
65 | |
66 | |
67 | Node* 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 | |
79 | Node* 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 | |
154 | Node* 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 | |
231 | Node* 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 | |
270 | Node* AbstractContainerNode::appendChild(Node* newChild) |
271 | { |
272 | return insertBefore(newChild, 0); |
273 | } |
274 | |
275 | |
276 | void 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 | |
288 | void 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 | |
300 | bool AbstractContainerNode::hasChildNodes() const |
301 | { |
302 | return _pFirstChild != 0; |
303 | } |
304 | |
305 | |
306 | bool AbstractContainerNode::hasAttributes() const |
307 | { |
308 | return false; |
309 | } |
310 | |
311 | |
312 | Node* 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 | |
340 | Node* 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 | |
382 | const 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 | |
454 | const 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 | |
466 | const 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 | |
485 | const 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 | |
506 | const 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 | |
530 | bool 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 | |
537 | bool 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 | |
550 | bool 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 | |