NodeEvaluator.java

package org.jsoup.select;

import org.jsoup.internal.StringUtil;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.LeafNode;
import org.jsoup.nodes.Node;

import java.util.regex.Pattern;

import static org.jsoup.internal.Normalizer.lowerCase;
import static org.jsoup.internal.StringUtil.normaliseWhitespace;

abstract class NodeEvaluator extends Evaluator {

    @Override
    public boolean matches(Element root, Element element) {
        return evaluateMatch(element);
    }

    @Override boolean matches(Element root, LeafNode leaf) {
        return evaluateMatch(leaf);
    }

    abstract boolean evaluateMatch(Node node);
    
    @Override boolean wantsNodes() {
        return true;
    }

    static class InstanceType extends NodeEvaluator {
        final java.lang.Class<? extends Node> type;
        final String selector;

        InstanceType(java.lang.Class<? extends Node> type, String selector) {
            super();
            this.type = type;
            this.selector = "::" + selector;
        }

        @Override
        boolean evaluateMatch(Node node) {
            return type.isInstance(node);
        }

        @Override
        protected int cost() {
            return 1;
        }

        @Override
        public String toString() {
            return selector;
        }
    }

    static class ContainsValue extends NodeEvaluator {
        private final String searchText;

        public ContainsValue(String searchText) {
            this.searchText = lowerCase(normaliseWhitespace(searchText));
        }

        @Override
        boolean evaluateMatch(Node node) {
            return lowerCase(node.nodeValue()).contains(searchText);
        }

        @Override
        protected int cost() {
            return 6;
        }

        @Override
        public String toString() {
            return String.format(":contains(%s)", searchText);
        }
    }

    /**
     Matches nodes with no value or only whitespace.
     */
    static class BlankValue extends NodeEvaluator {

        @Override
        boolean evaluateMatch(Node node) {
            return StringUtil.isBlank(node.nodeValue());
        }

        @Override
        protected int cost() {
            return 4;
        }

        @Override
        public String toString() {
            return ":blank";
        }
    }

    static class MatchesValue extends NodeEvaluator {
        private final Pattern pattern;

        protected MatchesValue(Pattern pattern) {
            this.pattern = pattern;
        }

        @Override
        boolean evaluateMatch(Node node) {
            return pattern.matcher(node.nodeValue()).find();
        }

        @Override
        protected int cost() {
            return 8;
        }

        @Override
        public String toString() {
            return String.format(":matches(%s)", pattern);
        }
    }
}