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        @Override
049        public Object checkElement(Object element) {
050          return checkNotNull(element);
051        }
052    
053        @Override public String toString() {
054          return "Not null";
055        }
056      }
057    
058      /**
059       * Returns a constraint that verifies that the element is not null. If the
060       * element is null, a {@link NullPointerException} is thrown.
061       */
062      // safe to narrow the type since checkElement returns its argument directly
063      @SuppressWarnings("unchecked")
064      public static <E> Constraint<E> notNull() {
065        return (Constraint<E>) NotNullConstraint.INSTANCE;
066      }
067    
068      /**
069       * Returns a constrained view of the specified collection, using the specified
070       * constraint. Any operations that add new elements to the collection will
071       * call the provided constraint. However, this method does not verify that
072       * existing elements satisfy the constraint.
073       *
074       * <p>The returned collection is not serializable.
075       *
076       * @param collection the collection to constrain
077       * @param constraint the constraint that validates added elements
078       * @return a constrained view of the collection
079       */
080      public static <E> Collection<E> constrainedCollection(
081          Collection<E> collection, Constraint<? super E> constraint) {
082        return new ConstrainedCollection<E>(collection, constraint);
083      }
084    
085      /** @see Constraints#constrainedCollection */
086      static class ConstrainedCollection<E> extends ForwardingCollection<E> {
087        private final Collection<E> delegate;
088        private final Constraint<? super E> constraint;
089    
090        public ConstrainedCollection(
091            Collection<E> delegate, Constraint<? super E> constraint) {
092          this.delegate = checkNotNull(delegate);
093          this.constraint = checkNotNull(constraint);
094        }
095        @Override protected Collection<E> delegate() {
096          return delegate;
097        }
098        @Override public boolean add(E element) {
099          constraint.checkElement(element);
100          return delegate.add(element);
101        }
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       */
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    
129        public ConstrainedSet(Set<E> delegate, Constraint<? super E> constraint) {
130          this.delegate = checkNotNull(delegate);
131          this.constraint = checkNotNull(constraint);
132        }
133        @Override protected Set<E> delegate() {
134          return delegate;
135        }
136        @Override public boolean add(E element) {
137          constraint.checkElement(element);
138          return delegate.add(element);
139        }
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       */
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    
167        ConstrainedSortedSet(
168            SortedSet<E> delegate, Constraint<? super E> constraint) {
169          this.delegate = checkNotNull(delegate);
170          this.constraint = checkNotNull(constraint);
171        }
172        @Override protected SortedSet<E> delegate() {
173          return delegate;
174        }
175        @Override public SortedSet<E> headSet(E toElement) {
176          return constrainedSortedSet(delegate.headSet(toElement), constraint);
177        }
178        @Override public SortedSet<E> subSet(E fromElement, E toElement) {
179          return constrainedSortedSet(
180              delegate.subSet(fromElement, toElement), constraint);
181        }
182        @Override public SortedSet<E> tailSet(E fromElement) {
183          return constrainedSortedSet(delegate.tailSet(fromElement), constraint);
184        }
185        @Override public boolean add(E element) {
186          constraint.checkElement(element);
187          return delegate.add(element);
188        }
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       */
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    
220        ConstrainedList(List<E> delegate, Constraint<? super E> constraint) {
221          this.delegate = checkNotNull(delegate);
222          this.constraint = checkNotNull(constraint);
223        }
224        @Override protected List<E> delegate() {
225          return delegate;
226        }
227    
228        @Override public boolean add(E element) {
229          constraint.checkElement(element);
230          return delegate.add(element);
231        }
232        @Override public void add(int index, E element) {
233          constraint.checkElement(element);
234          delegate.add(index, element);
235        }
236        @Override public boolean addAll(Collection<? extends E> elements) {
237          return delegate.addAll(checkElements(elements, constraint));
238        }
239        @Override public boolean addAll(int index, Collection<? extends E> elements)
240        {
241          return delegate.addAll(index, checkElements(elements, constraint));
242        }
243        @Override public ListIterator<E> listIterator() {
244          return constrainedListIterator(delegate.listIterator(), constraint);
245        }
246        @Override public ListIterator<E> listIterator(int index) {
247          return constrainedListIterator(delegate.listIterator(index), constraint);
248        }
249        @Override public E set(int index, E element) {
250          constraint.checkElement(element);
251          return delegate.set(index, element);
252        }
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 {
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       */
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    
287        public ConstrainedListIterator(
288            ListIterator<E> delegate, Constraint<? super E> constraint) {
289          this.delegate = delegate;
290          this.constraint = constraint;
291        }
292        @Override protected ListIterator<E> delegate() {
293          return delegate;
294        }
295    
296        @Override public void add(E element) {
297          constraint.checkElement(element);
298          delegate.add(element);
299        }
300        @Override public void set(E element) {
301          constraint.checkElement(element);
302          delegate.set(element);
303        }
304      }
305    
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       */
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    
341        public ConstrainedMultiset(
342            Multiset<E> delegate, Constraint<? super E> constraint) {
343          this.delegate = checkNotNull(delegate);
344          this.constraint = checkNotNull(constraint);
345        }
346        @Override protected Multiset<E> delegate() {
347          return delegate;
348        }
349        @Override public boolean add(E element) {
350          return standardAdd(element);
351        }
352        @Override public boolean addAll(Collection<? extends E> elements) {
353          return delegate.addAll(checkElements(elements, constraint));
354        }
355        @Override public int add(E element, int occurrences) {
356          constraint.checkElement(element);
357          return delegate.add(element, occurrences);
358        }
359        @Override public int setCount(E element, int count) {
360          constraint.checkElement(element);
361          return delegate.setCount(element, count);
362        }
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    
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    }