Java tutorial
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; /** * Depth-first node traversor. Use to iterate through all nodes under and including the specified root node. * <p> * This implementation does not use recursion, so a deep DOM does not risk blowing the stack. * </p> */ public class NodeTraversor { private NodeVisitor visitor; /** * Create a new traversor. * @param visitor a class implementing the {@link NodeVisitor} interface, to be called when visiting each node. * @deprecated Just use the static {@link NodeTraversor#filter(NodeFilter, Node)} method. */ public NodeTraversor(NodeVisitor visitor) { this.visitor = visitor; } /** * Start a depth-first traverse of the root and all of its descendants. * @param root the root node point to traverse. * @deprecated Just use the static {@link NodeTraversor#filter(NodeFilter, Node)} method. */ public void traverse(Node root) { traverse(visitor, root); } /** * Start a depth-first traverse of the root and all of its descendants. * @param visitor Node visitor. * @param root the root node point to traverse. */ public static void traverse(NodeVisitor visitor, Node root) { Node node = root; int depth = 0; while (node != null) { visitor.head(node, depth); if (node.childNodeSize() > 0) { node = node.childNode(0); depth++; } else { while (node.nextSibling() == null && depth > 0) { visitor.tail(node, depth); node = node.parentNode(); depth--; } visitor.tail(node, depth); if (node == root) break; node = node.nextSibling(); } } } /** * Start a depth-first traverse of all elements. * @param visitor Node visitor. * @param elements Elements to filter. */ public static void traverse(NodeVisitor visitor, Elements elements) { Validate.notNull(visitor); Validate.notNull(elements); for (Element el : elements) traverse(visitor, el); } /** * Start a depth-first filtering of the root and all of its descendants. * @param filter Node visitor. * @param root the root node point to traverse. * @return The filter result of the root node, or {@link FilterResult#STOP}. */ 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 (node.nextSibling() == null && depth > 0) { // '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; } /** * Start a depth-first filtering of all elements. * @param filter Node filter. * @param elements Elements to filter. */ 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; } }