001package org.jsoup.nodes;
002
003import ai.platon.pulsar.jsoup.ext.NodeExt;
004import org.jsoup.helper.Validate;
005import org.jsoup.internal.QuietAppendable;
006import org.jsoup.internal.StringUtil;
007import org.jsoup.parser.ParseSettings;
008import org.jsoup.select.NodeFilter;
009import org.jsoup.select.NodeVisitor;
010import org.jspecify.annotations.Nullable;
011
012import java.io.IOException;
013import java.util.ArrayList;
014import java.util.Arrays;
015import java.util.Collections;
016import java.util.Iterator;
017import java.util.LinkedList;
018import java.util.List;
019import java.util.function.Consumer;
020import java.util.stream.Stream;
021
022/**
023 The base, abstract Node model. {@link Element}, {@link Document}, {@link Comment}, {@link TextNode}, et al.,
024 are instances of Node.
025
026 @author Jonathan Hedley, jonathan@hedley.net */
027public abstract class Node implements Cloneable {
028    static final List<Node> EmptyNodes = Collections.emptyList();
029    static final String EmptyString = "";
030    @Nullable Element parentNode; // Nodes don't always have parents
031    int siblingIndex;
032    final NodeExt extension = new NodeExt();
033
034    /**
035     * Default constructor. Doesn't set up base uri, children, or attributes; use with caution.
036     */
037    protected Node() {
038    }
039
040    /**
041     Get the node name of this node. Use for debugging purposes and not logic switching (for that, use instanceof).
042     @return node name
043     */
044    public abstract String nodeName();
045
046    /**
047     Get the normalized name of this node. For node types other than Element, this is the same as {@link #nodeName()}.
048     For an Element, will be the lower-cased tag name.
049     @return normalized node name
050     @since 1.15.4.
051     */
052    public String normalName() {
053        return nodeName();
054    }
055
056    /**
057     Get the node's value. For a TextNode, the whole text; for a Comment, the comment data; for an Element,
058     wholeOwnText. Returns "" if there is no value.
059     @return the node's value
060     */
061    public String nodeValue() {
062        return "";
063    }
064
065    /**
066     Test if this node has the specified normalized name, in any namespace.
067     * @param normalName a normalized element name (e.g. {@code div}).
068     * @return true if the element's normal name matches exactly
069     * @since 1.17.2
070     */
071    public boolean nameIs(String normalName) {
072        return normalName().equals(normalName);
073    }
074
075    /**
076     Test if this node's parent has the specified normalized name.
077     * @param normalName a normalized name (e.g. {@code div}).
078     * @return true if the parent element's normal name matches exactly
079     * @since 1.17.2
080     */
081    public boolean parentNameIs(String normalName) {
082        return parentNode != null && parentNode.normalName().equals(normalName);
083    }
084
085    /**
086     Test if this node's parent is an Element with the specified normalized name and namespace.
087     * @param normalName a normalized element name (e.g. {@code div}).
088     * @param namespace the namespace
089     * @return true if the parent element's normal name matches exactly, and that element is in the specified namespace
090     * @since 1.17.2
091     */
092    public boolean parentElementIs(String normalName, String namespace) {
093        return parentNode != null && parentNode instanceof Element
094            && ((Element) parentNode).elementIs(normalName, namespace);
095    }
096
097    /**
098     * Check if this Node has an actual Attributes object.
099     */
100    protected abstract boolean hasAttributes();
101
102    /**
103     Checks if this node has a parent. Nodes won't have parents if (e.g.) they are newly created and not added as a child
104     to an existing node, or if they are a {@link #shallowClone()}. In such cases, {@link #parent()} will return {@code null}.
105     @return if this node has a parent.
106     */
107    public boolean hasParent() {
108        return parentNode != null;
109    }
110
111    /**
112     * Get an attribute's value by its key. <b>Case insensitive</b>
113     * <p>
114     * To get an absolute URL from an attribute that may be a relative URL, prefix the key with <code><b>abs:</b></code>,
115     * which is a shortcut to the {@link #absUrl} method.
116     * </p>
117     * E.g.:
118     * <blockquote><code>String url = a.attr("abs:href");</code></blockquote>
119     *
120     * @param attributeKey The attribute key.
121     * @return The attribute, or empty string if not present (to avoid nulls).
122     * @see #attributes()
123     * @see #hasAttr(String)
124     * @see #absUrl(String)
125     */
126    public String attr(String attributeKey) {
127        Validate.notNull(attributeKey);
128        if (!hasAttributes())
129            return EmptyString;
130
131        String val = attributes().getIgnoreCase(attributeKey);
132        if (val.length() > 0)
133            return val;
134        else if (attributeKey.startsWith("abs:"))
135            return absUrl(attributeKey.substring("abs:".length()));
136        else return "";
137    }
138
139    /**
140     * Get each of the Element's attributes.
141     * @return attributes (which implements Iterable, with the same order as presented in the original HTML).
142     */
143    public abstract Attributes attributes();
144
145    /**
146     Get the number of attributes that this Node has.
147     @return the number of attributes
148     @since 1.14.2
149     */
150    public int attributesSize() {
151        // added so that we can test how many attributes exist without implicitly creating the Attributes object
152        return hasAttributes() ? attributes().size() : 0;
153    }
154
155    /**
156     * Set an attribute (key=value). If the attribute already exists, it is replaced. The attribute key comparison is
157     * <b>case insensitive</b>. The key will be set with case sensitivity as set in the parser settings.
158     * @param attributeKey The attribute key.
159     * @param attributeValue The attribute value.
160     * @return this (for chaining)
161     */
162    public Node attr(String attributeKey, String attributeValue) {
163        Document doc = ownerDocument();
164        ParseSettings settings = doc != null ? doc.parser().settings() : ParseSettings.htmlDefault;
165        attributeKey = settings.normalizeAttribute(attributeKey);
166        attributes().putIgnoreCase(attributeKey, attributeValue);
167        return this;
168    }
169
170    /**
171     * Test if this Node has an attribute. <b>Case insensitive</b>.
172     * @param attributeKey The attribute key to check.
173     * @return true if the attribute exists, false if not.
174     */
175    public boolean hasAttr(String attributeKey) {
176        Validate.notNull(attributeKey);
177        if (!hasAttributes())
178            return false;
179
180        if (attributeKey.startsWith("abs:")) {
181            String key = attributeKey.substring("abs:".length());
182            if (attributes().hasKeyIgnoreCase(key) && !absUrl(key).isEmpty())
183                return true;
184        }
185        return attributes().hasKeyIgnoreCase(attributeKey);
186    }
187
188    /**
189     * Remove an attribute from this node.
190     * @param attributeKey The attribute to remove.
191     * @return this (for chaining)
192     */
193    public Node removeAttr(String attributeKey) {
194        Validate.notNull(attributeKey);
195        if (hasAttributes())
196            attributes().removeIgnoreCase(attributeKey);
197        return this;
198    }
199
200    /**
201     * Clear (remove) each of the attributes in this node.
202     * @return this, for chaining
203     */
204    public Node clearAttributes() {
205        if (hasAttributes()) {
206            Iterator<Attribute> it = attributes().iterator();
207            while (it.hasNext()) {
208                it.next();
209                it.remove();
210            }
211        }
212        return this;
213    }
214
215    /**
216     Get the base URI that applies to this node. Will return an empty string if not defined. Used to make relative links
217     absolute.
218
219     @return base URI
220     @see #absUrl
221     */
222    public abstract String baseUri();
223
224    /**
225     * Set the baseUri for just this node (not its descendants), if this Node tracks base URIs.
226     * @param baseUri new URI
227     */
228    protected abstract void doSetBaseUri(String baseUri);
229
230    /**
231     Update the base URI of this node and all of its descendants.
232     @param baseUri base URI to set
233     */
234    public void setBaseUri(final String baseUri) {
235        Validate.notNull(baseUri);
236        doSetBaseUri(baseUri);
237    }
238
239    /**
240     * Get an absolute URL from a URL attribute that may be relative (such as an <code>&lt;a href&gt;</code> or
241     * <code>&lt;img src&gt;</code>).
242     * <p>
243     * E.g.: <code>String absUrl = linkEl.absUrl("href");</code>
244     * </p>
245     * <p>
246     * If the attribute value is already absolute (i.e. it starts with a protocol, like
247     * <code>http://</code> or <code>https://</code> etc), and it successfully parses as a URL, the attribute is
248     * returned directly. Otherwise, it is treated as a URL relative to the element's {@link #baseUri}, and made
249     * absolute using that.
250     * </p>
251     * <p>
252     * As an alternate, you can use the {@link #attr} method with the <code>abs:</code> prefix, e.g.:
253     * <code>String absUrl = linkEl.attr("abs:href");</code>
254     * </p>
255     *
256     * @param attributeKey The attribute key
257     * @return An absolute URL if one could be made, or an empty string (not null) if the attribute was missing or
258     * could not be made successfully into a URL.
259     * @see #attr
260     * @see java.net.URL#URL(java.net.URL, String)
261     */
262    public String absUrl(String attributeKey) {
263        Validate.notEmpty(attributeKey);
264        if (!(hasAttributes() && attributes().hasKeyIgnoreCase(attributeKey))) // not using hasAttr, so that we don't recurse down hasAttr->absUrl
265            return "";
266
267        return StringUtil.resolve(baseUri(), attributes().getIgnoreCase(attributeKey));
268    }
269
270    protected abstract List<Node> ensureChildNodes();
271
272    /**
273     Get a child node by its 0-based index.
274     @param index index of child node
275     @return the child node at this index.
276     @throws IndexOutOfBoundsException if the index is out of bounds.
277     */
278    public Node childNode(int index) {
279        return ensureChildNodes().get(index);
280    }
281
282    /**
283     Get this node's children. Presented as an unmodifiable list: new children can not be added, but the child nodes
284     themselves can be manipulated.
285     @return list of children. If no children, returns an empty list.
286     */
287    public List<Node> childNodes() {
288        if (childNodeSize() == 0)
289            return EmptyNodes;
290
291        List<Node> children = ensureChildNodes();
292        List<Node> rewrap = new ArrayList<>(children.size()); // wrapped so that looping and moving will not throw a CME as the source changes
293        rewrap.addAll(children);
294        return Collections.unmodifiableList(rewrap);
295    }
296
297    /**
298     * Returns a deep copy of this node's children. Changes made to these nodes will not be reflected in the original
299     * nodes
300     * @return a deep copy of this node's children
301     */
302    public List<Node> childNodesCopy() {
303        final List<Node> nodes = ensureChildNodes();
304        final ArrayList<Node> children = new ArrayList<>(nodes.size());
305        for (Node node : nodes) {
306            children.add(node.clone());
307        }
308        return children;
309    }
310
311    /**
312     * Get the number of child nodes that this node holds.
313     * @return the number of child nodes that this node holds.
314     */
315    public abstract int childNodeSize();
316
317    protected Node[] childNodesAsArray() {
318        return ensureChildNodes().toArray(new Node[0]);
319    }
320
321    /**
322     * Delete all this node's children.
323     * @return this node, for chaining
324     */
325    public abstract Node empty();
326
327    /**
328     Gets this node's parent node. This is always an Element.
329     @return parent node; or null if no parent.
330     @see #hasParent()
331     @see #parentElement();
332     */
333    public @Nullable Node parent() {
334        return parentNode;
335    }
336
337    /**
338     Gets this node's parent Element.
339     @return parent element; or null if this node has no parent.
340     @see #hasParent()
341     @since 1.21.1
342     */
343    public @Nullable Element parentElement() {
344        return parentNode;
345    }
346
347    /**
348     Gets this node's parent node. Not overridable by extending classes, so useful if you really just need the Node type.
349     @return parent node; or null if no parent.
350     */
351    public @Nullable final Node parentNode() {
352        return parentNode;
353    }
354
355    /**
356     * Get this node's root node; that is, its topmost ancestor. If this node is the top ancestor, returns {@code this}.
357     * @return topmost ancestor.
358     */
359    public Node root() {
360        Node node = this;
361        while (node.parentNode != null)
362            node = node.parentNode;
363        return node;
364    }
365
366    /**
367     * Gets the Document associated with this Node.
368     * @return the Document associated with this Node, or null if there is no such Document.
369     */
370    public @Nullable Document ownerDocument() {
371        Node node = this;
372        while (node != null) {
373            if (node instanceof Document) return (Document) node;
374            node = node.parentNode;
375        }
376        return null;
377    }
378
379    /**
380     * Remove (delete) this node from the DOM tree. If this node has children, they are also removed. If this node is
381     * an orphan, nothing happens.
382     */
383    public void remove() {
384        if (parentNode != null)
385            parentNode.removeChild(this);
386    }
387
388    /**
389     * Insert the specified HTML into the DOM before this node (as a preceding sibling).
390     * @param html HTML to add before this node
391     * @return this node, for chaining
392     * @see #after(String)
393     */
394    public Node before(String html) {
395        addSiblingHtml(siblingIndex(), html);
396        return this;
397    }
398
399    /**
400     * Insert the specified node into the DOM before this node (as a preceding sibling).
401     * @param node to add before this node
402     * @return this node, for chaining
403     * @see #after(Node)
404     */
405    public Node before(Node node) {
406        Validate.notNull(node);
407        Validate.notNull(parentNode);
408
409        // if the incoming node is a sibling of this, remove it first so siblingIndex is correct on add
410        if (node.parentNode == parentNode) node.remove();
411
412        parentNode.addChildren(siblingIndex(), node);
413        return this;
414    }
415
416    /**
417     * Insert the specified HTML into the DOM after this node (as a following sibling).
418     * @param html HTML to add after this node
419     * @return this node, for chaining
420     * @see #before(String)
421     */
422    public Node after(String html) {
423        addSiblingHtml(siblingIndex() + 1, html);
424        return this;
425    }
426
427    /**
428     * Insert the specified node into the DOM after this node (as a following sibling).
429     * @param node to add after this node
430     * @return this node, for chaining
431     * @see #before(Node)
432     */
433    public Node after(Node node) {
434        Validate.notNull(node);
435        Validate.notNull(parentNode);
436
437        // if the incoming node is a sibling of this, remove it first so siblingIndex is correct on add
438        if (node.parentNode == parentNode) node.remove();
439
440        parentNode.addChildren(siblingIndex() + 1, node);
441        return this;
442    }
443
444    private void addSiblingHtml(int index, String html) {
445        Validate.notNull(html);
446        Validate.notNull(parentNode);
447
448        Element context = parentNode instanceof Element ? (Element) parentNode : null;
449        List<Node> nodes = NodeUtils.parser(this).parseFragmentInput(html, context, baseUri());
450        parentNode.addChildren(index, nodes.toArray(new Node[0]));
451    }
452
453    /**
454     Wrap the supplied HTML around this node.
455
456     @param html HTML to wrap around this node, e.g. {@code <div class="head"></div>}. Can be arbitrarily deep. If
457     the input HTML does not parse to a result starting with an Element, this will be a no-op.
458     @return this node, for chaining.
459     */
460    public Node wrap(String html) {
461        Validate.notEmpty(html);
462
463        // Parse context - parent (because wrapping), this, or null
464        Element context =
465            parentNode != null && parentNode instanceof Element ? (Element) parentNode :
466                this instanceof Element ? (Element) this :
467                    null;
468        List<Node> wrapChildren = NodeUtils.parser(this).parseFragmentInput(html, context, baseUri());
469        Node wrapNode = wrapChildren.get(0);
470        if (!(wrapNode instanceof Element)) // nothing to wrap with; noop
471            return this;
472
473        Element wrap = (Element) wrapNode;
474        Element deepest = getDeepChild(wrap);
475        if (parentNode != null)
476            parentNode.replaceChild(this, wrap);
477        deepest.addChildren(this); // side effect of tricking wrapChildren to lose first
478
479        // remainder (unbalanced wrap, like <div></div><p></p> -- The <p> is remainder
480        if (wrapChildren.size() > 0) {
481            //noinspection ForLoopReplaceableByForEach (beacause it allocates an Iterator which is wasteful here)
482            for (int i = 0; i < wrapChildren.size(); i++) {
483                Node remainder = wrapChildren.get(i);
484                // if no parent, this could be the wrap node, so skip
485                if (wrap == remainder)
486                    continue;
487
488                if (remainder.parentNode != null)
489                    remainder.parentNode.removeChild(remainder);
490                wrap.after(remainder);
491            }
492        }
493        return this;
494    }
495
496    /**
497     * Removes this node from the DOM, and moves its children up into the node's parent. This has the effect of dropping
498     * the node but keeping its children.
499     * <p>
500     * For example, with the input html:
501     * </p>
502     * <p>{@code <div>One <span>Two <b>Three</b></span></div>}</p>
503     * Calling {@code element.unwrap()} on the {@code span} element will result in the html:
504     * <p>{@code <div>One Two <b>Three</b></div>}</p>
505     * and the {@code "Two "} {@link TextNode} being returned.
506     *
507     * @return the first child of this node, after the node has been unwrapped. @{code Null} if the node had no children.
508     * @see #remove()
509     * @see #wrap(String)
510     */
511    public @Nullable Node unwrap() {
512        Validate.notNull(parentNode);
513        Node firstChild = firstChild();
514        parentNode.addChildren(siblingIndex(), this.childNodesAsArray());
515        this.remove();
516
517        return firstChild;
518    }
519
520    private static Element getDeepChild(Element el) {
521        Element child = el.firstElementChild();
522        while (child != null) {
523            el = child;
524            child = child.firstElementChild();
525        }
526        return el;
527    }
528
529    /**
530     * Replace this node in the DOM with the supplied node.
531     * @param in the node that will replace the existing node.
532     */
533    public void replaceWith(Node in) {
534        Validate.notNull(in);
535        if (parentNode == null) parentNode = in.parentNode; // allows old to have been temp removed before replacing
536        Validate.notNull(parentNode);
537        parentNode.replaceChild(this, in);
538    }
539
540    protected void setParentNode(Node parentNode) {
541        Validate.notNull(parentNode);
542        if (this.parentNode != null)
543            this.parentNode.removeChild(this);
544        assert parentNode instanceof Element;
545        this.parentNode = (Element) parentNode;
546    }
547
548    protected void replaceChild(Node out, Node in) {
549        Validate.isTrue(out.parentNode == this);
550        Validate.notNull(in);
551        if (out == in) return; // no-op self replacement
552
553        if (in.parentNode != null)
554            in.parentNode.removeChild(in);
555
556        final int index = out.siblingIndex();
557        ensureChildNodes().set(index, in);
558        in.parentNode = (Element) this;
559        in.setSiblingIndex(index);
560        out.parentNode = null;
561
562        ((Element) this).childNodes.incrementMod(); // as mod count not changed in set(), requires explicit update, to invalidate the child element cache
563    }
564
565    protected void removeChild(Node out) {
566        Validate.isTrue(out.parentNode == this);
567        Element el = (Element) this;
568        if (el.hasValidChildren()) // can remove by index
569            ensureChildNodes().remove(out.siblingIndex);
570        else
571            ensureChildNodes().remove(out); // iterates, but potentially not every one
572
573        el.invalidateChildren();
574        out.parentNode = null;
575    }
576
577    protected void addChildren(Node... children) {
578        //most used. short circuit addChildren(int), which hits reindex children and array copy
579        final List<Node> nodes = ensureChildNodes();
580
581        for (Node child: children) {
582            reparentChild(child);
583            nodes.add(child);
584            child.setSiblingIndex(nodes.size()-1);
585        }
586    }
587
588    protected void addChildren(int index, Node... children) {
589        // todo clean up all these and use the list, not the var array. just need to be careful when iterating the incoming (as we are removing as we go)
590        Validate.notNull(children);
591        if (children.length == 0) return;
592        final List<Node> nodes = ensureChildNodes();
593
594        // fast path - if used as a wrap (index=0, children = child[0].parent.children - do inplace
595        final Node firstParent = children[0].parent();
596        if (firstParent != null && firstParent.childNodeSize() == children.length) {
597            boolean sameList = true;
598            final List<Node> firstParentNodes = firstParent.ensureChildNodes();
599            // identity check contents to see if same
600            int i = children.length;
601            while (i-- > 0) {
602                if (children[i] != firstParentNodes.get(i)) {
603                    sameList = false;
604                    break;
605                }
606            }
607            if (sameList) { // moving, so OK to empty firstParent and short-circuit
608                firstParent.empty();
609                nodes.addAll(index, Arrays.asList(children));
610                i = children.length;
611                assert this instanceof Element;
612                while (i-- > 0) {
613                    children[i].parentNode = (Element) this;
614                }
615                ((Element) this).invalidateChildren();
616                return;
617            }
618        }
619
620        Validate.noNullElements(children);
621        for (Node child : children) {
622            reparentChild(child);
623        }
624        nodes.addAll(index, Arrays.asList(children));
625        ((Element) this).invalidateChildren();
626    }
627    
628    protected void reparentChild(Node child) {
629        child.setParentNode(this);
630    }
631
632    /**
633     Retrieves this node's sibling nodes. Similar to {@link #childNodes() node.parent.childNodes()}, but does not
634     include this node (a node is not a sibling of itself).
635     @return node siblings. If the node has no parent, returns an empty list.
636     */
637    public List<Node> siblingNodes() {
638        if (parentNode == null)
639            return Collections.emptyList();
640
641        List<Node> nodes = parentNode.ensureChildNodes();
642        List<Node> siblings = new ArrayList<>(nodes.size() - 1);
643        for (Node node: nodes)
644            if (node != this)
645                siblings.add(node);
646        return siblings;
647    }
648
649    /**
650     Get this node's next sibling.
651     @return next sibling, or {@code null} if this is the last sibling
652     */
653    public @Nullable Node nextSibling() {
654        if (parentNode == null)
655            return null; // root
656
657        final List<Node> siblings = parentNode.ensureChildNodes();
658        final int index = siblingIndex() + 1;
659        if (siblings.size() > index) {
660            Node node = siblings.get(index);
661            assert (node.siblingIndex == index); // sanity test that invalidations haven't missed
662            return node;
663        } else
664            return null;
665    }
666
667    /**
668     Get this node's previous sibling.
669     @return the previous sibling, or @{code null} if this is the first sibling
670     */
671    public @Nullable Node previousSibling() {
672        if (parentNode == null)
673            return null; // root
674
675        if (siblingIndex() > 0)
676            return parentNode.ensureChildNodes().get(siblingIndex-1);
677        else
678            return null;
679    }
680
681    /**
682     * Get the list index of this node in its node sibling list. E.g. if this is the first node
683     * sibling, returns 0.
684     * @return position in node sibling list
685     * @see org.jsoup.nodes.Element#elementSiblingIndex()
686     */
687    public int siblingIndex() {
688        if (parentNode != null && !parentNode.childNodes.validChildren)
689            parentNode.reindexChildren();
690
691        return siblingIndex;
692    }
693
694    protected void setSiblingIndex(int siblingIndex) {
695        this.siblingIndex = siblingIndex;
696    }
697
698    /**
699     Gets the first child node of this node, or {@code null} if there is none. This could be any Node type, such as an
700     Element, TextNode, Comment, etc. Use {@link Element#firstElementChild()} to get the first Element child.
701     @return the first child node, or null if there are no children.
702     @see Element#firstElementChild()
703     @see #lastChild()
704     @since 1.15.2
705     */
706    public @Nullable Node firstChild() {
707        if (childNodeSize() == 0) return null;
708        return ensureChildNodes().get(0);
709    }
710
711    /**
712     Gets the last child node of this node, or {@code null} if there is none.
713     @return the last child node, or null if there are no children.
714     @see Element#lastElementChild()
715     @see #firstChild()
716     @since 1.15.2
717     */
718    public @Nullable Node lastChild() {
719        final int size = childNodeSize();
720        if (size == 0) return null;
721        List<Node> children = ensureChildNodes();
722        return children.get(size - 1);
723    }
724
725    /**
726     Gets the first sibling of this node. That may be this node.
727
728     @return the first sibling node
729     @since 1.21.1
730     */
731    public Node firstSibling() {
732        if (parentNode != null) {
733            //noinspection DataFlowIssue
734            return parentNode.firstChild();
735        } else
736            return this; // orphan is its own first sibling
737    }
738
739    /**
740     Gets the last sibling of this node. That may be this node.
741
742     @return the last sibling (aka the parent's last child)
743     @since 1.21.1
744     */
745    public Node lastSibling() {
746        if (parentNode != null) {
747            //noinspection DataFlowIssue (not nullable, would be this if no other sibs)
748            return parentNode.lastChild();
749        } else
750            return this;
751    }
752
753    /**
754     Gets the next sibling Element of this node. E.g., if a {@code div} contains two {@code p}s, the
755     {@code nextElementSibling} of the first {@code p} is the second {@code p}.
756     <p>This is similar to {@link #nextSibling()}, but specifically finds only Elements.</p>
757
758     @return the next element, or null if there is no next element
759     @see #previousElementSibling()
760     */
761    public @Nullable Element nextElementSibling() {
762        Node next = this;
763        while ((next = next.nextSibling()) != null) {
764            if (next instanceof Element) return (Element) next;
765        }
766        return null;
767    }
768
769    /**
770     Gets the previous Element sibling of this node.
771
772     @return the previous element, or null if there is no previous element
773     @see #nextElementSibling()
774     */
775    public @Nullable Element previousElementSibling() {
776        Node prev = this;
777        while ((prev = prev.previousSibling()) != null) {
778            if (prev instanceof Element) return (Element) prev;
779        }
780        return null;
781    }
782
783    /**
784     * Perform a depth-first traversal through this node and its descendants.
785     * @param nodeVisitor the visitor callbacks to perform on each node
786     * @return this node, for chaining
787     */
788    public Node traverse(NodeVisitor nodeVisitor) {
789        Validate.notNull(nodeVisitor);
790        nodeVisitor.traverse(this);
791        return this;
792    }
793
794    /**
795     Perform the supplied action on this Node and each of its descendants, during a depth-first traversal. Nodes may be
796     inspected, changed, added, replaced, or removed.
797     @param action the function to perform on the node
798     @return this Node, for chaining
799     @see Element#forEach(Consumer)
800     */
801    public Node forEachNode(Consumer<? super Node> action) {
802        Validate.notNull(action);
803        nodeStream().forEach(action);
804        return this;
805    }
806
807    /**
808     * Perform a depth-first controllable traversal through this node and its descendants.
809     * @param nodeFilter the filter callbacks to perform on each node
810     * @return this node, for chaining
811     */
812    public Node filter(NodeFilter nodeFilter) {
813        Validate.notNull(nodeFilter);
814        nodeFilter.traverse(this);
815        return this;
816    }
817
818    /**
819     Returns a Stream of this Node and all of its descendant Nodes. The stream has document order.
820     @return a stream of all nodes.
821     @see Element#stream()
822     @since 1.17.1
823     */
824    public Stream<Node> nodeStream() {
825        return NodeUtils.stream(this, Node.class);
826    }
827
828    /**
829     Returns a Stream of this and descendant nodes, containing only nodes of the specified type. The stream has document
830     order.
831     @return a stream of nodes filtered by type.
832     @see Element#stream()
833     @since 1.17.1
834     */
835    public <T extends Node> Stream<T> nodeStream(Class<T> type) {
836        return NodeUtils.stream(this, type);
837    }
838
839    /**
840     Get the outer HTML of this node. For example, on a {@code p} element, may return {@code <p>Para</p>}.
841     @return outer HTML
842     @see Element#html()
843     @see Element#text()
844     */
845    public String outerHtml() {
846        StringBuilder sb = StringUtil.borrowBuilder();
847        outerHtml(QuietAppendable.wrap(sb));
848        return StringUtil.releaseBuilder(sb);
849    }
850
851    protected void outerHtml(Appendable accum) {
852        outerHtml(QuietAppendable.wrap(accum));
853    }
854
855    protected void outerHtml(QuietAppendable accum) {
856        Printer printer = Printer.printerFor(this, accum);
857        printer.traverse(this);
858    }
859
860    /**
861     Get the outer HTML of this node.
862
863     @param accum accumulator to place HTML into
864     @param out
865     */
866    abstract void outerHtmlHead(final QuietAppendable accum, final Document.OutputSettings out);
867
868    abstract void outerHtmlTail(final QuietAppendable accum, final Document.OutputSettings out);
869
870    /**
871     Write this node and its children to the given {@link Appendable}.
872
873     @param appendable the {@link Appendable} to write to.
874     @return the supplied {@link Appendable}, for chaining.
875     @throws org.jsoup.SerializationException if the appendable throws an IOException.
876     */
877    public <T extends Appendable> T html(T appendable) {
878        outerHtml(appendable);
879        return appendable;
880    }
881
882    /**
883     Get the source range (start and end positions) in the original input source from which this node was parsed.
884     Position tracking must be enabled prior to parsing the content. For an Element, this will be the positions of the
885     start tag.
886     @return the range for the start of the node, or {@code untracked} if its range was not tracked.
887     @see org.jsoup.parser.Parser#setTrackPosition(boolean)
888     @see Range#isImplicit()
889     @see Element#endSourceRange()
890     @see Attributes#sourceRange(String name)
891     @since 1.15.2
892     */
893    public Range sourceRange() {
894        return Range.ofStart(this);
895    }
896
897    /**
898     Gets the range spans, if source tracking was used.
899     */
900    Range.@Nullable Spans spans() {
901        if (!hasAttributes()) return null;
902        return attributes().spans();
903    }
904
905    /**
906     Gets or creates range spans for this node.
907     */
908    Range.Spans ensureSpans() {
909        return attributes().ensureSpans();
910    }
911
912    /**
913     * Gets this node's outer HTML.
914     * @return outer HTML.
915     * @see #outerHtml()
916     */
917    @Override
918    public String toString() {
919        return outerHtml();
920    }
921
922    /** @deprecated internal method moved into Printer; will be removed in jsoup 1.24.1. */
923    @Deprecated
924    protected void indent(Appendable accum, int depth, Document.OutputSettings out) throws IOException {
925        accum.append('\n').append(StringUtil.padding(depth * out.indentAmount(), out.maxPaddingWidth()));
926    }
927
928    /**
929     * Check if this node is the same instance of another (object identity test).
930     * <p>For a node value equality check, see {@link #hasSameValue(Object)}</p>
931     * @param o other object to compare to
932     * @return true if the content of this node is the same as the other
933     * @see Node#hasSameValue(Object)
934     */
935    @Override
936    public boolean equals(@Nullable Object o) {
937        // implemented just so that javadoc is clear this is an identity test
938        return this == o;
939    }
940
941    /**
942     Provides a hashCode for this Node, based on its object identity. Changes to the Node's content will not impact the
943     result.
944     @return an object identity based hashcode for this Node
945     */
946    @Override
947    public int hashCode() {
948        // implemented so that javadoc and scanners are clear this is an identity test
949        return super.hashCode();
950    }
951
952    /**
953     * Check if this node has the same content as another node. A node is considered the same if its name, attributes and content match the
954     * other node; particularly its position in the tree does not influence its similarity.
955     * @param o other object to compare to
956     * @return true if the content of this node is the same as the other
957     */
958    public boolean hasSameValue(@Nullable Object o) {
959        if (this == o) return true;
960        if (o == null || getClass() != o.getClass()) return false;
961
962        return this.outerHtml().equals(((Node) o).outerHtml());
963    }
964
965    /**
966     Create a stand-alone, deep copy of this node, and all of its children. The cloned node will have no siblings.
967     <p><ul>
968     <li>If this node is a {@link LeafNode}, the clone will have no parent.</li>
969     <li>If this node is an {@link Element}, the clone will have a simple owning {@link Document} to retain the
970     configured output settings and parser.</li>
971     </ul></p>
972     <p>The cloned node may be adopted into another Document or node structure using
973     {@link Element#appendChild(Node)}.</p>
974
975     @return a stand-alone cloned node, including clones of any children
976     @see #shallowClone()
977     */
978    @SuppressWarnings("MethodDoesntCallSuperMethod")
979    // because it does call super.clone in doClone - analysis just isn't following
980    @Override
981    public Node clone() {
982        Node thisClone = doClone(null); // splits for orphan
983
984        // Queue up nodes that need their children cloned (BFS).
985        final LinkedList<Node> nodesToProcess = new LinkedList<>();
986        nodesToProcess.add(thisClone);
987
988        while (!nodesToProcess.isEmpty()) {
989            Node currParent = nodesToProcess.remove();
990
991            final int size = currParent.childNodeSize();
992            for (int i = 0; i < size; i++) {
993                final List<Node> childNodes = currParent.ensureChildNodes();
994                Node childClone = childNodes.get(i).doClone(currParent);
995                childNodes.set(i, childClone);
996                nodesToProcess.add(childClone);
997            }
998        }
999
1000        return thisClone;
1001    }
1002
1003    /**
1004     * Create a stand-alone, shallow copy of this node. None of its children (if any) will be cloned, and it will have
1005     * no parent or sibling nodes.
1006     * @return a single independent copy of this node
1007     * @see #clone()
1008     */
1009    public Node shallowClone() {
1010        return doClone(null);
1011    }
1012
1013    /*
1014     * Return a clone of the node using the given parent (which can be null).
1015     * Not a deep copy of children.
1016     */
1017    protected Node doClone(@Nullable Node parent) {
1018        assert parent == null || parent instanceof Element;
1019        Node clone;
1020
1021        try {
1022            clone = (Node) super.clone();
1023        } catch (CloneNotSupportedException e) {
1024            throw new RuntimeException(e);
1025        }
1026
1027        clone.parentNode = (Element) parent; // can be null, to create an orphan split
1028        clone.siblingIndex = parent == null ? 0 : siblingIndex();
1029        // if not keeping the parent, shallowClone the ownerDocument to preserve its settings
1030        if (parent == null && !(this instanceof Document)) {
1031            Document doc = ownerDocument();
1032            if (doc != null) {
1033                Document docClone = doc.shallowClone();
1034                clone.parentNode = docClone;
1035                docClone.ensureChildNodes().add(clone);
1036            }
1037        }
1038
1039        return clone;
1040    }
1041}