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><a href></code> or 239 * <code><img src></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 407 // if the incoming node is a sibling of this, remove it first so siblingIndex is correct on add 408 if (node.parentNode == parentNode) node.remove(); 409 410 parentNode.addChildren(siblingIndex(), node); 411 return this; 412 } 413 414 /** 415 * Insert the specified HTML into the DOM after this node (as a following sibling). 416 * @param html HTML to add after this node 417 * @return this node, for chaining 418 * @see #before(String) 419 */ 420 public Node after(String html) { 421 addSiblingHtml(siblingIndex() + 1, html); 422 return this; 423 } 424 425 /** 426 * Insert the specified node into the DOM after this node (as a following sibling). 427 * @param node to add after this node 428 * @return this node, for chaining 429 * @see #before(Node) 430 */ 431 public Node after(Node node) { 432 Validate.notNull(node); 433 Validate.notNull(parentNode); 434 435 // if the incoming node is a sibling of this, remove it first so siblingIndex is correct on add 436 if (node.parentNode == parentNode) node.remove(); 437 438 parentNode.addChildren(siblingIndex() + 1, node); 439 return this; 440 } 441 442 private void addSiblingHtml(int index, String html) { 443 Validate.notNull(html); 444 Validate.notNull(parentNode); 445 446 Element context = parentNode instanceof Element ? (Element) parentNode : null; 447 List<Node> nodes = NodeUtils.parser(this).parseFragmentInput(html, context, baseUri()); 448 parentNode.addChildren(index, nodes.toArray(new Node[0])); 449 } 450 451 /** 452 Wrap the supplied HTML around this node. 453 454 @param html HTML to wrap around this node, e.g. {@code <div class="head"></div>}. Can be arbitrarily deep. If 455 the input HTML does not parse to a result starting with an Element, this will be a no-op. 456 @return this node, for chaining. 457 */ 458 public Node wrap(String html) { 459 Validate.notEmpty(html); 460 461 // Parse context - parent (because wrapping), this, or null 462 Element context = 463 parentNode != null && parentNode instanceof Element ? (Element) parentNode : 464 this instanceof Element ? (Element) this : 465 null; 466 List<Node> wrapChildren = NodeUtils.parser(this).parseFragmentInput(html, context, baseUri()); 467 Node wrapNode = wrapChildren.get(0); 468 if (!(wrapNode instanceof Element)) // nothing to wrap with; noop 469 return this; 470 471 Element wrap = (Element) wrapNode; 472 Element deepest = getDeepChild(wrap); 473 if (parentNode != null) 474 parentNode.replaceChild(this, wrap); 475 deepest.addChildren(this); // side effect of tricking wrapChildren to lose first 476 477 // remainder (unbalanced wrap, like <div></div><p></p> -- The <p> is remainder 478 if (wrapChildren.size() > 0) { 479 //noinspection ForLoopReplaceableByForEach (beacause it allocates an Iterator which is wasteful here) 480 for (int i = 0; i < wrapChildren.size(); i++) { 481 Node remainder = wrapChildren.get(i); 482 // if no parent, this could be the wrap node, so skip 483 if (wrap == remainder) 484 continue; 485 486 if (remainder.parentNode != null) 487 remainder.parentNode.removeChild(remainder); 488 wrap.after(remainder); 489 } 490 } 491 return this; 492 } 493 494 /** 495 * Removes this node from the DOM, and moves its children up into the node's parent. This has the effect of dropping 496 * the node but keeping its children. 497 * <p> 498 * For example, with the input html: 499 * </p> 500 * <p>{@code <div>One <span>Two <b>Three</b></span></div>}</p> 501 * Calling {@code element.unwrap()} on the {@code span} element will result in the html: 502 * <p>{@code <div>One Two <b>Three</b></div>}</p> 503 * and the {@code "Two "} {@link TextNode} being returned. 504 * 505 * @return the first child of this node, after the node has been unwrapped. @{code Null} if the node had no children. 506 * @see #remove() 507 * @see #wrap(String) 508 */ 509 public @Nullable Node unwrap() { 510 Validate.notNull(parentNode); 511 Node firstChild = firstChild(); 512 parentNode.addChildren(siblingIndex(), this.childNodesAsArray()); 513 this.remove(); 514 515 return firstChild; 516 } 517 518 private static Element getDeepChild(Element el) { 519 Element child = el.firstElementChild(); 520 while (child != null) { 521 el = child; 522 child = child.firstElementChild(); 523 } 524 return el; 525 } 526 527 /** 528 * Replace this node in the DOM with the supplied node. 529 * @param in the node that will replace the existing node. 530 */ 531 public void replaceWith(Node in) { 532 Validate.notNull(in); 533 if (parentNode == null) parentNode = in.parentNode; // allows old to have been temp removed before replacing 534 Validate.notNull(parentNode); 535 parentNode.replaceChild(this, in); 536 } 537 538 protected void setParentNode(Node parentNode) { 539 Validate.notNull(parentNode); 540 assert parentNode instanceof Element; 541 parentNode.validateChild(this); 542 setParentNodeUnchecked((Element) parentNode); 543 } 544 545 /** Reparents this node without cycle validation; callers must validate first. */ 546 private void setParentNodeUnchecked(Element parentNode) { 547 if (this.parentNode != null) 548 this.parentNode.removeChild(this); 549 this.parentNode = parentNode; 550 } 551 552 private static final String CycleError = "Cannot add a node here because it would create a cycle."; 553 554 /** Checks that the child is neither this node nor an ancestor of this node. */ 555 private void validateChild(Node child) { 556 Validate.isFalse(child == this, CycleError); 557 if (child.childNodeSize() == 0) return; 558 559 for (Node ancestor = parentNode; ancestor != null; ancestor = ancestor.parentNode) 560 Validate.isFalse(ancestor == child, CycleError); 561 } 562 563 /** Checks every child before reparenting any of them. */ 564 private void validateChildren(Node[] children) { 565 Validate.notNull(children); 566 for (Node child : children) { 567 Validate.notNull(child, "Array must not contain any null objects"); 568 validateChild(child); 569 } 570 } 571 572 protected void replaceChild(Node out, Node in) { 573 Validate.isTrue(out.parentNode == this); 574 Validate.notNull(in); 575 if (out == in) return; // no-op self replacement 576 577 Element parent = (Element) this; 578 validateChild(in); 579 in.setParentNodeUnchecked(parent); 580 581 final int index = out.siblingIndex(); 582 ensureChildNodes().set(index, in); 583 in.setSiblingIndex(index); 584 out.parentNode = null; 585 586 parent.childNodes.incrementMod(); // as mod count not changed in set(), requires explicit update, to invalidate the child element cache 587 } 588 589 protected void removeChild(Node out) { 590 Validate.isTrue(out.parentNode == this); 591 Element el = (Element) this; 592 if (el.hasValidChildren()) // can remove by index 593 ensureChildNodes().remove(out.siblingIndex); 594 else 595 ensureChildNodes().remove(out); // iterates, but potentially not every one 596 597 el.invalidateChildren(); 598 out.parentNode = null; 599 } 600 601 protected void addChildren(Node... children) { 602 //most used. short circuit addChildren(int), which hits reindex children and array copy 603 validateChildren(children); 604 605 final List<Node> nodes = ensureChildNodes(); 606 assert this instanceof Element; 607 Element parent = (Element) this; 608 609 for (Node child: children) { 610 child.setParentNodeUnchecked(parent); 611 nodes.add(child); 612 child.setSiblingIndex(nodes.size()-1); 613 } 614 } 615 616 protected void addChildren(int index, Node... children) { 617 // 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) 618 Validate.notNull(children); 619 if (children.length == 0) return; 620 validateChildren(children); 621 622 final List<Node> nodes = ensureChildNodes(); 623 assert this instanceof Element; 624 Element parent = (Element) this; 625 626 // fast path - if used as a wrap (index=0, children = child[0].parent.children - do inplace 627 final Node firstParent = children[0].parent(); 628 if (firstParent != null && firstParent.childNodeSize() == children.length) { 629 boolean sameList = true; 630 final List<Node> firstParentNodes = firstParent.ensureChildNodes(); 631 // identity check contents to see if same 632 int i = children.length; 633 while (i-- > 0) { 634 if (children[i] != firstParentNodes.get(i)) { 635 sameList = false; 636 break; 637 } 638 } 639 if (sameList) { // moving, so OK to empty firstParent and short-circuit 640 firstParent.empty(); 641 nodes.addAll(index, Arrays.asList(children)); 642 i = children.length; 643 while (i-- > 0) { 644 children[i].setParentNodeUnchecked(parent); 645 } 646 parent.invalidateChildren(); 647 return; 648 } 649 } 650 651 for (Node child : children) { 652 child.setParentNodeUnchecked(parent); 653 } 654 nodes.addAll(index, Arrays.asList(children)); 655 parent.invalidateChildren(); 656 } 657 658 protected void reparentChild(Node child) { 659 child.setParentNode(this); 660 } 661 662 /** 663 Retrieves this node's sibling nodes. Similar to {@link #childNodes() node.parent.childNodes()}, but does not 664 include this node (a node is not a sibling of itself). 665 @return node siblings. If the node has no parent, returns an empty list. 666 */ 667 public List<Node> siblingNodes() { 668 if (parentNode == null) 669 return Collections.emptyList(); 670 671 List<Node> nodes = parentNode.ensureChildNodes(); 672 List<Node> siblings = new ArrayList<>(nodes.size() - 1); 673 for (Node node: nodes) 674 if (node != this) 675 siblings.add(node); 676 return siblings; 677 } 678 679 /** 680 Get this node's next sibling. 681 @return next sibling, or {@code null} if this is the last sibling 682 */ 683 public @Nullable Node nextSibling() { 684 if (parentNode == null) 685 return null; // root 686 687 final List<Node> siblings = parentNode.ensureChildNodes(); 688 final int index = siblingIndex() + 1; 689 if (siblings.size() > index) { 690 Node node = siblings.get(index); 691 assert (node.siblingIndex == index); // sanity test that invalidations haven't missed 692 return node; 693 } else 694 return null; 695 } 696 697 /** 698 Get this node's previous sibling. 699 @return the previous sibling, or @{code null} if this is the first sibling 700 */ 701 public @Nullable Node previousSibling() { 702 if (parentNode == null) 703 return null; // root 704 705 if (siblingIndex() > 0) 706 return parentNode.ensureChildNodes().get(siblingIndex-1); 707 else 708 return null; 709 } 710 711 /** 712 * Get the list index of this node in its node sibling list. E.g. if this is the first node 713 * sibling, returns 0. 714 * @return position in node sibling list 715 * @see org.jsoup.nodes.Element#elementSiblingIndex() 716 */ 717 public int siblingIndex() { 718 if (parentNode != null && !parentNode.childNodes.validChildren) 719 parentNode.reindexChildren(); 720 721 return siblingIndex; 722 } 723 724 protected void setSiblingIndex(int siblingIndex) { 725 this.siblingIndex = siblingIndex; 726 } 727 728 /** 729 Gets the first child node of this node, or {@code null} if there is none. This could be any Node type, such as an 730 Element, TextNode, Comment, etc. Use {@link Element#firstElementChild()} to get the first Element child. 731 @return the first child node, or null if there are no children. 732 @see Element#firstElementChild() 733 @see #lastChild() 734 @since 1.15.2 735 */ 736 public @Nullable Node firstChild() { 737 if (childNodeSize() == 0) return null; 738 return ensureChildNodes().get(0); 739 } 740 741 /** 742 Gets the last child node of this node, or {@code null} if there is none. 743 @return the last child node, or null if there are no children. 744 @see Element#lastElementChild() 745 @see #firstChild() 746 @since 1.15.2 747 */ 748 public @Nullable Node lastChild() { 749 final int size = childNodeSize(); 750 if (size == 0) return null; 751 List<Node> children = ensureChildNodes(); 752 return children.get(size - 1); 753 } 754 755 /** 756 Gets the first sibling of this node. That may be this node. 757 758 @return the first sibling node 759 @since 1.21.1 760 */ 761 public Node firstSibling() { 762 if (parentNode != null) { 763 //noinspection DataFlowIssue 764 return parentNode.firstChild(); 765 } else 766 return this; // orphan is its own first sibling 767 } 768 769 /** 770 Gets the last sibling of this node. That may be this node. 771 772 @return the last sibling (aka the parent's last child) 773 @since 1.21.1 774 */ 775 public Node lastSibling() { 776 if (parentNode != null) { 777 //noinspection DataFlowIssue (not nullable, would be this if no other sibs) 778 return parentNode.lastChild(); 779 } else 780 return this; 781 } 782 783 /** 784 Gets the next sibling Element of this node. E.g., if a {@code div} contains two {@code p}s, the 785 {@code nextElementSibling} of the first {@code p} is the second {@code p}. 786 <p>This is similar to {@link #nextSibling()}, but specifically finds only Elements.</p> 787 788 @return the next element, or null if there is no next element 789 @see #previousElementSibling() 790 */ 791 public @Nullable Element nextElementSibling() { 792 Node next = this; 793 while ((next = next.nextSibling()) != null) { 794 if (next instanceof Element) return (Element) next; 795 } 796 return null; 797 } 798 799 /** 800 Gets the previous Element sibling of this node. 801 802 @return the previous element, or null if there is no previous element 803 @see #nextElementSibling() 804 */ 805 public @Nullable Element previousElementSibling() { 806 Node prev = this; 807 while ((prev = prev.previousSibling()) != null) { 808 if (prev instanceof Element) return (Element) prev; 809 } 810 return null; 811 } 812 813 /** 814 * Perform a depth-first traversal through this node and its descendants. 815 * @param nodeVisitor the visitor callbacks to perform on each node 816 * @return this node, for chaining 817 */ 818 public Node traverse(NodeVisitor nodeVisitor) { 819 Validate.notNull(nodeVisitor); 820 nodeVisitor.traverse(this); 821 return this; 822 } 823 824 /** 825 Perform the supplied action on this Node and each of its descendants, during a depth-first traversal. Nodes may be 826 inspected, changed, added, replaced, or removed. 827 @param action the function to perform on the node 828 @return this Node, for chaining 829 @see Element#forEach(Consumer) 830 */ 831 public Node forEachNode(Consumer<? super Node> action) { 832 Validate.notNull(action); 833 nodeStream().forEach(action); 834 return this; 835 } 836 837 /** 838 * Perform a depth-first controllable traversal through this node and its descendants. 839 * @param nodeFilter the filter callbacks to perform on each node 840 * @return this node, for chaining 841 */ 842 public Node filter(NodeFilter nodeFilter) { 843 Validate.notNull(nodeFilter); 844 nodeFilter.traverse(this); 845 return this; 846 } 847 848 /** 849 Returns a Stream of this Node and all of its descendant Nodes. The stream has document order. 850 @return a stream of all nodes. 851 @see Element#stream() 852 @since 1.17.1 853 */ 854 public Stream<Node> nodeStream() { 855 return NodeUtils.stream(this, Node.class); 856 } 857 858 /** 859 Returns a Stream of this and descendant nodes, containing only nodes of the specified type. The stream has document 860 order. 861 @return a stream of nodes filtered by type. 862 @see Element#stream() 863 @since 1.17.1 864 */ 865 public <T extends Node> Stream<T> nodeStream(Class<T> type) { 866 return NodeUtils.stream(this, type); 867 } 868 869 /** 870 Get the outer HTML of this node. For example, on a {@code p} element, this may return {@code <p>Para</p>}. 871 @return the outer HTML of this node 872 @see Element#html() 873 @see #outerHtml(Appendable) 874 @see Element#text() 875 */ 876 public String outerHtml() { 877 StringBuilder sb = StringUtil.borrowBuilder(); 878 outerHtml(QuietAppendable.wrap(sb)); 879 return StringUtil.releaseBuilder(sb); 880 } 881 882 /** 883 Append the outer HTML of this node to the supplied {@link Appendable}. This includes the node itself; for example, 884 on a {@code p} element this appends {@code <p>Para</p>}. To append only an Element's contents, use 885 {@link Element#html(Appendable)}. 886 887 @param appendable the {@link Appendable} that will receive the HTML. 888 @return the supplied {@link Appendable}, for chaining. 889 @throws org.jsoup.SerializationException if the appendable throws an IOException. 890 @see #outerHtml() 891 @see Element#html(Appendable) 892 @since 1.23.1 893 */ 894 public <T extends Appendable> T outerHtml(T appendable) { 895 outerHtml(QuietAppendable.wrap(appendable)); 896 return appendable; 897 } 898 899 /** Append the outer HTML of this node to the internal output. */ 900 protected void outerHtml(QuietAppendable accum) { 901 Printer printer = Printer.printerFor(this, accum); 902 printer.traverse(this); 903 } 904 905 /** 906 Append this node's opening or complete HTML to the internal output. 907 @param accum the internal output 908 @param out the output settings 909 */ 910 abstract void outerHtmlHead(final QuietAppendable accum, final Document.OutputSettings out); 911 912 /** 913 Append this node's closing HTML, if any, to the internal output. 914 @param accum the internal output 915 @param out the output settings 916 */ 917 abstract void outerHtmlTail(final QuietAppendable accum, final Document.OutputSettings out); 918 919 /** 920 Append the HTML of this node to the supplied {@link Appendable}. For an {@link Element}, this appends the element's 921 inner HTML, consistent with {@link Element#html()}. For other node types, this appends the node's outer HTML. 922 923 @param appendable the {@link Appendable} that will receive the HTML. 924 @return the supplied {@link Appendable}, for chaining. 925 @throws org.jsoup.SerializationException if the appendable throws an IOException. 926 @see Element#html(Appendable) 927 @see #outerHtml(Appendable) 928 */ 929 public <T extends Appendable> T html(T appendable) { 930 outerHtml(appendable); 931 return appendable; 932 } 933 934 /** 935 Get the source range (start and end positions) in the original input source from which this node was parsed. 936 Position tracking must be enabled prior to parsing the content. For an Element, this will be the positions of the 937 start tag. 938 @return the range for the start of the node, or {@code untracked} if its range was not tracked. 939 @see org.jsoup.parser.Parser#setTrackPosition(boolean) 940 @see Range#isImplicit() 941 @see Element#endSourceRange() 942 @see Attributes#sourceRange(String name) 943 @since 1.15.2 944 */ 945 public Range sourceRange() { 946 return Range.ofStart(this); 947 } 948 949 /** 950 Gets the range spans, if source tracking was used. 951 */ 952 Range.@Nullable Spans spans() { 953 if (!hasAttributes()) return null; 954 return attributes().spans(); 955 } 956 957 /** 958 Gets or creates range spans for this node. 959 */ 960 Range.Spans ensureSpans() { 961 return attributes().ensureSpans(); 962 } 963 964 /** 965 * Gets this node's outer HTML. 966 * @return outer HTML. 967 * @see #outerHtml() 968 */ 969 @Override 970 public String toString() { 971 return outerHtml(); 972 } 973 974 /** @deprecated internal method moved into Printer; will be removed in jsoup 1.24.1. */ 975 @Deprecated 976 protected void indent(Appendable accum, int depth, Document.OutputSettings out) throws IOException { 977 accum.append('\n').append(StringUtil.padding(depth * out.indentAmount(), out.maxPaddingWidth())); 978 } 979 980 /** 981 * Check if this node is the same instance of another (object identity test). 982 * <p>For a node value equality check, see {@link #hasSameValue(Object)}</p> 983 * @param o other object to compare to 984 * @return true if the content of this node is the same as the other 985 * @see Node#hasSameValue(Object) 986 */ 987 @Override 988 public boolean equals(@Nullable Object o) { 989 // implemented just so that javadoc is clear this is an identity test 990 return this == o; 991 } 992 993 /** 994 Provides a hashCode for this Node, based on its object identity. Changes to the Node's content will not impact the 995 result. 996 @return an object identity based hashcode for this Node 997 */ 998 @Override 999 public int hashCode() { 1000 // implemented so that javadoc and scanners are clear this is an identity test 1001 return super.hashCode(); 1002 } 1003 1004 /** 1005 * 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 1006 * other node; particularly its position in the tree does not influence its similarity. 1007 * @param o other object to compare to 1008 * @return true if the content of this node is the same as the other 1009 */ 1010 public boolean hasSameValue(@Nullable Object o) { 1011 if (this == o) return true; 1012 if (o == null || getClass() != o.getClass()) return false; 1013 1014 return this.outerHtml().equals(((Node) o).outerHtml()); 1015 } 1016 1017 /** 1018 Create a stand-alone, deep copy of this node, and all of its children. The cloned node will have no siblings. 1019 <p><ul> 1020 <li>If this node is a {@link LeafNode}, the clone will have no parent.</li> 1021 <li>If this node is an {@link Element}, the clone will have a simple owning {@link Document} to retain the 1022 configured output settings and parser.</li> 1023 </ul></p> 1024 <p>The cloned node may be adopted into another Document or node structure using 1025 {@link Element#appendChild(Node)}.</p> 1026 1027 @return a stand-alone cloned node, including clones of any children 1028 @see #shallowClone() 1029 */ 1030 @SuppressWarnings("MethodDoesntCallSuperMethod") 1031 // because it does call super.clone in doClone - analysis just isn't following 1032 @Override 1033 public Node clone() { 1034 Node thisClone = doClone(null); // splits for orphan 1035 1036 // Queue up nodes that need their children cloned (BFS). 1037 final LinkedList<Node> nodesToProcess = new LinkedList<>(); 1038 nodesToProcess.add(thisClone); 1039 1040 while (!nodesToProcess.isEmpty()) { 1041 Node currParent = nodesToProcess.remove(); 1042 1043 final int size = currParent.childNodeSize(); 1044 for (int i = 0; i < size; i++) { 1045 final List<Node> childNodes = currParent.ensureChildNodes(); 1046 Node childClone = childNodes.get(i).doClone(currParent); 1047 childNodes.set(i, childClone); 1048 nodesToProcess.add(childClone); 1049 } 1050 } 1051 1052 return thisClone; 1053 } 1054 1055 /** 1056 * Create a stand-alone, shallow copy of this node. None of its children (if any) will be cloned, and it will have 1057 * no parent or sibling nodes. 1058 * @return a single independent copy of this node 1059 * @see #clone() 1060 */ 1061 public Node shallowClone() { 1062 return doClone(null); 1063 } 1064 1065 /* 1066 * Return a clone of the node using the given parent (which can be null). 1067 * Not a deep copy of children. 1068 */ 1069 protected Node doClone(@Nullable Node parent) { 1070 assert parent == null || parent instanceof Element; 1071 Node clone; 1072 1073 try { 1074 clone = (Node) super.clone(); 1075 } catch (CloneNotSupportedException e) { 1076 throw new RuntimeException(e); 1077 } 1078 1079 clone.parentNode = (Element) parent; // can be null, to create an orphan split 1080 clone.siblingIndex = parent == null ? 0 : siblingIndex(); 1081 // if not keeping the parent, shallowClone the ownerDocument to preserve its settings 1082 if (parent == null && !(this instanceof Document)) { 1083 Document doc = ownerDocument(); 1084 if (doc != null) { 1085 Document docClone = doc.shallowClone(); 1086 clone.parentNode = docClone; 1087 docClone.ensureChildNodes().add(clone); 1088 } 1089 } 1090 1091 return clone; 1092 } 1093}