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.GwtCompatible;
22
23import java.util.Collection;
24import java.util.List;
25import java.util.ListIterator;
26import java.util.RandomAccess;
27import java.util.Set;
28import java.util.SortedSet;
29
30/**
31 * Factories and utilities pertaining to the {@link Constraint} interface.
32 *
33 * @author Mike Bostock
34 * @author Jared Levy
35 */
36@GwtCompatible
37final class Constraints {
38  private Constraints() {}
39
40  /**
41   * Returns a constrained view of the specified collection, using the specified
42   * constraint. Any operations that add new elements to the collection will
43   * call the provided constraint. However, this method does not verify that
44   * existing elements satisfy the constraint.
45   *
46   * <p>The returned collection is not serializable.
47   *
48   * @param collection the collection to constrain
49   * @param constraint the constraint that validates added elements
50   * @return a constrained view of the collection
51   */
52  public static <E> Collection<E> constrainedCollection(
53      Collection<E> collection, Constraint<? super E> constraint) {
54    return new ConstrainedCollection<E>(collection, constraint);
55  }
56
57  /** @see Constraints#constrainedCollection */
58  static class ConstrainedCollection<E> extends ForwardingCollection<E> {
59    private final Collection<E> delegate;
60    private final Constraint<? super E> constraint;
61
62    public ConstrainedCollection(
63        Collection<E> delegate, Constraint<? super E> constraint) {
64      this.delegate = checkNotNull(delegate);
65      this.constraint = checkNotNull(constraint);
66    }
67    @Override protected Collection<E> delegate() {
68      return delegate;
69    }
70    @Override public boolean add(E element) {
71      constraint.checkElement(element);
72      return delegate.add(element);
73    }
74    @Override public boolean addAll(Collection<? extends E> elements) {
75      return delegate.addAll(checkElements(elements, constraint));
76    }
77  }
78
79  /**
80   * Returns a constrained view of the specified set, using the specified
81   * constraint. Any operations that add new elements to the set will call the
82   * provided constraint. However, this method does not verify that existing
83   * elements satisfy the constraint.
84   *
85   * <p>The returned set is not serializable.
86   *
87   * @param set the set to constrain
88   * @param constraint the constraint that validates added elements
89   * @return a constrained view of the set
90   */
91  public static <E> Set<E> constrainedSet(
92      Set<E> set, Constraint<? super E> constraint) {
93    return new ConstrainedSet<E>(set, constraint);
94  }
95
96  /** @see Constraints#constrainedSet */
97  static class ConstrainedSet<E> extends ForwardingSet<E> {
98    private final Set<E> delegate;
99    private final Constraint<? super E> constraint;
100
101    public ConstrainedSet(Set<E> delegate, Constraint<? super E> constraint) {
102      this.delegate = checkNotNull(delegate);
103      this.constraint = checkNotNull(constraint);
104    }
105    @Override protected Set<E> delegate() {
106      return delegate;
107    }
108    @Override public boolean add(E element) {
109      constraint.checkElement(element);
110      return delegate.add(element);
111    }
112    @Override public boolean addAll(Collection<? extends E> elements) {
113      return delegate.addAll(checkElements(elements, constraint));
114    }
115  }
116
117  /**
118   * Returns a constrained view of the specified sorted set, using the specified
119   * constraint. Any operations that add new elements to the sorted set will
120   * call the provided constraint. However, this method does not verify that
121   * existing elements satisfy the constraint.
122   *
123   * <p>The returned set is not serializable.
124   *
125   * @param sortedSet the sorted set to constrain
126   * @param constraint the constraint that validates added elements
127   * @return a constrained view of the sorted set
128   */
129  public static <E> SortedSet<E> constrainedSortedSet(
130      SortedSet<E> sortedSet, Constraint<? super E> constraint) {
131    return new ConstrainedSortedSet<E>(sortedSet, constraint);
132  }
133
134  /** @see Constraints#constrainedSortedSet */
135  private static class ConstrainedSortedSet<E> extends ForwardingSortedSet<E> {
136    final SortedSet<E> delegate;
137    final Constraint<? super E> constraint;
138
139    ConstrainedSortedSet(
140        SortedSet<E> delegate, Constraint<? super E> constraint) {
141      this.delegate = checkNotNull(delegate);
142      this.constraint = checkNotNull(constraint);
143    }
144    @Override protected SortedSet<E> delegate() {
145      return delegate;
146    }
147    @Override public SortedSet<E> headSet(E toElement) {
148      return constrainedSortedSet(delegate.headSet(toElement), constraint);
149    }
150    @Override public SortedSet<E> subSet(E fromElement, E toElement) {
151      return constrainedSortedSet(
152          delegate.subSet(fromElement, toElement), constraint);
153    }
154    @Override public SortedSet<E> tailSet(E fromElement) {
155      return constrainedSortedSet(delegate.tailSet(fromElement), constraint);
156    }
157    @Override public boolean add(E element) {
158      constraint.checkElement(element);
159      return delegate.add(element);
160    }
161    @Override public boolean addAll(Collection<? extends E> elements) {
162      return delegate.addAll(checkElements(elements, constraint));
163    }
164  }
165
166  /**
167   * Returns a constrained view of the specified list, using the specified
168   * constraint. Any operations that add new elements to the list will call the
169   * provided constraint. However, this method does not verify that existing
170   * elements satisfy the constraint.
171   *
172   * <p>If {@code list} implements {@link RandomAccess}, so will the returned
173   * list. The returned list is not serializable.
174   *
175   * @param list the list to constrain
176   * @param constraint the constraint that validates added elements
177   * @return a constrained view of the list
178   */
179  public static <E> List<E> constrainedList(
180      List<E> list, Constraint<? super E> constraint) {
181    return (list instanceof RandomAccess)
182        ? new ConstrainedRandomAccessList<E>(list, constraint)
183        : new ConstrainedList<E>(list, constraint);
184  }
185
186  /** @see Constraints#constrainedList */
187  @GwtCompatible
188  private static class ConstrainedList<E> extends ForwardingList<E> {
189    final List<E> delegate;
190    final Constraint<? super E> constraint;
191
192    ConstrainedList(List<E> delegate, Constraint<? super E> constraint) {
193      this.delegate = checkNotNull(delegate);
194      this.constraint = checkNotNull(constraint);
195    }
196    @Override protected List<E> delegate() {
197      return delegate;
198    }
199
200    @Override public boolean add(E element) {
201      constraint.checkElement(element);
202      return delegate.add(element);
203    }
204    @Override public void add(int index, E element) {
205      constraint.checkElement(element);
206      delegate.add(index, element);
207    }
208    @Override public boolean addAll(Collection<? extends E> elements) {
209      return delegate.addAll(checkElements(elements, constraint));
210    }
211    @Override public boolean addAll(int index, Collection<? extends E> elements)
212    {
213      return delegate.addAll(index, checkElements(elements, constraint));
214    }
215    @Override public ListIterator<E> listIterator() {
216      return constrainedListIterator(delegate.listIterator(), constraint);
217    }
218    @Override public ListIterator<E> listIterator(int index) {
219      return constrainedListIterator(delegate.listIterator(index), constraint);
220    }
221    @Override public E set(int index, E element) {
222      constraint.checkElement(element);
223      return delegate.set(index, element);
224    }
225    @Override public List<E> subList(int fromIndex, int toIndex) {
226      return constrainedList(
227          delegate.subList(fromIndex, toIndex), constraint);
228    }
229  }
230
231  /** @see Constraints#constrainedList */
232  static class ConstrainedRandomAccessList<E> extends ConstrainedList<E>
233      implements RandomAccess {
234    ConstrainedRandomAccessList(
235        List<E> delegate, Constraint<? super E> constraint) {
236      super(delegate, constraint);
237    }
238  }
239
240  /**
241   * Returns a constrained view of the specified list iterator, using the
242   * specified constraint. Any operations that would add new elements to the
243   * underlying list will be verified by the constraint.
244   *
245   * @param listIterator the iterator for which to return a constrained view
246   * @param constraint the constraint for elements in the list
247   * @return a constrained view of the specified iterator
248   */
249  private static <E> ListIterator<E> constrainedListIterator(
250      ListIterator<E> listIterator, Constraint<? super E> constraint) {
251    return new ConstrainedListIterator<E>(listIterator, constraint);
252  }
253
254  /** @see Constraints#constrainedListIterator */
255  static class ConstrainedListIterator<E> extends ForwardingListIterator<E> {
256    private final ListIterator<E> delegate;
257    private final Constraint<? super E> constraint;
258
259    public ConstrainedListIterator(
260        ListIterator<E> delegate, Constraint<? super E> constraint) {
261      this.delegate = delegate;
262      this.constraint = constraint;
263    }
264    @Override protected ListIterator<E> delegate() {
265      return delegate;
266    }
267
268    @Override public void add(E element) {
269      constraint.checkElement(element);
270      delegate.add(element);
271    }
272    @Override public void set(E element) {
273      constraint.checkElement(element);
274      delegate.set(element);
275    }
276  }
277
278  static <E> Collection<E> constrainedTypePreservingCollection(
279      Collection<E> collection, Constraint<E> constraint) {
280    if (collection instanceof SortedSet) {
281      return constrainedSortedSet((SortedSet<E>) collection, constraint);
282    } else if (collection instanceof Set) {
283      return constrainedSet((Set<E>) collection, constraint);
284    } else if (collection instanceof List) {
285      return constrainedList((List<E>) collection, constraint);
286    } else {
287      return constrainedCollection(collection, constraint);
288    }
289  }
290
291  /*
292   * TODO(kevinb): For better performance, avoid making a copy of the elements
293   * by having addAll() call add() repeatedly instead.
294   */
295
296  private static <E> Collection<E> checkElements(
297      Collection<E> elements, Constraint<? super E> constraint) {
298    Collection<E> copy = Lists.newArrayList(elements);
299    for (E element : copy) {
300      constraint.checkElement(element);
301    }
302    return copy;
303  }
304}
305