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    }