1 package com.atlassian.pageobjects.elements.query;
2
3 import com.google.common.base.Supplier;
4 import org.apache.commons.lang.ArrayUtils;
5 import org.hamcrest.Matcher;
6 import org.hamcrest.StringDescription;
7 import org.slf4j.Logger;
8 import org.slf4j.LoggerFactory;
9
10 import java.util.List;
11
12 import static com.atlassian.pageobjects.elements.util.StringConcat.asString;
13 import static com.google.common.base.Preconditions.checkNotNull;
14
15
16
17
18
19 public final class Conditions
20 {
21 private static final Logger log = LoggerFactory.getLogger(Conditions.class);
22
23 private static final int DEFAULT_TIMEOUT = 100;
24
25
26 private Conditions()
27 {
28 throw new AssertionError("No way");
29 }
30
31
32
33
34
35
36
37 public static TimedQuery<Boolean> not(TimedQuery<Boolean> condition)
38 {
39 if (condition instanceof Not)
40 {
41 return asDecorator(condition).wrapped;
42 }
43 return new Not(condition);
44 }
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public static CombinableCondition and(TimedQuery<Boolean>... conditions)
61 {
62 return new And(conditions);
63 }
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 public static CombinableCondition and(List<TimedQuery<Boolean>> conditions)
80 {
81 return and(conditions.toArray(new TimedCondition[conditions.size()]));
82 }
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98 public static CombinableCondition or(TimedQuery<Boolean>... conditions)
99 {
100 return new Or(conditions);
101 }
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117 public static CombinableCondition or(List<TimedQuery<Boolean>> conditions)
118 {
119 return or(conditions.toArray(new TimedCondition[conditions.size()]));
120 }
121
122
123
124
125
126
127
128
129 public static TimedCondition falseCondition(long defaultTimeout)
130 {
131 return new AbstractTimedCondition(defaultTimeout, defaultTimeout)
132 {
133 @Override
134 public Boolean currentValue()
135 {
136 return false;
137 }
138 };
139 }
140
141
142
143
144
145
146 public static TimedCondition falseCondition()
147 {
148 return falseCondition(DEFAULT_TIMEOUT);
149 }
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170 public static TimedCondition dependantCondition(TimedQuery<Boolean> original, Supplier<TimedQuery<Boolean>> dependant)
171 {
172 return new DependantCondition(original, dependant);
173 }
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193 public static <T> TimedCondition forMatcher(TimedQuery<T> query, Matcher<T> matcher)
194 {
195 return new MatchingCondition<T>(query, matcher);
196 }
197
198
199
200
201
202
203
204
205 public static TimedCondition forSupplier(final Supplier<Boolean> supplier)
206 {
207 return forSupplier(DEFAULT_TIMEOUT, supplier);
208 }
209
210
211
212
213
214
215
216
217 public static TimedCondition forSupplier(long defaultTimeout, final Supplier<Boolean> supplier)
218 {
219 return new AbstractTimedCondition(defaultTimeout, PollingQuery.DEFAULT_INTERVAL) {
220 @Override
221 protected Boolean currentValue() {
222 return supplier.get();
223 }
224 };
225 }
226
227
228 private static AbstractConditionDecorator asDecorator(TimedQuery<Boolean> condition)
229 {
230 return (AbstractConditionDecorator) condition;
231 }
232
233
234
235
236
237
238 public static interface CombinableCondition extends TimedCondition
239 {
240
241
242
243
244
245
246
247 CombinableCondition and(TimedCondition other);
248
249
250
251
252
253
254
255
256 CombinableCondition or(TimedCondition other);
257
258 }
259
260
261 private abstract static class AbstractConditionDecorator extends AbstractTimedCondition
262 {
263 protected final TimedQuery<Boolean> wrapped;
264
265 public AbstractConditionDecorator(TimedQuery<Boolean> wrapped)
266 {
267 super(wrapped);
268 this.wrapped = checkNotNull(wrapped, "wrapped");
269 }
270 }
271
272 private abstract static class AbstractConditionsDecorator extends AbstractTimedCondition implements CombinableCondition
273 {
274 protected final TimedQuery<Boolean>[] conditions;
275
276 public AbstractConditionsDecorator(TimedQuery<Boolean>... conditions)
277 {
278 super(conditions[0]);
279 this.conditions = conditions;
280 }
281
282 @Override
283 public String toString()
284 {
285 StringBuilder answer = new StringBuilder(conditions.length * 20).append(getClass().getName()).append(":\n");
286 for (TimedQuery<Boolean> condition : conditions)
287 {
288 answer.append(" -").append(condition.toString()).append('\n');
289 }
290 return answer.deleteCharAt(answer.length()-1).toString();
291 }
292 }
293
294 private static class Not extends AbstractConditionDecorator
295 {
296 public Not(TimedQuery<Boolean> other)
297 {
298 super(other);
299 }
300
301 public Boolean currentValue()
302 {
303 return !wrapped.now();
304 }
305
306 @Override
307 public String toString()
308 {
309 return asString("Negated: <", wrapped, ">");
310 }
311 }
312
313 private static class And extends AbstractConditionsDecorator
314 {
315 public And(TimedQuery<Boolean>... conditions)
316 {
317 super(conditions);
318 }
319
320 And(TimedQuery<Boolean>[] somes, TimedQuery<Boolean>[] more)
321 {
322 super((TimedCondition[]) ArrayUtils.addAll(somes, more));
323 }
324
325 And(TimedQuery<Boolean>[] somes, TimedQuery<Boolean> oneMore)
326 {
327 super((TimedCondition[]) ArrayUtils.add(somes, oneMore));
328 }
329
330 public Boolean currentValue()
331 {
332 boolean result = true;
333 for (TimedQuery<Boolean> condition : conditions)
334 {
335
336 final boolean next = condition.now() != null ? condition.now() : false;
337 result &= next;
338 if (!result)
339 {
340 log.debug(asString("[And] Condition <",condition,"> returned false"));
341 break;
342 }
343 }
344 return result;
345 }
346
347 public CombinableCondition and(TimedCondition other)
348 {
349 if (other.getClass().equals(And.class))
350 {
351 return new And(this.conditions, ((And) other).conditions);
352 }
353 return new And(this.conditions, other);
354 }
355
356 public CombinableCondition or(TimedCondition other)
357 {
358 if (other instanceof Or)
359 {
360 return ((Or)other).or(this);
361 }
362 return new Or(this, other);
363 }
364 }
365
366 private static class Or extends AbstractConditionsDecorator
367 {
368 public Or(TimedQuery<Boolean>... conditions)
369 {
370 super(conditions);
371 }
372
373 Or(TimedQuery<Boolean>[] somes, TimedQuery<Boolean>[] more)
374 {
375 super((TimedCondition[]) ArrayUtils.addAll(somes, more));
376 }
377
378 Or(TimedQuery<Boolean>[] somes, TimedQuery<Boolean> oneMore)
379 {
380 super((TimedCondition[]) ArrayUtils.add(somes, oneMore));
381 }
382
383 public Boolean currentValue()
384 {
385 boolean result = false;
386 for (TimedQuery<Boolean> condition : conditions)
387 {
388
389 final boolean next = condition.now() != null ? condition.now() : false;
390 result |= next;
391 if (result)
392 {
393 break;
394 }
395 log.debug(asString("[Or] Condition <",condition,"> returned false"));
396 }
397 return result;
398 }
399
400 public CombinableCondition and(TimedCondition other)
401 {
402 if (other instanceof And)
403 {
404 return ((And)other).and(this);
405 }
406 return new And(this, other);
407 }
408
409 public CombinableCondition or(TimedCondition other)
410 {
411 if (other.getClass().equals(Or.class))
412 {
413 return new Or(this.conditions, ((Or) other).conditions);
414 }
415 return new Or(this.conditions, other);
416 }
417 }
418
419 private static final class DependantCondition extends AbstractConditionDecorator
420 {
421 private final Supplier<TimedQuery<Boolean>> dependant;
422
423 DependantCondition(TimedQuery<Boolean> original, Supplier<TimedQuery<Boolean>> dependant)
424 {
425 super(original);
426 this.dependant = checkNotNull(dependant, "dependant");
427 }
428
429 @Override
430 public Boolean currentValue()
431 {
432 return wrapped.now() && dependant.get().now();
433 }
434
435 @Override
436 public String toString()
437 {
438 if (wrapped.now())
439 {
440 TimedQuery<Boolean> dep = dependant.get();
441 return asString("DependantCondition[original=",wrapped,",dependant=",dep,"]");
442 }
443 return asString("DependantCondition[original=",wrapped,"]");
444 }
445 }
446
447
448 static final class MatchingCondition<T> extends AbstractTimedCondition
449 {
450 final TimedQuery<T> query;
451 final Matcher<T> matcher;
452
453 T lastValue;
454
455 public MatchingCondition(final TimedQuery<T> query, final Matcher<T> matcher)
456 {
457 super(query);
458 this.query = checkNotNull(query);
459 this.matcher = checkNotNull(matcher);
460 }
461
462 @Override
463 protected Boolean currentValue()
464 {
465 lastValue = query.now();
466 return matcher.matches(lastValue);
467 }
468
469 @Override
470 public String toString()
471 {
472 return super.toString() + new StringDescription().appendText("[query=").appendValue(query)
473 .appendText("][matcher=").appendDescriptionOf(matcher).appendText("]");
474 }
475 }
476 }