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