1 /***
2 * Copyright 2003-2010 Terracotta, Inc.
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
17 package net.sf.ehcache;
18
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.HashSet;
22 import java.util.List;
23 import java.util.Set;
24
25 import net.sf.ehcache.search.Attribute;
26 import net.sf.ehcache.search.Direction;
27 import net.sf.ehcache.search.Query;
28 import net.sf.ehcache.search.Results;
29 import net.sf.ehcache.search.SearchException;
30 import net.sf.ehcache.search.aggregator.Aggregator;
31 import net.sf.ehcache.search.aggregator.AggregatorException;
32 import net.sf.ehcache.search.aggregator.AggregatorInstance;
33 import net.sf.ehcache.search.expression.AlwaysMatch;
34 import net.sf.ehcache.search.expression.Criteria;
35 import net.sf.ehcache.store.StoreQuery;
36
37 /***
38 * Query builder implementation. Instances are bound to a specific cache
39 *
40 * @author teck
41 */
42 class CacheQuery implements Query, StoreQuery {
43
44 private volatile boolean frozen;
45 private volatile boolean includeKeys;
46 private volatile boolean includeValues;
47 private volatile int maxResults = -1;
48
49 private final List<Ordering> orderings = Collections.synchronizedList(new ArrayList<Ordering>());
50 private final Set<Attribute<?>> includedAttributes = Collections.synchronizedSet(new HashSet<Attribute<?>>());
51 private final List<Criteria> criteria = Collections.synchronizedList(new ArrayList<Criteria>());
52 private final List<Aggregator> aggregators = Collections.synchronizedList(new ArrayList<Aggregator>());
53 private final Cache cache;
54
55 /***
56 * Create a new builder instance
57 *
58 * @param cache
59 */
60 public CacheQuery(Cache cache) {
61 this.cache = cache;
62 }
63
64 /***
65 * {@inheritDoc}
66 */
67 public Query includeKeys() {
68 checkFrozen();
69 this.includeKeys = true;
70 return this;
71 }
72
73 /***
74 * {@inheritDoc}
75 */
76 public Query includeValues() {
77 checkFrozen();
78 this.includeValues = true;
79 return this;
80 }
81
82 /***
83 * {@inheritDoc}
84 */
85 public Query includeAttribute(Attribute<?>... attributes) {
86 checkFrozen();
87
88 if (attributes == null) {
89 throw new NullPointerException();
90 }
91
92 for (Attribute<?> attribute : attributes) {
93 if (attribute == null) {
94 throw new NullPointerException("null attribute");
95 }
96
97 this.includedAttributes.add(attribute);
98 }
99
100 return this;
101 }
102
103 /***
104 * {@inheritDoc}
105 */
106 public Query includeAggregator(Aggregator... aggregators) throws SearchException, AggregatorException {
107 checkFrozen();
108
109 if (aggregators == null) {
110 throw new NullPointerException();
111 }
112
113 for (Aggregator aggregator : aggregators) {
114 if (aggregator == null) {
115 throw new NullPointerException("null aggregator");
116 }
117
118 this.aggregators.add(aggregator);
119 }
120
121 return this;
122 }
123
124 /***
125 * {@inheritDoc}
126 */
127 public Query addOrderBy(Attribute<?> attribute, Direction direction) {
128 checkFrozen();
129 this.orderings.add(new OrderingImpl(attribute, direction));
130 return this;
131 }
132
133 /***
134 * {@inheritDoc}
135 */
136 public Query maxResults(int maxResults) {
137 checkFrozen();
138 this.maxResults = maxResults;
139 return this;
140 }
141
142 /***
143 * {@inheritDoc}
144 */
145 public Query addCriteria(Criteria criteria) {
146 checkFrozen();
147
148 if (criteria == null) {
149 throw new NullPointerException("null criteria");
150 }
151
152 this.criteria.add(criteria);
153 return this;
154 }
155
156 /***
157 * {@inheritDoc}
158 */
159 public Results execute() throws SearchException {
160 return cache.executeQuery(snapshot());
161 }
162
163 /***
164 * {@inheritDoc}
165 */
166 public Query end() {
167 frozen = true;
168 return this;
169 }
170
171 /***
172 * {@inheritDoc}
173 */
174 public List<Ordering> getOrdering() {
175 assertFrozen();
176 return Collections.unmodifiableList(orderings);
177 }
178
179 /***
180 * {@inheritDoc}
181 */
182 public Criteria getCriteria() {
183 assertFrozen();
184 return getEffectiveCriteriaCopy();
185 }
186
187 /***
188 * {@inheritDoc}
189 */
190 public boolean requestsKeys() {
191 assertFrozen();
192 return includeKeys;
193 }
194
195 /***
196 * {@inheritDoc}
197 */
198 public boolean requestsValues() {
199 assertFrozen();
200 return includeValues;
201 }
202
203 /***
204 * {@inheritDoc}
205 */
206 public Cache getCache() {
207 assertFrozen();
208 return cache;
209 }
210
211 /***
212 * {@inheritDoc}
213 */
214 public Set<Attribute<?>> requestedAttributes() {
215 assertFrozen();
216 return Collections.unmodifiableSet(this.includedAttributes);
217 }
218
219 /***
220 * {@inheritDoc}
221 */
222 public int maxResults() {
223 assertFrozen();
224 return maxResults;
225 }
226
227 /***
228 * {@inheritDoc}
229 */
230 public List<AggregatorInstance<?>> getAggregatorInstances() {
231 assertFrozen();
232 return Collections.unmodifiableList(createAggregatorInstances(aggregators));
233 }
234
235 private static List<AggregatorInstance<?>> createAggregatorInstances(List<Aggregator> aggregators) {
236 List<AggregatorInstance<?>> rv = new ArrayList<AggregatorInstance<?>>(aggregators.size());
237 for (Aggregator aggregator : aggregators) {
238 rv.add(aggregator.createInstance());
239 }
240
241 return rv;
242 }
243
244 private Criteria getEffectiveCriteriaCopy() {
245 Criteria result = new AlwaysMatch();
246 for (Criteria c : criteria) {
247 result = result.and(c);
248 }
249 return result;
250 }
251
252 private void assertFrozen() {
253 if (!frozen) {
254 throw new AssertionError("not frozen");
255 }
256 }
257
258 private StoreQuery snapshot() {
259 if (frozen) {
260 return this;
261 }
262
263 return new StoreQueryImpl();
264 }
265
266 private void checkFrozen() {
267 if (frozen) {
268 throw new SearchException("Query is frozen and cannot be mutated");
269 }
270 }
271
272 /***
273 * StoreQuery implementation (essentially a snapshot of this (non-frozen) query builder
274 */
275 private class StoreQueryImpl implements StoreQuery {
276 private final Criteria copiedCriteria = CacheQuery.this.getEffectiveCriteriaCopy();
277 private final boolean copiedIncludeKeys = includeKeys;
278 private final boolean copiedIncludeValues = includeValues;
279 private final Set<Attribute<?>> copiedAttributes = Collections.unmodifiableSet(new HashSet<Attribute<?>>(includedAttributes));
280 private final int copiedMaxResults = maxResults;
281 private final List<Ordering> copiedOrdering = Collections.unmodifiableList(new ArrayList<Ordering>(orderings));
282 private final List<AggregatorInstance<?>> copiedAggregators = Collections.unmodifiableList(createAggregatorInstances(aggregators));
283
284 public Criteria getCriteria() {
285 return copiedCriteria;
286 }
287
288 public boolean requestsKeys() {
289 return copiedIncludeKeys;
290 }
291
292 public boolean requestsValues() {
293 return copiedIncludeValues;
294 }
295
296 public Cache getCache() {
297 return cache;
298 }
299
300 public Set<Attribute<?>> requestedAttributes() {
301 return copiedAttributes;
302 }
303
304 public int maxResults() {
305 return copiedMaxResults;
306 }
307
308 public List<Ordering> getOrdering() {
309 return copiedOrdering;
310 }
311
312 public List<AggregatorInstance<?>> getAggregatorInstances() {
313 return copiedAggregators;
314 }
315 }
316
317 /***
318 * An attribute/direction pair
319 */
320 private static class OrderingImpl implements Ordering {
321
322 private final Attribute<?> attribute;
323 private final Direction direction;
324
325 public OrderingImpl(Attribute<?> attribute, Direction direction) {
326 if ((attribute == null) || (direction == null)) {
327 throw new NullPointerException();
328 }
329
330 this.attribute = attribute;
331 this.direction = direction;
332 }
333
334 public Attribute<?> getAttribute() {
335 return attribute;
336 }
337
338 public Direction getDirection() {
339 return direction;
340 }
341 }
342
343 }