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