001package org.jsoup.select;
002
003import org.jsoup.nodes.Element;
004import org.jspecify.annotations.Nullable;
005
006import java.util.stream.Collectors;
007import java.util.stream.Stream;
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     Build a list of elements, by visiting the root and every descendant of root, and testing it against the Evaluator.
020     @param eval Evaluator to test elements against
021     @param root root of tree to descend
022     @return list of matches; empty if none
023     */
024    public static Elements collect(Evaluator eval, Element root) {
025        return stream(eval, root).collect(Collectors.toCollection(Elements::new));
026    }
027
028    /**
029     Obtain a Stream of elements by visiting the root and every descendant of root and testing it against the evaluator.
030
031     @param evaluator Evaluator to test elements against
032     @param root root of tree to descend
033     @return A {@link Stream} of matches
034     @since 1.19.1
035     */
036    public static Stream<Element> stream(Evaluator evaluator, Element root) {
037        evaluator.reset();
038        return root.stream().filter(evaluator.asPredicate(root));
039    }
040
041    /**
042     Finds the first Element that matches the Evaluator that descends from the root, and stops the query once that first
043     match is found.
044     @param eval Evaluator to test elements against
045     @param root root of tree to descend
046     @return the first match; {@code null} if none
047     */
048    public static @Nullable Element findFirst(Evaluator eval, Element root) {
049        return stream(eval, root).findFirst().orElse(null);
050    }
051}