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
17package com.google.common.collect;
18
19import static com.google.common.base.Preconditions.checkNotNull;
20
21import com.google.common.annotations.Beta;
22import com.google.common.annotations.GwtCompatible;
23
24import java.util.Collection;
25import java.util.List;
26import java.util.ListIterator;
27import java.util.RandomAccess;
28import java.util.Set;
29import 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
41public 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