File indexing completed on 2024-04-28 11:39:29
0001 /** 0002 * This file is part of the DOM implementation for KDE. 0003 * 0004 * Copyright (C) 1999 Lars Knoll <knoll@kde.org> 0005 * Copyright (C) 2000 Frederik Holljen <frederik.holljen@hig.no> 0006 * Copyright (C) 2001 Peter Kelly <pmk@post.com> 0007 * Copyright (C) 2008 Maksim Orlovich <maksim@kde.org> 0008 * 0009 * This library is free software; you can redistribute it and/or 0010 * modify it under the terms of the GNU Library General Public 0011 * License as published by the Free Software Foundation; either 0012 * version 2 of the License, or (at your option) any later version. 0013 * 0014 * This library is distributed in the hope that it will be useful, 0015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0017 * Library General Public License for more details. 0018 * 0019 * You should have received a copy of the GNU Library General Public License 0020 * along with this library; see the file COPYING.LIB. If not, write to 0021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0022 * Boston, MA 02110-1301, USA. 0023 */ 0024 0025 #include "dom/dom_exception.h" 0026 #include "xml/dom_docimpl.h" 0027 0028 /** 0029 Robustness notes: we protect the object we hold on to, to 0030 prevent crashes. To ensure termination in JS used, we propagate exceptions, 0031 and rely on the JS CPU guard to set one to force us to bail out. 0032 0033 ### TODO/CHECKONE 0034 */ 0035 0036 using namespace DOM; 0037 0038 NodeIteratorImpl::NodeIteratorImpl(NodeImpl *_root, unsigned long _whatToShow, 0039 NodeFilterImpl *_filter, bool _entityReferenceExpansion) 0040 { 0041 m_root = _root; 0042 m_whatToShow = _whatToShow; 0043 m_filter = _filter; 0044 m_expandEntityReferences = _entityReferenceExpansion; 0045 0046 m_referenceNode = _root; 0047 m_position = ITER_BEFORE_REF; 0048 0049 m_doc = m_root->document(); 0050 m_doc->attachNodeIterator(this); 0051 m_doc->ref(); 0052 0053 m_detached = false; 0054 } 0055 0056 NodeIteratorImpl::~NodeIteratorImpl() 0057 { 0058 m_doc->detachNodeIterator(this); 0059 m_doc->deref(); 0060 } 0061 0062 NodeImpl *NodeIteratorImpl::root() 0063 { 0064 return m_root.get(); 0065 } 0066 0067 unsigned long NodeIteratorImpl::whatToShow() 0068 { 0069 return m_whatToShow; 0070 } 0071 0072 NodeFilterImpl *NodeIteratorImpl::filter() 0073 { 0074 return m_filter.get(); 0075 } 0076 0077 bool NodeIteratorImpl::expandEntityReferences() 0078 { 0079 return m_expandEntityReferences; 0080 } 0081 0082 SharedPtr<NodeImpl> NodeIteratorImpl::nextNode(int &exceptioncode, void *&propagatedExceptionObject) 0083 { 0084 propagatedExceptionObject = nullptr; 0085 if (m_detached) { 0086 exceptioncode = DOMException::INVALID_STATE_ERR; 0087 return nullptr; 0088 } 0089 0090 SharedPtr<NodeImpl> oldReferenceNode = m_referenceNode; 0091 Position oldPosition = m_position; 0092 while (true) { 0093 SharedPtr<NodeImpl> cand = m_referenceNode; 0094 // If we're before a node, it's the next node to return, and we'll 0095 // be positioned after. 0096 if (m_position == ITER_BEFORE_REF) { 0097 m_position = ITER_AFTER_REF; 0098 if (isAccepted(m_referenceNode.get(), propagatedExceptionObject) == NodeFilter::FILTER_ACCEPT) { 0099 return cand; 0100 } 0101 if (propagatedExceptionObject) { 0102 m_position = oldPosition; 0103 m_referenceNode = oldReferenceNode; 0104 return nullptr; 0105 } 0106 } else { 0107 // Robustness note here: the filter could potentially yank any nodes out, 0108 // so we can't keep any state in the temporaries. We hence always rely on 0109 // m_referenceNode, which would be sync'd by the delete handler 0110 cand = getNextNode(m_referenceNode.get()); 0111 if (!cand) { 0112 return nullptr; 0113 } 0114 m_referenceNode = cand; 0115 if (isAccepted(cand.get(), propagatedExceptionObject) == NodeFilter::FILTER_ACCEPT) { 0116 return cand; 0117 } 0118 if (propagatedExceptionObject) { 0119 m_position = oldPosition; 0120 m_referenceNode = oldReferenceNode; 0121 return nullptr; 0122 } 0123 } 0124 } 0125 return nullptr; 0126 } 0127 0128 SharedPtr<NodeImpl> NodeIteratorImpl::previousNode(int &exceptioncode, void *&propagatedExceptionObject) 0129 { 0130 propagatedExceptionObject = nullptr; 0131 if (m_detached) { 0132 exceptioncode = DOMException::INVALID_STATE_ERR; 0133 return nullptr; 0134 } 0135 0136 SharedPtr<NodeImpl> oldReferenceNode = m_referenceNode; 0137 Position oldPosition = m_position; 0138 while (true) { 0139 SharedPtr<NodeImpl> cand = m_referenceNode; 0140 if (m_position == ITER_AFTER_REF) { 0141 m_position = ITER_BEFORE_REF; 0142 if (isAccepted(m_referenceNode.get(), propagatedExceptionObject) == NodeFilter::FILTER_ACCEPT) { 0143 return cand; 0144 } 0145 0146 if (propagatedExceptionObject) { 0147 m_position = oldPosition; 0148 m_referenceNode = oldReferenceNode; 0149 return nullptr; 0150 } 0151 } else { 0152 cand = getPrevNode(m_referenceNode.get()); 0153 if (!cand) { 0154 return nullptr; 0155 } 0156 0157 m_referenceNode = cand; 0158 if (isAccepted(cand.get(), propagatedExceptionObject) == NodeFilter::FILTER_ACCEPT) { 0159 return cand; 0160 } 0161 if (propagatedExceptionObject) { 0162 m_position = oldPosition; 0163 m_referenceNode = oldReferenceNode; 0164 return nullptr; 0165 } 0166 } 0167 } 0168 return nullptr; 0169 } 0170 0171 NodeImpl *NodeIteratorImpl::getNextNode(NodeImpl *from) 0172 { 0173 return from->traverseNextNode(m_root.get()); 0174 } 0175 0176 NodeImpl *NodeIteratorImpl::getPrevNode(NodeImpl *from) 0177 { 0178 if (from == m_root) { 0179 return nullptr; 0180 } else { 0181 return from->traversePreviousNode(); 0182 } 0183 } 0184 0185 NodeImpl *NodeIteratorImpl::getLastNode(NodeImpl *in) 0186 { 0187 while (NodeImpl *cand = in->lastChild()) { 0188 in = cand; 0189 } 0190 return in; 0191 } 0192 0193 void NodeIteratorImpl::detach(int &/*exceptioncode*/) 0194 { 0195 m_doc->detachNodeIterator(this); 0196 m_detached = true; 0197 } 0198 0199 void NodeIteratorImpl::notifyBeforeNodeRemoval(NodeImpl *removed) 0200 { 0201 // We care about removals if they happen on the path between us and root, 0202 // and not if it's just the root being yanked out. 0203 if (removed == m_root) { 0204 return; 0205 } 0206 0207 // Scan up from the reference node to root see if the removed node is there.. 0208 NodeImpl *c; 0209 for (c = m_referenceNode.get(); c != m_root; c = c->parentNode()) { 0210 if (c == removed) { 0211 break; 0212 } 0213 } 0214 0215 // If nothing was found, we were unaffected. 0216 if (c == m_root) { 0217 return; 0218 } 0219 0220 // Update the position.. Thankfully we don't have to call filters here. 0221 if (m_position == ITER_BEFORE_REF) { 0222 // We were positioned before some node in the removed subtree... 0223 // We want to be positioned before the first node after the 0224 // removed subtree. 0225 NodeImpl *next = getNextNode(getLastNode(removed)); 0226 if (next) { 0227 m_referenceNode = next; 0228 } else { 0229 // There is no where to go after this --- pick a node before the tree, 0230 // which in preorder is the immediate pred. of the remove root. 0231 m_position = ITER_AFTER_REF; 0232 m_referenceNode = getPrevNode(removed); 0233 } 0234 } else { // Iterator after ref. 0235 // We were after some node in the removed subtree, we want to be 0236 // after the node immediately before it. There -must- be one, 0237 // since at least the root preceeds it! 0238 m_referenceNode = getPrevNode(removed); 0239 } 0240 } 0241 0242 short NodeIteratorImpl::isAccepted(NodeImpl *n, void *&propagatedExceptionObject) 0243 { 0244 // if XML is implemented we have to check expandEntityRerefences in this function 0245 if (((1 << (n->nodeType() - 1)) & m_whatToShow) != 0) { 0246 if (!m_filter.isNull()) { 0247 return m_filter->acceptNode(n, propagatedExceptionObject); 0248 } else { 0249 return NodeFilter::FILTER_ACCEPT; 0250 } 0251 } 0252 return NodeFilter::FILTER_SKIP; 0253 } 0254 0255 // -------------------------------------------------------------- 0256 0257 NodeFilterImpl::NodeFilterImpl() 0258 { 0259 m_customNodeFilter = nullptr; 0260 } 0261 0262 NodeFilterImpl::~NodeFilterImpl() 0263 { 0264 if (m_customNodeFilter) { 0265 m_customNodeFilter->deref(); 0266 } 0267 } 0268 0269 bool NodeFilterImpl::isJSFilter() const 0270 { 0271 return false; 0272 } 0273 0274 short NodeFilterImpl::acceptNode(const Node &n, void *& /*bindingsException*/) 0275 { 0276 if (m_customNodeFilter) { 0277 return m_customNodeFilter->acceptNode(n); 0278 } else { 0279 return NodeFilter::FILTER_ACCEPT; 0280 } 0281 } 0282 0283 void NodeFilterImpl::setCustomNodeFilter(CustomNodeFilter *custom) 0284 { 0285 m_customNodeFilter = custom; 0286 if (m_customNodeFilter) { 0287 m_customNodeFilter->ref(); 0288 } 0289 } 0290 0291 CustomNodeFilter *NodeFilterImpl::customNodeFilter() 0292 { 0293 return m_customNodeFilter; 0294 } 0295 0296 // -------------------------------------------------------------- 0297 0298 TreeWalkerImpl::TreeWalkerImpl(NodeImpl *n, long _whatToShow, NodeFilterImpl *f, 0299 bool entityReferenceExpansion) 0300 { 0301 m_currentNode = n; 0302 m_rootNode = n; 0303 m_whatToShow = _whatToShow; 0304 m_filter = f; 0305 if (m_filter) { 0306 m_filter->ref(); 0307 } 0308 m_expandEntityReferences = entityReferenceExpansion; 0309 m_doc = m_rootNode->document(); 0310 m_doc->ref(); 0311 } 0312 0313 TreeWalkerImpl::~TreeWalkerImpl() 0314 { 0315 m_doc->deref(); 0316 if (m_filter) { 0317 m_filter->deref(); 0318 } 0319 } 0320 0321 NodeImpl *TreeWalkerImpl::getRoot() const 0322 { 0323 return m_rootNode.get(); 0324 } 0325 0326 unsigned long TreeWalkerImpl::getWhatToShow() const 0327 { 0328 return m_whatToShow; 0329 } 0330 0331 NodeFilterImpl *TreeWalkerImpl::getFilter() const 0332 { 0333 return m_filter; 0334 } 0335 0336 bool TreeWalkerImpl::getExpandEntityReferences() const 0337 { 0338 return m_expandEntityReferences; 0339 } 0340 0341 NodeImpl *TreeWalkerImpl::getCurrentNode() const 0342 { 0343 return m_currentNode.get(); 0344 } 0345 0346 void TreeWalkerImpl::setWhatToShow(long _whatToShow) 0347 { 0348 // do some testing whether this is an accepted value 0349 m_whatToShow = _whatToShow; 0350 } 0351 0352 void TreeWalkerImpl::setFilter(NodeFilterImpl *_filter) 0353 { 0354 m_filter->deref(); 0355 m_filter = _filter; 0356 if (m_filter) { 0357 m_filter->ref(); 0358 } 0359 } 0360 0361 void TreeWalkerImpl::setExpandEntityReferences(bool value) 0362 { 0363 m_expandEntityReferences = value; 0364 } 0365 0366 void TreeWalkerImpl::setCurrentNode(NodeImpl *n, int &exceptionCode) 0367 { 0368 if (n) { 0369 m_currentNode = n; 0370 } else { 0371 exceptionCode = DOMException::NOT_SUPPORTED_ERR; 0372 } 0373 } 0374 0375 NodeImpl *TreeWalkerImpl::parentNode(void *&filterException) 0376 { 0377 NodePtr n = getParentNode(m_currentNode, filterException); 0378 if (n) { 0379 m_currentNode = n; 0380 } 0381 return n.get(); 0382 } 0383 0384 NodeImpl *TreeWalkerImpl::firstChild(void *&filterException) 0385 { 0386 NodePtr n = getFirstChild(m_currentNode, filterException); 0387 if (n) { 0388 m_currentNode = n; 0389 } 0390 return n.get(); 0391 } 0392 0393 NodeImpl *TreeWalkerImpl::lastChild(void *&filterException) 0394 { 0395 NodePtr n = getLastChild(m_currentNode, filterException); 0396 if (n) { 0397 m_currentNode = n; 0398 } 0399 return n.get(); 0400 } 0401 0402 NodeImpl *TreeWalkerImpl::previousSibling(void *&filterException) 0403 { 0404 NodePtr n = getPreviousSibling(m_currentNode, filterException); 0405 if (n) { 0406 m_currentNode = n; 0407 } 0408 return n.get(); 0409 } 0410 0411 NodeImpl *TreeWalkerImpl::nextSibling(void *&filterException) 0412 { 0413 NodePtr n = getNextSibling(m_currentNode, filterException); 0414 if (n) { 0415 m_currentNode = n; 0416 } 0417 return n.get(); 0418 } 0419 0420 NodeImpl *TreeWalkerImpl::nextNode(void *&filterException) 0421 { 0422 NodePtr n = getNextNode(filterException); 0423 if (n) { 0424 m_currentNode = n; 0425 } 0426 return n.get(); 0427 } 0428 0429 NodeImpl *TreeWalkerImpl::previousNode(void *&filterException) 0430 { 0431 NodePtr n = getPreviousNode(filterException); 0432 if (n) { 0433 m_currentNode = n; 0434 } 0435 return n.get(); 0436 } 0437 0438 TreeWalkerImpl::NodePtr TreeWalkerImpl::getPreviousNode(void *&filterException) 0439 { 0440 filterException = nullptr; 0441 /* 1. last node in my previous sibling's subtree 0442 * 2. my previous sibling (special case of the above) 0443 * 3. my parent 0444 */ 0445 0446 NodePtr n = getPreviousSibling(m_currentNode, filterException); 0447 if (filterException) { 0448 return nullptr; 0449 } 0450 if (n) { 0451 // Find the last kid in the subtree's preorder traversal, if any, 0452 // by following the lastChild links. 0453 NodePtr desc = getLastChild(n, filterException); 0454 if (filterException) { 0455 return nullptr; 0456 } 0457 while (desc) { 0458 n = desc; 0459 desc = getLastChild(desc, filterException); 0460 if (filterException) { 0461 return nullptr; 0462 } 0463 } 0464 return n; 0465 } 0466 0467 return getParentNode(m_currentNode, filterException); 0468 } 0469 0470 TreeWalkerImpl::NodePtr TreeWalkerImpl::getNextNode(void *&filterException) 0471 { 0472 filterException = nullptr; 0473 /* 1. my first child 0474 * 2. my next sibling 0475 * 3. my parents sibling, or their parents sibling (loop) 0476 * 4. not found 0477 */ 0478 0479 NodePtr n = getFirstChild(m_currentNode, filterException); 0480 if (filterException) { 0481 return nullptr; 0482 } 0483 if (n) { // my first child 0484 return n; 0485 } 0486 0487 n = getNextSibling(m_currentNode, filterException); // my next sibling 0488 if (filterException) { 0489 return nullptr; 0490 } 0491 if (n) { 0492 return n; 0493 } 0494 0495 NodePtr parent = getParentNode(m_currentNode, filterException); 0496 if (filterException) { 0497 return nullptr; 0498 } 0499 while (parent) { // parent's sibling 0500 n = getNextSibling(parent, filterException); 0501 if (filterException) { 0502 return nullptr; 0503 } 0504 if (n) { 0505 return n; 0506 } 0507 0508 parent = getParentNode(parent, filterException); 0509 if (filterException) { 0510 return nullptr; 0511 } 0512 } 0513 return nullptr; 0514 } 0515 0516 short TreeWalkerImpl::isAccepted(TreeWalkerImpl::NodePtr n, void *&filterException) 0517 { 0518 // if XML is implemented we have to check expandEntityRerefences in this function 0519 if (((1 << (n->nodeType() - 1)) & m_whatToShow) != 0) { 0520 if (m_filter) { 0521 return m_filter->acceptNode(n.get(), filterException); 0522 } else { 0523 return NodeFilter::FILTER_ACCEPT; 0524 } 0525 } 0526 return NodeFilter::FILTER_SKIP; 0527 } 0528 0529 /** 0530 This is quite a bit more complicated than it would at first seem, due to the following note: 0531 "Note that the structure of a TreeWalker's filtered view of a document may differ significantly from that of the document itself. For example, a TreeWalker with only SHOW_TEXT specified in its whatToShow parameter would present all the Text nodes as if they were siblings of each other yet had no parent." 0532 0533 My read of this is that e.g. that when a node is FILTER_SKIP'd, its children are handled 0534 as the children of its parent. -M.O. 0535 */ 0536 0537 TreeWalkerImpl::NodePtr TreeWalkerImpl::getParentNode(TreeWalkerImpl::NodePtr n, void *&filterException) 0538 { 0539 filterException = nullptr; 0540 // Already on top of root's subtree tree... 0541 if (n == m_rootNode) { 0542 return nullptr; 0543 } 0544 0545 // Walk up, to find the first visible node != n, until we run out of 0546 // document or into the root (which we don't have to be inside of!) 0547 NodePtr cursor = n->parentNode(); 0548 while (cursor) { 0549 if (isAccepted(cursor, filterException) == NodeFilter::FILTER_ACCEPT) { 0550 return cursor; 0551 } 0552 if (filterException) { 0553 return nullptr; 0554 } 0555 0556 if (cursor == m_rootNode) { // We just checked root -- no where else to go up. 0557 return nullptr; 0558 } 0559 cursor = cursor->parentNode(); 0560 } 0561 0562 return nullptr; 0563 } 0564 0565 TreeWalkerImpl::NodePtr TreeWalkerImpl::getFirstChild(TreeWalkerImpl::NodePtr n, void *&filterException) 0566 { 0567 filterException = nullptr; 0568 NodePtr cursor; 0569 for (cursor = n->firstChild(); cursor; cursor = cursor->nextSibling()) { 0570 switch (isAccepted(cursor, filterException)) { 0571 case NodeFilter::FILTER_ACCEPT: 0572 return cursor; 0573 case NodeFilter::FILTER_SKIP: { 0574 // We ignore this candidate proper, but perhaps it has a kid? 0575 NodePtr cand = getFirstChild(cursor, filterException); 0576 if (filterException) { 0577 return nullptr; 0578 } 0579 if (cand) { 0580 return cand; 0581 } 0582 break; 0583 } 0584 case NodeFilter::FILTER_REJECT: 0585 // It effectively doesn't exist, or exception 0586 if (filterException) { 0587 return nullptr; 0588 } 0589 break; 0590 } 0591 // ### this is unclear: what should happen if we "pass through" the root?? 0592 } 0593 0594 // Nothing found.. 0595 return nullptr; 0596 } 0597 0598 TreeWalkerImpl::NodePtr TreeWalkerImpl::getLastChild(TreeWalkerImpl::NodePtr n, void *&filterException) 0599 { 0600 filterException = nullptr; 0601 TreeWalkerImpl::NodePtr cursor; 0602 for (cursor = n->lastChild(); cursor; cursor = cursor->previousSibling()) { 0603 switch (isAccepted(cursor, filterException)) { 0604 case NodeFilter::FILTER_ACCEPT: 0605 return cursor; 0606 case NodeFilter::FILTER_SKIP: { 0607 // We ignore this candidate proper, but perhaps it has a kid? 0608 TreeWalkerImpl::NodePtr cand = getLastChild(cursor, filterException); 0609 if (filterException) { 0610 return nullptr; 0611 } 0612 if (cand) { 0613 return cand; 0614 } 0615 break; 0616 } 0617 case NodeFilter::FILTER_REJECT: 0618 // It effectively doesn't exist.. 0619 if (filterException) { 0620 return nullptr; 0621 } 0622 break; 0623 } 0624 // ### this is unclear: what should happen if we "pass through" the root?? 0625 } 0626 0627 // Nothing found.. 0628 return nullptr; 0629 } 0630 0631 TreeWalkerImpl::NodePtr TreeWalkerImpl::getNextSibling(TreeWalkerImpl::NodePtr n, void *&filterException) 0632 { 0633 filterException = nullptr; 0634 0635 // If we're the root node, clearly we don't have any siblings. 0636 if (n == m_rootNode) { 0637 return nullptr; 0638 } 0639 0640 // What would can our siblings be? Well, they can be our actual siblings, 0641 // or if those are 'skip' their first kid. We may also have to walk up 0642 // through any skipped nodes. 0643 NodePtr cursor; 0644 for (cursor = n->nextSibling(); cursor; cursor = cursor->nextSibling()) { 0645 switch (isAccepted(cursor, filterException)) { 0646 case NodeFilter::FILTER_ACCEPT: 0647 return cursor; 0648 case NodeFilter::FILTER_SKIP: { 0649 NodePtr cand = getFirstChild(cursor, filterException); 0650 if (filterException) { 0651 return nullptr; 0652 } 0653 if (cand) { 0654 return cand; 0655 } 0656 break; 0657 } 0658 case NodeFilter::FILTER_REJECT: 0659 if (filterException) { 0660 return nullptr; 0661 } 0662 break; 0663 } 0664 } 0665 0666 // Now, we have scanned through all of our parent's kids. Our siblings may also be 0667 // above if we could have skipped the parent, and are not captured by it. 0668 NodePtr parent = n->parentNode(); 0669 if (!parent || parent == m_rootNode) { 0670 return nullptr; 0671 } 0672 0673 /* "In particular: If the currentNode becomes part of a subtree that would otherwise have 0674 been Rejected by the filter, that entire subtree may be added as transient members of the 0675 logical view. You will be able to navigate within that subtree (subject to all the usual 0676 filtering) until you move upward past the Rejected ancestor. The behavior is as if the Rejected 0677 node had only been Skipped (since we somehow wound up inside its subtree) until we leave it; 0678 thereafter, standard filtering applies.*/ 0679 if (isAccepted(parent, filterException) == NodeFilter::FILTER_ACCEPT) { 0680 return nullptr; 0681 } 0682 if (filterException) { 0683 return nullptr; 0684 } 0685 0686 return getNextSibling(parent, filterException); 0687 } 0688 0689 TreeWalkerImpl::NodePtr TreeWalkerImpl::getPreviousSibling(TreeWalkerImpl::NodePtr n, void *&filterException) 0690 { 0691 filterException = nullptr; 0692 // See the above for all the comments :-) 0693 if (n == m_rootNode) { 0694 return nullptr; 0695 } 0696 0697 NodePtr cursor; 0698 for (cursor = n->previousSibling(); cursor; cursor = cursor->previousSibling()) { 0699 switch (isAccepted(cursor, filterException)) { 0700 case NodeFilter::FILTER_ACCEPT: 0701 return cursor; 0702 case NodeFilter::FILTER_SKIP: { 0703 NodePtr cand = getLastChild(cursor, filterException); 0704 if (filterException) { 0705 return nullptr; 0706 } 0707 if (cand) { 0708 return cand; 0709 } 0710 break; 0711 } 0712 case NodeFilter::FILTER_REJECT: 0713 if (filterException) { 0714 return nullptr; 0715 } 0716 break; 0717 } 0718 } 0719 0720 NodePtr parent = n->parentNode(); 0721 if (!parent || parent == m_rootNode) { 0722 return nullptr; 0723 } 0724 0725 if (isAccepted(parent, filterException) == NodeFilter::FILTER_ACCEPT) { 0726 return nullptr; 0727 } 0728 if (filterException) { 0729 return nullptr; 0730 } 0731 0732 return getPreviousSibling(parent, filterException); 0733 } 0734