blob: bd915210d2e9154d80d48671cc2f02251d16ad8b [file] [log] [blame]
/**
* 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)
*
* 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$
*/
//#define EVENT_DEBUG
#include "dom_elementimpl.h"
#include "dom_exception.h"
#include "dom_node.h"
#include "dom_textimpl.h"
#include "dom_docimpl.h"
#include "dom2_eventsimpl.h"
#include "dtd.h"
#include "css/cssstyleselector.h"
#include "rendering/render_object.h"
#include "misc/htmlhashes.h"
#include <kdebug.h>
#include "css_valueimpl.h"
#include "css_stylesheetimpl.h"
#include "html/htmlparser.h"
using namespace DOM;
using namespace khtml;
/*
* NOTE:
* According to the DOM docs, an Attr stores the value directly in it's parsed
* form, but for values containing entities, creates a subtree with nodes
* containing the unexpanded form (for XML). On read, the value is always
* returned entity-free, so we decided for HTML we could only store a
* parsed DOMString and have no child-nodes.
*/
AttrImpl::AttrImpl()
: NodeImpl(0),
_name( 0 ),
_value( 0 ),
_element(0),
attrId(0)
{
}
AttrImpl::AttrImpl(DocumentPtr *doc, const DOMString &name)
: NodeImpl(doc),
_name( 0 ),
_value( 0 ),
_element(0),
attrId(0)
{
setName(name);
}
AttrImpl::AttrImpl(DocumentPtr *doc, int id)
: NodeImpl(doc),
_name( 0 ),
_value( 0 ),
_element(0),
attrId(id)
{
}
AttrImpl::AttrImpl(const AttrImpl &other) : NodeImpl(other.docPtr())
{
m_specified = other.specified();
_element = other._element;
_name = other._name;
if (_name) _name->ref();
_value = other._value;
if (_value) _value->ref();
attrId = other.attrId;
}
AttrImpl &AttrImpl::operator = (const AttrImpl &other)
{
NodeImpl::operator =(other);
m_specified = other.specified();
_element = other._element;
if (_name) _name->deref();
_name = other._name;
if (_name) _name->ref();
if (_value) _value->deref();
_value = other._value;
if (_value) _value->ref();
attrId = other.attrId;
return *this;
}
AttrImpl::~AttrImpl()
{
if(_name) _name->deref();
if(_value) _value->deref();
}
const DOMString AttrImpl::nodeName() const
{
return name();
}
unsigned short AttrImpl::nodeType() const
{
return Node::ATTRIBUTE_NODE;
}
DOMString AttrImpl::name() const
{
if(attrId)
return getAttrName(attrId);
else if (_name)
return _name;
else
return DOMString();
}
void AttrImpl::setName(const DOMString &n)
{
if(_name) _name->deref();
_name = n.implementation();
if(!_name) return;
attrId = khtml::getAttrID(QConstString(_name->s, _name->l).string().lower().ascii(), _name->l);
if (attrId)
_name = 0;
else
_name->ref();
}
DOMString AttrImpl::value() const {
return _value;
}
void AttrImpl::setValue( const DOMString &v )
{
// according to the DOM docs, we should create an unparsed Text child
// node here; we decided this was not necessary for HTML
// ### TODO: parse value string, interprete entities (not sure if we are supposed to do this)
if (_element)
_element->checkReadOnly();
DOMStringImpl *prevValue = _value;
_value = v.implementation();
if (_value) _value->ref();
m_specified = true;
if (_element) {
_element->parseAttribute(this);
_element->setChanged(true);
if (getDocument()->hasListenerType(DocumentImpl::DOMATTRMODIFIED_LISTENER)) {
int exceptioncode;
_element->dispatchEvent(new MutationEventImpl(EventImpl::DOMATTRMODIFIED_EVENT,true,false,this,prevValue,
_value,_name,MutationEvent::MODIFICATION),exceptioncode);
}
}
if (prevValue) prevValue->deref();
}
void AttrImpl::setNodeValue( const DOMString &v, int &exceptioncode )
{
exceptioncode = 0;
setValue(v);
}
AttrImpl::AttrImpl(const DOMString &name, const DOMString &value, DocumentPtr *doc)
: NodeImpl(doc)
{
attrId = 0;
_name = 0;
setName(name);
_value = value.implementation();
if (_value) _value->ref();
_element = 0;
m_specified = 1;
}
AttrImpl::AttrImpl(int _id, const DOMString &value, DocumentPtr *doc) : NodeImpl(doc)
{
attrId = _id;
_name = 0;
_value = value.implementation();
if (_value) _value->ref();
_element = 0;
m_specified = false;
}
NodeImpl *AttrImpl::parentNode() const
{
return 0;
}
NodeImpl *AttrImpl::previousSibling() const
{
return 0;
}
NodeImpl *AttrImpl::nextSibling() const
{
return 0;
}
NodeImpl *AttrImpl::cloneNode ( bool /*deep*/, int &/*exceptioncode*/ )
{
AttrImpl *newImpl = new AttrImpl(*this);
newImpl->_element = 0; // can't have two attributes with the same name/id attached to an element
return newImpl;
}
bool AttrImpl::deleteMe()
{
if(!_element && _ref <= 0) return true;
return false;
}
// DOM Section 1.1.1
bool AttrImpl::childAllowed( NodeImpl *newChild )
{
if(!newChild)
return false;
return childTypeAllowed(newChild->nodeType());
}
bool AttrImpl::childTypeAllowed( unsigned short type )
{
switch (type) {
case Node::TEXT_NODE:
case Node::ENTITY_REFERENCE_NODE:
return true;
break;
default:
return false;
}
}
// -------------------------------------------------------------------------
ElementImpl::ElementImpl(DocumentPtr *doc) : NodeBaseImpl(doc)
{
namedAttrMap = 0;
has_tabindex=false;
tabindex=0;
m_styleDecls = 0;
}
ElementImpl::~ElementImpl()
{
if (m_render)
detach();
if(namedAttrMap) {
namedAttrMap->detachFromElement();
namedAttrMap->deref();
}
if (m_styleDecls) {
m_styleDecls->setNode(0);
m_styleDecls->setParent(0);
m_styleDecls->deref();
}
}
bool ElementImpl::isInline() const
{
if(!m_style) return false;
return (m_style->display() == khtml::INLINE);
}
unsigned short ElementImpl::nodeType() const
{
return Node::ELEMENT_NODE;
}
DOMString ElementImpl::tagName() const
{
return nodeName();
}
DOMString ElementImpl::getAttribute( const DOMString &name ) const
{
// search in already set attributes first
int exceptioncode; // ### propogate
if(!namedAttrMap) return 0;
AttrImpl *attr = static_cast<AttrImpl*>(namedAttrMap->getNamedItem(name,exceptioncode));
if (attr) return attr->value();
// then search in default attr in case it is not yet set
NamedAttrMapImpl* dm = defaultMap();
if(!dm) return 0;
AttrImpl* defattr = static_cast<AttrImpl*>(dm->getNamedItem(name, exceptioncode));
if(!defattr || exceptioncode) return 0;
return defattr->value();
}
DOMString ElementImpl::getAttribute( int id )
{
// search in already set attributes first
if(!namedAttrMap) return DOMString();
AttrImpl *attr = static_cast<AttrImpl*>(namedAttrMap->getIdItem(id));
if (attr) return attr->value();
// then search in default attr in case it is not yet set
NamedAttrMapImpl* dm = defaultMap();
if(!dm) return 0;
AttrImpl* defattr = static_cast<AttrImpl*>(dm->getIdItem(id));
if(!defattr) return DOMString();
return defattr->value();
}
AttrImpl *ElementImpl::getAttributeNode ( int index ) const
{
return namedAttrMap ? namedAttrMap->getIdItem(index) : 0;
}
int ElementImpl::getAttributeCount() const
{
int exceptioncode; // ### propogate
return namedAttrMap ? namedAttrMap->length(exceptioncode) : 0;
}
void ElementImpl::setAttribute( const DOMString &name, const DOMString &value)
{
// ### check for invalid characters in value -> throw exception
int exceptioncode; // ### propogate
if(!namedAttrMap) {
namedAttrMap = new NamedAttrMapImpl(this);
namedAttrMap->ref();
}
if (value.isNull())
namedAttrMap->removeNamedItem(name,exceptioncode);
else {
AttrImpl *a = static_cast<AttrImpl*>(namedAttrMap->getNamedItem(name));
if (a)
a->setValue(value);
else
namedAttrMap->setNamedItem(new AttrImpl(name,value,docPtr()), exceptioncode);
}
}
void ElementImpl::setAttribute( int id, const DOMString &value )
{
if(!namedAttrMap) {
namedAttrMap = new NamedAttrMapImpl(this);
namedAttrMap->ref();
}
if (value.isNull())
namedAttrMap->removeIdItem(id);
else {
int exceptioncode;
AttrImpl* a = static_cast<AttrImpl*>(namedAttrMap->getIdItem(id));
if(a)
a->setValue(value);
else
namedAttrMap->setIdItem(new AttrImpl(id,value,docPtr() ), exceptioncode );
}
}
void ElementImpl::setAttributeMap( NamedAttrMapImpl* list )
{
if(namedAttrMap)
namedAttrMap->deref();
namedAttrMap = list;
if(namedAttrMap) {
namedAttrMap->ref();
namedAttrMap->element = this;
unsigned int len = namedAttrMap->length();
for(unsigned int i = 0; i < len; i++) {
AttrImpl* a = namedAttrMap->attrs[i];
if(a && !a->_element) {
a->_element = this;
parseAttribute(a);
}
}
}
}
void ElementImpl::removeAttribute( const DOMString &name )
{
int exceptioncode; // ### propogate
if(!namedAttrMap) return;
namedAttrMap->removeNamedItem(name,exceptioncode);
}
NodeImpl *ElementImpl::cloneNode ( bool deep, int &exceptioncode )
{
ElementImpl *newImpl = ownerDocument()->createElement(tagName());
if (!newImpl)
return 0;
// clone attributes
if(namedAttrMap)
*(static_cast<NamedAttrMapImpl*>(newImpl->attributes())) = *namedAttrMap;
if (deep)
cloneChildNodes(newImpl,exceptioncode);
return newImpl;
}
NamedNodeMapImpl *ElementImpl::attributes()
{
if(!namedAttrMap) {
namedAttrMap = new NamedAttrMapImpl(this);
namedAttrMap->ref();
}
return namedAttrMap;
}
AttrImpl *ElementImpl::getAttributeNode( const DOMString &name )
{
int exceptioncode; // ### propogate
// ### do we return attribute node if it is in the default map but not specified?
if(!namedAttrMap) return 0;
return static_cast<AttrImpl*>(namedAttrMap->getNamedItem(name,exceptioncode));
}
Attr ElementImpl::setAttributeNode( AttrImpl *newAttr, int &exceptioncode )
{
exceptioncode = 0;
if (!newAttr) {
exceptioncode = DOMException::NOT_FOUND_ERR;
return 0;
}
if(!namedAttrMap) {
namedAttrMap = new NamedAttrMapImpl(this);
namedAttrMap->ref();
}
if (newAttr->attrId)
return namedAttrMap->setIdItem(newAttr, exceptioncode);
else
return namedAttrMap->setNamedItem(newAttr, exceptioncode);
}
Attr ElementImpl::removeAttributeNode( AttrImpl *oldAttr, int &exceptioncode )
{
// ### should we replace with default in map? currently default attrs don't exist in map
exceptioncode = 0;
if(!namedAttrMap) return 0;
return namedAttrMap->removeAttr(oldAttr, exceptioncode);
}
NodeListImpl *ElementImpl::getElementsByTagName( const DOMString &name )
{
return new TagNodeListImpl( this, name );
}
short ElementImpl::tabIndex() const
{
if (has_tabindex)
return tabindex;
else
return -1;
}
void ElementImpl::setTabIndex( short _tabindex )
{
has_tabindex=true;
tabindex=_tabindex;
}
void ElementImpl::normalize( int &exceptioncode )
{
// In DOM level 2, this gets moved to Node
// ### normalize attributes? (when we store attributes using child nodes)
exceptioncode = 0;
NodeImpl *child = _first;
while (child) {
NodeImpl *nextChild = child->nextSibling();
if (child->nodeType() == Node::ELEMENT_NODE) {
static_cast<ElementImpl*>(child)->normalize(exceptioncode);
if (exceptioncode)
return;
child = nextChild;
}
else if (nextChild && child->nodeType() == Node::TEXT_NODE && nextChild->nodeType() == Node::TEXT_NODE) {
static_cast<TextImpl*>(child)->appendData(static_cast<TextImpl*>(nextChild)->data());
removeChild(nextChild,exceptioncode);
if (exceptioncode)
return;
}
else
child = nextChild;
}
}
NamedAttrMapImpl* ElementImpl::defaultMap() const
{
return 0;
}
void ElementImpl::attach()
{
if (!m_render)
{
#if SPEED_DEBUG < 2
setStyle(ownerDocument()->styleSelector()->styleForElement(this));
#if SPEED_DEBUG < 1
if(_parent && _parent->renderer())
{
m_render = khtml::RenderObject::createObject(this);
if(m_render)
{
_parent->renderer()->addChild(m_render, nextRenderer());
}
}
#endif
#endif
}
NodeBaseImpl::attach();
}
void ElementImpl::detach()
{
NodeBaseImpl::detach();
if ( m_render )
m_render->detach();
m_render = 0;
}
void ElementImpl::recalcStyle()
{
if(!m_style) return;
EDisplay oldDisplay = m_style->display();
int dynamicState = StyleSelector::None;
if ( m_mouseInside )
dynamicState |= StyleSelector::Hover;
if ( m_focused )
dynamicState |= StyleSelector::Focus;
if ( m_active )
dynamicState |= StyleSelector::Active;
setStyle( ownerDocument()->styleSelector()->styleForElement(this, dynamicState) );
if (oldDisplay != m_style->display()) {
detach();
attach();
}
if( m_render && m_style )
m_render->setStyle(m_style);
NodeImpl *n;
for (n = _first; n; n = n->nextSibling())
n->recalcStyle();
}
bool ElementImpl::prepareMouseEvent( int _x, int _y,
int _tx, int _ty,
MouseEvent *ev)
{
#ifdef EVENT_DEBUG
kdDebug( 6030 ) << nodeName().string() << "::prepareMouseEvent" << endl;
#endif
bool inside = false;
if(!m_render) return false;
int origTx = _tx;
int origTy = _ty;
RenderObject *p = m_render->parent();
while( p && p->isAnonymousBox() ) {
// kdDebug( 6030 ) << "parent is anonymous!" << endl;
// we need to add the offset of the anonymous box
_tx += p->xPos();
_ty += p->yPos();
p = p->parent();
}
bool positioned = m_render->isPositioned();
int oldZIndex = ev->currentZIndex;
// Positioned element -> store current zIndex, for children to use
if ( positioned ) {
ev->currentZIndex = m_render->style()->zIndex();
//kdDebug() << "ElementImpl::prepareMouseEvent storing currentZIndex=" << ev->currentZIndex << endl;
}
if(!m_render->isInline() || !m_render->firstChild() || m_render->isFloating() ) {
bool known = m_render->absolutePosition(_tx, _ty);
if (known && m_render->containsPoint(_x,_y,_tx,_ty)) {
if ( m_render->style() && !m_render->style()->visiblity() == HIDDEN ) {
//if ( positioned )
// kdDebug(6030) << " currentZIndex=" << ev->currentZIndex << " ev->zIndex=" << ev->zIndex << endl;
if ( ev->currentZIndex >= ev->zIndex ) {
//kdDebug(6030) << nodeName().string() << " SETTING innerNode " << endl;
ev->innerNode = Node(this);
ev->nodeAbsX = origTx;
ev->nodeAbsY = origTy;
ev->zIndex = ev->currentZIndex;
inside = true;
}
}
}
}
NodeImpl *child = firstChild();
while(child != 0) {
if(child->prepareMouseEvent(_x, _y, _tx, _ty, ev))
inside = true;
child = child->nextSibling();
}
#ifdef EVENT_DEBUG
if(inside) kdDebug( 6030 ) << nodeName().string() << " --> inside" << endl;
#endif
#if 0
// #############
if(inside || mouseInside())
if ( ! (m_render->style() && m_render->style()->visiblity() == HIDDEN) )
{
// dynamic HTML...
// ### mouseEventHandler(ev, inside);
}
#endif
bool oldinside=mouseInside();
setMouseInside(inside);
bool oldactive = active();
if ( inside ) {
if ( ev->type == MousePress )
m_active = true;
else if ( ev->type == MouseRelease )
m_active = false;
} else if ( m_active ) {
m_active = false;
}
if ( (oldinside != inside && m_style->hasHover()) ||
( oldactive != m_active && m_style->hasActive() ) )
applyChanges(true, false);
// reset previous z index
if ( positioned )
ev->currentZIndex = oldZIndex;
return inside;
}
void ElementImpl::setFocus(bool received)
{
NodeBaseImpl::setFocus(received);
applyChanges(true, false);
}
void ElementImpl::setActive(bool down)
{
NodeBaseImpl::setActive(down);
applyChanges(true, false);
}
khtml::FindSelectionResult ElementImpl::findSelectionNode( int _x, int _y, int _tx, int _ty, DOM::Node & node, int & offset )
{
//kdDebug(6030) << "ElementImpl::findSelectionNode " << this << " _x=" << _x << " _y=" << _y
// << " _tx=" << _tx << " _ty=" << _ty << endl;
// ######### Duplicated code from mouseEvent
// TODO put the code above (getting _tx,_ty) in a common place and call it from here
if (!m_render) return SelectionPointAfter;
RenderObject *p = m_render->parent();
while( p && p->isAnonymousBox() ) {
// kdDebug( 6030 ) << "parent is anonymous!" << endl;
// we need to add the offset of the anonymous box
_tx += p->xPos();
_ty += p->yPos();
p = p->parent();
}
if(!m_render->isInline() || !m_render->firstChild() || m_render->isFloating() )
{
m_render->absolutePosition(_tx, _ty);
}
int off=0, lastOffset=0;
DOM::Node nod;
DOM::Node lastNode;
NodeImpl *child = firstChild();
while(child != 0)
{
khtml::FindSelectionResult pos = child->findSelectionNode(_x, _y, _tx, _ty, nod, off);
//kdDebug(6030) << this << " child->findSelectionNode returned " << pos << endl;
if ( pos == SelectionPointInside ) // perfect match
{
node = nod;
offset = off;
//kdDebug(6030) << "ElementImpl::findSelectionNode " << this << " match offset=" << offset << endl;
return SelectionPointInside;
} else if ( pos == SelectionPointBefore )
{
//x,y is before this element -> stop here
if ( !lastNode.isNull() ) {
node = lastNode;
offset = lastOffset;
//kdDebug(6030) << "ElementImpl::findSelectionNode " << this << " before this child -> returning offset=" << offset << endl;
return SelectionPointInside;
} else {
//kdDebug(6030) << "ElementImpl::findSelectionNode " << this << " before us -> returning -2" << endl;
return SelectionPointBefore;
}
}
// SelectionPointAfter -> keep going
if ( !nod.isNull() )
{
lastNode = nod;
lastOffset = off;
}
child = child->nextSibling();
}
node = lastNode;
offset = lastOffset;
return SelectionPointAfter;
}
bool ElementImpl::isSelectable() const
{
return false;
}
// DOM Section 1.1.1
bool ElementImpl::childAllowed( NodeImpl *newChild )
{
if (!childTypeAllowed(newChild->nodeType()))
return false;
// ### check xml element allowedness according to DTD
if (id() && newChild->id()) // if one if these is 0 then it is an xml element and we allow it anyway
return checkChild(id(), newChild->id());
else
return true;
}
bool ElementImpl::childTypeAllowed( unsigned short type )
{
switch (type) {
case Node::ELEMENT_NODE:
case Node::TEXT_NODE:
case Node::COMMENT_NODE:
case Node::PROCESSING_INSTRUCTION_NODE:
case Node::CDATA_SECTION_NODE:
case Node::ENTITY_REFERENCE_NODE:
return true;
break;
default:
return false;
}
}
void ElementImpl::createDecl( )
{
m_styleDecls = new CSSStyleDeclarationImpl(0);
m_styleDecls->ref();
m_styleDecls->setParent(ownerDocument()->elementSheet());
m_styleDecls->setNode(this);
m_styleDecls->setStrictParsing( ownerDocument()->parseMode() == DocumentImpl::Strict );
}
void ElementImpl::dispatchAttrRemovalEvent(NodeImpl *attr)
{
if (!getDocument()->hasListenerType(DocumentImpl::DOMATTRMODIFIED_LISTENER))
return;
int exceptioncode;
AttrImpl *att = static_cast<AttrImpl*>(attr);
dispatchEvent(new MutationEventImpl(EventImpl::DOMATTRMODIFIED_EVENT,true,false,attr,att->value(),
att->value(),att->name(),MutationEvent::REMOVAL),exceptioncode);
}
void ElementImpl::dispatchAttrAdditionEvent(NodeImpl *attr)
{
if (!getDocument()->hasListenerType(DocumentImpl::DOMATTRMODIFIED_LISTENER))
return;
int exceptioncode;
AttrImpl *att = static_cast<AttrImpl*>(attr);
dispatchEvent(new MutationEventImpl(EventImpl::DOMATTRMODIFIED_EVENT,true,false,attr,att->value(),
att->value(),att->name(),MutationEvent::ADDITION),exceptioncode);
}
// -------------------------------------------------------------------------
XMLElementImpl::XMLElementImpl(DocumentPtr *doc, DOMStringImpl *_name) : ElementImpl(doc)
{
m_name = _name;
if (m_name)
m_name->ref();
m_namespaceURI = 0;
m_id = ownerDocument()->elementId(_name);
}
XMLElementImpl::XMLElementImpl(DocumentPtr *doc, DOMStringImpl *_name, DOMStringImpl *_namespaceURI) : ElementImpl(doc)
{
m_name = _name;
if (m_name)
m_name->ref();
m_namespaceURI = _namespaceURI;
if (m_namespaceURI)
m_namespaceURI->ref();
m_id = ownerDocument()->elementId(_name);
}
XMLElementImpl::~XMLElementImpl()
{
if (m_name)
m_name->deref();
if (m_namespaceURI)
m_namespaceURI->deref();
}
const DOMString XMLElementImpl::nodeName() const
{
return m_name;
}
DOMString XMLElementImpl::namespaceURI() const
{
return m_namespaceURI;
}
bool XMLElementImpl::isXMLElementNode() const
{
return true;
}
// -------------------------------------------------------------------------
NamedAttrMapImpl::NamedAttrMapImpl(ElementImpl *e) : element(e)
{
attrs = 0;
len = 0;
}
NamedAttrMapImpl::~NamedAttrMapImpl()
{
clearAttrs();
}
NamedAttrMapImpl &NamedAttrMapImpl::operator =(const NamedAttrMapImpl &other)
{
// clone all attributes in the other map, but attach to our element
if (!element)
return *this;
clearAttrs();
len = other.len;
attrs = new AttrImpl* [len];
uint i;
// first initialize attrs vector, then call parseAttribute on it
// this allows parseAttribute to use getAttribute
for (i = 0; i < len; i++) {
int exceptioncode; // ### propogate
attrs[i] = static_cast<AttrImpl*>(other.attrs[i]->cloneNode(true,exceptioncode));
attrs[i]->_element = element;
attrs[i]->ref();
}
for(i = 0; i < len; i++)
element->parseAttribute(attrs[i]);
element->setChanged(true);
return *this;
}
unsigned long NamedAttrMapImpl::length(int &/*exceptioncode*/) const
{
return length();
}
unsigned long NamedAttrMapImpl::length() const
{
return len;
}
NodeImpl *NamedAttrMapImpl::getNamedItem ( const DOMString &name, int &/*exceptioncode*/ ) const
{
return getNamedItem(name);
}
NodeImpl *NamedAttrMapImpl::getNamedItem ( const DOMString &name ) const
{
uint i;
for (i = 0; i < len; i++) {
if (!strcasecmp(attrs[i]->name(),name))
return attrs[i];
}
return 0;
}
AttrImpl *NamedAttrMapImpl::getIdItem ( int id ) const
{
uint i;
for (i = 0; i < len; i++) {
if (attrs[i]->attrId == id)
return attrs[i];
}
return 0;
}
Node NamedAttrMapImpl::setNamedItem ( const Node &arg, int &exceptioncode )
{
// ### check for invalid chars in name ?
// ### check same document
exceptioncode = 0;
if (!element) {
exceptioncode = DOMException::NOT_FOUND_ERR;
return 0;
}
element->checkReadOnly();
if (arg.nodeType() != Node::ATTRIBUTE_NODE) {
exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
return 0;
}
AttrImpl *attr = static_cast<AttrImpl*>(arg.handle());
if (attr->_element) {
exceptioncode = DOMException::INUSE_ATTRIBUTE_ERR;
return 0;
}
uint i;
for (i = 0; i < len; i++) {
// ### for XML attributes are case sensitive (?) - check this elsewhere also
if (!strcasecmp(attrs[i]->name(),attr->name())) {
// attribute with this id already in list
Attr oldAttr = attrs[i];
attrs[i]->_element = 0;
attrs[i]->deref();
attrs[i] = attr;
attrs[i]->ref();
attr->_element = element;
element->parseAttribute(attr);
element->setChanged(true);
element->dispatchAttrRemovalEvent(oldAttr.handle());
element->dispatchAttrAdditionEvent(attrs[i]);
element->dispatchSubtreeModifiedEvent();
return oldAttr;
}
}
// attribute with this name not yet in list
AttrImpl **newAttrs = new AttrImpl* [len+1];
if (attrs) {
for (i = 0; i < len; i++)
newAttrs[i] = attrs[i];
delete [] attrs;
}
attrs = newAttrs;
attrs[len] = attr;
attr->ref();
len++;
attr->_element = element;
element->parseAttribute(attr);
element->setChanged(true);
element->dispatchAttrAdditionEvent(attr);
element->dispatchSubtreeModifiedEvent();
return 0;
}
Attr NamedAttrMapImpl::setIdItem ( AttrImpl *attr, int &exceptioncode )
{
exceptioncode = 0;
if (!element) {
exceptioncode = DOMException::NOT_FOUND_ERR;
return 0;
}
element->checkReadOnly();
if (attr->_element) {
exceptioncode = DOMException::INUSE_ATTRIBUTE_ERR;
return 0;
}
uint i;
for (i = 0; i < len; i++) {
if (attrs[i]->attrId == attr->attrId) {
// attribute with this id already in list
Attr oldAttr = attrs[i];
attrs[i]->_element = 0;
attrs[i]->deref();
attrs[i] = attr;
attrs[i]->ref();
attr->_element = element;
element->parseAttribute(attr);
element->setChanged(true);
element->dispatchAttrRemovalEvent(oldAttr.handle());
element->dispatchAttrAdditionEvent(attrs[i]);
element->dispatchSubtreeModifiedEvent();
return oldAttr; // ### check this gets deleted if ref = 0 and it's not assigned to anything
}
}
// attribute with this id not yet in list
AttrImpl **newAttrs = new AttrImpl* [len+1];
if (attrs) {
for (i = 0; i < len; i++)
newAttrs[i] = attrs[i];
delete [] attrs;
}
attrs = newAttrs;
attrs[len] = attr;
attr->ref();
len++;
attr->_element = element;
element->parseAttribute(attr);
element->setChanged(true);
element->dispatchAttrAdditionEvent(attr);
element->dispatchSubtreeModifiedEvent();
return 0;
}
Node NamedAttrMapImpl::removeNamedItem ( const DOMString &name, int &exceptioncode )
{
if (element)
element->checkReadOnly();
if (!attrs) {
exceptioncode = DOMException::NOT_FOUND_ERR;
return 0;
}
uint i;
int found = -1;
for (i = 0; i < len && found < 0; i++) {
if (!strcasecmp(attrs[i]->name(),name))
found = i;
}
if (found < 0) {
exceptioncode = DOMException::NOT_FOUND_ERR;
return 0;
}
Attr ret = attrs[found];
attrs[found]->_element = 0;
attrs[found]->deref();
if (len == 1) {
delete [] attrs;
attrs = 0;
len = 0;
} else {
AttrImpl **newAttrs = new AttrImpl* [len-1];
for (i = 0; i < uint(found); i++)
newAttrs[i] = attrs[i];
len--;
for (; i < len; i++)
newAttrs[i] = attrs[i+1];
delete [] attrs;
attrs = newAttrs;
}
DOMString nullStr;
AttrImpl a(name,nullStr,element->docPtr());
element->parseAttribute(&a);
element->setChanged(true);
element->dispatchAttrRemovalEvent(ret.handle());
element->dispatchSubtreeModifiedEvent();
return ret;
}
Attr NamedAttrMapImpl::removeIdItem ( int id )
{
if (!attrs)
return 0;
uint i;
int found = -1;
for (i = 0; i < len && found < 0; i++) {
if (attrs[i]->attrId == id)
found = i;
}
if (found < 0)
return 0;
Attr ret = attrs[found];
attrs[found]->_element = 0;
attrs[found]->deref();
if (len == 1) {
delete [] attrs;
attrs = 0;
len = 0;
} else {
AttrImpl **newAttrs = new AttrImpl* [len-1];
for (i = 0; i < uint(found); i++)
newAttrs[i] = attrs[i];
len--;
for (; i < len; i++)
newAttrs[i] = attrs[i+1];
delete [] attrs;
attrs = newAttrs;
}
DOMString nullStr;
AttrImpl a(id,nullStr,element->docPtr());
element->parseAttribute(&a);
element->setChanged(true);
element->dispatchAttrRemovalEvent(ret.handle());
element->dispatchSubtreeModifiedEvent();
return ret;
}
NodeImpl *NamedAttrMapImpl::item ( unsigned long index, int &/*exceptioncode*/ ) const
{
return item(index);
}
NodeImpl *NamedAttrMapImpl::item ( unsigned long index ) const
{
if (index >= len)
return 0;
else
return attrs[index];
}
void NamedAttrMapImpl::clearAttrs()
{
if (attrs) {
uint i;
for (i = 0; i < len; i++) {
attrs[i]->_element = 0;
attrs[i]->deref();
}
delete [] attrs;
attrs = 0;
}
len = 0;
}
void NamedAttrMapImpl::insertAttr( AttrImpl *a )
{
// only add if not already there
if( !a->attrId || !getIdItem(a->attrId)) {
AttrImpl** nList = new AttrImpl* [ len+1 ];
if(attrs) {
for(uint i = 0; i < len; i++)
nList[i] = attrs[i];
delete [] attrs;
}
attrs = nList;
attrs[len++] = a;
a->ref();
}
}
Attr NamedAttrMapImpl::removeAttr( AttrImpl *oldAttr, int &exceptioncode )
{
exceptioncode = 0;
uint i;
for (i = 0; i < len; i++) {
if (attrs[i] == oldAttr) {
Attr ret = attrs[i];
attrs[i]->_element = 0;
attrs[i]->deref();
if (len == 1) {
delete [] attrs;
attrs = 0;
len = 0;
}
else {
AttrImpl **newAttrs = new AttrImpl* [len-1];
uint ni;
for (ni = 0; ni < i; ni++)
newAttrs[ni] = attrs[ni];
len--;
for (; ni < len; ni++)
newAttrs[ni] = attrs[ni+1];
delete attrs;
attrs = newAttrs;
}
AttrImpl a = oldAttr->attrId ? AttrImpl(oldAttr->attrId,"",element->docPtr()) :
AttrImpl(oldAttr->name(),"",element->docPtr());
element->parseAttribute(&a);
element->setChanged(true);
element->dispatchAttrRemovalEvent(ret.handle());
element->dispatchSubtreeModifiedEvent();
return ret;
}
}
exceptioncode = DOMException::NOT_FOUND_ERR;
return 0;
}
void NamedAttrMapImpl::detachFromElement()
{
// we allow a NamedAttrMapImpl w/o an element in case someone still has a reference
// to if after the element gets deleted - but the map is now invalid
element = 0;
clearAttrs();
}