/* | |
* Copyright (C) 1999 Lars Knoll (knoll@kde.org) | |
* (C) 1999 Antti Koivisto (koivisto@kde.org) | |
* (C) 2001 Dirk Mueller (mueller@kde.org) | |
* Copyright (C) 2004, 2006, 2007, 2008 Apple Inc. All rights reserved. | |
* | |
* 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 "config.h" | |
#include "DynamicNodeList.h" | |
#include "Document.h" | |
#include "Element.h" | |
namespace WebCore { | |
DynamicNodeList::DynamicNodeList(PassRefPtr<Node> rootNode) | |
: m_rootNode(rootNode) | |
, m_caches(Caches::create()) | |
, m_ownsCaches(true) | |
{ | |
m_rootNode->registerDynamicNodeList(this); | |
} | |
DynamicNodeList::DynamicNodeList(PassRefPtr<Node> rootNode, DynamicNodeList::Caches* caches) | |
: m_rootNode(rootNode) | |
, m_caches(caches) | |
, m_ownsCaches(false) | |
{ | |
m_rootNode->registerDynamicNodeList(this); | |
} | |
DynamicNodeList::~DynamicNodeList() | |
{ | |
m_rootNode->unregisterDynamicNodeList(this); | |
} | |
unsigned DynamicNodeList::length() const | |
{ | |
if (m_caches->isLengthCacheValid) | |
return m_caches->cachedLength; | |
unsigned length = 0; | |
for (Node* n = m_rootNode->firstChild(); n; n = n->traverseNextNode(m_rootNode.get())) | |
length += n->isElementNode() && nodeMatches(static_cast<Element*>(n)); | |
m_caches->cachedLength = length; | |
m_caches->isLengthCacheValid = true; | |
return length; | |
} | |
Node* DynamicNodeList::itemForwardsFromCurrent(Node* start, unsigned offset, int remainingOffset) const | |
{ | |
ASSERT(remainingOffset >= 0); | |
for (Node* n = start; n; n = n->traverseNextNode(m_rootNode.get())) { | |
if (n->isElementNode() && nodeMatches(static_cast<Element*>(n))) { | |
if (!remainingOffset) { | |
m_caches->lastItem = n; | |
m_caches->lastItemOffset = offset; | |
m_caches->isItemCacheValid = true; | |
return n; | |
} | |
--remainingOffset; | |
} | |
} | |
return 0; // no matching node in this subtree | |
} | |
Node* DynamicNodeList::itemBackwardsFromCurrent(Node* start, unsigned offset, int remainingOffset) const | |
{ | |
ASSERT(remainingOffset < 0); | |
for (Node* n = start; n; n = n->traversePreviousNode(m_rootNode.get())) { | |
if (n->isElementNode() && nodeMatches(static_cast<Element*>(n))) { | |
if (!remainingOffset) { | |
m_caches->lastItem = n; | |
m_caches->lastItemOffset = offset; | |
m_caches->isItemCacheValid = true; | |
return n; | |
} | |
++remainingOffset; | |
} | |
} | |
return 0; // no matching node in this subtree | |
} | |
Node* DynamicNodeList::item(unsigned offset) const | |
{ | |
int remainingOffset = offset; | |
Node* start = m_rootNode->firstChild(); | |
if (m_caches->isItemCacheValid) { | |
if (offset == m_caches->lastItemOffset) | |
return m_caches->lastItem; | |
else if (offset > m_caches->lastItemOffset || m_caches->lastItemOffset - offset < offset) { | |
start = m_caches->lastItem; | |
remainingOffset -= m_caches->lastItemOffset; | |
} | |
} | |
if (remainingOffset < 0) | |
return itemBackwardsFromCurrent(start, offset, remainingOffset); | |
return itemForwardsFromCurrent(start, offset, remainingOffset); | |
} | |
Node* DynamicNodeList::itemWithName(const AtomicString& elementId) const | |
{ | |
if (m_rootNode->isDocumentNode() || m_rootNode->inDocument()) { | |
Element* node = m_rootNode->document()->getElementById(elementId); | |
if (node && nodeMatches(node)) { | |
for (Node* p = node->parentNode(); p; p = p->parentNode()) { | |
if (p == m_rootNode) | |
return node; | |
} | |
} | |
if (!node) | |
return 0; | |
// In the case of multiple nodes with the same name, just fall through. | |
} | |
unsigned length = this->length(); | |
for (unsigned i = 0; i < length; i++) { | |
Node* node = item(i); | |
if (node->isElementNode() && static_cast<Element*>(node)->getIDAttribute() == elementId) | |
return node; | |
} | |
return 0; | |
} | |
void DynamicNodeList::invalidateCache() | |
{ | |
// This should only be called for node lists that own their own caches. | |
ASSERT(m_ownsCaches); | |
m_caches->reset(); | |
} | |
DynamicNodeList::Caches::Caches() | |
: lastItem(0) | |
, isLengthCacheValid(false) | |
, isItemCacheValid(false) | |
{ | |
} | |
PassRefPtr<DynamicNodeList::Caches> DynamicNodeList::Caches::create() | |
{ | |
return adoptRef(new Caches()); | |
} | |
void DynamicNodeList::Caches::reset() | |
{ | |
lastItem = 0; | |
isLengthCacheValid = false; | |
isItemCacheValid = false; | |
} | |
} // namespace WebCore |