Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / xmlsecurity / source / framework / buffernode.cxx
blobdfffa6650c5b94f9e614196047da887115adf426
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "elementmark.hxx"
22 #include "elementcollector.hxx"
23 #include "buffernode.hxx"
24 #include <com/sun/star/xml/crypto/sax/ConstOfSecurityId.hpp>
25 #include <osl/diagnose.h>
26 #include <rtl/ustrbuf.hxx>
28 namespace cssu = com::sun::star::uno;
29 namespace cssxw = com::sun::star::xml::wrapper;
30 namespace cssxc = com::sun::star::xml::crypto;
32 BufferNode::BufferNode( const cssu::Reference< cssxw::XXMLElementWrapper >& xXMLElement )
33 :m_pParent(nullptr),
34 m_pBlocker(nullptr),
35 m_bAllReceived(false),
36 m_xXMLElement(xXMLElement)
40 bool BufferNode::isECOfBeforeModifyIncluded(sal_Int32 nIgnoredSecurityId) const
41 /****** BufferNode/isECOfBeforeModifyIncluded ********************************
43 * NAME
44 * isECOfBeforeModifyIncluded -- checks whether there is some
45 * ElementCollector on this BufferNode, that has BEFORE-MODIFY priority.
47 * SYNOPSIS
48 * bExist = isECOfBeforeModifyIncluded(nIgnoredSecurityId);
50 * FUNCTION
51 * checks each ElementCollector on this BufferNode, if all following
52 * conditions are satisfied, then returns true:
53 * 1. the ElementCollector's priority is BEFOREMODIFY;
54 * 2. the ElementCollector's securityId can't be ignored.
55 * otherwise, returns false.
57 * INPUTS
58 * nIgnoredSecurityId - the security Id to be ignored. If it equals
59 * to UNDEFINEDSECURITYID, then no security Id
60 * will be ignored.
62 * RESULT
63 * bExist - true if a match found, false otherwise
64 ******************************************************************************/
66 return std::any_of(m_vElementCollectors.cbegin(), m_vElementCollectors.cend(),
67 [nIgnoredSecurityId](const ElementCollector* pElementCollector) {
68 return (nIgnoredSecurityId == cssxc::sax::ConstOfSecurityId::UNDEFINEDSECURITYID ||
69 pElementCollector->getSecurityId() != nIgnoredSecurityId) &&
70 (pElementCollector->getPriority() == cssxc::sax::ElementMarkPriority_BEFOREMODIFY);
71 });
74 void BufferNode::setReceivedAll()
75 /****** BufferNode/setReceiveAll *********************************************
77 * NAME
78 * setReceivedAll -- indicates that the element in this BufferNode has
79 * been completely buffered.
81 * SYNOPSIS
82 * setReceivedAll();
84 * FUNCTION
85 * sets the all-received flag and launches ElementCollector's notify
86 * process.
88 * INPUTS
89 * empty
91 * RESULT
92 * empty
93 ******************************************************************************/
95 m_bAllReceived = true;
96 elementCollectorNotify();
100 void BufferNode::addElementCollector(const ElementCollector* pElementCollector)
101 /****** BufferNode/addElementCollector ***************************************
103 * NAME
104 * addElementCollector -- adds a new ElementCollector to this BufferNode.
106 * SYNOPSIS
107 * addElementCollector(pElementCollector);
109 * FUNCTION
110 * see NAME
112 * INPUTS
113 * pElementCollector - the ElementCollector to be added
115 * RESULT
116 * empty
117 ******************************************************************************/
119 m_vElementCollectors.push_back( pElementCollector );
120 const_cast<ElementCollector*>(pElementCollector)->setBufferNode(this);
123 void BufferNode::removeElementCollector(const ElementCollector* pElementCollector)
124 /****** BufferNode/removeElementCollector ************************************
126 * NAME
127 * removeElementCollector -- removes an ElementCollector from this
128 * BufferNode.
130 * SYNOPSIS
131 * removeElementCollector(pElementCollector);
133 * FUNCTION
134 * see NAME
136 * INPUTS
137 * pElementCollector - the ElementCollector to be removed
139 * RESULT
140 * empty
141 ******************************************************************************/
143 auto ii = std::find(m_vElementCollectors.begin(), m_vElementCollectors.end(), pElementCollector);
144 if (ii != m_vElementCollectors.end())
146 m_vElementCollectors.erase( ii );
147 const_cast<ElementCollector*>(pElementCollector)->setBufferNode(nullptr);
152 void BufferNode::setBlocker(const ElementMark* pBlocker)
153 /****** BufferNode/setBlocker ************************************************
155 * NAME
156 * setBlocker -- adds a blocker to this BufferNode.
158 * SYNOPSIS
159 * setBlocker(pBlocker);
161 * FUNCTION
162 * see NAME
164 * INPUTS
165 * pBlocker - the new blocker to be attached
167 * RESULT
168 * empty
170 * NOTES
171 * Because there is only one blocker permitted for a BufferNode, so the
172 * old blocker on this BufferNode, if there is one, will be overcasted.
173 ******************************************************************************/
175 OSL_ASSERT(!(m_pBlocker != nullptr && pBlocker != nullptr));
177 m_pBlocker = const_cast<ElementMark*>(pBlocker);
178 if (m_pBlocker != nullptr)
180 m_pBlocker->setBufferNode(this);
184 OUString BufferNode::printChildren() const
185 /****** BufferNode/printChildren *********************************************
187 * NAME
188 * printChildren -- prints children information into a string.
190 * SYNOPSIS
191 * result = printChildren();
193 * FUNCTION
194 * see NAME
196 * INPUTS
197 * empty
199 * RESULT
200 * result - the information string
201 ******************************************************************************/
203 OUStringBuffer rc;
205 for( const ElementCollector* ii : m_vElementCollectors )
207 rc.append("BufID=").append(OUString::number(ii->getBufferId()));
209 if (ii->getModify())
211 rc.append("[M]");
214 rc.append(",Pri=");
216 switch (ii->getPriority())
218 case cssxc::sax::ElementMarkPriority_BEFOREMODIFY:
219 rc.append("BEFOREMODIFY");
220 break;
221 case cssxc::sax::ElementMarkPriority_AFTERMODIFY:
222 rc.append("AFTERMODIFY");
223 break;
224 default:
225 rc.append("UNKNOWN");
226 break;
229 rc.append("(SecID=").append(OUString::number(ii->getSecurityId())).append(") ");
232 return rc.makeStringAndClear();
235 bool BufferNode::hasAnything() const
236 /****** BufferNode/hasAnything ***********************************************
238 * NAME
239 * hasAnything -- checks whether there is any ElementCollector or blocker
240 * on this BufferNode.
242 * SYNOPSIS
243 * bExist = hasAnything();
245 * FUNCTION
246 * see NAME
248 * INPUTS
249 * empty
251 * RESULT
252 * bExist - true if there is, false otherwise.
253 ******************************************************************************/
255 return (m_pBlocker || !m_vElementCollectors.empty());
258 bool BufferNode::hasChildren() const
259 /****** BufferNode/hasChildren ***********************************************
261 * NAME
262 * hasChildren -- checks whether this BufferNode has any child
263 * BufferNode.
265 * SYNOPSIS
266 * bExist = hasChildren();
268 * FUNCTION
269 * see NAME
271 * INPUTS
272 * empty
274 * RESULT
275 * bExist - true if there is, false otherwise.
276 ******************************************************************************/
278 return (!m_vChildren.empty());
281 std::vector< std::unique_ptr<BufferNode> > const & BufferNode::getChildren() const
283 return m_vChildren;
286 std::vector< std::unique_ptr<BufferNode> > BufferNode::releaseChildren()
288 return std::move(m_vChildren);
291 const BufferNode* BufferNode::getFirstChild() const
292 /****** BufferNode/getFirstChild *********************************************
294 * NAME
295 * getFirstChild -- retrieves the first child BufferNode.
297 * SYNOPSIS
298 * child = getFirstChild();
300 * FUNCTION
301 * see NAME
303 * INPUTS
304 * empty
306 * RESULT
307 * child - the first child BufferNode, or NULL if there is no child
308 * BufferNode.
309 ******************************************************************************/
311 BufferNode* rc = nullptr;
313 if (!m_vChildren.empty())
315 rc = m_vChildren.front().get();
318 return rc;
321 void BufferNode::addChild(std::unique_ptr<BufferNode> pChild, sal_Int32 nPosition)
322 /****** BufferNode/addChild(pChild,nPosition) ********************************
324 * NAME
325 * addChild -- inserts a child BufferNode at specific position.
327 * SYNOPSIS
328 * addChild(pChild, nPosition);
330 * FUNCTION
331 * see NAME
333 * INPUTS
334 * pChild - the child BufferNode to be added.
335 * nPosition - the position where the new child locates.
337 * RESULT
338 * empty
340 * NOTES
341 * If the nPosition is -1, then the new child BufferNode is appended
342 * at the end.
343 ******************************************************************************/
345 if (nPosition == -1)
347 m_vChildren.push_back( std::move(pChild) );
349 else
351 m_vChildren.insert(m_vChildren.begin() + nPosition, std::move(pChild));
355 void BufferNode::addChild(std::unique_ptr<BufferNode> pChild)
356 /****** BufferNode/addChild() ************************************************
358 * NAME
359 * addChild -- add a new child BufferNode.
361 * SYNOPSIS
362 * addChild(pChild);
364 * FUNCTION
365 * see NAME
367 * INPUTS
368 * pChild - the child BufferNode to be added.
370 * RESULT
371 * empty
373 * NOTES
374 * The new child BufferNode is appended at the end.
375 ******************************************************************************/
377 addChild(std::move(pChild), -1);
380 void BufferNode::removeChild(const BufferNode* pChild)
381 /****** BufferNode/removeChild ***********************************************
383 * NAME
384 * removeChild -- removes and deletes a child BufferNode from the children list.
386 * SYNOPSIS
387 * removeChild(pChild);
389 * FUNCTION
390 * see NAME
392 * INPUTS
393 * pChild - the child BufferNode to be removed
395 * RESULT
396 * empty
397 ******************************************************************************/
399 auto ii = std::find_if(m_vChildren.begin(), m_vChildren.end(),
400 [pChild] (const std::unique_ptr<BufferNode>& i)
401 { return i.get() == pChild; });
402 if (ii != m_vChildren.end())
403 m_vChildren.erase( ii );
406 sal_Int32 BufferNode::indexOfChild(const BufferNode* pChild) const
407 /****** BufferNode/indexOfChild **********************************************
409 * NAME
410 * indexOfChild -- gets the index of a child BufferNode.
412 * SYNOPSIS
413 * index = indexOfChild(pChild);
415 * FUNCTION
416 * see NAME
418 * INPUTS
419 * pChild - the child BufferNode whose index to be gotten
421 * RESULT
422 * index - the index of that child BufferNode. If that child BufferNode
423 * is not found, -1 is returned.
424 ******************************************************************************/
426 auto ii = std::find_if(m_vChildren.begin(), m_vChildren.end(),
427 [pChild] (const std::unique_ptr<BufferNode>& i)
428 { return i.get() == pChild; });
429 if (ii == m_vChildren.end())
430 return -1;
432 return std::distance(m_vChildren.begin(), ii);
436 void BufferNode::setParent(const BufferNode* pParent)
438 m_pParent = const_cast<BufferNode*>(pParent);
441 const BufferNode* BufferNode::getNextSibling() const
442 /****** BufferNode/getNextSibling ********************************************
444 * NAME
445 * getNextSibling -- retrieves the next sibling BufferNode.
447 * SYNOPSIS
448 * sibling = getNextSibling();
450 * FUNCTION
451 * see NAME
453 * INPUTS
454 * empty
456 * RESULT
457 * sibling - the next sibling BufferNode, or NULL if there is none.
458 ******************************************************************************/
460 BufferNode* rc = nullptr;
462 if (m_pParent != nullptr)
464 rc = const_cast<BufferNode*>(m_pParent->getNextChild(this));
467 return rc;
470 const BufferNode* BufferNode::isAncestor(const BufferNode* pDescendant) const
471 /****** BufferNode/isAncestor ************************************************
473 * NAME
474 * isAncestor -- checks whether this BufferNode is an ancestor of another
475 * BufferNode.
477 * SYNOPSIS
478 * bIs = isAncestor(pDescendant);
480 * FUNCTION
481 * see NAME
483 * INPUTS
484 * pDescendant - the BufferNode to be checked as a descendant
486 * RESULT
487 * bIs - true if this BufferNode is an ancestor of the pDescendant,
488 * false otherwise.
489 ******************************************************************************/
491 BufferNode* rc = nullptr;
493 if (pDescendant != nullptr)
495 auto ii = std::find_if(m_vChildren.cbegin(), m_vChildren.cend(),
496 [&pDescendant](const std::unique_ptr<BufferNode>& pChild) {
497 return (pChild.get() == pDescendant) || (pChild->isAncestor(pDescendant) != nullptr);
500 if (ii != m_vChildren.end())
501 rc = ii->get();
504 return rc;
507 bool BufferNode::isPrevious(const BufferNode* pFollowing) const
508 /****** BufferNode/isPrevious ************************************************
510 * NAME
511 * isPrevious -- checks whether this BufferNode is ahead of another
512 * BufferNode in the tree order.
514 * SYNOPSIS
515 * bIs = isPrevious(pFollowing);
517 * FUNCTION
518 * see NAME
520 * INPUTS
521 * pFollowing - the BufferNode to be checked as a following
523 * RESULT
524 * bIs - true if this BufferNode is ahead in the tree order, false
525 * otherwise.
526 ******************************************************************************/
528 bool rc = false;
530 BufferNode* pNextBufferNode = const_cast<BufferNode*>(getNextNodeByTreeOrder());
531 while (pNextBufferNode != nullptr)
533 if (pNextBufferNode == pFollowing)
535 rc = true;
536 break;
539 pNextBufferNode = const_cast<BufferNode*>(pNextBufferNode->getNextNodeByTreeOrder());
542 return rc;
545 const BufferNode* BufferNode::getNextNodeByTreeOrder() const
546 /****** BufferNode/getNextNodeByTreeOrder ************************************
548 * NAME
549 * getNextNodeByTreeOrder -- retrieves the next BufferNode in the tree
550 * order.
552 * SYNOPSIS
553 * next = getNextNodeByTreeOrder();
555 * FUNCTION
556 * see NAME
558 * INPUTS
559 * empty
561 * RESULT
562 * next - the BufferNode following this BufferNode in the tree order,
563 * or NULL if there is none.
565 * NOTES
566 * The "next" node in tree order is defined as:
567 * 1. If a node has children, then the first child is;
568 * 2. otherwise, if it has a following sibling, then this sibling node is;
569 * 3. otherwise, if it has a parent node, the parent's next sibling
570 * node is;
571 * 4. otherwise, no "next" node exists.
572 ******************************************************************************/
575 * If this buffer node has m_vChildren, then return the first
576 * child.
578 if (hasChildren())
580 return getFirstChild();
584 * Otherwise, it this buffer node has a following sibling,
585 * then return that sibling.
587 BufferNode* pNextSibling = const_cast<BufferNode*>(getNextSibling());
588 if (pNextSibling != nullptr)
590 return pNextSibling;
594 * Otherwise, it this buffer node has parent, then return
595 * its parent's following sibling.
597 BufferNode* pNode = const_cast<BufferNode*>(this);
598 BufferNode* pParent;
599 BufferNode* pNextSiblingParent = nullptr;
603 if (pNode == nullptr)
605 break;
608 pParent = const_cast<BufferNode*>(pNode->getParent());
609 if (pParent != nullptr)
611 pNextSiblingParent = const_cast<BufferNode*>(pParent->getNextSibling());
613 pNode = pParent;
615 } while (pNextSiblingParent == nullptr);
617 return pNextSiblingParent;
621 void BufferNode::setXMLElement( const cssu::Reference< cssxw::XXMLElementWrapper >& xXMLElement )
623 m_xXMLElement = xXMLElement;
626 void BufferNode::notifyBranch()
627 /****** BufferNode/notifyBranch **********************************************
629 * NAME
630 * notifyBranch -- notifies each BufferNode in the branch of this
631 * BufferNode in the tree order.
633 * SYNOPSIS
634 * notifyBranch();
636 * FUNCTION
637 * see NAME
639 * INPUTS
640 * empty
642 * RESULT
643 * empty
644 ******************************************************************************/
646 for( std::unique_ptr<BufferNode>& pBufferNode : m_vChildren )
648 pBufferNode->elementCollectorNotify();
649 pBufferNode->notifyBranch();
653 void BufferNode::elementCollectorNotify()
654 /****** BufferNode/elementCollectorNotify ************************************
656 * NAME
657 * elementCollectorNotify -- notifies this BufferNode.
659 * SYNOPSIS
660 * elementCollectorNotify();
662 * FUNCTION
663 * Notifies this BufferNode if the notification is not suppressed.
665 * INPUTS
666 * empty
668 * RESULT
669 * child - the first child BufferNode, or NULL if there is no child
670 * BufferNode.
671 ******************************************************************************/
673 if (!m_vElementCollectors.empty())
675 cssxc::sax::ElementMarkPriority nMaxPriority = cssxc::sax::ElementMarkPriority_MINIMUM;
676 cssxc::sax::ElementMarkPriority nPriority;
679 * get the max priority among ElementCollectors on this BufferNode
681 for( const ElementCollector* pElementCollector : m_vElementCollectors )
683 nPriority = pElementCollector->getPriority();
684 if (nPriority > nMaxPriority)
686 nMaxPriority = nPriority;
690 std::vector< const ElementCollector* > vElementCollectors( m_vElementCollectors );
692 for( const ElementCollector* ii : vElementCollectors )
694 ElementCollector* pElementCollector = const_cast<ElementCollector*>(ii);
695 nPriority = pElementCollector->getPriority();
696 bool bToModify = pElementCollector->getModify();
699 * Only ElementCollector with the max priority can
700 * perform notify operation.
701 * Moreover, if any blocker exists in the subtree of
702 * this BufferNode, this ElementCollector can't do notify
703 * unless its priority is BEFOREMODIFY.
705 if (nPriority == nMaxPriority &&
706 (nPriority == cssxc::sax::ElementMarkPriority_BEFOREMODIFY ||
707 !isBlockerInSubTreeIncluded(pElementCollector->getSecurityId())))
710 * If this ElementCollector will modify the buffered element, then
711 * special attention must be paid.
713 * If there is any ElementCollector in the subtree or any ancestor
714 * ElementCollector with PRI_BEFPREMODIFY priority, this
715 * ElementCollector can't perform notify operation, otherwise, it
716 * will destroy the buffered element, in turn, ElementCollectors
717 * mentioned above can't perform their mission.
719 //if (!(nMaxPriority == cssxc::sax::ElementMarkPriority_PRI_MODIFY &&
720 if (!(bToModify &&
721 (isECInSubTreeIncluded(pElementCollector->getSecurityId()) ||
722 isECOfBeforeModifyInAncestorIncluded(pElementCollector->getSecurityId()))
725 pElementCollector->notifyListener();
732 bool BufferNode::isECInSubTreeIncluded(sal_Int32 nIgnoredSecurityId) const
733 /****** BufferNode/isECInSubTreeIncluded *************************************
735 * NAME
736 * isECInSubTreeIncluded -- checks whether there is any ElementCollector
737 * in the branch of this BufferNode.
739 * SYNOPSIS
740 * bExist = isECInSubTreeIncluded(nIgnoredSecurityId);
742 * FUNCTION
743 * checks each BufferNode in the branch of this BufferNode, if there is
744 * an ElementCollector whose signatureId is not ignored, then return
745 * true, otherwise, false returned.
747 * INPUTS
748 * nIgnoredSecurityId - the security Id to be ignored. If it equals
749 * to UNDEFINEDSECURITYID, then no security Id
750 * will be ignored.
752 * RESULT
753 * bExist - true if a match found, false otherwise.
754 ******************************************************************************/
756 bool rc = std::any_of(m_vElementCollectors.begin(), m_vElementCollectors.end(),
757 [nIgnoredSecurityId](const ElementCollector* pElementCollector) {
758 return nIgnoredSecurityId == cssxc::sax::ConstOfSecurityId::UNDEFINEDSECURITYID ||
759 pElementCollector->getSecurityId() != nIgnoredSecurityId;
762 if ( !rc )
764 rc = std::any_of(m_vChildren.begin(), m_vChildren.end(),
765 [nIgnoredSecurityId](const std::unique_ptr<BufferNode>& pBufferNode) {
766 return pBufferNode->isECInSubTreeIncluded(nIgnoredSecurityId);
770 return rc;
773 bool BufferNode::isECOfBeforeModifyInAncestorIncluded(sal_Int32 nIgnoredSecurityId) const
774 /****** BufferNode/isECOfBeforeModifyInAncestorIncluded **********************
776 * NAME
777 * isECOfBeforeModifyInAncestorIncluded -- checks whether there is some
778 * ancestor BufferNode which has ElementCollector with PRI_BEFPREMODIFY
779 * priority.
781 * SYNOPSIS
782 * bExist = isECOfBeforeModifyInAncestorIncluded(nIgnoredSecurityId);
784 * FUNCTION
785 * checks each ancestor BufferNode through the parent link, if there is
786 * an ElementCollector with PRI_BEFPREMODIFY priority and its
787 * signatureId is not ignored, then return true, otherwise, false
788 * returned.
790 * INPUTS
791 * nIgnoredSecurityId - the security Id to be ignored. If it equals
792 * to UNDEFINEDSECURITYID, then no security Id
793 * will be ignored.
795 * RESULT
796 * bExist - true if a match found, false otherwise.
797 ******************************************************************************/
799 bool rc = false;
801 BufferNode* pParentNode = m_pParent;
802 while (pParentNode != nullptr)
804 if (pParentNode->isECOfBeforeModifyIncluded(nIgnoredSecurityId))
806 rc = true;
807 break;
810 pParentNode = const_cast<BufferNode*>(pParentNode->getParent());
813 return rc;
816 bool BufferNode::isBlockerInSubTreeIncluded(sal_Int32 nIgnoredSecurityId) const
817 /****** BufferNode/isBlockerInSubTreeIncluded ********************************
819 * NAME
820 * isBlockerInSubTreeIncluded -- checks whether there is some BufferNode
821 * which has blocker on it
823 * SYNOPSIS
824 * bExist = isBlockerInSubTreeIncluded(nIgnoredSecurityId);
826 * FUNCTION
827 * checks each BufferNode in the branch of this BufferNode, if one has
828 * a blocker on it, and the blocker's securityId is not ignored, then
829 * returns true; otherwise, false returns.
831 * INPUTS
832 * nIgnoredSecurityId - the security Id to be ignored. If it equals
833 * to UNDEFINEDSECURITYID, then no security Id
834 * will be ignored.
836 * RESULT
837 * bExist - true if a match found, false otherwise.
838 ******************************************************************************/
840 return std::any_of(m_vChildren.begin(), m_vChildren.end(),
841 [nIgnoredSecurityId](const std::unique_ptr<BufferNode>& pBufferNode) {
842 ElementMark* pBlocker = pBufferNode->getBlocker();
843 return (pBlocker != nullptr &&
844 (nIgnoredSecurityId == cssxc::sax::ConstOfSecurityId::UNDEFINEDSECURITYID ||
845 pBlocker->getSecurityId() != nIgnoredSecurityId )) ||
846 pBufferNode->isBlockerInSubTreeIncluded(nIgnoredSecurityId);
850 const BufferNode* BufferNode::getNextChild(const BufferNode* pChild) const
851 /****** BufferNode/getNextChild **********************************************
853 * NAME
854 * getNextChild -- get the next child BufferNode.
856 * SYNOPSIS
857 * nextChild = getNextChild();
859 * FUNCTION
860 * see NAME
862 * INPUTS
863 * pChild - the child BufferNode whose next node is retrieved.
865 * RESULT
866 * nextChild - the next child BufferNode after the pChild, or NULL if
867 * there is none.
868 ******************************************************************************/
870 BufferNode* rc = nullptr;
871 bool bChildFound = false;
873 for( std::unique_ptr<BufferNode> const & i : m_vChildren )
875 if (bChildFound)
877 rc = i.get();
878 break;
881 if( i.get() == pChild )
883 bChildFound = true;
887 return rc;
890 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */