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}