001 /*
002 * Copyright (C) 2007 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package com.google.common.collect;
018
019 import static com.google.common.base.Preconditions.checkNotNull;
020
021 import com.google.common.annotations.Beta;
022 import com.google.common.annotations.GwtCompatible;
023
024 import java.util.Collection;
025 import java.util.List;
026 import java.util.ListIterator;
027 import java.util.RandomAccess;
028 import java.util.Set;
029 import java.util.SortedSet;
030
031 /**
032 * Factories and utilities pertaining to the {@link Constraint} interface.
033 *
034 * @see MapConstraints
035 * @author Mike Bostock
036 * @author Jared Levy
037 * @since 3.0
038 */
039 @Beta
040 @GwtCompatible
041 public final class Constraints {
042 private Constraints() {}
043
044 // enum singleton pattern
045 private enum NotNullConstraint implements Constraint<Object> {
046 INSTANCE;
047
048 public Object checkElement(Object element) {
049 return checkNotNull(element);
050 }
051
052
053 @Override
054 public String toString() {
055 return "Not null";
056 }
057 }
058
059 /**
060 * Returns a constraint that verifies that the element is not null. If the
061 * element is null, a {@link NullPointerException} is thrown.
062 */
063 // safe to narrow the type since checkElement returns its argument directly
064 @SuppressWarnings("unchecked")
065 public static <E> Constraint<E> notNull() {
066 return (Constraint<E>) NotNullConstraint.INSTANCE;
067 }
068
069 /**
070 * Returns a constrained view of the specified collection, using the specified
071 * constraint. Any operations that add new elements to the collection will
072 * call the provided constraint. However, this method does not verify that
073 * existing elements satisfy the constraint.
074 *
075 * <p>The returned collection is not serializable.
076 *
077 * @param collection the collection to constrain
078 * @param constraint the constraint that validates added elements
079 * @return a constrained view of the collection
080 */
081 public static <E> Collection<E> constrainedCollection(
082 Collection<E> collection, Constraint<? super E> constraint) {
083 return new ConstrainedCollection<E>(collection, constraint);
084 }
085
086 /** @see Constraints#constrainedCollection */
087 static class ConstrainedCollection<E> extends ForwardingCollection<E> {
088 private final Collection<E> delegate;
089 private final Constraint<? super E> constraint;
090
091 public ConstrainedCollection(
092 Collection<E> delegate, Constraint<? super E> constraint) {
093 this.delegate = checkNotNull(delegate);
094 this.constraint = checkNotNull(constraint);
095 }
096
097 @Override
098 protected Collection<E> delegate() {
099 return delegate;
100 }
101
102 @Override
103 public boolean add(E element) {
104 constraint.checkElement(element);
105 return delegate.add(element);
106 }
107
108 @Override
109 public boolean addAll(Collection<? extends E> elements) {
110 return delegate.addAll(checkElements(elements, constraint));
111 }
112 }
113
114 /**
115 * Returns a constrained view of the specified set, using the specified
116 * constraint. Any operations that add new elements to the set will call the
117 * provided constraint. However, this method does not verify that existing
118 * elements satisfy the constraint.
119 *
120 * <p>The returned set is not serializable.
121 *
122 * @param set the set to constrain
123 * @param constraint the constraint that validates added elements
124 * @return a constrained view of the set
125 */
126 public static <E> Set<E> constrainedSet(
127 Set<E> set, Constraint<? super E> constraint) {
128 return new ConstrainedSet<E>(set, constraint);
129 }
130
131 /** @see Constraints#constrainedSet */
132 static class ConstrainedSet<E> extends ForwardingSet<E> {
133 private final Set<E> delegate;
134 private final Constraint<? super E> constraint;
135
136 public ConstrainedSet(Set<E> delegate, Constraint<? super E> constraint) {
137 this.delegate = checkNotNull(delegate);
138 this.constraint = checkNotNull(constraint);
139 }
140
141 @Override
142 protected Set<E> delegate() {
143 return delegate;
144 }
145
146 @Override
147 public boolean add(E element) {
148 constraint.checkElement(element);
149 return delegate.add(element);
150 }
151
152 @Override
153 public boolean addAll(Collection<? extends E> elements) {
154 return delegate.addAll(checkElements(elements, constraint));
155 }
156 }
157
158 /**
159 * Returns a constrained view of the specified sorted set, using the specified
160 * constraint. Any operations that add new elements to the sorted set will
161 * call the provided constraint. However, this method does not verify that
162 * existing elements satisfy the constraint.
163 *
164 * <p>The returned set is not serializable.
165 *
166 * @param sortedSet the sorted set to constrain
167 * @param constraint the constraint that validates added elements
168 * @return a constrained view of the sorted set
169 */
170 public static <E> SortedSet<E> constrainedSortedSet(
171 SortedSet<E> sortedSet, Constraint<? super E> constraint) {
172 return new ConstrainedSortedSet<E>(sortedSet, constraint);
173 }
174
175 /** @see Constraints#constrainedSortedSet */
176 private static class ConstrainedSortedSet<E> extends ForwardingSortedSet<E> {
177 final SortedSet<E> delegate;
178 final Constraint<? super E> constraint;
179
180 ConstrainedSortedSet(
181 SortedSet<E> delegate, Constraint<? super E> constraint) {
182 this.delegate = checkNotNull(delegate);
183 this.constraint = checkNotNull(constraint);
184 }
185
186 @Override
187 protected SortedSet<E> delegate() {
188 return delegate;
189 }
190
191 @Override
192 public SortedSet<E> headSet(E toElement) {
193 return constrainedSortedSet(delegate.headSet(toElement), constraint);
194 }
195
196 @Override
197 public SortedSet<E> subSet(E fromElement, E toElement) {
198 return constrainedSortedSet(
199 delegate.subSet(fromElement, toElement), constraint);
200 }
201
202 @Override
203 public SortedSet<E> tailSet(E fromElement) {
204 return constrainedSortedSet(delegate.tailSet(fromElement), constraint);
205 }
206
207 @Override
208 public boolean add(E element) {
209 constraint.checkElement(element);
210 return delegate.add(element);
211 }
212
213 @Override
214 public boolean addAll(Collection<? extends E> elements) {
215 return delegate.addAll(checkElements(elements, constraint));
216 }
217 }
218
219 /**
220 * Returns a constrained view of the specified list, using the specified
221 * constraint. Any operations that add new elements to the list will call the
222 * provided constraint. However, this method does not verify that existing
223 * elements satisfy the constraint.
224 *
225 * <p>If {@code list} implements {@link RandomAccess}, so will the returned
226 * list. The returned list is not serializable.
227 *
228 * @param list the list to constrain
229 * @param constraint the constraint that validates added elements
230 * @return a constrained view of the list
231 */
232 public static <E> List<E> constrainedList(
233 List<E> list, Constraint<? super E> constraint) {
234 return (list instanceof RandomAccess)
235 ? new ConstrainedRandomAccessList<E>(list, constraint)
236 : new ConstrainedList<E>(list, constraint);
237 }
238
239 /** @see Constraints#constrainedList */
240 @GwtCompatible
241 private static class ConstrainedList<E> extends ForwardingList<E> {
242 final List<E> delegate;
243 final Constraint<? super E> constraint;
244
245 ConstrainedList(List<E> delegate, Constraint<? super E> constraint) {
246 this.delegate = checkNotNull(delegate);
247 this.constraint = checkNotNull(constraint);
248 }
249
250 @Override
251 protected List<E> delegate() {
252 return delegate;
253 }
254
255
256 @Override
257 public boolean add(E element) {
258 constraint.checkElement(element);
259 return delegate.add(element);
260 }
261
262 @Override
263 public void add(int index, E element) {
264 constraint.checkElement(element);
265 delegate.add(index, element);
266 }
267
268 @Override
269 public boolean addAll(Collection<? extends E> elements) {
270 return delegate.addAll(checkElements(elements, constraint));
271 }
272
273 @Override
274 public boolean addAll(int index, Collection<? extends E> elements)
275 {
276 return delegate.addAll(index, checkElements(elements, constraint));
277 }
278
279 @Override
280 public ListIterator<E> listIterator() {
281 return constrainedListIterator(delegate.listIterator(), constraint);
282 }
283
284 @Override
285 public ListIterator<E> listIterator(int index) {
286 return constrainedListIterator(delegate.listIterator(index), constraint);
287 }
288
289 @Override
290 public E set(int index, E element) {
291 constraint.checkElement(element);
292 return delegate.set(index, element);
293 }
294
295 @Override
296 public List<E> subList(int fromIndex, int toIndex) {
297 return constrainedList(
298 delegate.subList(fromIndex, toIndex), constraint);
299 }
300 }
301
302 /** @see Constraints#constrainedList */
303 static class ConstrainedRandomAccessList<E> extends ConstrainedList<E>
304 implements RandomAccess {
305 ConstrainedRandomAccessList(
306 List<E> delegate, Constraint<? super E> constraint) {
307 super(delegate, constraint);
308 }
309 }
310
311 /**
312 * Returns a constrained view of the specified list iterator, using the
313 * specified constraint. Any operations that would add new elements to the
314 * underlying list will be verified by the constraint.
315 *
316 * @param listIterator the iterator for which to return a constrained view
317 * @param constraint the constraint for elements in the list
318 * @return a constrained view of the specified iterator
319 */
320 private static <E> ListIterator<E> constrainedListIterator(
321 ListIterator<E> listIterator, Constraint<? super E> constraint) {
322 return new ConstrainedListIterator<E>(listIterator, constraint);
323 }
324
325 /** @see Constraints#constrainedListIterator */
326 static class ConstrainedListIterator<E> extends ForwardingListIterator<E> {
327 private final ListIterator<E> delegate;
328 private final Constraint<? super E> constraint;
329
330 public ConstrainedListIterator(
331 ListIterator<E> delegate, Constraint<? super E> constraint) {
332 this.delegate = delegate;
333 this.constraint = constraint;
334 }
335
336 @Override
337 protected ListIterator<E> delegate() {
338 return delegate;
339 }
340
341
342 @Override
343 public void add(E element) {
344 constraint.checkElement(element);
345 delegate.add(element);
346 }
347
348 @Override
349 public void set(E element) {
350 constraint.checkElement(element);
351 delegate.set(element);
352 }
353 }
354
355 static <E> Collection<E> constrainedTypePreservingCollection(
356 Collection<E> collection, Constraint<E> constraint) {
357 if (collection instanceof SortedSet) {
358 return constrainedSortedSet((SortedSet<E>) collection, constraint);
359 } else if (collection instanceof Set) {
360 return constrainedSet((Set<E>) collection, constraint);
361 } else if (collection instanceof List) {
362 return constrainedList((List<E>) collection, constraint);
363 } else {
364 return constrainedCollection(collection, constraint);
365 }
366 }
367
368 /**
369 * Returns a constrained view of the specified multiset, using the specified
370 * constraint. Any operations that add new elements to the multiset will call
371 * the provided constraint. However, this method does not verify that
372 * existing elements satisfy the constraint.
373 *
374 * <p>The returned multiset is not serializable.
375 *
376 * @param multiset the multiset to constrain
377 * @param constraint the constraint that validates added elements
378 * @return a constrained view of the multiset
379 */
380 public static <E> Multiset<E> constrainedMultiset(
381 Multiset<E> multiset, Constraint<? super E> constraint) {
382 return new ConstrainedMultiset<E>(multiset, constraint);
383 }
384
385 /** @see Constraints#constrainedMultiset */
386 static class ConstrainedMultiset<E> extends ForwardingMultiset<E> {
387 private Multiset<E> delegate;
388 private final Constraint<? super E> constraint;
389
390 public ConstrainedMultiset(
391 Multiset<E> delegate, Constraint<? super E> constraint) {
392 this.delegate = checkNotNull(delegate);
393 this.constraint = checkNotNull(constraint);
394 }
395
396 @Override
397 protected Multiset<E> delegate() {
398 return delegate;
399 }
400
401 @Override
402 public boolean add(E element) {
403 return standardAdd(element);
404 }
405
406 @Override
407 public boolean addAll(Collection<? extends E> elements) {
408 return delegate.addAll(checkElements(elements, constraint));
409 }
410
411 @Override
412 public int add(E element, int occurrences) {
413 constraint.checkElement(element);
414 return delegate.add(element, occurrences);
415 }
416
417 @Override
418 public int setCount(E element, int count) {
419 constraint.checkElement(element);
420 return delegate.setCount(element, count);
421 }
422
423 @Override
424 public boolean setCount(E element, int oldCount, int newCount) {
425 constraint.checkElement(element);
426 return delegate.setCount(element, oldCount, newCount);
427 }
428 }
429
430 /*
431 * TODO(kevinb): For better performance, avoid making a copy of the elements
432 * by having addAll() call add() repeatedly instead.
433 */
434
435 private static <E> Collection<E> checkElements(
436 Collection<E> elements, Constraint<? super E> constraint) {
437 Collection<E> copy = Lists.newArrayList(elements);
438 for (E element : copy) {
439 constraint.checkElement(element);
440 }
441 return copy;
442 }
443 }