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 }