    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    }