001package org.jsoup.select; 002 003import org.jsoup.internal.CssMatching; 004import org.jsoup.internal.QueryMode; 005import org.jsoup.nodes.Element; 006import org.jsoup.nodes.Node; 007import org.jspecify.annotations.Nullable; 008 009import java.util.function.Predicate; 010import java.util.stream.Stream; 011 012/** 013 Shared query operations for {@link Element}, {@link Collector}, and {@link org.jsoup.parser.StreamParser}. 014 Public for access between jsoup packages; not a supported user API. 015 String-query methods report compatibility differences when in check mode. 016 */ 017public final class EvaluatorInternals { 018 private EvaluatorInternals() {} 019 020 /** 021 Filters candidates using the evaluator's captured query mode, excluding {@code root} in Web mode. 022 */ 023 public static Stream<Element> stream(Evaluator evaluator, Element root, Stream<Element> candidates) { 024 return stream(evaluator, root, candidates, evaluator.asPredicate(root)); 025 } 026 027 /** 028 Shares stream filtering between element and node queries. 029 */ 030 static <T extends Node> Stream<T> stream(Evaluator evaluator, Element root, Stream<T> candidates, 031 Predicate<? super T> predicate) { 032 evaluator.reset(); 033 if (evaluator.web) 034 candidates = candidates.filter(node -> node != root); 035 return candidates.filter(documentPredicate(evaluator, root, predicate)); 036 } 037 038 /** 039 Resolves the context document lazily and reuses it for Web matching. 040 */ 041 private static <T> Predicate<T> documentPredicate(Evaluator evaluator, Element root, Predicate<T> predicate) { 042 return evaluator.web ? CssMatching.withDocument(root, predicate) : predicate; 043 } 044 045 /** 046 Matches ID values exactly for {@link Element#getElementById(String)}, even in quirks mode. 047 */ 048 public static Evaluator byId(String id) { 049 return new Evaluator() { 050 @Override 051 public boolean matches(Element root, Element element) { 052 String actual = web ? 053 CssMatching.attributeValue(element, "id", CssMatching.isHtmlElement(root, element)) : element.id(); 054 return id.equals(actual); 055 } 056 }; 057 } 058 059 /** 060 Tests whether the supplied element matches the query. 061 */ 062 public static boolean matches(String query, Element element) { 063 QueryMode mode = QueryMode.current(); 064 boolean result = matches(QueryParser.parse(query, mode == QueryMode.Web), element); 065 if (mode == QueryMode.Check) 066 QueryCompat.check(query, result, evaluator -> matches(evaluator, element)); 067 return result; 068 } 069 070 /** 071 Returns the element if it matches the query, or its nearest matching ancestor. 072 */ 073 public static @Nullable Element closest(String query, Element element) { 074 QueryMode mode = QueryMode.current(); 075 Element result = closest(QueryParser.parse(query, mode == QueryMode.Web), element); 076 if (mode == QueryMode.Check) 077 QueryCompat.check("closest", query, result, evaluator -> closest(evaluator, element)); 078 return result; 079 } 080 081 /** 082 Tests the element itself, using it as {@code :scope} in Web mode. 083 */ 084 public static boolean matches(Evaluator evaluator, Element element) { 085 evaluator.reset(); 086 Element root = evaluator.web ? element : element.root(); 087 boolean matches = evaluator.web ? 088 CssMatching.<Element>withDocument(root, candidate -> evaluator.matches(root, candidate)).test(element) : 089 evaluator.matches(root, element); 090 evaluator.reset(); 091 return matches; 092 } 093 094 /** 095 Returns the element if it matches the evaluator, or its nearest matching ancestor. 096 In Web mode, {@code :scope} remains the supplied element. 097 */ 098 public static @Nullable Element closest(Evaluator evaluator, Element element) { 099 evaluator.reset(); 100 Element root = evaluator.web ? element : element.root(); 101 Predicate<Element> predicate = documentPredicate(evaluator, root, candidate -> evaluator.matches(root, candidate)); 102 Element candidate = element; 103 while (candidate != null && !predicate.test(candidate)) 104 candidate = candidate.parent(); 105 evaluator.reset(); 106 return candidate; 107 } 108}