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