001package org.jsoup.nodes;
002
003import org.jsoup.helper.Validate;
004import org.jsoup.internal.Normalizer;
005import org.jsoup.internal.QuietAppendable;
006import org.jsoup.helper.Regex;
007import org.jsoup.internal.StringUtil;
008import org.jsoup.parser.ParseSettings;
009import org.jsoup.parser.Parser;
010import org.jsoup.parser.Tag;
011import org.jsoup.parser.TokenQueue;
012import org.jsoup.select.Collector;
013import org.jsoup.select.Elements;
014import org.jsoup.select.Evaluator;
015import org.jsoup.select.NodeFilter;
016import org.jsoup.select.NodeVisitor;
017import org.jsoup.select.Nodes;
018import org.jsoup.select.Selector;
019import org.jspecify.annotations.Nullable;
020
021import java.lang.ref.WeakReference;
022import java.util.ArrayList;
023import java.util.Collection;
024import java.util.Collections;
025import java.util.Iterator;
026import java.util.LinkedHashSet;
027import java.util.List;
028import java.util.Map;
029import java.util.Set;
030import java.util.concurrent.atomic.AtomicBoolean;
031import java.util.function.Consumer;
032import java.util.regex.Pattern;
033import java.util.regex.PatternSyntaxException;
034import java.util.stream.Collectors;
035import java.util.stream.Stream;
036
037import static org.jsoup.internal.Normalizer.normalize;
038import static org.jsoup.nodes.Document.OutputSettings.Syntax.xml;
039import static org.jsoup.nodes.TextNode.lastCharIsWhitespace;
040import static org.jsoup.parser.Parser.NamespaceHtml;
041import static org.jsoup.parser.TokenQueue.escapeCssIdentifier;
042import static org.jsoup.select.Selector.evaluatorOf;
043
044/**
045 An HTML Element consists of a tag name, attributes, and child nodes (including text nodes and other elements).
046 <p>
047 From an Element, you can extract data, traverse the node graph, and manipulate the HTML.
048*/
049public class Element extends Node implements Iterable<Element> {
050    private static final List<Element> EmptyChildren = Collections.emptyList();
051    private static final NodeList EmptyNodeList = new NodeList(0);
052    static final String BaseUriKey = Attributes.internalKey("baseUri");
053    Tag tag;
054    NodeList childNodes;
055    @Nullable Attributes attributes; // field is nullable but all methods for attributes are non-null
056
057    /**
058     * Create a new, standalone element, in the specified namespace.
059     * @param tag tag name
060     * @param namespace namespace for this element
061     */
062    public Element(String tag, String namespace) {
063        this(Tag.valueOf(tag, namespace, ParseSettings.preserveCase), null);
064    }
065
066    /**
067     * Create a new, standalone element, in the HTML namespace.
068     * @param tag tag name
069     * @see #Element(String tag, String namespace)
070     */
071    public Element(String tag) {
072        this(tag, Parser.NamespaceHtml);
073    }
074
075    /**
076     * Create a new, standalone Element. (Standalone in that it has no parent.)
077     *
078     * @param tag tag of this element
079     * @param baseUri the base URI (optional, may be null to inherit from parent, or "" to clear parent's)
080     * @param attributes initial attributes (optional, may be null)
081     * @see #appendChild(Node)
082     * @see #appendElement(String)
083     */
084    public Element(Tag tag, @Nullable String baseUri, @Nullable Attributes attributes) {
085        Validate.notNull(tag);
086        childNodes = EmptyNodeList;
087        this.attributes = attributes;
088        this.tag = tag;
089        if (!StringUtil.isBlank(baseUri)) this.setBaseUri(baseUri);
090    }
091
092    /**
093     * Create a new Element from a Tag and a base URI.
094     *
095     * @param tag element tag
096     * @param baseUri the base URI of this element. Optional, and will inherit from its parent, if any.
097     * @see Tag#valueOf(String, ParseSettings)
098     */
099    public Element(Tag tag, @Nullable String baseUri) {
100        this(tag, baseUri, null);
101    }
102
103    /**
104     Internal test to check if a nodelist object has been created.
105     */
106    protected boolean hasChildNodes() {
107        return childNodes != EmptyNodeList;
108    }
109
110    @Override protected List<Node> ensureChildNodes() {
111        if (childNodes == EmptyNodeList) {
112            childNodes = new NodeList(4);
113        }
114        return childNodes;
115    }
116
117    @Override
118    protected boolean hasAttributes() {
119        return attributes != null;
120    }
121
122    @Override
123    public Attributes attributes() {
124        if (attributes == null) // not using hasAttributes, as doesn't clear warning
125            attributes = new Attributes();
126        return attributes;
127    }
128
129    @Override
130    public String baseUri() {
131        String baseUri = searchUpForAttribute(this, BaseUriKey);
132        return baseUri != null ? baseUri : "";
133    }
134
135    @Nullable
136    static String searchUpForAttribute(final Element start, final String key) {
137        Element el = start;
138        while (el != null) {
139            if (el.attributes != null && el.attributes.hasKey(key))
140                return el.attributes.get(key);
141            el = el.parent();
142        }
143        return null;
144    }
145
146    @Override
147    protected void doSetBaseUri(String baseUri) {
148        attributes().put(BaseUriKey, baseUri);
149    }
150
151    @Override
152    public int childNodeSize() {
153        return childNodes.size();
154    }
155
156    @Override
157    public String nodeName() {
158        return tag.getName();
159    }
160
161    /**
162     * Get the name of the tag for this element. E.g. {@code div}. If you are using {@link ParseSettings#preserveCase
163     * case preserving parsing}, this will return the source's original case.
164     *
165     * @return the tag name
166     */
167    public String tagName() {
168        return tag.getName();
169    }
170
171    /**
172     * Get the normalized name of this Element's tag. This will always be the lower-cased version of the tag, regardless
173     * of the tag case preserving setting of the parser. For e.g., {@code <DIV>} and {@code <div>} both have a
174     * normal name of {@code div}.
175     * @return normal name
176     */
177    @Override
178    public String normalName() {
179        return tag.normalName();
180    }
181
182    /**
183     Test if this Element has the specified normalized name, and is in the specified namespace.
184     * @param normalName a normalized element name (e.g. {@code div}).
185     * @param namespace the namespace
186     * @return true if the element's normal name matches exactly, and is in the specified namespace
187     * @since 1.17.2
188     */
189    public boolean elementIs(String normalName, String namespace) {
190        return tag.normalName().equals(normalName) && tag.namespace().equals(namespace);
191    }
192
193    /**
194     * Change (rename) the tag of this element. For example, convert a {@code <span>} to a {@code <div>} with
195     * {@code el.tagName("div");}.
196     *
197     * @param tagName new tag name for this element
198     * @return this element, for chaining
199     * @see Elements#tagName(String)
200     */
201    public Element tagName(String tagName) {
202        return tagName(tagName, tag.namespace());
203    }
204
205    /**
206     * Change (rename) the tag of this element. For example, convert a {@code <span>} to a {@code <div>} with
207     * {@code el.tagName("div");}.
208     *
209     * @param tagName new tag name for this element
210     * @param namespace the new namespace for this element
211     * @return this element, for chaining
212     * @see Elements#tagName(String)
213     */
214    public Element tagName(String tagName, String namespace) {
215        Validate.notEmptyParam(tagName, "tagName");
216        Validate.notEmptyParam(namespace, "namespace");
217        Parser parser = NodeUtils.parser(this);
218        tag = parser.tagSet().valueOf(tagName, namespace, parser.settings()); // maintains the case option of the original parse
219        return this;
220    }
221
222    /**
223     * Get the Tag for this element.
224     *
225     * @return the tag object
226     */
227    public Tag tag() {
228        return tag;
229    }
230
231    /**
232     Change the Tag of this element.
233     @param tag the new tag
234     @return this element, for chaining
235     @since 1.20.1
236     */
237    public Element tag(Tag tag) {
238        Validate.notNull(tag);
239        this.tag = tag;
240        return this;
241    }
242
243    /**
244     * Test if this element is a block-level element. (E.g. {@code <div> == true} or an inline element
245     * {@code <span> == false}).
246     *
247     * @return true if block, false if not (and thus inline)
248     */
249    public boolean isBlock() {
250        return tag.isBlock();
251    }
252
253    /**
254     * Get the {@code id} attribute of this element.
255     *
256     * @return The id attribute, if present, or an empty string if not.
257     */
258    public String id() {
259        return attributes != null ? attributes.getIgnoreCase("id") :"";
260    }
261
262    /**
263     Set the {@code id} attribute of this element.
264     @param id the ID value to use
265     @return this Element, for chaining
266     */
267    public Element id(String id) {
268        Validate.notNull(id);
269        attr("id", id);
270        return this;
271    }
272
273    /**
274     * Set an attribute value on this element. If this element already has an attribute with the
275     * key, its value is updated; otherwise, a new attribute is added.
276     *
277     * @return this element
278     */
279    @Override public Element attr(String attributeKey, String attributeValue) {
280        super.attr(attributeKey, attributeValue);
281        return this;
282    }
283
284    /**
285     * Set a boolean attribute value on this element. Setting to <code>true</code> sets the attribute value to "" and
286     * marks the attribute as boolean so no value is written out. Setting to <code>false</code> removes the attribute
287     * with the same key if it exists.
288     *
289     * @param attributeKey the attribute key
290     * @param attributeValue the attribute value
291     *
292     * @return this element
293     */
294    public Element attr(String attributeKey, boolean attributeValue) {
295        attributes().put(attributeKey, attributeValue);
296        return this;
297    }
298
299    /**
300     Get an Attribute by key. Changes made via {@link Attribute#setKey(String)}, {@link Attribute#setValue(String)} etc
301     will cascade back to this Element.
302     @param key the (case-sensitive) attribute key
303     @return the Attribute for this key, or null if not present.
304     @since 1.17.2
305     */
306    @Nullable public Attribute attribute(String key) {
307        return hasAttributes() ? attributes().attribute(key) : null;
308    }
309
310    /**
311     * Get this element's HTML5 custom data attributes. Each attribute in the element that has a key
312     * starting with "data-" is included the dataset.
313     * <p>
314     * E.g., the element {@code <div data-package="jsoup" data-language="Java" class="group">...} has the dataset
315     * {@code package=jsoup, language=java}.
316     * <p>
317     * This map is a filtered view of the element's attribute map. Changes to one map (add, remove, update) are reflected
318     * in the other map.
319     * <p>
320     * You can find elements that have data attributes using the {@code [^data-]} attribute key prefix selector.
321     * @return a map of {@code key=value} custom data attributes.
322     */
323    public Map<String, String> dataset() {
324        return attributes().dataset();
325    }
326
327    @Override @Nullable
328    public final Element parent() {
329        return (Element) parentNode;
330    }
331
332    /**
333     * Get this element's parent and ancestors, up to the document root.
334     * @return this element's stack of parents, starting with the closest first.
335     */
336    public Elements parents() {
337        Elements parents = new Elements();
338        Element parent = this.parent();
339        while (parent != null && !parent.nameIs("#root")) {
340            parents.add(parent);
341            parent = parent.parent();
342        }
343        return parents;
344    }
345
346    /**
347     * Get a child element of this element, by its 0-based index number.
348     * <p>
349     * Note that an element can have both mixed Nodes and Elements as children. This method inspects
350     * a filtered list of children that are elements, and the index is based on that filtered list.
351     * </p>
352     *
353     * @param index the index number of the element to retrieve
354     * @return the child element, if it exists, otherwise throws an {@code IndexOutOfBoundsException}
355     * @see #childNode(int)
356     */
357    public Element child(int index) {
358        Validate.isTrue(index >= 0, "Index must be >= 0");
359        List<Element> cached = cachedChildren();
360        if (cached != null) return cached.get(index);
361        // otherwise, iter on elementChild; saves creating list
362        int size = childNodes.size();
363        for (int i = 0, e = 0; i < size; i++) { // direct iter is faster than chasing firstElSib, nextElSibd
364            Node node = childNodes.get(i);
365            if (node instanceof Element) {
366                if (e++ == index) return (Element) node;
367            }
368        }
369        throw new IndexOutOfBoundsException("No child at index: " + index);
370    }
371
372    /**
373     * Get the number of child nodes of this element that are elements.
374     * <p>
375     * This method works on the same filtered list like {@link #child(int)}. Use {@link #childNodes()} and {@link
376     * #childNodeSize()} to get the unfiltered Nodes (e.g. includes TextNodes etc.)
377     * </p>
378     *
379     * @return the number of child nodes that are elements
380     * @see #children()
381     * @see #child(int)
382     */
383    public int childrenSize() {
384        if (childNodeSize() == 0) return 0;
385        return childElementsList().size(); // gets children into cache; faster subsequent child(i) if unmodified
386    }
387
388    /**
389     * Get this element's child elements.
390     * <p>
391     * This is effectively a filter on {@link #childNodes()} to get Element nodes.
392     * </p>
393     * @return child elements. If this element has no children, returns an empty list.
394     * @see #childNodes()
395     */
396    public Elements children() {
397        return new Elements(childElementsList());
398    }
399
400    /**
401     * Maintains a shadow copy of this element's child elements. If the nodelist is changed, this cache is invalidated.
402     * @return a list of child elements
403     */
404    List<Element> childElementsList() {
405        if (childNodeSize() == 0) return EmptyChildren; // short circuit creating empty
406        // set atomically, so works in multi-thread. Calling methods look like reads, so should be thread-safe
407        synchronized (childNodes) { // sync vs re-entrant lock, to save another field
408            List<Element> children = cachedChildren();
409            if (children == null) {
410                children = filterNodes(Element.class);
411                stashChildren(children);
412            }
413            return children;
414        }
415    }
416
417    private static final String childElsKey = "jsoup.childEls";
418    private static final String childElsMod = "jsoup.childElsMod";
419
420    /** returns the cached child els, if they exist, and the modcount of our childnodes matches the stashed modcount */
421    @SuppressWarnings("unchecked")
422    @Nullable List<Element> cachedChildren() {
423        if (attributes == null || !attributes.hasUserData()) return null; // don't create empty userdata
424        Map<String, Object> userData = attributes.userData();
425        WeakReference<List<Element>> ref = (WeakReference<List<Element>>) userData.get(childElsKey);
426        if (ref != null) {
427            List<Element> els = ref.get();
428            if (els != null) {
429                Integer modCount = (Integer) userData.get(childElsMod);
430                if (modCount != null && modCount == childNodes.modCount())
431                    return els;
432            }
433        }
434        return null;
435    }
436
437    /** caches the child els into the Attribute user data. */
438    private void stashChildren(List<Element> els) {
439        Map<String, Object> userData = attributes().userData();
440        WeakReference<List<Element>> ref = new WeakReference<>(els);
441        userData.put(childElsKey, ref);
442        userData.put(childElsMod, childNodes.modCount());
443    }
444
445    /**
446     Returns a Stream of this Element and all of its descendant Elements. The stream has document order.
447     @return a stream of this element and its descendants.
448     @see #nodeStream()
449     @since 1.17.1
450     */
451    public Stream<Element> stream() {
452        return NodeUtils.stream(this, Element.class);
453    }
454
455    private <T> List<T> filterNodes(Class<T> clazz) {
456        return childNodes.stream()
457                .filter(clazz::isInstance)
458                .map(clazz::cast)
459                .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
460    }
461
462    /**
463     * Get this element's child text nodes. The list is unmodifiable but the text nodes may be manipulated.
464     * <p>
465     * This is effectively a filter on {@link #childNodes()} to get Text nodes.
466     * @return child text nodes. If this element has no text nodes, returns an
467     * empty list.
468     * </p>
469     * For example, with the input HTML: {@code <p>One <span>Two</span> Three <br> Four</p>} with the {@code p} element selected:
470     * <ul>
471     *     <li>{@code p.text()} = {@code "One Two Three Four"}</li>
472     *     <li>{@code p.ownText()} = {@code "One Three Four"}</li>
473     *     <li>{@code p.children()} = {@code Elements[<span>, <br>]}</li>
474     *     <li>{@code p.childNodes()} = {@code List<Node>["One ", <span>, " Three ", <br>, " Four"]}</li>
475     *     <li>{@code p.textNodes()} = {@code List<TextNode>["One ", " Three ", " Four"]}</li>
476     * </ul>
477     */
478    public List<TextNode> textNodes() {
479        return filterNodes(TextNode.class);
480    }
481
482    /**
483     * Get this element's child data nodes. The list is unmodifiable but the data nodes may be manipulated.
484     * <p>
485     * This is effectively a filter on {@link #childNodes()} to get Data nodes.
486     * </p>
487     * @return child data nodes. If this element has no data nodes, returns an
488     * empty list.
489     * @see #data()
490     */
491    public List<DataNode> dataNodes() {
492        return filterNodes(DataNode.class);
493    }
494
495    /**
496     * Find elements that match the {@link Selector} CSS query, with this element as the starting context. Matched elements
497     * may include this element, or any of its descendents.
498     * <p>If the query starts with a combinator (e.g. {@code *} or {@code >}), that will combine to this element.</p>
499     * <p>This method is generally more powerful to use than the DOM-type {@code getElementBy*} methods, because
500     * multiple filters can be combined, e.g.:</p>
501     * <ul>
502     * <li>{@code el.select("a[href]")} - finds links ({@code a} tags with {@code href} attributes)</li>
503     * <li>{@code el.select("a[href*=example.com]")} - finds links pointing to example.com (loosely)</li>
504     * <li>{@code el.select("* div")} - finds all divs that descend from this element (and excludes this element)</li>
505     * <li>{@code el.select("> div")} - finds all divs that are direct children of this element (and excludes this element)</li>
506     * </ul>
507     * <p>See the query syntax documentation in {@link org.jsoup.select.Selector}.</p>
508     * <p>Also known as {@code querySelectorAll()} in the Web DOM.</p>
509     *
510     * @param cssQuery a {@link Selector} CSS-like query
511     * @return an {@link Elements} list containing elements that match the query (empty if none match)
512     * @see Selector selector query syntax
513     * @see #select(Evaluator)
514     * @throws Selector.SelectorParseException (unchecked) on an invalid CSS query.
515     */
516    public Elements select(String cssQuery) {
517        return Selector.select(cssQuery, this);
518    }
519
520    /**
521     * Find elements that match the supplied Evaluator. This has the same functionality as {@link #select(String)}, but
522     * may be useful if you are running the same query many times (on many documents) and want to save the overhead of
523     * repeatedly parsing the CSS query.
524     * @param evaluator an element evaluator
525     * @return an {@link Elements} list containing elements that match the query (empty if none match)
526     * @see Selector#evaluatorOf(String css)
527     */
528    public Elements select(Evaluator evaluator) {
529        return Selector.select(evaluator, this);
530    }
531
532    /**
533     Selects elements from the given root that match the specified {@link Selector} CSS query, with this element as the
534     starting context, and returns them as a lazy Stream. Matched elements may include this element, or any of its
535     children.
536     <p>
537     Unlike {@link #select(String query)}, which returns a complete list of all matching elements, this method returns a
538     {@link Stream} that processes elements lazily as they are needed. The stream operates in a "pull" model — elements
539     are fetched from the root as the stream is traversed. You can use standard {@code Stream} operations such as
540     {@code filter}, {@code map}, or {@code findFirst} to process elements on demand.
541     </p>
542
543     @param cssQuery a {@link Selector} CSS-like query
544     @return a {@link Stream} containing elements that match the query (empty if none match)
545     @throws Selector.SelectorParseException (unchecked) on an invalid CSS query.
546     @see Selector selector query syntax
547     @see #selectStream(Evaluator eval)
548     @since 1.19.1
549     */
550    public Stream<Element> selectStream(String cssQuery) {
551        return Selector.selectStream(cssQuery, this);
552    }
553
554    /**
555     Find a Stream of elements that match the supplied Evaluator.
556
557     @param evaluator an element Evaluator
558     @return a {@link Stream} containing elements that match the query (empty if none match)
559     @see Selector#evaluatorOf(String css)
560     @since 1.19.1
561     */
562    public Stream<Element> selectStream(Evaluator evaluator) {
563        return Selector.selectStream(evaluator, this);
564    }
565
566    /**
567     * Find the first Element that matches the {@link Selector} CSS query, with this element as the starting context.
568     * <p>This is effectively the same as calling {@code element.select(query).first()}, but is more efficient as query
569     * execution stops on the first hit.</p>
570     * <p>Also known as {@code querySelector()} in the Web DOM.</p>
571     * @param cssQuery cssQuery a {@link Selector} CSS-like query
572     * @return the first matching element, or <b>{@code null}</b> if there is no match.
573     * @see #expectFirst(String)
574     */
575    public @Nullable Element selectFirst(String cssQuery) {
576        return Selector.selectFirst(cssQuery, this);
577    }
578
579    /**
580     * Finds the first Element that matches the supplied Evaluator, with this element as the starting context, or
581     * {@code null} if none match.
582     *
583     * @param evaluator an element evaluator
584     * @return the first matching element (walking down the tree, starting from this element), or {@code null} if none
585     * match.
586     */
587    public @Nullable Element selectFirst(Evaluator evaluator) {
588        return Collector.findFirst(evaluator, this);
589    }
590
591    /**
592     Just like {@link #selectFirst(String)}, but if there is no match, throws an {@link IllegalArgumentException}. This
593     is useful if you want to simply abort processing on a failed match.
594     @param cssQuery a {@link Selector} CSS-like query
595     @return the first matching element
596     @throws IllegalArgumentException if no match is found
597     @since 1.15.2
598     */
599    public Element expectFirst(String cssQuery) {
600        return Validate.expectNotNull(
601            Selector.selectFirst(cssQuery, this),
602            parent() != null ?
603                "No elements matched the query '%s' on element '%s'." :
604                "No elements matched the query '%s' in the document."
605            , cssQuery, this.tagName()
606        );
607    }
608
609    /**
610     Find nodes that match the supplied {@link Evaluator}, with this element as the starting context. Matched
611     nodes may include this element, or any of its descendents.
612
613     @param evaluator an evaluator
614     @return a list of nodes that match the query (empty if none match)
615     @since 1.21.1
616     */
617    public Nodes<Node> selectNodes(Evaluator evaluator) {
618        return selectNodes(evaluator, Node.class);
619    }
620
621    /**
622     Find nodes that match the supplied {@link Selector} CSS query, with this element as the starting context. Matched
623     nodes may include this element, or any of its descendents.
624     <p>To select leaf nodes, the query should specify the node type, e.g. {@code ::text},
625     {@code ::comment}, {@code ::data}, {@code ::leafnode}.</p>
626
627     @param cssQuery a {@link Selector} CSS query
628     @return a list of nodes that match the query (empty if none match)
629     @since 1.21.1
630     */
631    public Nodes<Node> selectNodes(String cssQuery) {
632        return selectNodes(cssQuery, Node.class);
633    }
634
635    /**
636     Find nodes that match the supplied Evaluator, with this element as the starting context. Matched
637     nodes may include this element, or any of its descendents.
638
639     @param evaluator an evaluator
640     @param type the type of node to collect (e.g. {@link Element}, {@link LeafNode}, {@link TextNode} etc)
641     @param <T> the type of node to collect
642     @return a list of nodes that match the query (empty if none match)
643     @since 1.21.1
644     */
645    public <T extends Node> Nodes<T> selectNodes(Evaluator evaluator, Class<T> type) {
646        Validate.notNull(evaluator);
647        return Collector.collectNodes(evaluator, this, type);
648    }
649
650    /**
651     Find nodes that match the supplied {@link Selector} CSS query, with this element as the starting context. Matched
652     nodes may include this element, or any of its descendents.
653     <p>To select specific node types, use {@code ::text}, {@code ::comment}, {@code ::leafnode}, etc. For example, to
654     select all text nodes under {@code p} elements: </p>
655     <pre>    Nodes&lt;TextNode&gt; textNodes = doc.selectNodes("p ::text", TextNode.class);</pre>
656
657     @param cssQuery a {@link Selector} CSS query
658     @param type the type of node to collect (e.g. {@link Element}, {@link LeafNode}, {@link TextNode} etc)
659     @param <T> the type of node to collect
660     @return a list of nodes that match the query (empty if none match)
661     @since 1.21.1
662     */
663    public <T extends Node> Nodes<T> selectNodes(String cssQuery, Class<T> type) {
664        Validate.notEmpty(cssQuery);
665        return selectNodes(evaluatorOf(cssQuery), type);
666    }
667
668    /**
669     Find the first Node that matches the {@link Selector} CSS query, with this element as the starting context.
670     <p>This is effectively the same as calling {@code element.selectNodes(query).first()}, but is more efficient as
671     query
672     execution stops on the first hit.</p>
673     <p>Also known as {@code querySelector()} in the Web DOM.</p>
674
675     @param cssQuery cssQuery a {@link Selector} CSS-like query
676     @return the first matching node, or <b>{@code null}</b> if there is no match.
677     @since 1.21.1
678     @see #expectFirst(String)
679     */
680    public @Nullable <T extends Node> T selectFirstNode(String cssQuery, Class<T> type) {
681        return selectFirstNode(evaluatorOf(cssQuery), type);
682    }
683
684    /**
685     Finds the first Node that matches the supplied Evaluator, with this element as the starting context, or
686     {@code null} if none match.
687
688     @param evaluator an element evaluator
689     @return the first matching node (walking down the tree, starting from this element), or {@code null} if none
690     match.
691     @since 1.21.1
692     */
693    public @Nullable <T extends Node> T selectFirstNode(Evaluator evaluator, Class<T> type) {
694        return Collector.findFirstNode(evaluator, this, type);
695    }
696
697    /**
698     Just like {@link #selectFirstNode(String, Class)}, but if there is no match, throws an
699     {@link IllegalArgumentException}. This is useful if you want to simply abort processing on a failed match.
700
701     @param cssQuery a {@link Selector} CSS-like query
702     @return the first matching node
703     @throws IllegalArgumentException if no match is found
704     @since 1.21.1
705     */
706    public <T extends Node> T expectFirstNode(String cssQuery, Class<T> type) {
707        return Validate.expectNotNull(
708            selectFirstNode(cssQuery, type),
709            parent() != null ?
710                "No nodes matched the query '%s' on element '%s'.":
711                "No nodes matched the query '%s' in the document."
712            , cssQuery, this.tagName()
713        );
714    }
715
716    /**
717     * Checks if this element matches the given {@link Selector} CSS query. Also knows as {@code matches()} in the Web
718     * DOM.
719     *
720     * @param cssQuery a {@link Selector} CSS query
721     * @return if this element matches the query
722     */
723    public boolean is(String cssQuery) {
724        return is(evaluatorOf(cssQuery));
725    }
726
727    /**
728     * Check if this element matches the given evaluator.
729     * @param evaluator an element evaluator
730     * @return if this element matches
731     */
732    public boolean is(Evaluator evaluator) {
733        return evaluator.matches(this.root(), this);
734    }
735
736    /**
737     * Find the closest element up the tree of parents that matches the specified CSS query. Will return itself, an
738     * ancestor, or {@code null} if there is no such matching element.
739     * @param cssQuery a {@link Selector} CSS query
740     * @return the closest ancestor element (possibly itself) that matches the provided evaluator. {@code null} if not
741     * found.
742     */
743    public @Nullable Element closest(String cssQuery) {
744        return closest(evaluatorOf(cssQuery));
745    }
746
747    /**
748     * Find the closest element up the tree of parents that matches the specified evaluator. Will return itself, an
749     * ancestor, or {@code null} if there is no such matching element.
750     * @param evaluator a query evaluator
751     * @return the closest ancestor element (possibly itself) that matches the provided evaluator. {@code null} if not
752     * found.
753     */
754    public @Nullable Element closest(Evaluator evaluator) {
755        Validate.notNull(evaluator);
756        Element el = this;
757        final Element root = root();
758        do {
759            if (evaluator.matches(root, el))
760                return el;
761            el = el.parent();
762        } while (el != null);
763        return null;
764    }
765
766    /**
767     Find Elements that match the supplied {@index XPath} expression.
768     <p>Note that for convenience of writing the Xpath expression, namespaces are disabled, and queries can be
769     expressed using the element's local name only.</p>
770     <p>By default, XPath 1.0 expressions are supported. If you would to use XPath 2.0 or higher, you can provide an
771     alternate XPathFactory implementation:</p>
772     <ol>
773     <li>Add the implementation to your classpath. E.g. to use <a href="https://www.saxonica.com/products/products.xml">Saxon-HE</a>, add <a href="https://mvnrepository.com/artifact/net.sf.saxon/Saxon-HE">net.sf.saxon:Saxon-HE</a> to your build.</li>
774     <li>Set the system property <code>javax.xml.xpath.XPathFactory:jsoup</code> to the implementing classname. E.g.:<br>
775     <code>System.setProperty(W3CDom.XPathFactoryProperty, "net.sf.saxon.xpath.XPathFactoryImpl");</code>
776     </li>
777     </ol>
778
779     @param xpath XPath expression
780     @return matching elements, or an empty list if none match.
781     @see #selectXpath(String, Class)
782     @since 1.14.3
783     */
784    public Elements selectXpath(String xpath) {
785        return new Elements(NodeUtils.selectXpath(xpath, this, Element.class));
786    }
787
788    /**
789     Find Nodes that match the supplied XPath expression.
790     <p>For example, to select TextNodes under {@code p} elements: </p>
791     <pre>List&lt;TextNode&gt; textNodes = doc.selectXpath("//body//p//text()", TextNode.class);</pre>
792     <p>Note that in the jsoup DOM, Attribute objects are not Nodes. To directly select attribute values, do something
793     like:</p>
794     <pre>List&lt;String&gt; hrefs = doc.selectXpath("//a").eachAttr("href");</pre>
795     @param xpath XPath expression
796     @param nodeType the jsoup node type to return
797     @see #selectXpath(String)
798     @return a list of matching nodes
799     @since 1.14.3
800     */
801    public <T extends Node> List<T> selectXpath(String xpath, Class<T> nodeType) {
802        return NodeUtils.selectXpath(xpath, this, nodeType);
803    }
804
805    /**
806     * Insert a node to the end of this Element's children. The incoming node will be re-parented.
807     *
808     * @param child node to add.
809     * @return this Element, for chaining
810     * @see #prependChild(Node)
811     * @see #insertChildren(int, Collection)
812     */
813    public Element appendChild(Node child) {
814        Validate.notNull(child);
815
816        // was - Node#addChildren(child). short-circuits an array create and a loop.
817        reparentChild(child);
818        ensureChildNodes();
819        childNodes.add(child);
820        child.setSiblingIndex(childNodes.size() - 1);
821        return this;
822    }
823
824    /**
825     Insert the given nodes to the end of this Element's children.
826
827     @param children nodes to add
828     @return this Element, for chaining
829     @see #insertChildren(int, Collection)
830     */
831    public Element appendChildren(Collection<? extends Node> children) {
832        insertChildren(-1, children);
833        return this;
834    }
835
836    /**
837     * Add this element to the supplied parent element, as its next child.
838     *
839     * @param parent element to which this element will be appended
840     * @return this element, so that you can continue modifying the element
841     */
842    public Element appendTo(Element parent) {
843        Validate.notNull(parent);
844        parent.appendChild(this);
845        return this;
846    }
847
848    /**
849     * Add a node to the start of this element's children.
850     *
851     * @param child node to add.
852     * @return this element, so that you can add more child nodes or elements.
853     */
854    public Element prependChild(Node child) {
855        Validate.notNull(child);
856
857        addChildren(0, child);
858        return this;
859    }
860
861    /**
862     Insert the given nodes to the start of this Element's children.
863
864     @param children nodes to add
865     @return this Element, for chaining
866     @see #insertChildren(int, Collection)
867     */
868    public Element prependChildren(Collection<? extends Node> children) {
869        insertChildren(0, children);
870        return this;
871    }
872
873
874    /**
875     * Inserts the given child nodes into this element at the specified index. Current nodes will be shifted to the
876     * right. The inserted nodes will be moved from their current parent. To prevent moving, copy the nodes first.
877     *
878     * @param index 0-based index to insert children at. Specify {@code 0} to insert at the start, {@code -1} at the
879     * end
880     * @param children child nodes to insert
881     * @return this element, for chaining.
882     */
883    public Element insertChildren(int index, Collection<? extends Node> children) {
884        Validate.notNull(children, "Children collection to be inserted must not be null.");
885        int currentSize = childNodeSize();
886        if (index < 0) index += currentSize +1; // roll around
887        Validate.isTrue(index >= 0 && index <= currentSize, "Insert position out of bounds.");
888        addChildren(index, children.toArray(new Node[0]));
889        return this;
890    }
891
892    /**
893     * Inserts the given child nodes into this element at the specified index. Current nodes will be shifted to the
894     * right. The inserted nodes will be moved from their current parent. To prevent moving, copy the nodes first.
895     *
896     * @param index 0-based index to insert children at. Specify {@code 0} to insert at the start, {@code -1} at the
897     * end
898     * @param children child nodes to insert
899     * @return this element, for chaining.
900     */
901    public Element insertChildren(int index, Node... children) {
902        Validate.notNull(children, "Children collection to be inserted must not be null.");
903        int currentSize = childNodeSize();
904        if (index < 0) index += currentSize +1; // roll around
905        Validate.isTrue(index >= 0 && index <= currentSize, "Insert position out of bounds.");
906
907        addChildren(index, children);
908        return this;
909    }
910
911    /**
912     * Create a new element by tag name, and add it as this Element's last child.
913     *
914     * @param tagName the name of the tag (e.g. {@code div}).
915     * @return the new element, to allow you to add content to it, e.g.:
916     *  {@code parent.appendElement("h1").attr("id", "header").text("Welcome");}
917     */
918    public Element appendElement(String tagName) {
919        return appendElement(tagName, tag.namespace());
920    }
921
922    /**
923     * Create a new element by tag name and namespace, add it as this Element's last child.
924     *
925     * @param tagName the name of the tag (e.g. {@code div}).
926     * @param namespace the namespace of the tag (e.g. {@link Parser#NamespaceHtml})
927     * @return the new element, in the specified namespace
928     */
929    public Element appendElement(String tagName, String namespace) {
930        Parser parser = NodeUtils.parser(this);
931        Element child = new Element(parser.tagSet().valueOf(tagName, namespace, parser.settings()), baseUri());
932        appendChild(child);
933        return child;
934    }
935
936    /**
937     * Create a new element by tag name, and add it as this Element's first child.
938     *
939     * @param tagName the name of the tag (e.g. {@code div}).
940     * @return the new element, to allow you to add content to it, e.g.:
941     *  {@code parent.prependElement("h1").attr("id", "header").text("Welcome");}
942     */
943    public Element prependElement(String tagName) {
944        return prependElement(tagName, tag.namespace());
945    }
946
947    /**
948     * Create a new element by tag name and namespace, and add it as this Element's first child.
949     *
950     * @param tagName the name of the tag (e.g. {@code div}).
951     * @param namespace the namespace of the tag (e.g. {@link Parser#NamespaceHtml})
952     * @return the new element, in the specified namespace
953     */
954    public Element prependElement(String tagName, String namespace) {
955        Parser parser = NodeUtils.parser(this);
956        Element child = new Element(parser.tagSet().valueOf(tagName, namespace, parser.settings()), baseUri());
957        prependChild(child);
958        return child;
959    }
960
961    /**
962     * Create and append a new TextNode to this element.
963     *
964     * @param text the (un-encoded) text to add
965     * @return this element
966     */
967    public Element appendText(String text) {
968        Validate.notNull(text);
969        TextNode node = new TextNode(text);
970        appendChild(node);
971        return this;
972    }
973
974    /**
975     * Create and prepend a new TextNode to this element.
976     *
977     * @param text the decoded text to add
978     * @return this element
979     */
980    public Element prependText(String text) {
981        Validate.notNull(text);
982        TextNode node = new TextNode(text);
983        prependChild(node);
984        return this;
985    }
986
987    /**
988     * Add inner HTML to this element. The supplied HTML will be parsed, and each node appended to the end of the children.
989     * @param html HTML to add inside this element, after the existing HTML
990     * @return this element
991     * @see #html(String)
992     */
993    public Element append(String html) {
994        Validate.notNull(html);
995        List<Node> nodes = NodeUtils.parser(this).parseFragmentInput(html, this, baseUri());
996        addChildren(nodes.toArray(new Node[0]));
997        return this;
998    }
999
1000    /**
1001     * Add inner HTML into this element. The supplied HTML will be parsed, and each node prepended to the start of the element's children.
1002     * @param html HTML to add inside this element, before the existing HTML
1003     * @return this element
1004     * @see #html(String)
1005     */
1006    public Element prepend(String html) {
1007        Validate.notNull(html);
1008        List<Node> nodes = NodeUtils.parser(this).parseFragmentInput(html, this, baseUri());
1009        addChildren(0, nodes.toArray(new Node[0]));
1010        return this;
1011    }
1012
1013    /**
1014     * Insert the specified HTML into the DOM before this element (as a preceding sibling).
1015     *
1016     * @param html HTML to add before this element
1017     * @return this element, for chaining
1018     * @see #after(String)
1019     */
1020    @Override
1021    public Element before(String html) {
1022        return (Element) super.before(html);
1023    }
1024
1025    /**
1026     * Insert the specified node into the DOM before this node (as a preceding sibling).
1027     * @param node to add before this element
1028     * @return this Element, for chaining
1029     * @see #after(Node)
1030     */
1031    @Override
1032    public Element before(Node node) {
1033        return (Element) super.before(node);
1034    }
1035
1036    /**
1037     * Insert the specified HTML into the DOM after this element (as a following sibling).
1038     *
1039     * @param html HTML to add after this element
1040     * @return this element, for chaining
1041     * @see #before(String)
1042     */
1043    @Override
1044    public Element after(String html) {
1045        return (Element) super.after(html);
1046    }
1047
1048    /**
1049     * Insert the specified node into the DOM after this node (as a following sibling).
1050     * @param node to add after this element
1051     * @return this element, for chaining
1052     * @see #before(Node)
1053     */
1054    @Override
1055    public Element after(Node node) {
1056        return (Element) super.after(node);
1057    }
1058
1059    /**
1060     * Remove all the element's child nodes. Any attributes are left as-is. Each child node has its parent set to
1061     * {@code null}.
1062     * @return this element
1063     */
1064    @Override
1065    public Element empty() {
1066        // Detach each of the children -> parent links:
1067        int size = childNodes.size();
1068        for (int i = 0; i < size; i++)
1069            childNodes.get(i).parentNode = null;
1070        childNodes.clear();
1071        return this;
1072    }
1073
1074    /**
1075     * Wrap the supplied HTML around this element.
1076     *
1077     * @param html HTML to wrap around this element, e.g. {@code <div class="head"></div>}. Can be arbitrarily deep.
1078     * @return this element, for chaining.
1079     */
1080    @Override
1081    public Element wrap(String html) {
1082        return (Element) super.wrap(html);
1083    }
1084
1085    /**
1086     Gets an #id selector for this element, if it has a unique ID. Otherwise, returns an empty string.
1087
1088     @param ownerDoc the document that owns this element, if there is one
1089     */
1090    private String uniqueIdSelector(@Nullable Document ownerDoc) {
1091        String id = id();
1092        if (!id.isEmpty()) { // check if the ID is unique and matches this
1093            String idSel = "#" + escapeCssIdentifier(id);
1094            if (ownerDoc != null) {
1095                Elements els = ownerDoc.select(idSel);
1096                if (els.size() == 1 && els.get(0) == this) return idSel;
1097            } else {
1098                return idSel;
1099            }
1100        }
1101        return EmptyString;
1102    }
1103
1104    /**
1105     Get a CSS selector that will uniquely select this element.
1106     <p>
1107     If the element has an ID, returns #id; otherwise returns the parent (if any) CSS selector, followed by
1108     {@literal '>'}, followed by a unique selector for the element (tag.class.class:nth-child(n)).
1109     </p>
1110
1111     @return the CSS Path that can be used to retrieve the element in a selector.
1112     */
1113    public String cssSelector() {
1114        Document ownerDoc = ownerDocument();
1115        String idSel = uniqueIdSelector(ownerDoc);
1116        if (!idSel.isEmpty()) return idSel;
1117
1118        // No unique ID, work up the parent stack and find either a unique ID to hang from, or just a GP > Parent > Child chain
1119        StringBuilder selector = StringUtil.borrowBuilder();
1120        Element el = this;
1121        while (el != null && !(el instanceof Document)) {
1122            idSel = el.uniqueIdSelector(ownerDoc);
1123            if (!idSel.isEmpty()) {
1124                selector.insert(0, idSel);
1125                break; // found a unique ID to use as ancestor; stop
1126            }
1127            selector.insert(0, el.cssSelectorComponent());
1128            el = el.parent();
1129        }
1130        return StringUtil.releaseBuilder(selector);
1131    }
1132
1133    private String cssSelectorComponent() {
1134        // Escape tagname, and translate HTML namespace ns:tag to CSS namespace syntax ns|tag
1135        String tagName = escapeCssIdentifier(tagName()).replace("\\:", "|");
1136        StringBuilder selector = StringUtil.borrowBuilder().append(tagName);
1137        String classes = classNames().stream().map(TokenQueue::escapeCssIdentifier)
1138                .collect(StringUtil.joining("."));
1139        if (!classes.isEmpty())
1140            selector.append('.').append(classes);
1141
1142        if (parent() == null || parent() instanceof Document) // don't add Document to selector, as will always have a html node
1143            return StringUtil.releaseBuilder(selector);
1144
1145        selector.insert(0, " > ");
1146        if (parent().select(selector.toString()).size() > 1)
1147            selector.append(String.format(
1148                ":nth-child(%d)", elementSiblingIndex() + 1));
1149
1150        return StringUtil.releaseBuilder(selector);
1151    }
1152
1153    /**
1154     * Get sibling elements. If the element has no sibling elements, returns an empty list. An element is not a sibling
1155     * of itself, so will not be included in the returned list.
1156     * @return sibling elements
1157     */
1158    public Elements siblingElements() {
1159        if (parentNode == null)
1160            return new Elements(0);
1161
1162        List<Element> elements = parent().childElementsList();
1163        Elements siblings = new Elements(elements.size() - 1);
1164        for (Element el: elements)
1165            if (el != this)
1166                siblings.add(el);
1167        return siblings;
1168    }
1169
1170
1171
1172    /**
1173     * Get each of the sibling elements that come after this element.
1174     *
1175     * @return each of the element siblings after this element, or an empty list if there are no next sibling elements
1176     */
1177    public Elements nextElementSiblings() {
1178        return nextElementSiblings(true);
1179    }
1180
1181    /**
1182     * Get each of the element siblings before this element.
1183     *
1184     * @return the previous element siblings, or an empty list if there are none.
1185     */
1186    public Elements previousElementSiblings() {
1187        return nextElementSiblings(false);
1188    }
1189
1190    private Elements nextElementSiblings(boolean next) {
1191        Elements els = new Elements();
1192        if (parentNode == null)
1193            return  els;
1194        els.add(this);
1195        return next ?  els.nextAll() : els.prevAll();
1196    }
1197
1198    /**
1199     * Gets the first Element sibling of this element. That may be this element.
1200     * @return the first sibling that is an element (aka the parent's first element child)
1201     */
1202    public Element firstElementSibling() {
1203        if (parent() != null) {
1204            //noinspection DataFlowIssue (not nullable, would be this is no other sibs)
1205            return parent().firstElementChild();
1206        } else
1207            return this; // orphan is its own first sibling
1208    }
1209
1210    /**
1211     * Get the list index of this element in its element sibling list. I.e. if this is the first element
1212     * sibling, returns 0.
1213     * @return position in element sibling list
1214     */
1215    public int elementSiblingIndex() {
1216       if (parent() == null) return 0;
1217       return indexInList(this, parent().childElementsList());
1218    }
1219
1220    /**
1221     * Gets the last element sibling of this element. That may be this element.
1222     * @return the last sibling that is an element (aka the parent's last element child)
1223     */
1224    public Element lastElementSibling() {
1225        if (parent() != null) {
1226            //noinspection DataFlowIssue (not nullable, would be this if no other sibs)
1227            return parent().lastElementChild();
1228        } else
1229            return this;
1230    }
1231
1232    private static <E extends Element> int indexInList(Element search, List<E> elements) {
1233        final int size = elements.size();
1234        for (int i = 0; i < size; i++) {
1235            if (elements.get(i) == search)
1236                return i;
1237        }
1238        return 0;
1239    }
1240
1241    /**
1242     Gets the first child of this Element that is an Element, or {@code null} if there is none.
1243     @return the first Element child node, or null.
1244     @see #firstChild()
1245     @see #lastElementChild()
1246     @since 1.15.2
1247     */
1248    public @Nullable Element firstElementChild() {
1249        int size = childNodes.size();
1250        for (int i = 0; i < size; i++) {
1251            Node node = childNodes.get(i);
1252            if (node instanceof Element) return (Element) node;
1253        }
1254        return null;
1255    }
1256
1257    /**
1258     Gets the last child of this Element that is an Element, or @{code null} if there is none.
1259     @return the last Element child node, or null.
1260     @see #lastChild()
1261     @see #firstElementChild()
1262     @since 1.15.2
1263     */
1264    public @Nullable Element lastElementChild() {
1265        for (int i = childNodes.size() - 1; i >= 0; i--) {
1266            Node node = childNodes.get(i);
1267            if (node instanceof Element) return (Element) node;
1268        }
1269        return null;
1270    }
1271
1272    // DOM type methods
1273
1274    /**
1275     * Finds elements, including and recursively under this element, with the specified tag name.
1276     * @param tagName The tag name to search for (case insensitively).
1277     * @return a matching unmodifiable list of elements. Will be empty if this element and none of its children match.
1278     */
1279    public Elements getElementsByTag(String tagName) {
1280        Validate.notEmpty(tagName);
1281        tagName = normalize(tagName);
1282
1283        return Collector.collect(new Evaluator.Tag(tagName), this);
1284    }
1285
1286    /**
1287     * Find an element by ID, including or under this element.
1288     * <p>
1289     * Note that this finds the first matching ID, starting with this element. If you search down from a different
1290     * starting point, it is possible to find a different element by ID. For unique element by ID within a Document,
1291     * use {@link Document#getElementById(String)}
1292     * @param id The ID to search for.
1293     * @return The first matching element by ID, starting with this element, or null if none found.
1294     */
1295    public @Nullable Element getElementById(String id) {
1296        Validate.notEmpty(id);
1297        return Collector.findFirst(new Evaluator.Id(id), this);
1298    }
1299
1300    /**
1301     * Find elements that have this class, including or under this element. Case-insensitive.
1302     * <p>
1303     * Elements can have multiple classes (e.g. {@code <div class="header round first">}). This method
1304     * checks each class, so you can find the above with {@code el.getElementsByClass("header");}.
1305     *
1306     * @param className the name of the class to search for.
1307     * @return elements with the supplied class name, empty if none
1308     * @see #hasClass(String)
1309     * @see #classNames()
1310     */
1311    public Elements getElementsByClass(String className) {
1312        Validate.notEmpty(className);
1313
1314        return Collector.collect(new Evaluator.Class(className), this);
1315    }
1316
1317    /**
1318     * Find elements that have a named attribute set. Case-insensitive.
1319     *
1320     * @param key name of the attribute, e.g. {@code href}
1321     * @return elements that have this attribute, empty if none
1322     */
1323    public Elements getElementsByAttribute(String key) {
1324        Validate.notEmpty(key);
1325        key = key.trim();
1326
1327        return Collector.collect(new Evaluator.Attribute(key), this);
1328    }
1329
1330    /**
1331     * Find elements that have an attribute name starting with the supplied prefix. Use {@code data-} to find elements
1332     * that have HTML5 datasets.
1333     * @param keyPrefix name prefix of the attribute e.g. {@code data-}
1334     * @return elements that have attribute names that start with the prefix, empty if none.
1335     */
1336    public Elements getElementsByAttributeStarting(String keyPrefix) {
1337        Validate.notEmpty(keyPrefix);
1338        keyPrefix = keyPrefix.trim();
1339
1340        return Collector.collect(new Evaluator.AttributeStarting(keyPrefix), this);
1341    }
1342
1343    /**
1344     * Find elements that have an attribute with the specific value. Case-insensitive.
1345     *
1346     * @param key name of the attribute
1347     * @param value value of the attribute
1348     * @return elements that have this attribute with this value, empty if none
1349     */
1350    public Elements getElementsByAttributeValue(String key, String value) {
1351        return Collector.collect(new Evaluator.AttributeWithValue(key, value), this);
1352    }
1353
1354    /**
1355     * Find elements that either do not have this attribute, or have it with a different value. Case-insensitive.
1356     *
1357     * @param key name of the attribute
1358     * @param value value of the attribute
1359     * @return elements that do not have a matching attribute
1360     */
1361    public Elements getElementsByAttributeValueNot(String key, String value) {
1362        return Collector.collect(new Evaluator.AttributeWithValueNot(key, value), this);
1363    }
1364
1365    /**
1366     * Find elements that have attributes that start with the value prefix. Case-insensitive.
1367     *
1368     * @param key name of the attribute
1369     * @param valuePrefix start of attribute value
1370     * @return elements that have attributes that start with the value prefix
1371     */
1372    public Elements getElementsByAttributeValueStarting(String key, String valuePrefix) {
1373        return Collector.collect(new Evaluator.AttributeWithValueStarting(key, valuePrefix), this);
1374    }
1375
1376    /**
1377     * Find elements that have attributes that end with the value suffix. Case-insensitive.
1378     *
1379     * @param key name of the attribute
1380     * @param valueSuffix end of the attribute value
1381     * @return elements that have attributes that end with the value suffix
1382     */
1383    public Elements getElementsByAttributeValueEnding(String key, String valueSuffix) {
1384        return Collector.collect(new Evaluator.AttributeWithValueEnding(key, valueSuffix), this);
1385    }
1386
1387    /**
1388     * Find elements that have attributes whose value contains the match string. Case-insensitive.
1389     *
1390     * @param key name of the attribute
1391     * @param match substring of value to search for
1392     * @return elements that have attributes containing this text
1393     */
1394    public Elements getElementsByAttributeValueContaining(String key, String match) {
1395        return Collector.collect(new Evaluator.AttributeWithValueContaining(key, match), this);
1396    }
1397
1398    /**
1399     * Find elements that have an attribute whose value matches the supplied regular expression.
1400     * @param key name of the attribute
1401     * @param pattern compiled regular expression to match against attribute values
1402     * @return elements that have attributes matching this regular expression
1403     */
1404    public Elements getElementsByAttributeValueMatching(String key, Pattern pattern) {
1405        return Collector.collect(new Evaluator.AttributeWithValueMatching(key, pattern), this);
1406    }
1407
1408    /**
1409     * Find elements that have attributes whose values match the supplied regular expression.
1410     * @param key name of the attribute
1411     * @param regex regular expression to match against attribute values. You can use <a href="http://java.sun.com/docs/books/tutorial/essential/regex/pattern.html#embedded">embedded flags</a> (such as {@code (?i)} and {@code (?m)}) to control regex options.
1412     * @return elements that have attributes matching this regular expression
1413     */
1414    public Elements getElementsByAttributeValueMatching(String key, String regex) {
1415        Regex pattern;
1416        try {
1417            pattern = Regex.compile(regex);
1418        } catch (PatternSyntaxException e) {
1419            throw new IllegalArgumentException("Pattern syntax error: " + regex, e);
1420        }
1421        return Collector.collect(new Evaluator.AttributeWithValueMatching(key, pattern), this);
1422    }
1423
1424    /**
1425     * Find elements whose sibling index is less than the supplied index.
1426     * @param index 0-based index
1427     * @return elements less than index
1428     */
1429    public Elements getElementsByIndexLessThan(int index) {
1430        return Collector.collect(new Evaluator.IndexLessThan(index), this);
1431    }
1432
1433    /**
1434     * Find elements whose sibling index is greater than the supplied index.
1435     * @param index 0-based index
1436     * @return elements greater than index
1437     */
1438    public Elements getElementsByIndexGreaterThan(int index) {
1439        return Collector.collect(new Evaluator.IndexGreaterThan(index), this);
1440    }
1441
1442    /**
1443     * Find elements whose sibling index is equal to the supplied index.
1444     * @param index 0-based index
1445     * @return elements equal to index
1446     */
1447    public Elements getElementsByIndexEquals(int index) {
1448        return Collector.collect(new Evaluator.IndexEquals(index), this);
1449    }
1450
1451    /**
1452     * Find elements that contain the specified string. The search is case-insensitive. The text may appear directly
1453     * in the element, or in any of its descendants.
1454     * @param searchText to look for in the element's text
1455     * @return elements that contain the string, case-insensitive.
1456     * @see Element#text()
1457     */
1458    public Elements getElementsContainingText(String searchText) {
1459        return Collector.collect(new Evaluator.ContainsText(searchText), this);
1460    }
1461
1462    /**
1463     * Find elements that directly contain the specified string. The search is case-insensitive. The text must appear directly
1464     * in the element, not in any of its descendants.
1465     * @param searchText to look for in the element's own text
1466     * @return elements that contain the string, case-insensitive.
1467     * @see Element#ownText()
1468     */
1469    public Elements getElementsContainingOwnText(String searchText) {
1470        return Collector.collect(new Evaluator.ContainsOwnText(searchText), this);
1471    }
1472
1473    /**
1474     * Find elements whose text matches the supplied regular expression.
1475     * @param pattern regular expression to match text against
1476     * @return elements matching the supplied regular expression.
1477     * @see Element#text()
1478     */
1479    public Elements getElementsMatchingText(Pattern pattern) {
1480        return Collector.collect(new Evaluator.Matches(pattern), this);
1481    }
1482
1483    /**
1484     * Find elements whose text matches the supplied regular expression.
1485     * @param regex regular expression to match text against. You can use <a href="http://java.sun.com/docs/books/tutorial/essential/regex/pattern.html#embedded">embedded flags</a> (such as {@code (?i)} and {@code (?m)}) to control regex options.
1486     * @return elements matching the supplied regular expression.
1487     * @see Element#text()
1488     */
1489    public Elements getElementsMatchingText(String regex) {
1490        Regex pattern;
1491        try {
1492            pattern = Regex.compile(regex);
1493        } catch (PatternSyntaxException e) {
1494            throw new IllegalArgumentException("Pattern syntax error: " + regex, e);
1495        }
1496        return Collector.collect(new Evaluator.Matches(pattern), this);
1497    }
1498
1499    /**
1500     * Find elements whose own text matches the supplied regular expression.
1501     * @param pattern regular expression to match text against
1502     * @return elements matching the supplied regular expression.
1503     * @see Element#ownText()
1504     */
1505    public Elements getElementsMatchingOwnText(Pattern pattern) {
1506        return Collector.collect(new Evaluator.MatchesOwn(pattern), this);
1507    }
1508
1509    /**
1510     * Find elements whose own text matches the supplied regular expression.
1511     * @param regex regular expression to match text against. You can use <a href="http://java.sun.com/docs/books/tutorial/essential/regex/pattern.html#embedded">embedded flags</a> (such as {@code (?i)} and {@code (?m)}) to control regex options.
1512     * @return elements matching the supplied regular expression.
1513     * @see Element#ownText()
1514     */
1515    public Elements getElementsMatchingOwnText(String regex) {
1516        Regex pattern;
1517        try {
1518            pattern = Regex.compile(regex);
1519        } catch (PatternSyntaxException e) {
1520            throw new IllegalArgumentException("Pattern syntax error: " + regex, e);
1521        }
1522        return Collector.collect(new Evaluator.MatchesOwn(pattern), this);
1523    }
1524
1525    /**
1526     * Find all elements under this element (including self, and children of children).
1527     *
1528     * @return all elements
1529     */
1530    public Elements getAllElements() {
1531        return Collector.collect(new Evaluator.AllElements(), this);
1532    }
1533
1534    /**
1535     Gets the <b>normalized, combined text</b> of this element and all its children. Whitespace is normalized and
1536     trimmed.
1537     <p>For example, given HTML {@code <p>Hello  <b>there</b> now! </p>}, {@code p.text()} returns {@code "Hello there
1538    now!"}
1539     <p>If you do not want normalized text, use {@link #wholeText()}. If you want just the text of this node (and not
1540     children), use {@link #ownText()}.
1541     <p>This method returns normalized, readable plain text for downstream uses such as data extraction,
1542     indexing, and accessibility-oriented processing. The contents of data nodes (such as
1543     {@code <script>} tags) are not considered text. Use {@link #data()} or {@link #html()} to retrieve
1544     that content.
1545
1546     @return decoded, normalized text, or empty string if none.
1547     @see #wholeText()
1548     @see #ownText()
1549     @see #textNodes()
1550     */
1551    public String text() {
1552        final StringBuilder accum = StringUtil.borrowBuilder();
1553        new TextAccumulator(accum).traverse(this);
1554        return StringUtil.releaseBuilder(accum).trim();
1555    }
1556
1557    private static class TextAccumulator implements NodeVisitor {
1558        private final StringBuilder accum;
1559
1560        public TextAccumulator(StringBuilder accum) {
1561            this.accum = accum;
1562        }
1563
1564        @Override public void head(Node node, int depth) {
1565            if (node instanceof TextNode) {
1566                TextNode textNode = (TextNode) node;
1567                appendNormalisedText(accum, textNode);
1568            } else if (node instanceof Element) {
1569                Element element = (Element) node;
1570                // add a synthetic space before leading blocks and readable boundaries when text would otherwise run together
1571                if (accum.length() > 0 && needsLeadingTextSeparator(element) && !lastCharIsWhitespace(accum))
1572                    accum.append(' ');
1573            }
1574        }
1575
1576        @Override public void tail(Node node, int depth) {
1577            // make sure there is a space between block or readable-boundary tags and immediately following text nodes or inline elements.
1578            if (node instanceof Element) {
1579                Element element = (Element) node;
1580                Node next = node.nextSibling();
1581                if (needsTrailingTextSeparator(element) &&
1582                    (next instanceof TextNode || next instanceof Element && ((Element) next).tag.isInline()) &&
1583                    !lastCharIsWhitespace(accum))
1584                    accum.append(' ');
1585            }
1586
1587        }
1588
1589        /** check if an element should separate preceding text during text() */
1590        private static boolean needsLeadingTextSeparator(Element element) {
1591            return element.isBlock()
1592                || element.nameIs("br")
1593                || element.tag.is(Tag.TextBoundary) && element.childNodeSize() > 0 && element.hasText();
1594        }
1595
1596        /** check if an element should separate following text during text() */
1597        private static boolean needsTrailingTextSeparator(Element element) {
1598            return element.tag.is(Tag.TextBoundary)
1599                || !element.tag.isInline()
1600                || hasBlockChild(element);
1601        }
1602
1603        /** check if an inline wrapper contains direct block children and should close with a separator */
1604        private static boolean hasBlockChild(Element element) {
1605            for (int i = 0; i < element.childNodeSize(); i++) {
1606                Node child = element.childNode(i);
1607                if (child instanceof Element && ((Element) child).isBlock())
1608                    return true;
1609            }
1610            return false;
1611        }
1612    }
1613
1614    /**
1615     Get the decoded text of this element and its children, preserving source whitespace and newlines from text nodes.
1616     Unlike {@link #text()}, no separators are inferred around element boundaries; {@code <br>} elements are returned
1617     as newlines.
1618     @return decoded, non-normalized text
1619     @see #text()
1620     @see #wholeOwnText()
1621     */
1622    public String wholeText() {
1623        return wholeTextOf(nodeStream());
1624    }
1625
1626    /**
1627     An Element's nodeValue is its whole own text.
1628     */
1629    @Override
1630    public String nodeValue() {
1631        return wholeOwnText();
1632    }
1633
1634    private static String wholeTextOf(Stream<Node> stream) {
1635        return stream.map(node -> {
1636            if (node instanceof TextNode) return ((TextNode) node).getWholeText();
1637            if (node.nameIs("br")) return "\n";
1638            return "";
1639        }).collect(StringUtil.joining(""));
1640    }
1641
1642    /**
1643     Get the non-normalized, decoded text of this element, <b>not including</b> any child elements, including any
1644     newlines and spaces present in the original source.
1645     @return decoded, non-normalized text that is a direct child of this Element
1646     @see #text()
1647     @see #wholeText()
1648     @see #ownText()
1649     @since 1.15.1
1650     */
1651    public String wholeOwnText() {
1652        return wholeTextOf(childNodes.stream());
1653    }
1654
1655    /**
1656     * Gets the (normalized) text owned by this element only; does not get the combined text of all children.
1657     * <p>
1658     * For example, given HTML {@code <p>Hello <b>there</b> now!</p>}, {@code p.ownText()} returns {@code "Hello now!"},
1659     * whereas {@code p.text()} returns {@code "Hello there now!"}.
1660     * Note that the text within the {@code b} element is not returned, as it is not a direct child of the {@code p} element.
1661     *
1662     * @return decoded text, or empty string if none.
1663     * @see #text()
1664     * @see #textNodes()
1665     */
1666    public String ownText() {
1667        StringBuilder sb = StringUtil.borrowBuilder();
1668        ownText(sb);
1669        return StringUtil.releaseBuilder(sb).trim();
1670    }
1671
1672    private void ownText(StringBuilder accum) {
1673        for (int i = 0; i < childNodeSize(); i++) {
1674            Node child = childNodes.get(i);
1675            if (child instanceof TextNode) {
1676                TextNode textNode = (TextNode) child;
1677                appendNormalisedText(accum, textNode);
1678            } else if (child.nameIs("br") && !lastCharIsWhitespace(accum)) {
1679                accum.append(" ");
1680            }
1681        }
1682    }
1683
1684    private static void appendNormalisedText(StringBuilder accum, TextNode textNode) {
1685        String text = textNode.getWholeText();
1686        if (preserveWhitespace(textNode.parentNode) || textNode instanceof CDataNode)
1687            accum.append(text);
1688        else
1689            StringUtil.appendNormalisedWhitespace(accum, text, lastCharIsWhitespace(accum));
1690    }
1691
1692    static boolean preserveWhitespace(@Nullable Node node) {
1693        // looks only at this element and five levels up, to prevent recursion & needless stack searches
1694        if (node instanceof Element) {
1695            Element el = (Element) node;
1696            int i = 0;
1697            do {
1698                if (el.tag.preserveWhitespace())
1699                    return true;
1700                el = el.parent();
1701                i++;
1702            } while (i < 6 && el != null);
1703        }
1704        return false;
1705    }
1706
1707    /**
1708     * Set the text of this element. Any existing contents (text or elements) will be cleared.
1709     * <p>As a special case, for {@code <script>} and {@code <style>} tags, the input text will be treated as data,
1710     * not visible text.</p>
1711     * @param text decoded text
1712     * @return this element
1713     */
1714    public Element text(String text) {
1715        Validate.notNull(text);
1716        empty();
1717        // special case for script/style in HTML (or customs): should be data node
1718        if (tag().is(Tag.Data))
1719            appendChild(new DataNode(text));
1720        else
1721            appendChild(new TextNode(text));
1722
1723        return this;
1724    }
1725
1726    /**
1727     Checks if the current element or any of its child elements contain non-whitespace text.
1728     @return {@code true} if the element has non-blank text content, {@code false} otherwise.
1729     */
1730    public boolean hasText() {
1731        AtomicBoolean hasText = new AtomicBoolean(false);
1732        filter((node, depth) -> {
1733            if (node instanceof TextNode) {
1734                TextNode textNode = (TextNode) node;
1735                if (!textNode.isBlank()) {
1736                    hasText.set(true);
1737                    return NodeFilter.FilterResult.STOP;
1738                }
1739            }
1740            return NodeFilter.FilterResult.CONTINUE;
1741        });
1742        return hasText.get();
1743    }
1744
1745    /**
1746     Get the combined data of this element. Data is e.g. the inside of a {@code <script>} tag. Note that data is NOT the
1747     plain text of the element. Use {@link #text()} to get normalized, readable text for extraction, indexing, or
1748     accessibility-oriented processing, and {@code data()} for the contents of scripts, comments, CSS styles, etc.
1749
1750     @return the data, or empty string if none
1751     @see #dataNodes()
1752     */
1753    public String data() {
1754        StringBuilder sb = StringUtil.borrowBuilder();
1755        traverse((childNode, depth) -> {
1756            if (childNode instanceof DataNode) {
1757                DataNode data = (DataNode) childNode;
1758                sb.append(data.getWholeData());
1759            } else if (childNode instanceof Comment) {
1760                Comment comment = (Comment) childNode;
1761                sb.append(comment.getData());
1762            } else if (childNode instanceof CDataNode) {
1763                // this shouldn't really happen because the html parser won't see the cdata as anything special when parsing script.
1764                // but in case another type gets through.
1765                CDataNode cDataNode = (CDataNode) childNode;
1766                sb.append(cDataNode.getWholeText());
1767            }
1768        });
1769        return StringUtil.releaseBuilder(sb);
1770    }
1771
1772    /**
1773     * Gets the literal value of this element's "class" attribute, which may include multiple class names, space
1774     * separated. (E.g. on <code>&lt;div class="header gray"&gt;</code> returns, "<code>header gray</code>")
1775     * @return The literal class attribute, or <b>empty string</b> if no class attribute set.
1776     */
1777    public String className() {
1778        return attr("class").trim();
1779    }
1780
1781    /**
1782     Get each of the element's class names. E.g. on element {@code <div class="header gray">},
1783     returns a set of two elements {@code "header", "gray"}.
1784     <p>Note that modifications to this set are not pushed to the backing {@code class} attribute; use
1785     {@link #classNames(Set)} to persist them.</p>
1786     <p>Use {@link #classList()} for a more efficient, read-only list that preserves duplicate class names.</p>
1787
1788     @return set of class names, empty if no class attribute
1789     @see #classNames(Set)
1790     @see #hasClass(String) 
1791     @see #classList()
1792     */
1793    public Set<String> classNames() {
1794        Set<String> classNames = new LinkedHashSet<>(4);
1795        if (attributes == null) return classNames;
1796
1797        String classAttr = attributes.getIgnoreCase("class");
1798        int len = classAttr.length();
1799        for (int i = 0; i < len; ) {
1800            int start = nextClassStart(classAttr, i, len);
1801            if (start == len) break;
1802
1803            int end = nextClassEnd(classAttr, start, len);
1804            classNames.add(classToken(classAttr, start, end));
1805            i = end;
1806        }
1807        return classNames;
1808    }
1809
1810    /**
1811     Get each of the element's class names, in attribute order. E.g. on element
1812     {@code <div class="header gray">}, returns a list of two elements {@code "header", "gray"}.
1813     <p>This immutable snapshot preserves duplicate class names, and is more memory efficient than
1814     {@link #classNames()} when a read-only result is sufficient, particularly for elements without class names.
1815     Use {@link #classNames()} for a mutable set of unique class names.</p>
1816
1817     @return immutable list of class names, empty if no class attribute
1818     @see #classNames()
1819     @see #hasClass(String)
1820     @since 1.23.1
1821     */
1822    public List<String> classList() {
1823        if (attributes == null) return Collections.emptyList();
1824
1825        String attr = attributes.getIgnoreCase("class");
1826        int len = attr.length();
1827        int start = nextClassStart(attr, 0, len);
1828        if (start == len) return Collections.emptyList();
1829
1830        int end = nextClassEnd(attr, start, len);
1831        String first = classToken(attr, start, end);
1832        start = nextClassStart(attr, end, len);
1833        if (start == len) return Collections.singletonList(first);
1834
1835        List<String> classes = new ArrayList<>(4);
1836        classes.add(first);
1837        do {
1838            end = nextClassEnd(attr, start, len);
1839            classes.add(classToken(attr, start, end));
1840            start = nextClassStart(attr, end, len);
1841        } while (start < len);
1842        return Collections.unmodifiableList(classes);
1843    }
1844
1845    /**
1846     Find the next class token start.
1847     */
1848    private static int nextClassStart(String classAttr, int offset, int len) {
1849        while (offset < len && StringUtil.isWhitespace(classAttr.charAt(offset))) offset++;
1850        return offset;
1851    }
1852
1853    /**
1854     Find the next class token end.
1855     */
1856    private static int nextClassEnd(String classAttr, int offset, int len) {
1857        while (offset < len && !StringUtil.isWhitespace(classAttr.charAt(offset))) offset++;
1858        return offset;
1859    }
1860
1861    /**
1862     Returns the class token while preserving the original string for a single unpadded class.
1863     */
1864    private static String classToken(String classAttr, int start, int end) {
1865        return start == 0 && end == classAttr.length() ? classAttr : classAttr.substring(start, end);
1866    }
1867
1868    /**
1869     Set the element's {@code class} attribute to the supplied class names.
1870     @param classNames set of classes
1871     @return this element, for chaining
1872     */
1873    public Element classNames(Set<String> classNames) {
1874        Validate.notNull(classNames);
1875        if (classNames.isEmpty()) {
1876            attributes().remove("class");
1877        } else {
1878            attributes().put("class", StringUtil.join(classNames, " "));
1879        }
1880        return this;
1881    }
1882
1883    /**
1884     * Tests if this element has a class. Case-insensitive.
1885     * @param className name of class to check for
1886     * @return true if it does, false if not
1887     */
1888    // performance sensitive
1889    public boolean hasClass(String className) {
1890        if (attributes == null) return false;
1891
1892        final String classAttr = attributes.getIgnoreCase("class");
1893        final int len = classAttr.length();
1894        final int wantLen = className.length();
1895
1896        if (len == 0 || len < wantLen) return false;
1897
1898        // if both lengths are equal, only need to compare the className with the attribute
1899        if (len == wantLen) return className.equalsIgnoreCase(classAttr);
1900
1901        // otherwise, scan for whitespace and compare regions (with no string or list allocations)
1902        for (int i = 0; i < len; ) {
1903            int start = nextClassStart(classAttr, i, len);
1904            if (start == len) return false;
1905
1906            int end = nextClassEnd(classAttr, start, len);
1907            if (end - start == wantLen && classAttr.regionMatches(true, start, className, 0, wantLen)) return true;
1908            i = end;
1909        }
1910
1911        return false;
1912    }
1913
1914    /**
1915     Add a class name to this element's {@code class} attribute.
1916     @param className class name to add
1917     @return this element
1918     */
1919    public Element addClass(String className) {
1920        Validate.notNull(className);
1921
1922        Set<String> classes = classNames();
1923        classes.add(className);
1924        classNames(classes);
1925
1926        return this;
1927    }
1928
1929    /**
1930     Remove a class name from this element's {@code class} attribute.
1931     @param className class name to remove
1932     @return this element
1933     */
1934    public Element removeClass(String className) {
1935        Validate.notNull(className);
1936
1937        Set<String> classes = classNames();
1938        classes.remove(className);
1939        classNames(classes);
1940
1941        return this;
1942    }
1943
1944    /**
1945     Toggle a class name on this element's {@code class} attribute: if present, remove it; otherwise add it.
1946     @param className class name to toggle
1947     @return this element
1948     */
1949    public Element toggleClass(String className) {
1950        Validate.notNull(className);
1951
1952        Set<String> classes = classNames();
1953        if (classes.contains(className))
1954            classes.remove(className);
1955        else
1956            classes.add(className);
1957        classNames(classes);
1958
1959        return this;
1960    }
1961
1962    /**
1963     * Get the value of a form element (input, textarea, etc).
1964     * @return the value of the form element, or empty string if not set.
1965     */
1966    public String val() {
1967        if (elementIs("textarea", NamespaceHtml))
1968            return text();
1969        else
1970            return attr("value");
1971    }
1972
1973    /**
1974     * Set the value of a form element (input, textarea, etc).
1975     * @param value value to set
1976     * @return this element (for chaining)
1977     */
1978    public Element val(String value) {
1979        if (elementIs("textarea", NamespaceHtml))
1980            text(value);
1981        else
1982            attr("value", value);
1983        return this;
1984    }
1985
1986    /**
1987     Get the source range (start and end positions) of the end (closing) tag for this Element. Position tracking must be
1988     enabled before parsing the content.
1989     @return the range of the closing tag for this element, or {@code untracked} if its range was not tracked.
1990     @see org.jsoup.parser.Parser#setTrackPosition(boolean)
1991     @see Node#sourceRange()
1992     @see Range#isImplicit()
1993     @since 1.15.2
1994     */
1995    public Range endSourceRange() {
1996        return Range.ofEnd(this);
1997    }
1998
1999    @Override
2000    void outerHtmlHead(final QuietAppendable accum, Document.OutputSettings out) {
2001        String tagName = safeTagName(out.syntax());
2002        accum.append('<').append(tagName);
2003        if (attributes != null) attributes.html(accum, out);
2004
2005        if (childNodes.isEmpty()) {
2006            boolean xmlMode = out.syntax() == xml || !tag.namespace().equals(NamespaceHtml);
2007            if (xmlMode && (tag.is(Tag.SeenSelfClose) || (tag.isKnownTag() && (tag.isEmpty() || tag.isSelfClosing())))) {
2008                accum.append(" />");
2009            } else if (!xmlMode && tag.isEmpty()) { // html void element
2010                accum.append('>');
2011            } else {
2012                accum.append("></").append(tagName).append('>');
2013            }
2014        } else {
2015            accum.append('>');
2016        }
2017    }
2018
2019    @Override
2020    void outerHtmlTail(QuietAppendable accum, Document.OutputSettings out) {
2021        if (!childNodes.isEmpty())
2022            accum.append("</").append(safeTagName(out.syntax())).append('>');
2023        // if empty, we have already closed in htmlHead
2024    }
2025
2026    /* If XML syntax, normalizes < to _ in tag name. */
2027    @Nullable private String safeTagName(Document.OutputSettings.Syntax syntax) {
2028        return syntax == xml ? Normalizer.xmlSafeTagName(tagName()) : tagName();
2029    }
2030
2031    /**
2032     Get the inner HTML of this element. For example, on a {@code <div>} with one empty {@code <p>}, this returns
2033     {@code <p></p>}, whereas {@link #outerHtml()} returns {@code <div><p></p></div>}.
2034
2035     @return the inner HTML of this element
2036     @see #html(Appendable)
2037     @see #outerHtml()
2038     */
2039    public String html() {
2040        StringBuilder sb = StringUtil.borrowBuilder();
2041        html(sb);
2042        String html = StringUtil.releaseBuilder(sb);
2043        return NodeUtils.outputSettings(this).prettyPrint() ? html.trim() : html;
2044    }
2045
2046    /**
2047     Append the inner HTML of this element to the supplied {@link Appendable}.
2048
2049     @param appendable the {@link Appendable} that will receive the HTML
2050     @return the supplied {@link Appendable}, for chaining
2051     @throws org.jsoup.SerializationException if the appendable throws an IOException
2052     @see #html()
2053     @see #outerHtml(Appendable)
2054     @see #outerHtml()
2055     */
2056    @Override
2057    public <T extends Appendable> T html(T appendable) {
2058        html(QuietAppendable.wrap(appendable));
2059        return appendable;
2060    }
2061
2062    /** Append the inner HTML of this element to the supplied {@link QuietAppendable}. */
2063    void html(QuietAppendable accum) {
2064        Node child = firstChild();
2065        if (child != null) {
2066            Printer printer = Printer.printerFor(child, accum);
2067            while (child != null) {
2068                printer.traverse(child);
2069                child = child.nextSibling();
2070            }
2071        }
2072    }
2073
2074    /**
2075     * Set this element's inner HTML. Clears the existing HTML first.
2076     * @param html HTML to parse and set into this element
2077     * @return this element
2078     * @see #append(String)
2079     */
2080    public Element html(String html) {
2081        empty();
2082        append(html);
2083        return this;
2084    }
2085
2086    @Override
2087    public Element clone() {
2088        return (Element) super.clone();
2089    }
2090
2091    @Override
2092    public Element shallowClone() {
2093        // simpler than implementing a clone version with no child copy
2094        String baseUri = baseUri();
2095        if (baseUri.isEmpty()) baseUri = null; // saves setting a blank internal attribute
2096        return new Element(tag, baseUri, attributes == null ? null : attributes.clone());
2097    }
2098
2099    @Override
2100    protected Element doClone(@Nullable Node parent) {
2101        Element clone = (Element) super.doClone(parent);
2102        clone.childNodes = new NodeList(childNodes.size());
2103        clone.childNodes.addAll(childNodes); // the children then get iterated and cloned in Node.clone
2104        if (attributes != null) {
2105            clone.attributes = attributes.clone();
2106            // clear any cached children
2107            clone.attributes.userData(childElsKey, null);
2108        }
2109
2110        return clone;
2111    }
2112
2113    // overrides of Node for call chaining
2114    @Override
2115    public Element clearAttributes() {
2116        if (attributes != null) {
2117            super.clearAttributes(); // keeps internal attributes via iterator
2118            if (attributes.size == 0)
2119                attributes = null; // only remove entirely if no internal attributes
2120        }
2121
2122        return this;
2123    }
2124
2125    @Override
2126    public Element removeAttr(String attributeKey) {
2127        return (Element) super.removeAttr(attributeKey);
2128    }
2129
2130    @Override
2131    public Element root() {
2132        return (Element) super.root(); // probably a document, but always at least an element
2133    }
2134
2135    @Override
2136    public Element traverse(NodeVisitor nodeVisitor) {
2137        return (Element) super.traverse(nodeVisitor);
2138    }
2139
2140    @Override
2141    public Element forEachNode(Consumer<? super Node> action) {
2142        return (Element) super.forEachNode(action);
2143    }
2144
2145    /**
2146     Perform the supplied action on this Element and each of its descendant Elements, during a depth-first traversal.
2147     Elements may be inspected, changed, added, replaced, or removed.
2148     @param action the function to perform on the element
2149     @see Node#forEachNode(Consumer)
2150     */
2151    @Override
2152    public void forEach(Consumer<? super Element> action) {
2153        stream().forEach(action);
2154    }
2155
2156    /**
2157     Returns an Iterator that iterates this Element and each of its descendant Elements, in document order.
2158     @return an Iterator
2159     */
2160    @Override
2161    public Iterator<Element> iterator() {
2162        return new NodeIterator<>(this, Element.class);
2163    }
2164
2165    @Override
2166    public Element filter(NodeFilter nodeFilter) {
2167        return  (Element) super.filter(nodeFilter);
2168    }
2169
2170    static final class NodeList extends ArrayList<Node> {
2171        /** Tracks if the children have valid sibling indices. We only need to reindex on siblingIndex() demand. */
2172        boolean validChildren = true;
2173
2174        public NodeList(int size) {
2175            super(size);
2176        }
2177
2178        /** The modCount is used to invalidate the cached element children. */
2179        int modCount() {
2180            return this.modCount;
2181        }
2182
2183        void incrementMod() {
2184            this.modCount++;
2185        }
2186    }
2187
2188    void reindexChildren() {
2189        final int size = childNodes.size();
2190        for (int i = 0; i < size; i++) {
2191            childNodes.get(i).setSiblingIndex(i);
2192        }
2193        childNodes.validChildren = true;
2194    }
2195
2196    void invalidateChildren() {
2197        childNodes.validChildren = false;
2198    }
2199
2200    boolean hasValidChildren() {
2201        return childNodes.validChildren;
2202    }
2203}