Home | History | Annotate | Download | only in collect
      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 {
     42   private Constraints() {}
     43 
     44   // enum singleton pattern
     45   private enum NotNullConstraint implements Constraint<Object> {
     46     INSTANCE;
     47 
     48     @Override
     49     public Object checkElement(Object element) {
     50       return checkNotNull(element);
     51     }
     52 
     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")
     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    */
     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 
     90     public ConstrainedCollection(
     91         Collection<E> delegate, Constraint<? super E> constraint) {
     92       this.delegate = checkNotNull(delegate);
     93       this.constraint = checkNotNull(constraint);
     94     }
     95     @Override protected Collection<E> delegate() {
     96       return delegate;
     97     }
     98     @Override public boolean add(E element) {
     99       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 }
    383