All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jsoup.select.NodeTraversor Maven / Gradle / Ivy

Go to download

SDK for dev_appserver (local development) with some of the dependencies shaded (repackaged)

There is a newer version: 2.0.31
Show newest version
package org.jsoup.select;

import org.jsoup.helper.Validate;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.select.NodeFilter.FilterResult;

/**
 A depth-first node traversor. Use to walk through all nodes under and including the specified root node, in document
 order. The {@link NodeVisitor#head(Node, int)} and {@link NodeVisitor#tail(Node, int)} methods will be called for
 each node.
 

During traversal, structural changes to nodes are supported (e.g. {{@link Node#replaceWith(Node)}, {@link Node#remove()}}

*/ public class NodeTraversor { /** Run a depth-first traverse of the root and all of its descendants. @param visitor Node visitor. @param root the initial node point to traverse. @see NodeVisitor */ public static void traverse(NodeVisitor visitor, Node root) { Validate.notNull(visitor); Validate.notNull(root); Node node = root; int depth = 0; while (node != null) { Node parent = node.parentNode(); // remember parent to find nodes that get replaced in .head int origSize = parent != null ? parent.childNodeSize() : 0; Node next = node.nextSibling(); visitor.head(node, depth); // visit current node if (parent != null && !node.hasParent()) { // removed or replaced if (origSize == parent.childNodeSize()) { // replaced node = parent.childNode(node.siblingIndex()); // replace ditches parent but keeps sibling index } else { // removed node = next; if (node == null) { // last one, go up node = parent; depth--; } continue; // don't tail removed } } if (node.childNodeSize() > 0) { // descend node = node.childNode(0); depth++; } else { while (true) { assert node != null; // as depth > 0, will have parent if (!(node.nextSibling() == null && depth > 0)) break; visitor.tail(node, depth); // when no more siblings, ascend node = node.parentNode(); depth--; } visitor.tail(node, depth); if (node == root) break; node = node.nextSibling(); } } } /** Run a depth-first traversal of each Element. @param visitor Node visitor. @param elements Elements to traverse. */ public static void traverse(NodeVisitor visitor, Elements elements) { Validate.notNull(visitor); Validate.notNull(elements); for (Element el : elements) traverse(visitor, el); } /** Run a depth-first filtered traversal of the root and all of its descendants. @param filter NodeFilter visitor. @param root the root node point to traverse. @return The filter result of the root node, or {@link FilterResult#STOP}. @see NodeFilter */ public static FilterResult filter(NodeFilter filter, Node root) { Node node = root; int depth = 0; while (node != null) { FilterResult result = filter.head(node, depth); if (result == FilterResult.STOP) return result; // Descend into child nodes: if (result == FilterResult.CONTINUE && node.childNodeSize() > 0) { node = node.childNode(0); ++depth; continue; } // No siblings, move upwards: while (true) { assert node != null; // depth > 0, so has parent if (!(node.nextSibling() == null && depth > 0)) break; // 'tail' current node: if (result == FilterResult.CONTINUE || result == FilterResult.SKIP_CHILDREN) { result = filter.tail(node, depth); if (result == FilterResult.STOP) return result; } Node prev = node; // In case we need to remove it below. node = node.parentNode(); depth--; if (result == FilterResult.REMOVE) prev.remove(); // Remove AFTER finding parent. result = FilterResult.CONTINUE; // Parent was not pruned. } // 'tail' current node, then proceed with siblings: if (result == FilterResult.CONTINUE || result == FilterResult.SKIP_CHILDREN) { result = filter.tail(node, depth); if (result == FilterResult.STOP) return result; } if (node == root) return result; Node prev = node; // In case we need to remove it below. node = node.nextSibling(); if (result == FilterResult.REMOVE) prev.remove(); // Remove AFTER finding sibling. } // root == null? return FilterResult.CONTINUE; } /** Run a depth-first filtered traversal of each Element. @param filter NodeFilter visitor. @see NodeFilter */ public static void filter(NodeFilter filter, Elements elements) { Validate.notNull(filter); Validate.notNull(elements); for (Element el : elements) if (filter(filter, el) == FilterResult.STOP) break; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy