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 // javac doesn't need this suppressed. eclipse does. and idea thinks it's an
062 // error! TODO: justify safety
063 @SuppressWarnings("unchecked")
064 public static final <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 // TODO: Make public?
278 private static <E> ListIterator<E> constrainedListIterator(
279 ListIterator<E> listIterator, Constraint<? super E> constraint) {
280 return new ConstrainedListIterator<E>(listIterator, constraint);
281 }
282
283 /** @see Constraints#constrainedListIterator */
284 static class ConstrainedListIterator<E> extends ForwardingListIterator<E> {
285 private final ListIterator<E> delegate;
286 private final Constraint<? super E> constraint;
287
288 public ConstrainedListIterator(
289 ListIterator<E> delegate, Constraint<? super E> constraint) {
290 this.delegate = delegate;
291 this.constraint = constraint;
292 }
293 @Override protected ListIterator<E> delegate() {
294 return delegate;
295 }
296
297 @Override public void add(E element) {
298 constraint.checkElement(element);
299 delegate.add(element);
300 }
301 @Override public void set(E element) {
302 constraint.checkElement(element);
303 delegate.set(element);
304 }
305 }
306
307 static <E> Collection<E> constrainedTypePreservingCollection(
308 Collection<E> collection, Constraint<E> constraint) {
309 if (collection instanceof SortedSet) {
310 return constrainedSortedSet((SortedSet<E>) collection, constraint);
311 } else if (collection instanceof Set) {
312 return constrainedSet((Set<E>) collection, constraint);
313 } else if (collection instanceof List) {
314 return constrainedList((List<E>) collection, constraint);
315 } else {
316 return constrainedCollection(collection, constraint);
317 }
318 }
319
320 /**
321 * Returns a constrained view of the specified multiset, using the specified
322 * constraint. Any operations that add new elements to the multiset will call
323 * the provided constraint. However, this method does not verify that
324 * existing elements satisfy the constraint.
325 *
326 * <p>The returned multiset is not serializable.
327 *
328 * @param multiset the multiset to constrain
329 * @param constraint the constraint that validates added elements
330 * @return a constrained view of the multiset
331 */
332 public static <E> Multiset<E> constrainedMultiset(
333 Multiset<E> multiset, Constraint<? super E> constraint) {
334 return new ConstrainedMultiset<E>(multiset, constraint);
335 }
336
337 /** @see Constraints#constrainedMultiset */
338 static class ConstrainedMultiset<E> extends ForwardingMultiset<E> {
339 private Multiset<E> delegate;
340 private final Constraint<? super E> constraint;
341
342 public ConstrainedMultiset(
343 Multiset<E> delegate, Constraint<? super E> constraint) {
344 this.delegate = checkNotNull(delegate);
345 this.constraint = checkNotNull(constraint);
346 }
347 @Override protected Multiset<E> delegate() {
348 return delegate;
349 }
350
351 @Override public boolean add(E element) {
352 constraint.checkElement(element);
353 return delegate.add(element);
354 }
355 @Override public boolean addAll(Collection<? extends E> elements) {
356 return delegate.addAll(checkElements(elements, constraint));
357 }
358 @Override public int add(E element, int occurrences) {
359 constraint.checkElement(element);
360 return delegate.add(element, occurrences);
361 }
362 @Override public int setCount(E element, int count) {
363 constraint.checkElement(element);
364 return delegate.setCount(element, count);
365 }
366 @Override public boolean setCount(E element, int oldCount, int newCount) {
367 constraint.checkElement(element);
368 return delegate.setCount(element, oldCount, newCount);
369 }
370 }
371
372 // TODO: For better performance, avoid making a copy of the elements by having
373 // addAll() call add() repeatedly instead.
374
375 private static <E> Collection<E> checkElements(
376 Collection<E> elements, Constraint<? super E> constraint) {
377 Collection<E> copy = Lists.newArrayList(elements);
378 for (E element : copy) {
379 constraint.checkElement(element);
380 }
381 return copy;
382 }
383 }