• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Guava Authors
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 com.google.common.collect;
18 
19 import static com.google.common.base.Preconditions.checkNotNull;
20 
21 import com.google.common.annotations.Beta;
22 import com.google.common.annotations.GwtCompatible;
23 
24 import java.util.Collection;
25 import java.util.List;
26 import java.util.ListIterator;
27 import java.util.RandomAccess;
28 import java.util.Set;
29 import java.util.SortedSet;
30 
31 /**
32  * Factories and utilities pertaining to the {@link Constraint} interface.
33  *
34  * @see MapConstraints
35  * @author Mike Bostock
36  * @author Jared Levy
37  * @since 3.0
38  */
39 @Beta
40 @GwtCompatible
41 public final class Constraints {
Constraints()42   private Constraints() {}
43 
44   // enum singleton pattern
45   private enum NotNullConstraint implements Constraint<Object> {
46     INSTANCE;
47 
48     @Override
checkElement(Object element)49     public Object checkElement(Object element) {
50       return checkNotNull(element);
51     }
52 
toString()53     @Override public String toString() {
54       return "Not null";
55     }
56   }
57 
58   /**
59    * Returns a constraint that verifies that the element is not null. If the
60    * element is null, a {@link NullPointerException} is thrown.
61    */
62   // safe to narrow the type since checkElement returns its argument directly
63   @SuppressWarnings("unchecked")
notNull()64   public static <E> Constraint<E> notNull() {
65     return (Constraint<E>) NotNullConstraint.INSTANCE;
66   }
67 
68   /**
69    * Returns a constrained view of the specified collection, using the specified
70    * constraint. Any operations that add new elements to the collection will
71    * call the provided constraint. However, this method does not verify that
72    * existing elements satisfy the constraint.
73    *
74    * <p>The returned collection is not serializable.
75    *
76    * @param collection the collection to constrain
77    * @param constraint the constraint that validates added elements
78    * @return a constrained view of the collection
79    */
constrainedCollection( Collection<E> collection, Constraint<? super E> constraint)80   public static <E> Collection<E> constrainedCollection(
81       Collection<E> collection, Constraint<? super E> constraint) {
82     return new ConstrainedCollection<E>(collection, constraint);
83   }
84 
85   /** @see Constraints#constrainedCollection */
86   static class ConstrainedCollection<E> extends ForwardingCollection<E> {
87     private final Collection<E> delegate;
88     private final Constraint<? super E> constraint;
89 
ConstrainedCollection( Collection<E> delegate, Constraint<? super E> constraint)90     public ConstrainedCollection(
91         Collection<E> delegate, Constraint<? super E> constraint) {
92       this.delegate = checkNotNull(delegate);
93       this.constraint = checkNotNull(constraint);
94     }
delegate()95     @Override protected Collection<E> delegate() {
96       return delegate;
97     }
add(E element)98     @Override public boolean add(E element) {
99       constraint.checkElement(element);
100       return delegate.add(element);
101     }
addAll(Collection<? extends E> elements)102     @Override public boolean addAll(Collection<? extends E> elements) {
103       return delegate.addAll(checkElements(elements, constraint));
104     }
105   }
106 
107   /**
108    * Returns a constrained view of the specified set, using the specified
109    * constraint. Any operations that add new elements to the set will call the
110    * provided constraint. However, this method does not verify that existing
111    * elements satisfy the constraint.
112    *
113    * <p>The returned set is not serializable.
114    *
115    * @param set the set to constrain
116    * @param constraint the constraint that validates added elements
117    * @return a constrained view of the set
118    */
constrainedSet( Set<E> set, Constraint<? super E> constraint)119   public static <E> Set<E> constrainedSet(
120       Set<E> set, Constraint<? super E> constraint) {
121     return new ConstrainedSet<E>(set, constraint);
122   }
123 
124   /** @see Constraints#constrainedSet */
125   static class ConstrainedSet<E> extends ForwardingSet<E> {
126     private final Set<E> delegate;
127     private final Constraint<? super E> constraint;
128 
ConstrainedSet(Set<E> delegate, Constraint<? super E> constraint)129     public ConstrainedSet(Set<E> delegate, Constraint<? super E> constraint) {
130       this.delegate = checkNotNull(delegate);
131       this.constraint = checkNotNull(constraint);
132     }
delegate()133     @Override protected Set<E> delegate() {
134       return delegate;
135     }
add(E element)136     @Override public boolean add(E element) {
137       constraint.checkElement(element);
138       return delegate.add(element);
139     }
addAll(Collection<? extends E> elements)140     @Override public boolean addAll(Collection<? extends E> elements) {
141       return delegate.addAll(checkElements(elements, constraint));
142     }
143   }
144 
145   /**
146    * Returns a constrained view of the specified sorted set, using the specified
147    * constraint. Any operations that add new elements to the sorted set will
148    * call the provided constraint. However, this method does not verify that
149    * existing elements satisfy the constraint.
150    *
151    * <p>The returned set is not serializable.
152    *
153    * @param sortedSet the sorted set to constrain
154    * @param constraint the constraint that validates added elements
155    * @return a constrained view of the sorted set
156    */
constrainedSortedSet( SortedSet<E> sortedSet, Constraint<? super E> constraint)157   public static <E> SortedSet<E> constrainedSortedSet(
158       SortedSet<E> sortedSet, Constraint<? super E> constraint) {
159     return new ConstrainedSortedSet<E>(sortedSet, constraint);
160   }
161 
162   /** @see Constraints#constrainedSortedSet */
163   private static class ConstrainedSortedSet<E> extends ForwardingSortedSet<E> {
164     final SortedSet<E> delegate;
165     final Constraint<? super E> constraint;
166 
ConstrainedSortedSet( SortedSet<E> delegate, Constraint<? super E> constraint)167     ConstrainedSortedSet(
168         SortedSet<E> delegate, Constraint<? super E> constraint) {
169       this.delegate = checkNotNull(delegate);
170       this.constraint = checkNotNull(constraint);
171     }
delegate()172     @Override protected SortedSet<E> delegate() {
173       return delegate;
174     }
headSet(E toElement)175     @Override public SortedSet<E> headSet(E toElement) {
176       return constrainedSortedSet(delegate.headSet(toElement), constraint);
177     }
subSet(E fromElement, E toElement)178     @Override public SortedSet<E> subSet(E fromElement, E toElement) {
179       return constrainedSortedSet(
180           delegate.subSet(fromElement, toElement), constraint);
181     }
tailSet(E fromElement)182     @Override public SortedSet<E> tailSet(E fromElement) {
183       return constrainedSortedSet(delegate.tailSet(fromElement), constraint);
184     }
add(E element)185     @Override public boolean add(E element) {
186       constraint.checkElement(element);
187       return delegate.add(element);
188     }
addAll(Collection<? extends E> elements)189     @Override public boolean addAll(Collection<? extends E> elements) {
190       return delegate.addAll(checkElements(elements, constraint));
191     }
192   }
193 
194   /**
195    * Returns a constrained view of the specified list, using the specified
196    * constraint. Any operations that add new elements to the list will call the
197    * provided constraint. However, this method does not verify that existing
198    * elements satisfy the constraint.
199    *
200    * <p>If {@code list} implements {@link RandomAccess}, so will the returned
201    * list. The returned list is not serializable.
202    *
203    * @param list the list to constrain
204    * @param constraint the constraint that validates added elements
205    * @return a constrained view of the list
206    */
constrainedList( List<E> list, Constraint<? super E> constraint)207   public static <E> List<E> constrainedList(
208       List<E> list, Constraint<? super E> constraint) {
209     return (list instanceof RandomAccess)
210         ? new ConstrainedRandomAccessList<E>(list, constraint)
211         : new ConstrainedList<E>(list, constraint);
212   }
213 
214   /** @see Constraints#constrainedList */
215   @GwtCompatible
216   private static class ConstrainedList<E> extends ForwardingList<E> {
217     final List<E> delegate;
218     final Constraint<? super E> constraint;
219 
ConstrainedList(List<E> delegate, Constraint<? super E> constraint)220     ConstrainedList(List<E> delegate, Constraint<? super E> constraint) {
221       this.delegate = checkNotNull(delegate);
222       this.constraint = checkNotNull(constraint);
223     }
delegate()224     @Override protected List<E> delegate() {
225       return delegate;
226     }
227 
add(E element)228     @Override public boolean add(E element) {
229       constraint.checkElement(element);
230       return delegate.add(element);
231     }
add(int index, E element)232     @Override public void add(int index, E element) {
233       constraint.checkElement(element);
234       delegate.add(index, element);
235     }
addAll(Collection<? extends E> elements)236     @Override public boolean addAll(Collection<? extends E> elements) {
237       return delegate.addAll(checkElements(elements, constraint));
238     }
addAll(int index, Collection<? extends E> elements)239     @Override public boolean addAll(int index, Collection<? extends E> elements)
240     {
241       return delegate.addAll(index, checkElements(elements, constraint));
242     }
listIterator()243     @Override public ListIterator<E> listIterator() {
244       return constrainedListIterator(delegate.listIterator(), constraint);
245     }
listIterator(int index)246     @Override public ListIterator<E> listIterator(int index) {
247       return constrainedListIterator(delegate.listIterator(index), constraint);
248     }
set(int index, E element)249     @Override public E set(int index, E element) {
250       constraint.checkElement(element);
251       return delegate.set(index, element);
252     }
subList(int fromIndex, int toIndex)253     @Override public List<E> subList(int fromIndex, int toIndex) {
254       return constrainedList(
255           delegate.subList(fromIndex, toIndex), constraint);
256     }
257   }
258 
259   /** @see Constraints#constrainedList */
260   static class ConstrainedRandomAccessList<E> extends ConstrainedList<E>
261       implements RandomAccess {
ConstrainedRandomAccessList( List<E> delegate, Constraint<? super E> constraint)262     ConstrainedRandomAccessList(
263         List<E> delegate, Constraint<? super E> constraint) {
264       super(delegate, constraint);
265     }
266   }
267 
268   /**
269    * Returns a constrained view of the specified list iterator, using the
270    * specified constraint. Any operations that would add new elements to the
271    * underlying list will be verified by the constraint.
272    *
273    * @param listIterator the iterator for which to return a constrained view
274    * @param constraint the constraint for elements in the list
275    * @return a constrained view of the specified iterator
276    */
constrainedListIterator( ListIterator<E> listIterator, Constraint<? super E> constraint)277   private static <E> ListIterator<E> constrainedListIterator(
278       ListIterator<E> listIterator, Constraint<? super E> constraint) {
279     return new ConstrainedListIterator<E>(listIterator, constraint);
280   }
281 
282   /** @see Constraints#constrainedListIterator */
283   static class ConstrainedListIterator<E> extends ForwardingListIterator<E> {
284     private final ListIterator<E> delegate;
285     private final Constraint<? super E> constraint;
286 
ConstrainedListIterator( ListIterator<E> delegate, Constraint<? super E> constraint)287     public ConstrainedListIterator(
288         ListIterator<E> delegate, Constraint<? super E> constraint) {
289       this.delegate = delegate;
290       this.constraint = constraint;
291     }
delegate()292     @Override protected ListIterator<E> delegate() {
293       return delegate;
294     }
295 
add(E element)296     @Override public void add(E element) {
297       constraint.checkElement(element);
298       delegate.add(element);
299     }
set(E element)300     @Override public void set(E element) {
301       constraint.checkElement(element);
302       delegate.set(element);
303     }
304   }
305 
constrainedTypePreservingCollection( Collection<E> collection, Constraint<E> constraint)306   static <E> Collection<E> constrainedTypePreservingCollection(
307       Collection<E> collection, Constraint<E> constraint) {
308     if (collection instanceof SortedSet) {
309       return constrainedSortedSet((SortedSet<E>) collection, constraint);
310     } else if (collection instanceof Set) {
311       return constrainedSet((Set<E>) collection, constraint);
312     } else if (collection instanceof List) {
313       return constrainedList((List<E>) collection, constraint);
314     } else {
315       return constrainedCollection(collection, constraint);
316     }
317   }
318 
319   /**
320    * Returns a constrained view of the specified multiset, using the specified
321    * constraint. Any operations that add new elements to the multiset will call
322    * the provided constraint. However, this method does not verify that
323    * existing elements satisfy the constraint.
324    *
325    * <p>The returned multiset is not serializable.
326    *
327    * @param multiset the multiset to constrain
328    * @param constraint the constraint that validates added elements
329    * @return a constrained view of the multiset
330    */
constrainedMultiset( Multiset<E> multiset, Constraint<? super E> constraint)331   public static <E> Multiset<E> constrainedMultiset(
332       Multiset<E> multiset, Constraint<? super E> constraint) {
333     return new ConstrainedMultiset<E>(multiset, constraint);
334   }
335 
336   /** @see Constraints#constrainedMultiset */
337   static class ConstrainedMultiset<E> extends ForwardingMultiset<E> {
338     private Multiset<E> delegate;
339     private final Constraint<? super E> constraint;
340 
ConstrainedMultiset( Multiset<E> delegate, Constraint<? super E> constraint)341     public ConstrainedMultiset(
342         Multiset<E> delegate, Constraint<? super E> constraint) {
343       this.delegate = checkNotNull(delegate);
344       this.constraint = checkNotNull(constraint);
345     }
delegate()346     @Override protected Multiset<E> delegate() {
347       return delegate;
348     }
add(E element)349     @Override public boolean add(E element) {
350       return standardAdd(element);
351     }
addAll(Collection<? extends E> elements)352     @Override public boolean addAll(Collection<? extends E> elements) {
353       return delegate.addAll(checkElements(elements, constraint));
354     }
add(E element, int occurrences)355     @Override public int add(E element, int occurrences) {
356       constraint.checkElement(element);
357       return delegate.add(element, occurrences);
358     }
setCount(E element, int count)359     @Override public int setCount(E element, int count) {
360       constraint.checkElement(element);
361       return delegate.setCount(element, count);
362     }
setCount(E element, int oldCount, int newCount)363     @Override public boolean setCount(E element, int oldCount, int newCount) {
364       constraint.checkElement(element);
365       return delegate.setCount(element, oldCount, newCount);
366     }
367   }
368 
369   /*
370    * TODO(kevinb): For better performance, avoid making a copy of the elements
371    * by having addAll() call add() repeatedly instead.
372    */
373 
checkElements( Collection<E> elements, Constraint<? super E> constraint)374   private static <E> Collection<E> checkElements(
375       Collection<E> elements, Constraint<? super E> constraint) {
376     Collection<E> copy = Lists.newArrayList(elements);
377     for (E element : copy) {
378       constraint.checkElement(element);
379     }
380     return copy;
381   }
382 }
383