1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4 *           (C) 2001 Peter Kelly (pmk@post.com)
5 *           (C) 2001 Dirk Mueller (mueller@kde.org)
6 * Copyright (C) 2004, 2005, 2006, 2007, 2009 Apple Inc. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB.  If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23#include "config.h"
24#include "Attr.h"
25
26#include "Document.h"
27#include "Element.h"
28#include "ExceptionCode.h"
29#include "Text.h"
30#include "XMLNSNames.h"
31
32namespace WebCore {
33
34using namespace HTMLNames;
35
36inline Attr::Attr(Element* element, Document* document, PassRefPtr<Attribute> attribute)
37    : ContainerNode(document)
38    , m_element(element)
39    , m_attribute(attribute)
40    , m_ignoreChildrenChanged(0)
41    , m_specified(true)
42{
43    ASSERT(!m_attribute->attr());
44    m_attribute->m_impl = this;
45}
46
47PassRefPtr<Attr> Attr::create(Element* element, Document* document, PassRefPtr<Attribute> attribute)
48{
49    RefPtr<Attr> attr = adoptRef(new Attr(element, document, attribute));
50    attr->createTextChild();
51    return attr.release();
52}
53
54Attr::~Attr()
55{
56    ASSERT(m_attribute->attr() == this);
57    m_attribute->m_impl = 0;
58}
59
60void Attr::createTextChild()
61{
62    ASSERT(refCount());
63    if (!m_attribute->value().isEmpty()) {
64        RefPtr<Text> textNode = document()->createTextNode(m_attribute->value().string());
65
66        // This does everything appendChild() would do in this situation (assuming m_ignoreChildrenChanged was set),
67        // but much more efficiently.
68        textNode->setParent(this);
69        setFirstChild(textNode.get());
70        setLastChild(textNode.get());
71    }
72}
73
74String Attr::nodeName() const
75{
76    return name();
77}
78
79Node::NodeType Attr::nodeType() const
80{
81    return ATTRIBUTE_NODE;
82}
83
84const AtomicString& Attr::localName() const
85{
86    return m_attribute->localName();
87}
88
89const AtomicString& Attr::namespaceURI() const
90{
91    return m_attribute->namespaceURI();
92}
93
94const AtomicString& Attr::prefix() const
95{
96    return m_attribute->prefix();
97}
98
99void Attr::setPrefix(const AtomicString& prefix, ExceptionCode& ec)
100{
101    ec = 0;
102    checkSetPrefix(prefix, ec);
103    if (ec)
104        return;
105
106    if ((prefix == xmlnsAtom && namespaceURI() != XMLNSNames::xmlnsNamespaceURI)
107        || static_cast<Attr*>(this)->qualifiedName() == xmlnsAtom) {
108        ec = NAMESPACE_ERR;
109        return;
110    }
111
112    m_attribute->setPrefix(prefix.isEmpty() ? AtomicString() : prefix);
113}
114
115String Attr::nodeValue() const
116{
117    return value();
118}
119
120void Attr::setValue(const AtomicString& value, ExceptionCode&)
121{
122    m_ignoreChildrenChanged++;
123    removeChildren();
124    m_attribute->setValue(value);
125    createTextChild();
126    m_ignoreChildrenChanged--;
127
128    if (m_element)
129        m_element->attributeChanged(m_attribute.get());
130}
131
132void Attr::setNodeValue(const String& v, ExceptionCode& ec)
133{
134    setValue(v, ec);
135}
136
137PassRefPtr<Node> Attr::cloneNode(bool /*deep*/)
138{
139    RefPtr<Attr> clone = adoptRef(new Attr(0, document(), m_attribute->clone()));
140    cloneChildNodes(clone.get());
141    return clone.release();
142}
143
144// DOM Section 1.1.1
145bool Attr::childTypeAllowed(NodeType type)
146{
147    switch (type) {
148        case TEXT_NODE:
149        case ENTITY_REFERENCE_NODE:
150            return true;
151        default:
152            return false;
153    }
154}
155
156void Attr::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
157{
158    if (m_ignoreChildrenChanged > 0)
159        return;
160
161    Node::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
162
163    // FIXME: We should include entity references in the value
164
165    String val = "";
166    for (Node *n = firstChild(); n; n = n->nextSibling()) {
167        if (n->isTextNode())
168            val += static_cast<Text *>(n)->data();
169    }
170
171    m_attribute->setValue(val.impl());
172    if (m_element)
173        m_element->attributeChanged(m_attribute.get());
174}
175
176bool Attr::isId() const
177{
178    return qualifiedName().matches(m_element ? m_element->idAttributeName() : idAttr);
179}
180
181}
182