001    /*
002     * Copyright (C) 2007 Google Inc.
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
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        @Override public String toString() {
053          return "Not null";
054        }
055      }
056    
057      /**
058       * Returns a constraint that verifies that the element is not null. If the
059       * element is null, a {@link NullPointerException} is thrown.
060       */
061      // safe to narrow the type since checkElement returns its argument directly
062      @SuppressWarnings("unchecked")
063      public static <E> Constraint<E> notNull() {
064        return (Constraint) NotNullConstraint.INSTANCE;
065      }
066    
067      /**
068       * Returns a constrained view of the specified collection, using the specified
069       * constraint. Any operations that add new elements to the collection will
070       * call the provided constraint. However, this method does not verify that
071       * existing elements satisfy the constraint.
072       *
073       * <p>The returned collection is not serializable.
074       *
075       * @param collection the collection to constrain
076       * @param constraint the constraint that validates added elements
077       * @return a constrained view of the collection
078       */
079      public static <E> Collection<E> constrainedCollection(
080          Collection<E> collection, Constraint<? super E> constraint) {
081        return new ConstrainedCollection<E>(collection, constraint);
082      }
083    
084      /** @see Constraints#constrainedCollection */
085      static class ConstrainedCollection<E> extends ForwardingCollection<E> {
086        private final Collection<E> delegate;
087        private final Constraint<? super E> constraint;
088    
089        public ConstrainedCollection(
090            Collection<E> delegate, Constraint<? super E> constraint) {
091          this.delegate = checkNotNull(delegate);
092          this.constraint = checkNotNull(constraint);
093        }
094        @Override protected Collection<E> delegate() {
095          return delegate;
096        }
097        @Override public boolean add(E element) {
098          constraint.checkElement(element);
099          return delegate.add(element);
100        }
101        @Override public boolean addAll(Collection<? extends E> elements) {
102          return delegate.addAll(checkElements(elements, constraint));
103        }
104      }
105    
106      /**
107       * Returns a constrained view of the specified set, using the specified
108       * constraint. Any operations that add new elements to the set will call the
109       * provided constraint. However, this method does not verify that existing
110       * elements satisfy the constraint.
111       *
112       * <p>The returned set is not serializable.
113       *
114       * @param set the set to constrain
115       * @param constraint the constraint that validates added elements
116       * @return a constrained view of the set
117       */
118      public static <E> Set<E> constrainedSet(
119          Set<E> set, Constraint<? super E> constraint) {
120        return new ConstrainedSet<E>(set, constraint);
121      }
122    
123      /** @see Constraints#constrainedSet */
124      static class ConstrainedSet<E> extends ForwardingSet<E> {
125        private final Set<E> delegate;
126        private final Constraint<? super E> constraint;
127    
128        public ConstrainedSet(Set<E> delegate, Constraint<? super E> constraint) {
129          this.delegate = checkNotNull(delegate);
130          this.constraint = checkNotNull(constraint);
131        }
132        @Override protected Set<E> delegate() {
133          return delegate;
134        }
135        @Override public boolean add(E element) {
136          constraint.checkElement(element);
137          return delegate.add(element);
138        }
139        @Override public boolean addAll(Collection<? extends E> elements) {
140          return delegate.addAll(checkElements(elements, constraint));
141        }
142      }
143    
144      /**
145       * Returns a constrained view of the specified sorted set, using the specified
146       * constraint. Any operations that add new elements to the sorted set will
147       * call the provided constraint. However, this method does not verify that
148       * existing elements satisfy the constraint.
149       *
150       * <p>The returned set is not serializable.
151       *
152       * @param sortedSet the sorted set to constrain
153       * @param constraint the constraint that validates added elements
154       * @return a constrained view of the sorted set
155       */
156      public static <E> SortedSet<E> constrainedSortedSet(
157          SortedSet<E> sortedSet, Constraint<? super E> constraint) {
158        return new ConstrainedSortedSet<E>(sortedSet, constraint);
159      }
160    
161      /** @see Constraints#constrainedSortedSet */
162      private static class ConstrainedSortedSet<E> extends ForwardingSortedSet<E> {
163        final SortedSet<E> delegate;
164        final Constraint<? super E> constraint;
165    
166        ConstrainedSortedSet(
167            SortedSet<E> delegate, Constraint<? super E> constraint) {
168          this.delegate = checkNotNull(delegate);
169          this.constraint = checkNotNull(constraint);
170        }
171        @Override protected SortedSet<E> delegate() {
172          return delegate;
173        }
174        @Override public SortedSet<E> headSet(E toElement) {
175          return constrainedSortedSet(delegate.headSet(toElement), constraint);
176        }
177        @Override public SortedSet<E> subSet(E fromElement, E toElement) {
178          return constrainedSortedSet(
179              delegate.subSet(fromElement, toElement), constraint);
180        }
181        @Override public SortedSet<E> tailSet(E fromElement) {
182          return constrainedSortedSet(delegate.tailSet(fromElement), constraint);
183        }
184        @Override public boolean add(E element) {
185          constraint.checkElement(element);
186          return delegate.add(element);
187        }
188        @Override public boolean addAll(Collection<? extends E> elements) {
189          return delegate.addAll(checkElements(elements, constraint));
190        }
191      }
192    
193      /**
194       * Returns a constrained view of the specified list, using the specified
195       * constraint. Any operations that add new elements to the list will call the
196       * provided constraint. However, this method does not verify that existing
197       * elements satisfy the constraint.
198       *
199       * <p>If {@code list} implements {@link RandomAccess}, so will the returned
200       * list. The returned list is not serializable.
201       *
202       * @param list the list to constrain
203       * @param constraint the constraint that validates added elements
204       * @return a constrained view of the list
205       */
206      public static <E> List<E> constrainedList(
207          List<E> list, Constraint<? super E> constraint) {
208        return (list instanceof RandomAccess)
209            ? new ConstrainedRandomAccessList<E>(list, constraint)
210            : new ConstrainedList<E>(list, constraint);
211      }
212    
213      /** @see Constraints#constrainedList */
214      @GwtCompatible
215      private static class ConstrainedList<E> extends ForwardingList<E> {
216        final List<E> delegate;
217        final Constraint<? super E> constraint;
218    
219        ConstrainedList(List<E> delegate, Constraint<? super E> constraint) {
220          this.delegate = checkNotNull(delegate);
221          this.constraint = checkNotNull(constraint);
222        }
223        @Override protected List<E> delegate() {
224          return delegate;
225        }
226    
227        @Override public boolean add(E element) {
228          constraint.checkElement(element);
229          return delegate.add(element);
230        }
231        @Override public void add(int index, E element) {
232          constraint.checkElement(element);
233          delegate.add(index, element);
234        }
235        @Override public boolean addAll(Collection<? extends E> elements) {
236          return delegate.addAll(checkElements(elements, constraint));
237        }
238        @Override public boolean addAll(int index, Collection<? extends E> elements)
239        {
240          return delegate.addAll(index, checkElements(elements, constraint));
241        }
242        @Override public ListIterator<E> listIterator() {
243          return constrainedListIterator(delegate.listIterator(), constraint);
244        }
245        @Override public ListIterator<E> listIterator(int index) {
246          return constrainedListIterator(delegate.listIterator(index), constraint);
247        }
248        @Override public E set(int index, E element) {
249          constraint.checkElement(element);
250          return delegate.set(index, element);
251        }
252        @Override public List<E> subList(int fromIndex, int toIndex) {
253          return constrainedList(
254              delegate.subList(fromIndex, toIndex), constraint);
255        }
256      }
257    
258      /** @see Constraints#constrainedList */
259      static class ConstrainedRandomAccessList<E> extends ConstrainedList<E>
260          implements RandomAccess {
261        ConstrainedRandomAccessList(
262            List<E> delegate, Constraint<? super E> constraint) {
263          super(delegate, constraint);
264        }
265      }
266    
267      /**
268       * Returns a constrained view of the specified list iterator, using the
269       * specified constraint. Any operations that would add new elements to the
270       * underlying list will be verified by the constraint.
271       *
272       * @param listIterator the iterator for which to return a constrained view
273       * @param constraint the constraint for elements in the list
274       * @return a constrained view of the specified iterator
275       */
276      private static <E> ListIterator<E> constrainedListIterator(
277          ListIterator<E> listIterator, Constraint<? super E> constraint) {
278        return new ConstrainedListIterator<E>(listIterator, constraint);
279      }
280    
281      /** @see Constraints#constrainedListIterator */
282      static class ConstrainedListIterator<E> extends ForwardingListIterator<E> {
283        private final ListIterator<E> delegate;
284        private final Constraint<? super E> constraint;
285    
286        public ConstrainedListIterator(
287            ListIterator<E> delegate, Constraint<? super E> constraint) {
288          this.delegate = delegate;
289          this.constraint = constraint;
290        }
291        @Override protected ListIterator<E> delegate() {
292          return delegate;
293        }
294    
295        @Override public void add(E element) {
296          constraint.checkElement(element);
297          delegate.add(element);
298        }
299        @Override public void set(E element) {
300          constraint.checkElement(element);
301          delegate.set(element);
302        }
303      }
304    
305      static <E> Collection<E> constrainedTypePreservingCollection(
306          Collection<E> collection, Constraint<E> constraint) {
307        if (collection instanceof SortedSet) {
308          return constrainedSortedSet((SortedSet<E>) collection, constraint);
309        } else if (collection instanceof Set) {
310          return constrainedSet((Set<E>) collection, constraint);
311        } else if (collection instanceof List) {
312          return constrainedList((List<E>) collection, constraint);
313        } else {
314          return constrainedCollection(collection, constraint);
315        }
316      }
317    
318      /**
319       * Returns a constrained view of the specified multiset, using the specified
320       * constraint. Any operations that add new elements to the multiset will call
321       * the provided constraint. However, this method does not verify that
322       * existing elements satisfy the constraint.
323       *
324       * <p>The returned multiset is not serializable.
325       *
326       * @param multiset the multiset to constrain
327       * @param constraint the constraint that validates added elements
328       * @return a constrained view of the multiset
329       */
330      public static <E> Multiset<E> constrainedMultiset(
331          Multiset<E> multiset, Constraint<? super E> constraint) {
332        return new ConstrainedMultiset<E>(multiset, constraint);
333      }
334    
335      /** @see Constraints#constrainedMultiset */
336      static class ConstrainedMultiset<E> extends ForwardingMultiset<E> {
337        private Multiset<E> delegate;
338        private final Constraint<? super E> constraint;
339    
340        public ConstrainedMultiset(
341            Multiset<E> delegate, Constraint<? super E> constraint) {
342          this.delegate = checkNotNull(delegate);
343          this.constraint = checkNotNull(constraint);
344        }
345        @Override protected Multiset<E> delegate() {
346          return delegate;
347        }
348        @Override public boolean add(E element) {
349          return standardAdd(element);
350        }
351        @Override public boolean addAll(Collection<? extends E> elements) {
352          return delegate.addAll(checkElements(elements, constraint));
353        }
354        @Override public int add(E element, int occurrences) {
355          constraint.checkElement(element);
356          return delegate.add(element, occurrences);
357        }
358        @Override public int setCount(E element, int count) {
359          constraint.checkElement(element);
360          return delegate.setCount(element, count);
361        }
362        @Override public boolean setCount(E element, int oldCount, int newCount) {
363          constraint.checkElement(element);
364          return delegate.setCount(element, oldCount, newCount);
365        }
366      }
367    
368      /*
369       * TODO(kevinb): For better performance, avoid making a copy of the elements
370       * by having addAll() call add() repeatedly instead.
371       */
372    
373      private static <E> Collection<E> checkElements(
374          Collection<E> elements, Constraint<? super E> constraint) {
375        Collection<E> copy = Lists.newArrayList(elements);
376        for (E element : copy) {
377          constraint.checkElement(element);
378        }
379        return copy;
380      }
381    }