| /** |
| * This file is part of the DOM implementation for KDE. |
| * |
| * (C) 1999 Lars Knoll (knoll@kde.org) |
| * (C) 2000 Frederik Holljen (frederik.holljen@hig.no) |
| * (C) 2001 Peter Kelly (pmk@post.com) |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| * Boston, MA 02111-1307, USA. |
| * |
| * $Id$ |
| */ |
| |
| #include "dom2_traversal.h" |
| #include "dom_node.h" |
| #include "dom_exception.h" |
| #include "dom2_traversalimpl.h" |
| #include "dom_docimpl.h" |
| |
| |
| using namespace DOM; |
| |
| NodeIteratorImpl::NodeIteratorImpl(NodeImpl *_root, unsigned long _whatToShow, |
| NodeFilter _filter, bool _entityReferenceExpansion) |
| { |
| m_root = _root; |
| m_whatToShow = _whatToShow; |
| m_filter = _filter; |
| m_expandEntityReferences = _entityReferenceExpansion; |
| |
| m_referenceNode = _root; |
| m_inFront = false; |
| |
| if (_root->nodeType() == Node::DOCUMENT_NODE) |
| m_doc = static_cast<DocumentImpl*>(m_root); |
| else |
| m_doc = m_root->ownerDocument(); |
| m_doc->attachNodeIterator(this); |
| m_doc->ref(); |
| |
| m_detached = false; |
| } |
| |
| NodeIteratorImpl::~NodeIteratorImpl() |
| { |
| m_doc->detachNodeIterator(this); |
| m_doc->deref(); |
| } |
| |
| NodeImpl *NodeIteratorImpl::root() |
| { |
| return m_root; |
| } |
| |
| unsigned long NodeIteratorImpl::whatToShow() |
| { |
| return m_whatToShow; |
| } |
| |
| NodeFilter NodeIteratorImpl::filter() |
| { |
| return m_filter; |
| } |
| |
| bool NodeIteratorImpl::expandEntityReferences() |
| { |
| return m_expandEntityReferences; |
| } |
| |
| NodeImpl *NodeIteratorImpl::nextNode( int &exceptioncode ) |
| { |
| if (m_detached) { |
| exceptioncode = DOMException::INVALID_STATE_ERR; |
| return 0; |
| } |
| |
| if (!m_referenceNode) { |
| m_inFront = true; |
| return 0; |
| } |
| |
| if (!m_inFront) { |
| m_inFront = true; |
| if (isAccepted(m_referenceNode) == NodeFilter::FILTER_ACCEPT) |
| return m_referenceNode; |
| } |
| |
| NodeImpl *_tempCurrent = getNextNode(m_referenceNode); |
| while( _tempCurrent ) { |
| m_referenceNode = _tempCurrent; |
| if(isAccepted(_tempCurrent) == NodeFilter::FILTER_ACCEPT) |
| return m_referenceNode; |
| _tempCurrent = getNextNode(_tempCurrent); |
| } |
| |
| return 0; |
| } |
| |
| NodeImpl *NodeIteratorImpl::getNextNode(NodeImpl *n) |
| { |
| /* 1. my first child |
| * 2. my next sibling |
| * 3. my parents sibling, or their parents sibling (loop) |
| * 4. not found |
| */ |
| |
| if( !n ) |
| return 0; |
| |
| if( n->hasChildNodes() ) |
| return n->firstChild(); |
| |
| if( n->nextSibling() ) |
| return n->nextSibling(); |
| |
| if( m_root == n) |
| return 0; |
| |
| NodeImpl *parent = n->parentNode(); |
| while( parent ) |
| { |
| n = parent->nextSibling(); |
| if( n ) |
| return n; |
| |
| if( m_root == parent ) |
| return 0; |
| |
| parent = parent->parentNode(); |
| } |
| return 0; |
| } |
| |
| NodeImpl *NodeIteratorImpl::previousNode( int &exceptioncode ) |
| { |
| if (m_detached) { |
| exceptioncode = DOMException::INVALID_STATE_ERR; |
| return 0; |
| } |
| |
| if (!m_referenceNode) { |
| m_inFront = false; |
| return 0; |
| } |
| |
| if (m_inFront) { |
| m_inFront = false; |
| if (isAccepted(m_referenceNode) == NodeFilter::FILTER_ACCEPT) |
| return m_referenceNode; |
| } |
| |
| NodeImpl *_tempCurrent = getPreviousNode(m_referenceNode); |
| while( _tempCurrent ) { |
| m_referenceNode = _tempCurrent; |
| if(isAccepted(_tempCurrent) == NodeFilter::FILTER_ACCEPT) |
| return m_referenceNode; |
| _tempCurrent = getPreviousNode(_tempCurrent); |
| } |
| |
| return 0; |
| } |
| |
| NodeImpl *NodeIteratorImpl::getPreviousNode(NodeImpl *n) |
| { |
| /* 1. my previous sibling.lastchild |
| * 2. my previous sibling |
| * 3. my parent |
| */ |
| NodeImpl *_tempCurrent; |
| |
| if( !n ) |
| return 0; |
| |
| _tempCurrent = n->previousSibling(); |
| if( _tempCurrent ) |
| { |
| if( _tempCurrent->lastChild() ) |
| { |
| while( _tempCurrent->lastChild() ) |
| _tempCurrent = _tempCurrent->lastChild(); |
| return _tempCurrent; |
| } |
| else |
| return _tempCurrent; |
| } |
| |
| |
| if(n == m_root) |
| return 0; |
| |
| return n->parentNode(); |
| |
| |
| } |
| |
| void NodeIteratorImpl::detach(int &/*exceptioncode*/) |
| { |
| m_doc->detachNodeIterator(this); |
| m_detached = true; |
| } |
| |
| |
| void NodeIteratorImpl::notifyBeforeNodeRemoval(NodeImpl *removed) |
| { |
| // make sure the deleted node is with the root (but not the root itself) |
| if (removed == m_root) |
| return; |
| |
| NodeImpl *maybeRoot = removed->parentNode(); |
| while (maybeRoot && maybeRoot != m_root) |
| maybeRoot = maybeRoot->parentNode(); |
| if (!maybeRoot) |
| return; |
| |
| // did I get deleted, or one of my parents? |
| NodeImpl *_tempDeleted = m_referenceNode; |
| while( _tempDeleted && _tempDeleted != removed) |
| _tempDeleted = _tempDeleted->parentNode(); |
| |
| if( !_tempDeleted ) // someone that didn't consern me got deleted |
| return; |
| |
| if( !m_inFront) |
| { |
| NodeImpl *_next = getNextNode(_tempDeleted); |
| if( _next ) |
| m_referenceNode = _next; |
| else |
| { |
| // deleted node was at end of list |
| m_inFront = true; |
| m_referenceNode = getPreviousNode(_tempDeleted); |
| } |
| } |
| else { |
| NodeImpl *_prev = getPreviousNode(_tempDeleted); |
| if ( _prev ) |
| m_referenceNode = _prev; |
| else |
| { |
| // deleted node was at start of list |
| m_inFront = false; |
| m_referenceNode = getNextNode(_tempDeleted); |
| } |
| } |
| |
| } |
| |
| short NodeIteratorImpl::isAccepted(NodeImpl *n) |
| { |
| // if XML is implemented we have to check expandEntityRerefences in this function |
| if( ( ( 1 << n->nodeType()-1) & m_whatToShow) != 0 ) |
| { |
| if(!m_filter.isNull()) |
| return m_filter.acceptNode(n); |
| else |
| return NodeFilter::FILTER_ACCEPT; |
| } |
| return NodeFilter::FILTER_SKIP; |
| } |
| |
| // -------------------------------------------------------------- |
| |
| |
| NodeFilterImpl::NodeFilterImpl() |
| { |
| m_customNodeFilter = 0; |
| } |
| |
| NodeFilterImpl::~NodeFilterImpl() |
| { |
| if (m_customNodeFilter) |
| m_customNodeFilter->deref(); |
| } |
| |
| short NodeFilterImpl::acceptNode(const Node &n) |
| { |
| if (m_customNodeFilter) |
| return m_customNodeFilter->acceptNode(n); |
| else |
| return NodeFilter::FILTER_ACCEPT; |
| } |
| |
| void NodeFilterImpl::setCustomNodeFilter(CustomNodeFilter *custom) |
| { |
| m_customNodeFilter = custom; |
| if (m_customNodeFilter) |
| m_customNodeFilter->ref(); |
| } |
| |
| CustomNodeFilter *NodeFilterImpl::customNodeFilter() |
| { |
| return m_customNodeFilter; |
| } |
| |
| // -------------------------------------------------------------- |
| |
| TreeWalkerImpl::TreeWalkerImpl() |
| { |
| filter = 0; |
| whatToShow = 0x0000FFFF; |
| expandEntityReferences = true; |
| } |
| |
| TreeWalkerImpl::TreeWalkerImpl(const TreeWalkerImpl &other) : DomShared() |
| { |
| expandEntityReferences = other.expandEntityReferences; |
| filter = other.filter; |
| whatToShow = other.whatToShow; |
| currentNode = other.currentNode; |
| rootNode = other.rootNode; |
| } |
| |
| TreeWalkerImpl::TreeWalkerImpl(Node n, NodeFilter *f) |
| { |
| currentNode = n; |
| rootNode = n; |
| whatToShow = 0x0000FFFF; |
| filter = f; |
| } |
| |
| TreeWalkerImpl::TreeWalkerImpl(Node n, long _whatToShow, NodeFilter *f) |
| { |
| currentNode = n; |
| rootNode = n; |
| whatToShow = _whatToShow; |
| filter = f; |
| } |
| |
| TreeWalkerImpl &TreeWalkerImpl::operator = (const TreeWalkerImpl &other) |
| { |
| expandEntityReferences = other.expandEntityReferences; |
| filter = other.filter; |
| whatToShow = other.whatToShow; |
| currentNode = other.currentNode; |
| return *this; |
| } |
| |
| TreeWalkerImpl::~TreeWalkerImpl() |
| { |
| if(filter) |
| { |
| delete filter; |
| filter = 0; |
| } |
| } |
| |
| |
| |
| |
| |
| Node TreeWalkerImpl::getRoot() |
| { |
| // ### |
| return 0; |
| } |
| |
| unsigned long TreeWalkerImpl::getWhatToShow() |
| { |
| // ### |
| return 0; |
| } |
| |
| NodeFilter TreeWalkerImpl::getFilter() |
| { |
| // ### |
| return 0; |
| } |
| |
| bool TreeWalkerImpl::getExpandEntityReferences() |
| { |
| // ### |
| return 0; |
| } |
| |
| Node TreeWalkerImpl::getCurrentNode() |
| { |
| return currentNode; |
| } |
| |
| void TreeWalkerImpl::setWhatToShow(long _whatToShow) |
| { |
| // do some testing wether this is an accepted value |
| whatToShow = _whatToShow; |
| } |
| |
| void TreeWalkerImpl::setFilter(NodeFilter *_filter) |
| { |
| if(_filter) |
| filter = _filter; |
| } |
| |
| void TreeWalkerImpl::setExpandEntityReferences(bool value) |
| { |
| expandEntityReferences = value; |
| } |
| |
| void TreeWalkerImpl::setCurrentNode( const Node n ) |
| { |
| if( !n.isNull() ) |
| { |
| rootNode = n; |
| currentNode = n; |
| } |
| // else |
| // throw( DOMException::NOT_SUPPORTED_ERR ); |
| } |
| |
| Node TreeWalkerImpl::parentNode( ) |
| { |
| Node n = getParentNode(currentNode); |
| if( !n.isNull() ) |
| currentNode = n; |
| return n; |
| } |
| |
| |
| Node TreeWalkerImpl::firstChild( ) |
| { |
| Node n = getFirstChild(currentNode); |
| if( !n.isNull() ) |
| currentNode = n; |
| return n; |
| } |
| |
| |
| Node TreeWalkerImpl::lastChild( ) |
| { |
| Node n = getLastChild(currentNode); |
| if( !n.isNull() ) |
| currentNode = n; |
| return n; |
| } |
| |
| Node TreeWalkerImpl::previousSibling( ) |
| { |
| Node n = getPreviousSibling(currentNode); |
| if( !n.isNull() ) |
| currentNode = n; |
| return n; |
| } |
| |
| Node TreeWalkerImpl::nextSibling( ) |
| { |
| Node n = getNextSibling(currentNode); |
| if( !n.isNull() ) |
| currentNode = n; |
| return n; |
| } |
| |
| Node TreeWalkerImpl::previousNode( ) |
| { |
| /* 1. my previous sibling.lastchild |
| * 2. my previous sibling |
| * 3. my parent |
| */ |
| |
| Node n = getPreviousSibling(currentNode); |
| if( n.isNull() ) |
| { |
| n = getParentNode(currentNode); |
| if( !n.isNull() ) //parent |
| { |
| currentNode = n; |
| return currentNode; |
| } |
| else // parent failed.. no previous node |
| return Node(); |
| } |
| |
| Node child = getLastChild(n); |
| if( !child.isNull() ) // previous siblings last child |
| { |
| currentNode = child; |
| return currentNode; |
| } |
| else // previous sibling |
| { |
| currentNode = n; |
| return currentNode; |
| } |
| return Node(); // should never get here! |
| } |
| |
| Node TreeWalkerImpl::nextNode( ) |
| { |
| /* 1. my first child |
| * 2. my next sibling |
| * 3. my parents sibling, or their parents sibling (loop) |
| * 4. not found |
| */ |
| |
| Node n = getFirstChild(currentNode); |
| if( !n.isNull() ) // my first child |
| { |
| currentNode = n; |
| return n; |
| } |
| |
| n = getNextSibling(currentNode); // my next sibling |
| if( !n.isNull() ) |
| { |
| currentNode = n; |
| return currentNode; |
| } |
| Node parent = getParentNode(currentNode); |
| while( !parent.isNull() ) // parents sibling |
| { |
| n = getNextSibling(parent); |
| if( !n.isNull() ) |
| { |
| currentNode = n; |
| return currentNode; |
| } |
| else |
| parent = getParentNode(parent); |
| } |
| return Node(); |
| } |
| |
| short TreeWalkerImpl::isAccepted(Node n) |
| { |
| // if XML is implemented we have to check expandEntityRerefences in this function |
| if( ( ( 1 << n.nodeType()-1 ) & whatToShow) != 0 ) |
| { |
| if(filter) |
| return filter->acceptNode(n); |
| else |
| return NodeFilter::FILTER_ACCEPT; |
| } |
| return NodeFilter::FILTER_SKIP; |
| } |
| |
| Node TreeWalkerImpl::getParentNode(Node n) |
| { |
| short _result = NodeFilter::FILTER_ACCEPT; |
| |
| if( n == rootNode /*|| n.isNull()*/ ) |
| return Node(); |
| |
| Node _tempCurrent = n.parentNode(); |
| |
| if( _tempCurrent.isNull() ) |
| return Node(); |
| |
| _result = isAccepted(_tempCurrent ); |
| if(_result == NodeFilter::FILTER_ACCEPT) |
| return _tempCurrent; // match found |
| |
| return getParentNode(_tempCurrent); |
| } |
| |
| Node TreeWalkerImpl::getFirstChild(Node n) |
| { |
| short _result; |
| |
| if( n.isNull() || n.firstChild().isNull() ) |
| return Node(); |
| n = n.firstChild(); |
| |
| _result = isAccepted(n); |
| |
| switch(_result) |
| { |
| case NodeFilter::FILTER_ACCEPT: |
| return n; |
| break; |
| case NodeFilter::FILTER_SKIP: |
| if( n.hasChildNodes() ) |
| return getFirstChild(n); |
| else |
| return getNextSibling(n); |
| break; |
| |
| case NodeFilter::FILTER_REJECT: |
| return getNextSibling(n); |
| break; |
| } |
| return Node(); // should never get here! |
| } |
| |
| Node TreeWalkerImpl::getLastChild(Node n) |
| { |
| short _result; |
| |
| if( n.isNull() || n.lastChild().isNull() ) |
| return Node(); |
| n = n.lastChild(); |
| _result = isAccepted(n); |
| switch(_result) |
| { |
| case NodeFilter::FILTER_ACCEPT: |
| return n; |
| break; |
| |
| case NodeFilter::FILTER_SKIP: |
| if( n.hasChildNodes() ) |
| return getLastChild(n); |
| else |
| return getPreviousSibling(n); |
| break; |
| |
| case NodeFilter::FILTER_REJECT: |
| return getPreviousSibling(n); |
| break; |
| } |
| return Node(); |
| } |
| |
| Node TreeWalkerImpl::getPreviousSibling(Node n) |
| { |
| short _result; |
| Node _tempCurrent; |
| |
| if( n.isNull() ) |
| return Node(); |
| //first the cases if we have a previousSibling |
| _tempCurrent = n.previousSibling(); |
| if( !_tempCurrent.isNull() ) |
| { |
| _result = isAccepted(_tempCurrent); |
| switch(_result) |
| { |
| case NodeFilter::FILTER_ACCEPT: |
| return _tempCurrent; |
| break; |
| |
| case NodeFilter::FILTER_SKIP: |
| { |
| Node nskip = getLastChild(_tempCurrent); |
| if( !nskip.isNull() ) |
| return nskip; |
| return getPreviousSibling(_tempCurrent); |
| break; |
| } |
| |
| case NodeFilter::FILTER_REJECT: |
| return getPreviousSibling(_tempCurrent); |
| break; |
| } |
| } |
| // now the case if we don't have previous sibling |
| else |
| { |
| _tempCurrent = _tempCurrent.parentNode(); |
| if(_tempCurrent.isNull() || _tempCurrent == rootNode) |
| return Node(); |
| _result = isAccepted(_tempCurrent); |
| if(_result == NodeFilter::FILTER_SKIP) |
| return getPreviousSibling(_tempCurrent); |
| |
| return Node(); |
| |
| } |
| return Node(); // should never get here! |
| } |
| |
| Node TreeWalkerImpl::getNextSibling(Node n) |
| { |
| Node _tempCurrent; |
| short _result; |
| |
| if( n.isNull() || _tempCurrent == rootNode) |
| return Node(); |
| |
| _tempCurrent = n.nextSibling(); |
| if( !_tempCurrent.isNull() ) |
| { |
| _result = isAccepted(_tempCurrent); |
| switch(_result) |
| { |
| case NodeFilter::FILTER_ACCEPT: |
| return _tempCurrent; |
| break; |
| |
| case NodeFilter::FILTER_SKIP: |
| { |
| Node nskip = getFirstChild(_tempCurrent); |
| if( !nskip.isNull() ) |
| return nskip; |
| return getNextSibling(_tempCurrent); |
| break; |
| } |
| |
| case NodeFilter::FILTER_REJECT: |
| return getNextSibling(_tempCurrent); |
| break; |
| } |
| } |
| else |
| { |
| _tempCurrent = _tempCurrent.parentNode(); |
| if(_tempCurrent.isNull() || _tempCurrent == rootNode) |
| return Node(); |
| _result = isAccepted(_tempCurrent); |
| if(_result == NodeFilter::FILTER_SKIP) |
| return getNextSibling(_tempCurrent); |
| |
| return Node(); |
| } |
| return Node(); |
| } |
| |