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 }