001package org.jsoup.select;
002
003import org.jsoup.nodes.Element;
004
005/**
006 * Base structural evaluator.
007 */
008abstract class StructuralEvaluator extends Evaluator {
009    Evaluator evaluator;
010
011    static class Root extends Evaluator {
012        public boolean matches(Element root, Element element) {
013            return root == element;
014        }
015    }
016
017    static class Has extends StructuralEvaluator {
018        public Has(Evaluator evaluator) {
019            this.evaluator = evaluator;
020        }
021
022        public boolean matches(Element root, Element element) {
023            for (Element e : element.getAllElements()) {
024                if (e != element && evaluator.matches(root, e))
025                    return true;
026            }
027            return false;
028        }
029
030        @Override
031        public String toString() {
032            return String.format(":has(%s)", evaluator);
033        }
034    }
035
036    static class Not extends StructuralEvaluator {
037        public Not(Evaluator evaluator) {
038            this.evaluator = evaluator;
039        }
040
041        public boolean matches(Element root, Element node) {
042            return !evaluator.matches(root, node);
043        }
044
045        @Override
046        public String toString() {
047            return String.format(":not%s", evaluator);
048        }
049    }
050
051    static class Parent extends StructuralEvaluator {
052        public Parent(Evaluator evaluator) {
053            this.evaluator = evaluator;
054        }
055
056        public boolean matches(Element root, Element element) {
057            if (root == element)
058                return false;
059
060            Element parent = element.parent();
061            while (true) {
062                if (evaluator.matches(root, parent))
063                    return true;
064                if (parent == root)
065                    break;
066                parent = parent.parent();
067            }
068            return false;
069        }
070
071        @Override
072        public String toString() {
073            return String.format(":parent%s", evaluator);
074        }
075    }
076
077    static class ImmediateParent extends StructuralEvaluator {
078        public ImmediateParent(Evaluator evaluator) {
079            this.evaluator = evaluator;
080        }
081
082        public boolean matches(Element root, Element element) {
083            if (root == element)
084                return false;
085
086            Element parent = element.parent();
087            return parent != null && evaluator.matches(root, parent);
088        }
089
090        @Override
091        public String toString() {
092            return String.format(":ImmediateParent%s", evaluator);
093        }
094    }
095
096    static class PreviousSibling extends StructuralEvaluator {
097        public PreviousSibling(Evaluator evaluator) {
098            this.evaluator = evaluator;
099        }
100
101        public boolean matches(Element root, Element element) {
102            if (root == element)
103                return false;
104
105            Element prev = element.previousElementSibling();
106
107            while (prev != null) {
108                if (evaluator.matches(root, prev))
109                    return true;
110
111                prev = prev.previousElementSibling();
112            }
113            return false;
114        }
115
116        @Override
117        public String toString() {
118            return String.format(":prev*%s", evaluator);
119        }
120    }
121
122    static class ImmediatePreviousSibling extends StructuralEvaluator {
123        public ImmediatePreviousSibling(Evaluator evaluator) {
124            this.evaluator = evaluator;
125        }
126
127        public boolean matches(Element root, Element element) {
128            if (root == element)
129                return false;
130
131            Element prev = element.previousElementSibling();
132            return prev != null && evaluator.matches(root, prev);
133        }
134
135        @Override
136        public String toString() {
137            return String.format(":prev%s", evaluator);
138        }
139    }
140}