You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdelibs/tdehtml/xml/dom2_traversalimpl.cpp

668 lines
14 KiB

/**
* 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;
}