001 /* 002 * Copyright (C) 2007 The Guava Authors 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.0 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 @Override 049 public Object checkElement(Object element) { 050 return checkNotNull(element); 051 } 052 053 @Override public String toString() { 054 return "Not null"; 055 } 056 } 057 058 /** 059 * Returns a constraint that verifies that the element is not null. If the 060 * element is null, a {@link NullPointerException} is thrown. 061 */ 062 // safe to narrow the type since checkElement returns its argument directly 063 @SuppressWarnings("unchecked") 064 public static <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 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 }