/** * 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., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "dom/dom_exception.h" #include "xml/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; m_doc = m_root->getDocument(); 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( m_root == n) return 0; if( n->nextSibling() ) return n->nextSibling(); NodeImpl *parent = n->parentNode(); while( parent ) { if( m_root == parent ) return 0; n = parent->nextSibling(); if( n ) return n; 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 || m_root == n ) return 0; _tempCurrent = n->previousSibling(); if( _tempCurrent ) { if( _tempCurrent->lastChild() ) { while( _tempCurrent->lastChild() ) _tempCurrent = _tempCurrent->lastChild(); return _tempCurrent; } else return _tempCurrent; } 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(NodeImpl *n, long _whatToShow, NodeFilterImpl *f, bool entityReferenceExpansion) { m_currentNode = n; m_rootNode = n; m_whatToShow = _whatToShow; m_filter = f; if ( m_filter ) m_filter->ref(); m_expandEntityReferences = entityReferenceExpansion; m_doc = m_rootNode->getDocument(); m_doc->ref(); } TreeWalkerImpl::~TreeWalkerImpl() { m_doc->deref(); if ( m_filter ) m_filter->deref(); } NodeImpl *TreeWalkerImpl::getRoot() const { return m_rootNode; } unsigned long TreeWalkerImpl::getWhatToShow() const { return m_whatToShow; } NodeFilterImpl *TreeWalkerImpl::getFilter() const { return m_filter; } bool TreeWalkerImpl::getExpandEntityReferences() const { return m_expandEntityReferences; } NodeImpl *TreeWalkerImpl::getCurrentNode() const { return m_currentNode; } void TreeWalkerImpl::setWhatToShow(long _whatToShow) { // do some testing wether this is an accepted value m_whatToShow = _whatToShow; } void TreeWalkerImpl::setFilter(NodeFilterImpl *_filter) { m_filter->deref(); m_filter = _filter; if ( m_filter ) m_filter->ref(); } void TreeWalkerImpl::setExpandEntityReferences(bool value) { m_expandEntityReferences = value; } void TreeWalkerImpl::setCurrentNode( NodeImpl *n ) { if ( n ) { //m_rootNode = n; m_currentNode = n; } // else // throw( DOMException::NOT_SUPPORTED_ERR ); } NodeImpl *TreeWalkerImpl::parentNode( ) { NodeImpl *n = getParentNode( m_currentNode ); if ( n ) m_currentNode = n; return n; } NodeImpl *TreeWalkerImpl::firstChild( ) { NodeImpl *n = getFirstChild( m_currentNode ); if ( n ) m_currentNode = n; return n; } NodeImpl *TreeWalkerImpl::lastChild( ) { NodeImpl *n = getLastChild(m_currentNode); if( n ) m_currentNode = n; return n; } NodeImpl *TreeWalkerImpl::previousSibling( ) { NodeImpl *n = getPreviousSibling( m_currentNode ); if( n ) m_currentNode = n; return n; } NodeImpl *TreeWalkerImpl::nextSibling( ) { NodeImpl *n = getNextSibling( m_currentNode ); if( n ) m_currentNode = n; return n; } NodeImpl *TreeWalkerImpl::previousNode( ) { /* 1. my previous sibling.lastchild * 2. my previous sibling * 3. my parent */ NodeImpl *n = getPreviousSibling( m_currentNode ); if( !n ) { n = getParentNode( m_currentNode ); if( n ) //parent { m_currentNode = n; return m_currentNode; } else // parent failed.. no previous node return 0; } NodeImpl *child = getLastChild( n ); if( child ) // previous siblings last child { m_currentNode = child; return m_currentNode; } else // previous sibling { m_currentNode = n; return m_currentNode; } return 0; // should never get here! } NodeImpl *TreeWalkerImpl::nextNode( ) { /* 1. my first child * 2. my next sibling * 3. my parents sibling, or their parents sibling (loop) * 4. not found */ NodeImpl *n = getFirstChild( m_currentNode ); if( n ) // my first child { m_currentNode = n; return n; } n = getNextSibling( m_currentNode ); // my next sibling if( n ) { m_currentNode = n; return m_currentNode; } NodeImpl *parent = getParentNode( m_currentNode ); while( parent ) // parents sibling { n = getNextSibling( parent ); if( n ) { m_currentNode = n; return m_currentNode; } else parent = getParentNode( parent ); } return 0; } short TreeWalkerImpl::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) return m_filter->acceptNode(n); else return NodeFilter::FILTER_ACCEPT; } return NodeFilter::FILTER_SKIP; } NodeImpl *TreeWalkerImpl::getParentNode(NodeImpl *n) { short _result = NodeFilter::FILTER_ACCEPT; if( n == m_rootNode /*|| !n*/ ) return 0; NodeImpl *_tempCurrent = n->parentNode(); if( !_tempCurrent ) return 0; _result = isAccepted( _tempCurrent ); if ( _result == NodeFilter::FILTER_ACCEPT ) return _tempCurrent; // match found return getParentNode( _tempCurrent ); } NodeImpl *TreeWalkerImpl::getFirstChild(NodeImpl *n) { short _result; if( !n || !n->firstChild() ) return 0; 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 0; // should never get here! } NodeImpl *TreeWalkerImpl::getLastChild(NodeImpl *n) { short _result; if( !n || !n->lastChild() ) return 0; 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 0; } NodeImpl *TreeWalkerImpl::getPreviousSibling(NodeImpl *n) { short _result; NodeImpl *_tempCurrent; if( !n ) return 0; //first the cases if we have a previousSibling _tempCurrent = n->previousSibling(); if( _tempCurrent ) { _result = isAccepted( _tempCurrent ); switch ( _result ) { case NodeFilter::FILTER_ACCEPT: return _tempCurrent; break; case NodeFilter::FILTER_SKIP: { NodeImpl *nskip = getLastChild( _tempCurrent ); if( nskip ) 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 = n->parentNode(); if( !_tempCurrent || _tempCurrent == m_rootNode) return 0; _result = isAccepted( _tempCurrent ); if ( _result == NodeFilter::FILTER_SKIP ) return getPreviousSibling( _tempCurrent ); return 0; } return 0; // should never get here! } NodeImpl *TreeWalkerImpl::getNextSibling(NodeImpl *n) { NodeImpl *_tempCurrent = 0; short _result; if( !n ) return 0; _tempCurrent = n->nextSibling(); if( _tempCurrent ) { _result = isAccepted( _tempCurrent ); switch ( _result ) { case NodeFilter::FILTER_ACCEPT: return _tempCurrent; break; case NodeFilter::FILTER_SKIP: { NodeImpl *nskip = getFirstChild( _tempCurrent ); if( nskip ) return nskip; return getNextSibling( _tempCurrent ); break; } case NodeFilter::FILTER_REJECT: return getNextSibling( _tempCurrent ); break; } } else { _tempCurrent = n->parentNode(); if( !_tempCurrent || _tempCurrent == m_rootNode) return 0; _result = isAccepted( _tempCurrent ); if( _result == NodeFilter::FILTER_SKIP ) return getNextSibling( _tempCurrent ); return 0; } return 0; }