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