117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin/*
217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin * Copyright (C) 2013 DroidDriver committers
317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin *
417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin * Licensed under the Apache License, Version 2.0 (the "License");
517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin * you may not use this file except in compliance with the License.
617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin * You may obtain a copy of the License at
717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin *
817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin *      http://www.apache.org/licenses/LICENSE-2.0
917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin *
1017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin * Unless required by applicable law or agreed to in writing, software
1117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin * distributed under the License is distributed on an "AS IS" BASIS,
1217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin * See the License for the specific language governing permissions and
1417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin * limitations under the License.
1517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin */
1617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
1717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jinpackage com.google.android.droiddriver.finders;
1817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
1917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jinimport android.text.TextUtils;
2017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
2117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jinimport com.google.android.droiddriver.UiElement;
2217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
2317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin/**
2417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin * Static utility methods pertaining to {@code Predicate} instances.
2517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin */
2617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jinpublic final class Predicates {
2717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  private Predicates() {}
2817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
2917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  private static final Predicate<Object> ANY = new Predicate<Object>() {
3017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    @Override
3117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    public boolean apply(Object o) {
3217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      return true;
3317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    }
3417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
3517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    @Override
3617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    public String toString() {
3717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      return "any";
3817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    }
3917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  };
4017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
4117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  /**
4217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin   * Returns a predicate that always evaluates to {@code true}.
4317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin   */
4417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  @SuppressWarnings("unchecked")
4517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  public static <T> Predicate<T> any() {
4617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    return (Predicate<T>) ANY;
4717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  }
4817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
4917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  /**
50e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   * Returns a predicate that is the negation of the provided {@code predicate}.
51e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   */
52e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  public static <T> Predicate<T> not(final Predicate<T> predicate) {
53e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    return new Predicate<T>() {
54e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      @Override
55e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      public boolean apply(T input) {
56e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        return !predicate.apply(input);
57e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      }
58e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin
59e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      @Override
60e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      public String toString() {
61e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        return "not(" + predicate + ")";
62e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      }
63e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    };
64e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  }
65e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin
66e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  /**
67e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   * Returns a predicate that evaluates to {@code true} if both arguments
68e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   * evaluate to {@code true}. The arguments are evaluated in order, and
69e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   * evaluation will be "short-circuited" as soon as a false predicate is found.
70e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   */
71e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  @SuppressWarnings("unchecked")
72e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  public static <T> Predicate<T> allOf(final Predicate<? super T> first,
73e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      final Predicate<? super T> second) {
74e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    if (first == null || first == ANY) {
75e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      return (Predicate<T>) second;
76e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    }
77e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    if (second == null || second == ANY) {
78e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      return (Predicate<T>) first;
79e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    }
80e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin
81e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    return new Predicate<T>() {
82e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      @Override
83e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      public boolean apply(T input) {
84e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        return first.apply(input) && second.apply(input);
85e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      }
86e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin
87e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      @Override
88e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      public String toString() {
89e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        return "allOf(" + first + ", " + second + ")";
90e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      }
91e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    };
92e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  }
93e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin
94e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  /**
9517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin   * Returns a predicate that evaluates to {@code true} if each of its
9617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin   * components evaluates to {@code true}. The components are evaluated in
9717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin   * order, and evaluation will be "short-circuited" as soon as a false
9817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin   * predicate is found.
9917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin   */
10017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  public static <T> Predicate<T> allOf(
10117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      @SuppressWarnings("unchecked") final Predicate<? super T>... components) {
10217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    return new Predicate<T>() {
10317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      @Override
10417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      public boolean apply(T input) {
10517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        for (Predicate<? super T> each : components) {
10617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin          if (!each.apply(input)) {
10717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin            return false;
10817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin          }
10917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        }
11017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        return true;
11117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      }
11217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
11317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      @Override
11417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      public String toString() {
11517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        return "allOf(" + TextUtils.join(", ", components) + ")";
11617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      }
11717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    };
11817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  }
11917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
12017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  /**
12117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin   * Returns a predicate that evaluates to {@code true} if any one of its
12217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin   * components evaluates to {@code true}. The components are evaluated in
12317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin   * order, and evaluation will be "short-circuited" as soon as a true predicate
12417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin   * is found.
12517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin   */
12617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  public static <T> Predicate<T> anyOf(
12717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      @SuppressWarnings("unchecked") final Predicate<? super T>... components) {
12817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    return new Predicate<T>() {
12917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      @Override
13017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      public boolean apply(T input) {
13117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        for (Predicate<? super T> each : components) {
13217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin          if (each.apply(input)) {
13317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin            return true;
13417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin          }
13517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        }
13617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        return false;
13717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      }
13817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
13917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      @Override
14017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      public String toString() {
14117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        return "anyOf(" + TextUtils.join(", ", components) + ")";
14217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      }
14317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    };
14417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  }
14517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
146e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  /**
147e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   * Returns a predicate that evaluates to {@code true} on a {@link UiElement}
148e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   * if its {@code attribute} is {@code true}.
149e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   */
150e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  public static Predicate<UiElement> attributeTrue(final Attribute attribute) {
151e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    return new Predicate<UiElement>() {
152e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      @Override
153e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      public boolean apply(UiElement element) {
154e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        Boolean actual = element.get(attribute);
155e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        return actual != null && actual;
156e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      }
157e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin
158e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      @Override
159e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      public String toString() {
160e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        return String.format("{%s}", attribute);
161e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      }
162e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    };
163e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  }
164e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin
165e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  /**
166e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   * Returns a predicate that evaluates to {@code true} on a {@link UiElement}
167e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   * if its {@code attribute} is {@code false}.
168e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   */
169e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  public static Predicate<UiElement> attributeFalse(final Attribute attribute) {
170e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    return new Predicate<UiElement>() {
171e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      @Override
172e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      public boolean apply(UiElement element) {
173e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        Boolean actual = element.get(attribute);
174e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        return actual == null || !actual;
175e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      }
176e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin
177e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      @Override
178e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      public String toString() {
179e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        return String.format("{not %s}", attribute);
180e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      }
181e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    };
182e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  }
183e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin
184e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  /**
185e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   * Returns a predicate that evaluates to {@code true} on a {@link UiElement}
186e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   * if its {@code attribute} equals {@code expected}.
187e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   */
188e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  public static Predicate<UiElement> attributeEquals(final Attribute attribute,
189e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      final Object expected) {
190e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    return new Predicate<UiElement>() {
191e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      @Override
192e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      public boolean apply(UiElement element) {
193e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        Object actual = element.get(attribute);
194e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        return actual == expected || (actual != null && actual.equals(expected));
195e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      }
196e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin
197e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      @Override
198e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      public String toString() {
199e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        return String.format("{%s=%s}", attribute, expected);
200e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      }
201e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    };
202e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  }
203e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin
204e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  /**
205e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   * Returns a predicate that evaluates to {@code true} on a {@link UiElement}
206e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   * if its {@code attribute} matches {@code regex}.
207e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   */
208e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  public static Predicate<UiElement> attributeMatches(final Attribute attribute, final String regex) {
209e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    return new Predicate<UiElement>() {
210e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      @Override
211e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      public boolean apply(UiElement element) {
212e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        String actual = element.get(attribute);
213e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        return actual != null && actual.matches(regex);
214e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      }
215e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin
216e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      @Override
217e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      public String toString() {
218e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        return String.format("{%s matches %s}", attribute, regex);
219e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      }
220e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    };
221e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  }
222e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin
223e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  /**
224e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   * Returns a predicate that evaluates to {@code true} on a {@link UiElement}
225e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   * if its {@code attribute} contains {@code substring}.
226e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin   */
227e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  public static Predicate<UiElement> attributeContains(final Attribute attribute,
228e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      final String substring) {
229e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    return new Predicate<UiElement>() {
230e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      @Override
231e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      public boolean apply(UiElement element) {
232e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        String actual = element.get(attribute);
233e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        return actual != null && actual.contains(substring);
234e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      }
235e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin
236e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      @Override
237e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      public String toString() {
238e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        return String.format("{%s contains %s}", attribute, substring);
239e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin      }
240e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    };
241e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin  }
242e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin
24317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  public static Predicate<UiElement> withParent(final Predicate<? super UiElement> parentPredicate) {
24417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    return new Predicate<UiElement>() {
24517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      @Override
24617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      public boolean apply(UiElement element) {
24717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        UiElement parent = element.getParent();
24817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        return parent != null && parentPredicate.apply(parent);
24917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      }
25017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
25117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      @Override
25217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      public String toString() {
25317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        return "withParent(" + parentPredicate + ")";
25417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      }
25517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    };
25617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  }
25717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
25817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  public static Predicate<UiElement> withAncestor(
25917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      final Predicate<? super UiElement> ancestorPredicate) {
26017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    return new Predicate<UiElement>() {
26117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      @Override
26217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      public boolean apply(UiElement element) {
26317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        UiElement parent = element.getParent();
26417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        while (parent != null) {
26517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin          if (ancestorPredicate.apply(parent)) {
26617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin            return true;
26717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin          }
26817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin          parent = parent.getParent();
26917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        }
27017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        return false;
27117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      }
27217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
27317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      @Override
27417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      public String toString() {
27517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        return "withAncestor(" + ancestorPredicate + ")";
27617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      }
27717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    };
27817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  }
27917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
28017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  public static Predicate<UiElement> withSibling(final Predicate<? super UiElement> siblingPredicate) {
28117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    return new Predicate<UiElement>() {
28217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      @Override
28317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      public boolean apply(UiElement element) {
28417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        UiElement parent = element.getParent();
28517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        if (parent == null) {
28617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin          return false;
28717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        }
28817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        for (UiElement sibling : parent.getChildren(UiElement.VISIBLE)) {
28917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin          if (sibling != element && siblingPredicate.apply(sibling)) {
29017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin            return true;
29117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin          }
29217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        }
29317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        return false;
29417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      }
29517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
29617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      @Override
29717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      public String toString() {
29817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        return "withSibling(" + siblingPredicate + ")";
29917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      }
30017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    };
30117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  }
30217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
30317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  public static Predicate<UiElement> withChild(final Predicate<? super UiElement> childPredicate) {
30417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    return new Predicate<UiElement>() {
30517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      @Override
30617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      public boolean apply(UiElement element) {
30717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        for (UiElement child : element.getChildren(UiElement.VISIBLE)) {
30817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin          if (childPredicate.apply(child)) {
30917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin            return true;
31017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin          }
31117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        }
31217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        return false;
31317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      }
31417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin
31517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      @Override
31617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      public String toString() {
31717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin        return "withChild(" + childPredicate + ")";
31817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      }
31917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    };
32017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  }
32117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin}
322