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 017package com.google.common.collect; 018 019import static com.google.common.base.Preconditions.checkNotNull; 020 021import com.google.common.annotations.Beta; 022import com.google.common.annotations.GwtCompatible; 023import com.google.errorprone.annotations.CanIgnoreReturnValue; 024import com.google.j2objc.annotations.WeakOuter; 025import java.io.Serializable; 026import java.util.Collection; 027import java.util.Iterator; 028import java.util.LinkedHashMap; 029import java.util.List; 030import java.util.Map; 031import java.util.Map.Entry; 032import java.util.Set; 033import javax.annotation.Nullable; 034 035/** 036 * Factory and utilities pertaining to the {@code MapConstraint} interface. 037 * 038 * @see Constraints 039 * @author Mike Bostock 040 * @since 3.0 041 * @deprecated Use {@link Preconditions} for basic checks. In place of 042 * constrained maps, we encourage you to check your preconditions 043 * explicitly instead of leaving that work to the map implementation. 044 * For the specific case of rejecting null, consider {@link ImmutableMap}. 045 * This class is scheduled for removal in Guava 21.0. 046 */ 047@Beta 048@GwtCompatible 049@Deprecated 050public final class MapConstraints { 051 private MapConstraints() {} 052 053 /** 054 * Returns a constrained view of the specified map, using the specified 055 * constraint. Any operations that add new mappings will call the provided 056 * constraint. However, this method does not verify that existing mappings 057 * satisfy the constraint. 058 * 059 * <p>The returned map is not serializable. 060 * 061 * @param map the map to constrain 062 * @param constraint the constraint that validates added entries 063 * @return a constrained view of the specified map 064 */ 065 public static <K, V> Map<K, V> constrainedMap( 066 Map<K, V> map, MapConstraint<? super K, ? super V> constraint) { 067 return new ConstrainedMap<K, V>(map, constraint); 068 } 069 070 /** 071 * Returns a constrained view of the specified list multimap, using the 072 * specified constraint. Any operations that add new mappings will call the 073 * provided constraint. However, this method does not verify that existing 074 * mappings satisfy the constraint. 075 * 076 * <p>Note that the generated multimap's {@link Multimap#removeAll} and 077 * {@link Multimap#replaceValues} methods return collections that are not 078 * constrained. 079 * 080 * <p>The returned multimap is not serializable. 081 * 082 * @param multimap the multimap to constrain 083 * @param constraint the constraint that validates added entries 084 * @return a constrained view of the specified multimap 085 */ 086 public static <K, V> ListMultimap<K, V> constrainedListMultimap( 087 ListMultimap<K, V> multimap, MapConstraint<? super K, ? super V> constraint) { 088 return new ConstrainedListMultimap<K, V>(multimap, constraint); 089 } 090 091 /** 092 * Returns a constrained view of the specified entry, using the specified 093 * constraint. The {@link Entry#setValue} operation will be verified with the 094 * constraint. 095 * 096 * @param entry the entry to constrain 097 * @param constraint the constraint for the entry 098 * @return a constrained view of the specified entry 099 */ 100 private static <K, V> Entry<K, V> constrainedEntry( 101 final Entry<K, V> entry, final MapConstraint<? super K, ? super V> constraint) { 102 checkNotNull(entry); 103 checkNotNull(constraint); 104 return new ForwardingMapEntry<K, V>() { 105 @Override 106 protected Entry<K, V> delegate() { 107 return entry; 108 } 109 110 @Override 111 public V setValue(V value) { 112 constraint.checkKeyValue(getKey(), value); 113 return entry.setValue(value); 114 } 115 }; 116 } 117 118 /** 119 * Returns a constrained view of the specified {@code asMap} entry, using the 120 * specified constraint. The {@link Entry#setValue} operation will be verified 121 * with the constraint, and the collection returned by {@link Entry#getValue} 122 * will be similarly constrained. 123 * 124 * @param entry the {@code asMap} entry to constrain 125 * @param constraint the constraint for the entry 126 * @return a constrained view of the specified entry 127 */ 128 private static <K, V> Entry<K, Collection<V>> constrainedAsMapEntry( 129 final Entry<K, Collection<V>> entry, final MapConstraint<? super K, ? super V> constraint) { 130 checkNotNull(entry); 131 checkNotNull(constraint); 132 return new ForwardingMapEntry<K, Collection<V>>() { 133 @Override 134 protected Entry<K, Collection<V>> delegate() { 135 return entry; 136 } 137 138 @Override 139 public Collection<V> getValue() { 140 return Constraints.constrainedTypePreservingCollection( 141 entry.getValue(), 142 new Constraint<V>() { 143 @Override 144 public V checkElement(V value) { 145 constraint.checkKeyValue(getKey(), value); 146 return value; 147 } 148 }); 149 } 150 }; 151 } 152 153 /** 154 * Returns a constrained view of the specified set of {@code asMap} entries, 155 * using the specified constraint. The {@link Entry#setValue} operation will 156 * be verified with the constraint, and the collection returned by {@link 157 * Entry#getValue} will be similarly constrained. The {@code add} and {@code 158 * addAll} operations simply forward to the underlying set, which throws an 159 * {@link UnsupportedOperationException} per the multimap specification. 160 * 161 * @param entries the entries to constrain 162 * @param constraint the constraint for the entries 163 * @return a constrained view of the entries 164 */ 165 private static <K, V> Set<Entry<K, Collection<V>>> constrainedAsMapEntries( 166 Set<Entry<K, Collection<V>>> entries, MapConstraint<? super K, ? super V> constraint) { 167 return new ConstrainedAsMapEntries<K, V>(entries, constraint); 168 } 169 170 /** 171 * Returns a constrained view of the specified collection (or set) of entries, 172 * using the specified constraint. The {@link Entry#setValue} operation will 173 * be verified with the constraint, along with add operations on the returned 174 * collection. The {@code add} and {@code addAll} operations simply forward to 175 * the underlying collection, which throws an {@link 176 * UnsupportedOperationException} per the map and multimap specification. 177 * 178 * @param entries the entries to constrain 179 * @param constraint the constraint for the entries 180 * @return a constrained view of the specified entries 181 */ 182 private static <K, V> Collection<Entry<K, V>> constrainedEntries( 183 Collection<Entry<K, V>> entries, MapConstraint<? super K, ? super V> constraint) { 184 if (entries instanceof Set) { 185 return constrainedEntrySet((Set<Entry<K, V>>) entries, constraint); 186 } 187 return new ConstrainedEntries<K, V>(entries, constraint); 188 } 189 190 /** 191 * Returns a constrained view of the specified set of entries, using the 192 * specified constraint. The {@link Entry#setValue} operation will be verified 193 * with the constraint, along with add operations on the returned set. The 194 * {@code add} and {@code addAll} operations simply forward to the underlying 195 * set, which throws an {@link UnsupportedOperationException} per the map and 196 * multimap specification. 197 * 198 * <p>The returned multimap is not serializable. 199 * 200 * @param entries the entries to constrain 201 * @param constraint the constraint for the entries 202 * @return a constrained view of the specified entries 203 */ 204 private static <K, V> Set<Entry<K, V>> constrainedEntrySet( 205 Set<Entry<K, V>> entries, MapConstraint<? super K, ? super V> constraint) { 206 return new ConstrainedEntrySet<K, V>(entries, constraint); 207 } 208 209 /** @see MapConstraints#constrainedMap */ 210 static class ConstrainedMap<K, V> extends ForwardingMap<K, V> { 211 private final Map<K, V> delegate; 212 final MapConstraint<? super K, ? super V> constraint; 213 private transient Set<Entry<K, V>> entrySet; 214 215 ConstrainedMap(Map<K, V> delegate, MapConstraint<? super K, ? super V> constraint) { 216 this.delegate = checkNotNull(delegate); 217 this.constraint = checkNotNull(constraint); 218 } 219 220 @Override 221 protected Map<K, V> delegate() { 222 return delegate; 223 } 224 225 @Override 226 public Set<Entry<K, V>> entrySet() { 227 Set<Entry<K, V>> result = entrySet; 228 if (result == null) { 229 entrySet = result = constrainedEntrySet(delegate.entrySet(), constraint); 230 } 231 return result; 232 } 233 234 @CanIgnoreReturnValue // TODO(kak): Remove this? 235 @Override 236 public V put(K key, V value) { 237 constraint.checkKeyValue(key, value); 238 return delegate.put(key, value); 239 } 240 241 @Override 242 public void putAll(Map<? extends K, ? extends V> map) { 243 delegate.putAll(checkMap(map, constraint)); 244 } 245 } 246 247 /** @see MapConstraints#constrainedMultimap */ 248 private static class ConstrainedMultimap<K, V> extends ForwardingMultimap<K, V> 249 implements Serializable { 250 final MapConstraint<? super K, ? super V> constraint; 251 final Multimap<K, V> delegate; 252 253 transient Collection<Entry<K, V>> entries; 254 255 transient Map<K, Collection<V>> asMap; 256 257 public ConstrainedMultimap( 258 Multimap<K, V> delegate, MapConstraint<? super K, ? super V> constraint) { 259 this.delegate = checkNotNull(delegate); 260 this.constraint = checkNotNull(constraint); 261 } 262 263 @Override 264 protected Multimap<K, V> delegate() { 265 return delegate; 266 } 267 268 @Override 269 public Map<K, Collection<V>> asMap() { 270 Map<K, Collection<V>> result = asMap; 271 if (result == null) { 272 final Map<K, Collection<V>> asMapDelegate = delegate.asMap(); 273 274 @WeakOuter 275 class AsMap extends ForwardingMap<K, Collection<V>> { 276 Set<Entry<K, Collection<V>>> entrySet; 277 Collection<Collection<V>> values; 278 279 @Override 280 protected Map<K, Collection<V>> delegate() { 281 return asMapDelegate; 282 } 283 284 @Override 285 public Set<Entry<K, Collection<V>>> entrySet() { 286 Set<Entry<K, Collection<V>>> result = entrySet; 287 if (result == null) { 288 entrySet = result = constrainedAsMapEntries(asMapDelegate.entrySet(), constraint); 289 } 290 return result; 291 } 292 293 @SuppressWarnings("unchecked") 294 @Override 295 public Collection<V> get(Object key) { 296 try { 297 Collection<V> collection = ConstrainedMultimap.this.get((K) key); 298 return collection.isEmpty() ? null : collection; 299 } catch (ClassCastException e) { 300 return null; // key wasn't a K 301 } 302 } 303 304 @Override 305 public Collection<Collection<V>> values() { 306 Collection<Collection<V>> result = values; 307 if (result == null) { 308 values = result = new ConstrainedAsMapValues<K, V>(delegate().values(), entrySet()); 309 } 310 return result; 311 } 312 313 @Override 314 public boolean containsValue(Object o) { 315 return values().contains(o); 316 } 317 } 318 asMap = result = new AsMap(); 319 } 320 return result; 321 } 322 323 @Override 324 public Collection<Entry<K, V>> entries() { 325 Collection<Entry<K, V>> result = entries; 326 if (result == null) { 327 entries = result = constrainedEntries(delegate.entries(), constraint); 328 } 329 return result; 330 } 331 332 @Override 333 public Collection<V> get(final K key) { 334 return Constraints.constrainedTypePreservingCollection( 335 delegate.get(key), 336 new Constraint<V>() { 337 @Override 338 public V checkElement(V value) { 339 constraint.checkKeyValue(key, value); 340 return value; 341 } 342 }); 343 } 344 345 @Override 346 public boolean put(K key, V value) { 347 constraint.checkKeyValue(key, value); 348 return delegate.put(key, value); 349 } 350 351 @Override 352 public boolean putAll(K key, Iterable<? extends V> values) { 353 return delegate.putAll(key, checkValues(key, values, constraint)); 354 } 355 356 @Override 357 public boolean putAll(Multimap<? extends K, ? extends V> multimap) { 358 boolean changed = false; 359 for (Entry<? extends K, ? extends V> entry : multimap.entries()) { 360 changed |= put(entry.getKey(), entry.getValue()); 361 } 362 return changed; 363 } 364 365 @Override 366 public Collection<V> replaceValues(K key, Iterable<? extends V> values) { 367 return delegate.replaceValues(key, checkValues(key, values, constraint)); 368 } 369 } 370 371 /** @see ConstrainedMultimap#asMap */ 372 private static class ConstrainedAsMapValues<K, V> extends ForwardingCollection<Collection<V>> { 373 final Collection<Collection<V>> delegate; 374 final Set<Entry<K, Collection<V>>> entrySet; 375 376 /** 377 * @param entrySet map entries, linking each key with its corresponding 378 * values, that already enforce the constraint 379 */ 380 ConstrainedAsMapValues( 381 Collection<Collection<V>> delegate, Set<Entry<K, Collection<V>>> entrySet) { 382 this.delegate = delegate; 383 this.entrySet = entrySet; 384 } 385 386 @Override 387 protected Collection<Collection<V>> delegate() { 388 return delegate; 389 } 390 391 @Override 392 public Iterator<Collection<V>> iterator() { 393 final Iterator<Entry<K, Collection<V>>> iterator = entrySet.iterator(); 394 return new Iterator<Collection<V>>() { 395 @Override 396 public boolean hasNext() { 397 return iterator.hasNext(); 398 } 399 400 @Override 401 public Collection<V> next() { 402 return iterator.next().getValue(); 403 } 404 405 @Override 406 public void remove() { 407 iterator.remove(); 408 } 409 }; 410 } 411 412 @Override 413 public Object[] toArray() { 414 return standardToArray(); 415 } 416 417 @Override 418 public <T> T[] toArray(T[] array) { 419 return standardToArray(array); 420 } 421 422 @Override 423 public boolean contains(Object o) { 424 return standardContains(o); 425 } 426 427 @Override 428 public boolean containsAll(Collection<?> c) { 429 return standardContainsAll(c); 430 } 431 432 @Override 433 public boolean remove(Object o) { 434 return standardRemove(o); 435 } 436 437 @Override 438 public boolean removeAll(Collection<?> c) { 439 return standardRemoveAll(c); 440 } 441 442 @Override 443 public boolean retainAll(Collection<?> c) { 444 return standardRetainAll(c); 445 } 446 } 447 448 /** @see MapConstraints#constrainedEntries */ 449 private static class ConstrainedEntries<K, V> extends ForwardingCollection<Entry<K, V>> { 450 final MapConstraint<? super K, ? super V> constraint; 451 final Collection<Entry<K, V>> entries; 452 453 ConstrainedEntries( 454 Collection<Entry<K, V>> entries, MapConstraint<? super K, ? super V> constraint) { 455 this.entries = entries; 456 this.constraint = constraint; 457 } 458 459 @Override 460 protected Collection<Entry<K, V>> delegate() { 461 return entries; 462 } 463 464 @Override 465 public Iterator<Entry<K, V>> iterator() { 466 return new TransformedIterator<Entry<K, V>, Entry<K, V>>(entries.iterator()) { 467 @Override 468 Entry<K, V> transform(Entry<K, V> from) { 469 return constrainedEntry(from, constraint); 470 } 471 }; 472 } 473 474 // See Collections.CheckedMap.CheckedEntrySet for details on attacks. 475 476 @Override 477 public Object[] toArray() { 478 return standardToArray(); 479 } 480 481 @Override 482 public <T> T[] toArray(T[] array) { 483 return standardToArray(array); 484 } 485 486 @Override 487 public boolean contains(Object o) { 488 return Maps.containsEntryImpl(delegate(), o); 489 } 490 491 @Override 492 public boolean containsAll(Collection<?> c) { 493 return standardContainsAll(c); 494 } 495 496 @Override 497 public boolean remove(Object o) { 498 return Maps.removeEntryImpl(delegate(), o); 499 } 500 501 @Override 502 public boolean removeAll(Collection<?> c) { 503 return standardRemoveAll(c); 504 } 505 506 @Override 507 public boolean retainAll(Collection<?> c) { 508 return standardRetainAll(c); 509 } 510 } 511 512 /** @see MapConstraints#constrainedEntrySet */ 513 static class ConstrainedEntrySet<K, V> extends ConstrainedEntries<K, V> 514 implements Set<Entry<K, V>> { 515 ConstrainedEntrySet(Set<Entry<K, V>> entries, MapConstraint<? super K, ? super V> constraint) { 516 super(entries, constraint); 517 } 518 519 // See Collections.CheckedMap.CheckedEntrySet for details on attacks. 520 521 @Override 522 public boolean equals(@Nullable Object object) { 523 return Sets.equalsImpl(this, object); 524 } 525 526 @Override 527 public int hashCode() { 528 return Sets.hashCodeImpl(this); 529 } 530 } 531 532 /** @see MapConstraints#constrainedAsMapEntries */ 533 static class ConstrainedAsMapEntries<K, V> extends ForwardingSet<Entry<K, Collection<V>>> { 534 private final MapConstraint<? super K, ? super V> constraint; 535 private final Set<Entry<K, Collection<V>>> entries; 536 537 ConstrainedAsMapEntries( 538 Set<Entry<K, Collection<V>>> entries, MapConstraint<? super K, ? super V> constraint) { 539 this.entries = entries; 540 this.constraint = constraint; 541 } 542 543 @Override 544 protected Set<Entry<K, Collection<V>>> delegate() { 545 return entries; 546 } 547 548 @Override 549 public Iterator<Entry<K, Collection<V>>> iterator() { 550 return new TransformedIterator<Entry<K, Collection<V>>, Entry<K, Collection<V>>>( 551 entries.iterator()) { 552 @Override 553 Entry<K, Collection<V>> transform(Entry<K, Collection<V>> from) { 554 return constrainedAsMapEntry(from, constraint); 555 } 556 }; 557 } 558 559 // See Collections.CheckedMap.CheckedEntrySet for details on attacks. 560 561 @Override 562 public Object[] toArray() { 563 return standardToArray(); 564 } 565 566 @Override 567 public <T> T[] toArray(T[] array) { 568 return standardToArray(array); 569 } 570 571 @Override 572 public boolean contains(Object o) { 573 return Maps.containsEntryImpl(delegate(), o); 574 } 575 576 @Override 577 public boolean containsAll(Collection<?> c) { 578 return standardContainsAll(c); 579 } 580 581 @Override 582 public boolean equals(@Nullable Object object) { 583 return standardEquals(object); 584 } 585 586 @Override 587 public int hashCode() { 588 return standardHashCode(); 589 } 590 591 @Override 592 public boolean remove(Object o) { 593 return Maps.removeEntryImpl(delegate(), o); 594 } 595 596 @Override 597 public boolean removeAll(Collection<?> c) { 598 return standardRemoveAll(c); 599 } 600 601 @Override 602 public boolean retainAll(Collection<?> c) { 603 return standardRetainAll(c); 604 } 605 } 606 607 private static class ConstrainedListMultimap<K, V> extends ConstrainedMultimap<K, V> 608 implements ListMultimap<K, V> { 609 ConstrainedListMultimap( 610 ListMultimap<K, V> delegate, MapConstraint<? super K, ? super V> constraint) { 611 super(delegate, constraint); 612 } 613 614 @Override 615 public List<V> get(K key) { 616 return (List<V>) super.get(key); 617 } 618 619 @Override 620 public List<V> removeAll(Object key) { 621 return (List<V>) super.removeAll(key); 622 } 623 624 @Override 625 public List<V> replaceValues(K key, Iterable<? extends V> values) { 626 return (List<V>) super.replaceValues(key, values); 627 } 628 } 629 630 private static <K, V> Collection<V> checkValues( 631 K key, Iterable<? extends V> values, MapConstraint<? super K, ? super V> constraint) { 632 Collection<V> copy = Lists.newArrayList(values); 633 for (V value : copy) { 634 constraint.checkKeyValue(key, value); 635 } 636 return copy; 637 } 638 639 private static <K, V> Map<K, V> checkMap( 640 Map<? extends K, ? extends V> map, MapConstraint<? super K, ? super V> constraint) { 641 Map<K, V> copy = new LinkedHashMap<K, V>(map); 642 for (Entry<K, V> entry : copy.entrySet()) { 643 constraint.checkKeyValue(entry.getKey(), entry.getValue()); 644 } 645 return copy; 646 } 647}