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.
This implementation does not use recursion, so a deep DOM does not risk blowing the stack.
/**
* 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.
Params: - visitor – a class implementing the
NodeVisitor
interface, to be called when visiting each node.
Deprecated: Just use the static filter(NodeFilter, Node)
method.
/**
* 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.
Params: - root – the root node point to traverse.
Deprecated: Just use the static filter(NodeFilter, Node)
method.
/**
* 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.
Params: - visitor – Node visitor.
- root – the root node point to traverse.
/**
* 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.
Params: - visitor – Node visitor.
- elements – Elements to filter.
/**
* 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.
Params: - filter – Node visitor.
- root – the root node point to traverse.
Returns: The filter result of the root node, or FilterResult.STOP
.
/**
* 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.
Params: - filter – Node filter.
- elements – Elements to filter.
/**
* 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;
}
}