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}