1/* 2 * Copyright (C) 2013 DroidDriver committers 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package io.appium.droiddriver.finders; 18 19import android.text.TextUtils; 20 21import java.util.Arrays; 22 23import io.appium.droiddriver.UiElement; 24 25/** 26 * Static utility methods pertaining to {@code Predicate} instances. 27 */ 28public final class Predicates { 29 30 private Predicates() { 31 } 32 33 private static final Predicate<Object> ANY = new Predicate<Object>() { 34 @Override 35 public boolean apply(Object o) { 36 return true; 37 } 38 39 @Override 40 public String toString() { 41 return "any"; 42 } 43 }; 44 45 /** 46 * Returns a predicate that always evaluates to {@code true}. 47 */ 48 @SuppressWarnings("unchecked") 49 public static <T> Predicate<T> any() { 50 return (Predicate<T>) ANY; 51 } 52 53 /** 54 * Returns a predicate that is the negation of the provided {@code predicate}. 55 */ 56 public static <T> Predicate<T> not(final Predicate<T> predicate) { 57 return new Predicate<T>() { 58 @Override 59 public boolean apply(T input) { 60 return !predicate.apply(input); 61 } 62 63 @Override 64 public String toString() { 65 return "not(" + predicate + ")"; 66 } 67 }; 68 } 69 70 /** 71 * Returns a predicate that evaluates to {@code true} if both arguments evaluate to {@code true}. 72 * The arguments are evaluated in order, and evaluation will be "short-circuited" as soon as a 73 * false predicate is found. 74 */ 75 @SuppressWarnings("unchecked") 76 public static <T> Predicate<T> allOf(final Predicate<? super T> first, 77 final Predicate<? super T> second) { 78 if (first == null || first == ANY) { 79 return (Predicate<T>) second; 80 } 81 if (second == null || second == ANY) { 82 return (Predicate<T>) first; 83 } 84 85 return new Predicate<T>() { 86 @Override 87 public boolean apply(T input) { 88 return first.apply(input) && second.apply(input); 89 } 90 91 @Override 92 public String toString() { 93 return "allOf(" + first + ", " + second + ")"; 94 } 95 }; 96 } 97 98 /** 99 * Returns a predicate that evaluates to {@code true} if each of its components evaluates to 100 * {@code true}. The components are evaluated in order, and evaluation will be "short-circuited" 101 * as soon as a false predicate is found. 102 */ 103 public static <T> Predicate<T> allOf(final Iterable<Predicate<? super T>> components) { 104 return new Predicate<T>() { 105 @Override 106 public boolean apply(T input) { 107 for (Predicate<? super T> each : components) { 108 if (!each.apply(input)) { 109 return false; 110 } 111 } 112 return true; 113 } 114 115 @Override 116 public String toString() { 117 return "allOf(" + TextUtils.join(", ", components) + ")"; 118 } 119 }; 120 } 121 122 /** 123 * Returns a predicate that evaluates to {@code true} if each of its components evaluates to 124 * {@code true}. The components are evaluated in order, and evaluation will be "short-circuited" 125 * as soon as a false predicate is found. 126 */ 127 @SuppressWarnings("RedundantTypeArguments") // Some compilers cannot infer <T> 128 @SafeVarargs 129 public static <T> Predicate<T> allOf(final Predicate<? super T>... components) { 130 return Predicates.<T>allOf(Arrays.asList(components)); 131 } 132 133 /** 134 * Returns a predicate that evaluates to {@code true} if any one of its components evaluates to 135 * {@code true}. The components are evaluated in order, and evaluation will be "short-circuited" 136 * as soon as a true predicate is found. 137 */ 138 public static <T> Predicate<T> anyOf(final Iterable<Predicate<? super T>> components) { 139 return new Predicate<T>() { 140 @Override 141 public boolean apply(T input) { 142 for (Predicate<? super T> each : components) { 143 if (each.apply(input)) { 144 return true; 145 } 146 } 147 return false; 148 } 149 150 @Override 151 public String toString() { 152 return "anyOf(" + TextUtils.join(", ", components) + ")"; 153 } 154 }; 155 } 156 157 /** 158 * Returns a predicate that evaluates to {@code true} if any one of its components evaluates to 159 * {@code true}. The components are evaluated in order, and evaluation will be "short-circuited" 160 * as soon as a true predicate is found. 161 */ 162 @SuppressWarnings("RedundantTypeArguments") // Some compilers cannot infer <T> 163 @SafeVarargs 164 public static <T> Predicate<T> anyOf(final Predicate<? super T>... components) { 165 return Predicates.<T>anyOf(Arrays.asList(components)); 166 } 167 168 /** 169 * Returns a predicate that evaluates to {@code true} on a {@link UiElement} if its {@code 170 * attribute} is {@code true}. 171 */ 172 public static Predicate<UiElement> attributeTrue(final Attribute attribute) { 173 return new Predicate<UiElement>() { 174 @Override 175 public boolean apply(UiElement element) { 176 Boolean actual = element.get(attribute); 177 return actual != null && actual; 178 } 179 180 @Override 181 public String toString() { 182 return String.format("{%s}", attribute); 183 } 184 }; 185 } 186 187 /** 188 * Returns a predicate that evaluates to {@code true} on a {@link UiElement} if its {@code 189 * attribute} is {@code false}. 190 */ 191 public static Predicate<UiElement> attributeFalse(final Attribute attribute) { 192 return new Predicate<UiElement>() { 193 @Override 194 public boolean apply(UiElement element) { 195 Boolean actual = element.get(attribute); 196 return actual == null || !actual; 197 } 198 199 @Override 200 public String toString() { 201 return String.format("{not %s}", attribute); 202 } 203 }; 204 } 205 206 /** 207 * Returns a predicate that evaluates to {@code true} on a {@link UiElement} if its {@code 208 * attribute} equals {@code expected}. 209 */ 210 public static Predicate<UiElement> attributeEquals(final Attribute attribute, 211 final Object expected) { 212 return new Predicate<UiElement>() { 213 @Override 214 public boolean apply(UiElement element) { 215 Object actual = element.get(attribute); 216 return actual == expected || (actual != null && actual.equals(expected)); 217 } 218 219 @Override 220 public String toString() { 221 return String.format("{%s=%s}", attribute, expected); 222 } 223 }; 224 } 225 226 /** 227 * Returns a predicate that evaluates to {@code true} on a {@link UiElement} if its {@code 228 * attribute} matches {@code regex}. 229 */ 230 public static Predicate<UiElement> attributeMatches(final Attribute attribute, 231 final String regex) { 232 return new Predicate<UiElement>() { 233 @Override 234 public boolean apply(UiElement element) { 235 String actual = element.get(attribute); 236 return actual != null && actual.matches(regex); 237 } 238 239 @Override 240 public String toString() { 241 return String.format("{%s matches %s}", attribute, regex); 242 } 243 }; 244 } 245 246 /** 247 * Returns a predicate that evaluates to {@code true} on a {@link UiElement} if its {@code 248 * attribute} contains {@code substring}. 249 */ 250 public static Predicate<UiElement> attributeContains(final Attribute attribute, 251 final String substring) { 252 return new Predicate<UiElement>() { 253 @Override 254 public boolean apply(UiElement element) { 255 String actual = element.get(attribute); 256 return actual != null && actual.contains(substring); 257 } 258 259 @Override 260 public String toString() { 261 return String.format("{%s contains %s}", attribute, substring); 262 } 263 }; 264 } 265 266 public static Predicate<UiElement> withParent( 267 final Predicate<? super UiElement> parentPredicate) { 268 return new Predicate<UiElement>() { 269 @Override 270 public boolean apply(UiElement element) { 271 UiElement parent = element.getParent(); 272 return parent != null && parentPredicate.apply(parent); 273 } 274 275 @Override 276 public String toString() { 277 return "withParent(" + parentPredicate + ")"; 278 } 279 }; 280 } 281 282 public static Predicate<UiElement> withAncestor( 283 final Predicate<? super UiElement> ancestorPredicate) { 284 return new Predicate<UiElement>() { 285 @Override 286 public boolean apply(UiElement element) { 287 UiElement parent = element.getParent(); 288 while (parent != null) { 289 if (ancestorPredicate.apply(parent)) { 290 return true; 291 } 292 parent = parent.getParent(); 293 } 294 return false; 295 } 296 297 @Override 298 public String toString() { 299 return "withAncestor(" + ancestorPredicate + ")"; 300 } 301 }; 302 } 303 304 public static Predicate<UiElement> withSibling( 305 final Predicate<? super UiElement> siblingPredicate) { 306 return new Predicate<UiElement>() { 307 @Override 308 public boolean apply(UiElement element) { 309 UiElement parent = element.getParent(); 310 if (parent == null) { 311 return false; 312 } 313 for (UiElement sibling : parent.getChildren(UiElement.VISIBLE)) { 314 if (sibling != element && siblingPredicate.apply(sibling)) { 315 return true; 316 } 317 } 318 return false; 319 } 320 321 @Override 322 public String toString() { 323 return "withSibling(" + siblingPredicate + ")"; 324 } 325 }; 326 } 327 328 public static Predicate<UiElement> withChild(final Predicate<? super UiElement> childPredicate) { 329 return new Predicate<UiElement>() { 330 @Override 331 public boolean apply(UiElement element) { 332 for (UiElement child : element.getChildren(UiElement.VISIBLE)) { 333 if (childPredicate.apply(child)) { 334 return true; 335 } 336 } 337 return false; 338 } 339 340 @Override 341 public String toString() { 342 return "withChild(" + childPredicate + ")"; 343 } 344 }; 345 } 346 347 348} 349