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