mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-24 10:52:49 +00:00
402 lines
13 KiB
C++
402 lines
13 KiB
C++
/*
|
|
* This file is part of the DOM implementation for KDE.
|
|
*
|
|
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
|
|
* (C) 1999 Antti Koivisto (koivisto@kde.org)
|
|
* (C) 2001 Dirk Mueller (mueller@kde.org)
|
|
* (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
|
|
* (C) 2005, 2009, 2010 Maksim Orlovich (maksim@kde.org)
|
|
* (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com)
|
|
* (C) 2007 David Smith (catfish.man@gmail.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.
|
|
*
|
|
*
|
|
* The code for ClassNodeListImpl was originally licensed under the following terms
|
|
* (but in this version is available only as above):
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
|
|
* its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "dom_nodelistimpl.h"
|
|
#include "dom_nodeimpl.h"
|
|
#include "dom_docimpl.h"
|
|
#include <kdebug.h>
|
|
|
|
using namespace DOM;
|
|
using namespace khtml;
|
|
|
|
NodeImpl* DynamicNodeListImpl::item( unsigned long index ) const
|
|
{
|
|
unsigned long requestIndex = index;
|
|
|
|
m_cache->updateNodeListInfo(m_refNode->document());
|
|
|
|
NodeImpl* n = 0;
|
|
bool usedCache = false;
|
|
if (m_cache->current.node) {
|
|
//Compute distance from the requested index to the cache node
|
|
unsigned long cacheDist = qAbs(long(index) - long(m_cache->position));
|
|
|
|
if (cacheDist < (unsigned long)index) { //Closer to the cached position
|
|
usedCache = true;
|
|
if (index >= m_cache->position) { //Go ahead
|
|
unsigned long relIndex = index - m_cache->position;
|
|
n = recursiveItem(m_refNode, m_cache->current.node, relIndex);
|
|
} else { //Go backwards
|
|
unsigned long relIndex = m_cache->position - index;
|
|
n = recursiveItemBack(m_refNode, m_cache->current.node, relIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!usedCache)
|
|
n = recursiveItem(m_refNode, m_refNode->firstChild(), index);
|
|
|
|
//We always update the cache state, to make starting iteration
|
|
//where it was left off easy.
|
|
m_cache->current.node = n;
|
|
m_cache->position = requestIndex;
|
|
return n;
|
|
}
|
|
|
|
unsigned long DynamicNodeListImpl::length() const
|
|
{
|
|
m_cache->updateNodeListInfo(m_refNode->document());
|
|
if (!m_cache->hasLength) {
|
|
m_cache->length = calcLength( m_refNode );
|
|
m_cache->hasLength = true;
|
|
}
|
|
return m_cache->length;
|
|
}
|
|
|
|
unsigned long DynamicNodeListImpl::calcLength(NodeImpl *start) const
|
|
{
|
|
unsigned long len = 0;
|
|
for(NodeImpl *n = start->firstChild(); n != 0; n = n->nextSibling()) {
|
|
bool recurse = true;
|
|
if (nodeMatches(n, recurse))
|
|
len++;
|
|
if (recurse)
|
|
len+= DynamicNodeListImpl::calcLength(n);
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
DynamicNodeListImpl::DynamicNodeListImpl( NodeImpl *n, int type, CacheFactory* factory )
|
|
{
|
|
m_refNode = n;
|
|
m_refNode->ref();
|
|
|
|
m_cache = m_refNode->document()->acquireCachedNodeListInfo(
|
|
factory, n, type );
|
|
}
|
|
|
|
DynamicNodeListImpl::~DynamicNodeListImpl()
|
|
{
|
|
m_refNode->document()->releaseCachedNodeListInfo(m_cache);
|
|
m_refNode->deref();
|
|
}
|
|
|
|
/**
|
|
Next item in the pre-order walk of tree from node, but not going outside
|
|
absStart
|
|
*/
|
|
static NodeImpl* helperNext(NodeImpl* node, NodeImpl* absStart)
|
|
{
|
|
//Walk up until we wind a sibling to go to.
|
|
while (!node->nextSibling() && node != absStart)
|
|
node = node->parentNode();
|
|
|
|
if (node != absStart)
|
|
return node->nextSibling();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
NodeImpl *DynamicNodeListImpl::recursiveItem ( NodeImpl* absStart, NodeImpl *start, unsigned long &offset ) const
|
|
{
|
|
for(NodeImpl *n = start; n != 0; n = helperNext(n, absStart)) {
|
|
bool recurse = true;
|
|
if (nodeMatches(n, recurse))
|
|
if (!offset--)
|
|
return n;
|
|
|
|
NodeImpl *depthSearch = recurse ? recursiveItem(n, n->firstChild(), offset) : 0;
|
|
if (depthSearch)
|
|
return depthSearch;
|
|
}
|
|
|
|
return 0; // no matching node in this subtree
|
|
}
|
|
|
|
NodeImpl *DynamicNodeListImpl::recursiveItemBack ( NodeImpl* absStart, NodeImpl *start, unsigned long &offset ) const
|
|
{
|
|
//### it might be cleaner/faster to split nodeMatches and recursion
|
|
//filtering.
|
|
bool dummy = true;
|
|
NodeImpl* n = start;
|
|
|
|
do {
|
|
bool recurse = true;
|
|
|
|
//Check whether the current node matches.
|
|
if (nodeMatches(n, dummy))
|
|
if (!offset--)
|
|
return n;
|
|
|
|
if (n->previousSibling()) {
|
|
//Move to the last node of this whole subtree that we should recurse into
|
|
n = n->previousSibling();
|
|
recurse = true;
|
|
|
|
while (n->lastChild()) {
|
|
(void)nodeMatches(n, recurse);
|
|
if (!recurse)
|
|
break; //Don't go there
|
|
n = n->lastChild();
|
|
}
|
|
} else {
|
|
//We're done with this whole subtree, so move up
|
|
n = n->parentNode();
|
|
}
|
|
}
|
|
while (n && n != absStart);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
DynamicNodeListImpl::Cache* DynamicNodeListImpl::Cache::makeStructuralOnly()
|
|
{
|
|
return new Cache(DocumentImpl::TV_Structural); // will check the same ver twice
|
|
}
|
|
|
|
DynamicNodeListImpl::Cache* DynamicNodeListImpl::Cache::makeNameOrID()
|
|
{
|
|
return new Cache(DocumentImpl::TV_IDNameHref);
|
|
}
|
|
|
|
DynamicNodeListImpl::Cache* DynamicNodeListImpl::Cache::makeClassName()
|
|
{
|
|
return new Cache(DocumentImpl::TV_Class);
|
|
}
|
|
|
|
DynamicNodeListImpl::Cache::Cache(unsigned short relSecondaryVer):relevantSecondaryVer(relSecondaryVer)
|
|
{}
|
|
|
|
DynamicNodeListImpl::Cache::~Cache()
|
|
{}
|
|
|
|
void DynamicNodeListImpl::Cache::clear(DocumentImpl* doc)
|
|
{
|
|
hasLength = false;
|
|
current.node = 0;
|
|
version = doc->domTreeVersion(DocumentImpl::TV_Structural);
|
|
secondaryVersion = doc->domTreeVersion(relevantSecondaryVer);
|
|
}
|
|
|
|
void DynamicNodeListImpl::Cache::updateNodeListInfo(DocumentImpl* doc)
|
|
{
|
|
//If version doesn't match, clear
|
|
if (doc->domTreeVersion(DocumentImpl::TV_Structural) != version ||
|
|
doc->domTreeVersion(relevantSecondaryVer) != secondaryVersion)
|
|
clear(doc);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
ChildNodeListImpl::ChildNodeListImpl( NodeImpl *n ): DynamicNodeListImpl(n, CHILD_NODES, DynamicNodeListImpl::Cache::makeStructuralOnly)
|
|
{}
|
|
|
|
bool ChildNodeListImpl::nodeMatches( NodeImpl* /*testNode*/, bool& doRecurse ) const
|
|
{
|
|
doRecurse = false;
|
|
return true;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
TagNodeListImpl::TagNodeListImpl(NodeImpl *n, NamespaceName namespaceName, LocalName localName, PrefixName prefix)
|
|
: DynamicNodeListImpl(n, UNCACHEABLE, DynamicNodeListImpl::Cache::makeStructuralOnly),
|
|
m_namespaceAware(false)
|
|
{
|
|
m_namespace = namespaceName;
|
|
m_localName = localName;
|
|
m_prefix = prefix;
|
|
}
|
|
|
|
TagNodeListImpl::TagNodeListImpl( NodeImpl *n, const DOMString &namespaceURI, const DOMString &localName )
|
|
: DynamicNodeListImpl(n, UNCACHEABLE, DynamicNodeListImpl::Cache::makeStructuralOnly),
|
|
m_namespaceAware(true)
|
|
{
|
|
if (namespaceURI == "*")
|
|
m_namespace = NamespaceName::fromId(anyNamespace);
|
|
else
|
|
m_namespace = NamespaceName::fromString(namespaceURI);
|
|
if (localName == "*")
|
|
m_localName = LocalName::fromId(anyLocalName);
|
|
else
|
|
m_localName = LocalName::fromString(localName);
|
|
m_prefix = PrefixName::fromId(emptyPrefix);
|
|
}
|
|
|
|
|
|
bool TagNodeListImpl::nodeMatches(NodeImpl *testNode, bool& /*doRecurse*/) const
|
|
{
|
|
if (testNode->nodeType() != Node::ELEMENT_NODE)
|
|
return false;
|
|
|
|
if (m_namespaceAware) {
|
|
return (m_namespace.id() == anyNamespace || m_namespace.id() == namespacePart(testNode->id())) &&
|
|
(m_localName.id() == anyLocalName || m_localName.id() == localNamePart(testNode->id()));
|
|
} else {
|
|
return (m_localName.id() == anyLocalName) || (m_localName.id() == localNamePart(testNode->id()) &&
|
|
m_prefix == static_cast<ElementImpl*>(testNode)->prefixName());
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
NameNodeListImpl::NameNodeListImpl(NodeImpl *n, const DOMString &t )
|
|
: DynamicNodeListImpl(n, UNCACHEABLE, DynamicNodeListImpl::Cache::makeNameOrID),
|
|
nodeName(t)
|
|
{}
|
|
|
|
bool NameNodeListImpl::nodeMatches( NodeImpl *testNode, bool& /*doRecurse*/ ) const
|
|
{
|
|
if ( testNode->nodeType() != Node::ELEMENT_NODE ) return false;
|
|
return static_cast<ElementImpl *>(testNode)->getAttribute(ATTR_NAME) == nodeName;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
ClassNodeListImpl::ClassNodeListImpl(NodeImpl* rootNode, const DOMString& classNames)
|
|
: DynamicNodeListImpl(rootNode, UNCACHEABLE, DynamicNodeListImpl::Cache::makeClassName)
|
|
{
|
|
m_classNames.parseClassAttribute(classNames, m_refNode->document()->inCompatMode());
|
|
}
|
|
|
|
bool ClassNodeListImpl::nodeMatches(NodeImpl *testNode, bool& doRecurse) const
|
|
{
|
|
if (!testNode->isElementNode()) {
|
|
doRecurse = false;
|
|
return false;
|
|
}
|
|
|
|
if (!testNode->hasClass())
|
|
return false;
|
|
|
|
if (!m_classNames.size())
|
|
return false;
|
|
|
|
const ClassNames& classes = static_cast<ElementImpl*>(testNode)->classNames();
|
|
for (size_t i = 0; i < m_classNames.size(); ++i) {
|
|
if (!classes.contains(m_classNames[i]))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
StaticNodeListImpl::StaticNodeListImpl():
|
|
m_knownNormalization(DocumentOrder) // true vacuously
|
|
{}
|
|
|
|
StaticNodeListImpl::~StaticNodeListImpl() {}
|
|
|
|
void StaticNodeListImpl::append(NodeImpl* n)
|
|
{
|
|
assert(n);
|
|
m_kids.append(n);
|
|
m_knownNormalization = Unnormalized;
|
|
}
|
|
|
|
NodeImpl* StaticNodeListImpl::item ( unsigned long index ) const
|
|
{
|
|
return index < m_kids.size() ? m_kids[index].get() : 0;
|
|
}
|
|
|
|
unsigned long StaticNodeListImpl::length() const
|
|
{
|
|
return m_kids.size();
|
|
}
|
|
|
|
static bool nodeLess(const SharedPtr<NodeImpl>& n1, const SharedPtr<DOM::NodeImpl>& n2)
|
|
{
|
|
return n1->compareDocumentPosition(n2.get()) & Node::DOCUMENT_POSITION_FOLLOWING;
|
|
}
|
|
|
|
void StaticNodeListImpl::normalizeUpto(NormalizationKind kind)
|
|
{
|
|
if (m_knownNormalization == kind || m_knownNormalization == DocumentOrder)
|
|
return;
|
|
|
|
if (kind == Unnormalized)
|
|
return;
|
|
|
|
// First sort.
|
|
qSort(m_kids.begin(), m_kids.end(), nodeLess);
|
|
|
|
// Now get rid of dupes.
|
|
DOM::NodeImpl* last = 0;
|
|
unsigned out = 0;
|
|
for (unsigned in = 0; in < m_kids.size(); ++in) {
|
|
DOM::NodeImpl* cur = m_kids[in].get();
|
|
if (cur != last) {
|
|
m_kids[out] = cur;
|
|
++out;
|
|
}
|
|
|
|
last = cur;
|
|
}
|
|
m_kids.resize(out);
|
|
|
|
m_knownNormalization = DocumentOrder;
|
|
}
|
|
|
|
void StaticNodeListImpl::setKnownNormalization(NormalizationKind kind)
|
|
{
|
|
m_knownNormalization = kind;
|
|
}
|
|
|
|
StaticNodeListImpl::NormalizationKind StaticNodeListImpl::knownNormalization() const
|
|
{
|
|
return m_knownNormalization;
|
|
}
|
|
|
|
// kate: indent-width 4; replace-tabs on; tab-width 4; space-indent on;
|