001package org.jsoup.select; 002 003import org.jsoup.nodes.Element; 004import org.jsoup.nodes.Node; 005 006import static org.jsoup.select.NodeFilter.FilterResult.CONTINUE; 007import static org.jsoup.select.NodeFilter.FilterResult.STOP; 008 009/** 010 * Collects a list of elements that match the supplied criteria. 011 * 012 * @author Jonathan Hedley 013 */ 014public class Collector { 015 016 private Collector() { 017 } 018 019 /** 020 Build a list of elements, by visiting root and every descendant of root, and testing it against the evaluator. 021 @param eval Evaluator to test elements against 022 @param root root of tree to descend 023 @return list of matches; empty if none 024 */ 025 public static Elements collect (Evaluator eval, Element root) { 026 Elements elements = new Elements(); 027 NodeTraversor.traverse(new Accumulator(root, elements, eval), root); 028 return elements; 029 } 030 031 private static class Accumulator implements NodeVisitor { 032 private final Element root; 033 private final Elements elements; 034 private final Evaluator eval; 035 036 Accumulator(Element root, Elements elements, Evaluator eval) { 037 this.root = root; 038 this.elements = elements; 039 this.eval = eval; 040 } 041 042 public void head(Node node, int depth) { 043 if (node instanceof Element) { 044 Element el = (Element) node; 045 if (eval.matches(root, el)) 046 elements.add(el); 047 } 048 } 049 050 public void tail(Node node, int depth) { 051 // void 052 } 053 } 054 055 public static Element findFirst(Evaluator eval, Element root) { 056 FirstFinder finder = new FirstFinder(root, eval); 057 NodeTraversor.filter(finder, root); 058 return finder.match; 059 } 060 061 private static class FirstFinder implements NodeFilter { 062 private final Element root; 063 private Element match = null; 064 private final Evaluator eval; 065 066 FirstFinder(Element root, Evaluator eval) { 067 this.root = root; 068 this.eval = eval; 069 } 070 071 @Override 072 public FilterResult head(Node node, int depth) { 073 if (node instanceof Element) { 074 Element el = (Element) node; 075 if (eval.matches(root, el)) { 076 match = el; 077 return STOP; 078 } 079 } 080 return CONTINUE; 081 } 082 083 @Override 084 public FilterResult tail(Node node, int depth) { 085 return CONTINUE; 086 } 087 } 088 089}