001package org.jsoup.nodes;
002
003import org.jsoup.helper.Validate;
004
005import java.io.IOException;
006import java.util.List;
007
008/**
009 A node that does not hold any children. E.g.: {@link TextNode}, {@link DataNode}, {@link Comment}.
010 */
011public abstract class LeafNode extends Node {
012    Object value; // either a string value, or an attribute map (in the rare case multiple attributes are set)
013
014    public LeafNode() {
015        value = "";
016    }
017
018    protected LeafNode(String coreValue) {
019        Validate.notNull(coreValue);
020        value = coreValue;
021    }
022
023    @Override protected final boolean hasAttributes() {
024        return value instanceof Attributes;
025    }
026
027    @Override
028    public final Attributes attributes() {
029        ensureAttributes();
030        return (Attributes) value;
031    }
032
033    private void ensureAttributes() {
034        if (!hasAttributes()) { // then value is String coreValue
035            String coreValue = (String) value;
036            Attributes attributes = new Attributes();
037            value = attributes;
038            attributes.put(nodeName(), coreValue);
039        }
040    }
041
042    String coreValue() {
043        return attr(nodeName());
044    }
045
046    void coreValue(String value) {
047        attr(nodeName(), value);
048    }
049
050    @Override
051    public String attr(String key) {
052        if (!hasAttributes()) {
053            return nodeName().equals(key) ? (String) value : EmptyString;
054        }
055        return super.attr(key);
056    }
057
058    @Override
059    public Node attr(String key, String value) {
060        if (!hasAttributes() && key.equals(nodeName())) {
061            this.value = value;
062        } else {
063            ensureAttributes();
064            super.attr(key, value);
065        }
066        return this;
067    }
068
069    @Override
070    public boolean hasAttr(String key) {
071        ensureAttributes();
072        return super.hasAttr(key);
073    }
074
075    @Override
076    public Node removeAttr(String key) {
077        ensureAttributes();
078        return super.removeAttr(key);
079    }
080
081    @Override
082    public String absUrl(String key) {
083        ensureAttributes();
084        return super.absUrl(key);
085    }
086
087    @Override
088    public String baseUri() {
089        return parentNode != null ? parentNode.baseUri() : "";
090    }
091
092    @Override
093    protected void doSetBaseUri(String baseUri) {
094        // noop
095    }
096
097    @Override
098    public int childNodeSize() {
099        return 0;
100    }
101
102    @Override
103    public Node empty() {
104        return this;
105    }
106
107    @Override
108    protected List<Node> ensureChildNodes() {
109        return EmptyNodes;
110    }
111
112    @Override
113    void outerHtmlTail(Appendable accum, Document.OutputSettings out) throws IOException {}
114
115    @Override
116    protected LeafNode doClone(Node parent) {
117        LeafNode clone = (LeafNode) super.doClone(parent);
118
119        // Object value could be plain string or attributes - need to clone
120        if (hasAttributes())
121            clone.value = ((Attributes) value).clone();
122
123        return clone;
124    }
125}