1 package com.atlassian.pageobjects.internal.elements.search;
2
3 import com.atlassian.pageobjects.PageBinder;
4 import com.atlassian.pageobjects.PageObjects;
5 import com.atlassian.pageobjects.elements.query.Conditions;
6 import com.atlassian.pageobjects.elements.query.Queries;
7 import com.atlassian.pageobjects.elements.query.TimedCondition;
8 import com.atlassian.pageobjects.elements.query.TimedQuery;
9 import com.atlassian.pageobjects.elements.search.AnyQuery;
10 import com.atlassian.pageobjects.elements.search.SearchQuery;
11 import com.atlassian.pageobjects.elements.timeout.Timeouts;
12 import com.google.common.base.Function;
13 import com.google.common.base.Predicate;
14 import com.google.common.base.Supplier;
15 import com.google.common.collect.FluentIterable;
16 import com.google.common.collect.Iterables;
17
18 import javax.annotation.Nonnull;
19 import javax.annotation.Nullable;
20 import javax.inject.Inject;
21
22 import static com.google.common.base.Preconditions.checkNotNull;
23 import static com.google.common.collect.Iterables.getFirst;
24
25 public abstract class AbstractSearchQuery<E, Q extends SearchQuery<E, Q>> implements SearchQuery<E, Q>
26 {
27 @Inject
28 protected Timeouts timeouts;
29
30 @Inject
31 protected PageBinder pageBinder;
32
33 protected final Supplier<Iterable<E>> querySupplier;
34
35 protected AbstractSearchQuery(@Nonnull Supplier<Iterable<E>> querySupplier)
36 {
37 this.querySupplier = checkNotNull(querySupplier, "querySupplier");
38 }
39
40 @Nonnull
41 @Override
42 public final Q filter(@Nonnull final Predicate<? super E> predicate)
43 {
44 return newInstance(new Supplier<Iterable<E>>()
45 {
46 @Override
47 public Iterable<E> get()
48 {
49 return Iterables.filter(querySupplier.get(), predicate);
50 }
51 });
52 }
53
54 @Nonnull
55 @Override
56 public final <F> AnyQuery<F> map(@Nonnull final Function<? super E, F> mapper)
57 {
58 return newAnyQueryInstance(mapSupplier(mapper));
59 }
60
61 @Nonnull
62 @Override
63 public final <F> AnyQuery<F> flatMap(@Nonnull final Function<? super E, Iterable<F>> mapper)
64 {
65 return newAnyQueryInstance(flatMapSupplier(mapper));
66 }
67
68 @Nonnull
69 @Override
70 public final <F> AnyQuery<F> bindTo(@Nonnull Class<F> pageObjectClass, @Nonnull Object... extraArgs)
71 {
72 return map(PageObjects.bindTo(pageBinder, pageObjectClass, extraArgs));
73 }
74
75 @Override
76 public final Iterable<E> get()
77 {
78 return getResultNow();
79 }
80
81 @Nullable
82 @Override
83 public final E first()
84 {
85 return getFirst(getResultNow(), null);
86 }
87
88 @Nonnull
89 @Override
90 public final Iterable<E> now()
91 {
92 return getResultNow();
93 }
94
95 @Nonnull
96 @Override
97 public final TimedCondition hasResult()
98 {
99 return Conditions.forSupplier(timeouts, new Supplier<Boolean>()
100 {
101 @Override
102 public Boolean get()
103 {
104 return hasResultNow();
105 }
106 });
107 }
108
109 @Nonnull
110 @Override
111 public final TimedQuery<Iterable<E>> timed()
112 {
113 return Queries.forSupplier(timeouts, new Supplier<Iterable<E>>()
114 {
115 @Override
116 public Iterable<E> get()
117 {
118 return getResultNow();
119 }
120 });
121 }
122
123 @Nonnull
124 protected abstract Q newInstance(@Nonnull Supplier<Iterable<E>> supplier);
125
126 @Nonnull
127 protected abstract <F> AnyQuery<F> newAnyQueryInstance(@Nonnull Supplier<Iterable<F>> supplier);
128
129 protected final <F> Supplier<Iterable<F>> flatMapSupplier(@Nonnull final Function<? super E, Iterable<F>> mapper)
130 {
131 return new Supplier<Iterable<F>>()
132 {
133 @Override
134 public Iterable<F> get()
135 {
136 return FluentIterable.from(querySupplier.get()).transformAndConcat(mapper);
137 }
138 };
139 }
140
141 protected final <F> Supplier<Iterable<F>> mapSupplier(@Nonnull final Function<? super E, F> mapper)
142 {
143 return new Supplier<Iterable<F>>()
144 {
145 @Override
146 public Iterable<F> get()
147 {
148 return Iterables.transform(querySupplier.get(), mapper);
149 }
150 };
151 }
152
153 private Iterable<E> getResultNow()
154 {
155 return querySupplier.get();
156 }
157
158 private boolean hasResultNow()
159 {
160 return !Iterables.isEmpty(getResultNow());
161 }
162 }