All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.collect.MapConstraints Maven / Gradle / Ivy

There is a newer version: 3.9
Show newest version
/*
 * Copyright (C) 2007 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.common.collect;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;

import java.io.Serializable;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedSet;

import javax.annotation.Nullable;

/**
 * Factory and utilities pertaining to the {@code MapConstraint} interface.
 *
 * @see Constraints
 * @author Mike Bostock
 * @since 3.0
 */
@Beta
@GwtCompatible
public final class MapConstraints {
  private MapConstraints() {}

  /**
   * Returns a constraint that verifies that neither the key nor the value is
   * null. If either is null, a {@link NullPointerException} is thrown.
   */
  public static MapConstraint notNull() {
    return NotNullMapConstraint.INSTANCE;
  }

  // enum singleton pattern
  private enum NotNullMapConstraint implements MapConstraint {
    INSTANCE;

    @Override
    public void checkKeyValue(Object key, Object value) {
      checkNotNull(key);
      checkNotNull(value);
    }

    @Override public String toString() {
      return "Not null";
    }
  }

  /**
   * Returns a constrained view of the specified map, using the specified
   * constraint. Any operations that add new mappings will call the provided
   * constraint. However, this method does not verify that existing mappings
   * satisfy the constraint.
   *
   * 

The returned map is not serializable. * * @param map the map to constrain * @param constraint the constraint that validates added entries * @return a constrained view of the specified map */ public static Map constrainedMap( Map map, MapConstraint constraint) { return new ConstrainedMap(map, constraint); } /** * Returns a constrained view of the specified multimap, using the specified * constraint. Any operations that add new mappings will call the provided * constraint. However, this method does not verify that existing mappings * satisfy the constraint. * *

Note that the generated multimap's {@link Multimap#removeAll} and * {@link Multimap#replaceValues} methods return collections that are not * constrained. * *

The returned multimap is not serializable. * * @param multimap the multimap to constrain * @param constraint the constraint that validates added entries * @return a constrained view of the multimap */ public static Multimap constrainedMultimap( Multimap multimap, MapConstraint constraint) { return new ConstrainedMultimap(multimap, constraint); } /** * Returns a constrained view of the specified list multimap, using the * specified constraint. Any operations that add new mappings will call the * provided constraint. However, this method does not verify that existing * mappings satisfy the constraint. * *

Note that the generated multimap's {@link Multimap#removeAll} and * {@link Multimap#replaceValues} methods return collections that are not * constrained. * *

The returned multimap is not serializable. * * @param multimap the multimap to constrain * @param constraint the constraint that validates added entries * @return a constrained view of the specified multimap */ public static ListMultimap constrainedListMultimap( ListMultimap multimap, MapConstraint constraint) { return new ConstrainedListMultimap(multimap, constraint); } /** * Returns a constrained view of the specified set multimap, using the * specified constraint. Any operations that add new mappings will call the * provided constraint. However, this method does not verify that existing * mappings satisfy the constraint. * *

Note that the generated multimap's {@link Multimap#removeAll} and * {@link Multimap#replaceValues} methods return collections that are not * constrained. *

The returned multimap is not serializable. * * @param multimap the multimap to constrain * @param constraint the constraint that validates added entries * @return a constrained view of the specified multimap */ public static SetMultimap constrainedSetMultimap( SetMultimap multimap, MapConstraint constraint) { return new ConstrainedSetMultimap(multimap, constraint); } /** * Returns a constrained view of the specified sorted-set multimap, using the * specified constraint. Any operations that add new mappings will call the * provided constraint. However, this method does not verify that existing * mappings satisfy the constraint. * *

Note that the generated multimap's {@link Multimap#removeAll} and * {@link Multimap#replaceValues} methods return collections that are not * constrained. *

The returned multimap is not serializable. * * @param multimap the multimap to constrain * @param constraint the constraint that validates added entries * @return a constrained view of the specified multimap */ public static SortedSetMultimap constrainedSortedSetMultimap( SortedSetMultimap multimap, MapConstraint constraint) { return new ConstrainedSortedSetMultimap(multimap, constraint); } /** * Returns a constrained view of the specified entry, using the specified * constraint. The {@link Entry#setValue} operation will be verified with the * constraint. * * @param entry the entry to constrain * @param constraint the constraint for the entry * @return a constrained view of the specified entry */ private static Entry constrainedEntry( final Entry entry, final MapConstraint constraint) { checkNotNull(entry); checkNotNull(constraint); return new ForwardingMapEntry() { @Override protected Entry delegate() { return entry; } @Override public V setValue(V value) { constraint.checkKeyValue(getKey(), value); return entry.setValue(value); } }; } /** * Returns a constrained view of the specified {@code asMap} entry, using the * specified constraint. The {@link Entry#setValue} operation will be verified * with the constraint, and the collection returned by {@link Entry#getValue} * will be similarly constrained. * * @param entry the {@code asMap} entry to constrain * @param constraint the constraint for the entry * @return a constrained view of the specified entry */ private static Entry> constrainedAsMapEntry( final Entry> entry, final MapConstraint constraint) { checkNotNull(entry); checkNotNull(constraint); return new ForwardingMapEntry>() { @Override protected Entry> delegate() { return entry; } @Override public Collection getValue() { return Constraints.constrainedTypePreservingCollection( entry.getValue(), new Constraint() { @Override public V checkElement(V value) { constraint.checkKeyValue(getKey(), value); return value; } }); } }; } /** * Returns a constrained view of the specified set of {@code asMap} entries, * using the specified constraint. The {@link Entry#setValue} operation will * be verified with the constraint, and the collection returned by {@link * Entry#getValue} will be similarly constrained. The {@code add} and {@code * addAll} operations simply forward to the underlying set, which throws an * {@link UnsupportedOperationException} per the multimap specification. * * @param entries the entries to constrain * @param constraint the constraint for the entries * @return a constrained view of the entries */ private static Set>> constrainedAsMapEntries( Set>> entries, MapConstraint constraint) { return new ConstrainedAsMapEntries(entries, constraint); } /** * Returns a constrained view of the specified collection (or set) of entries, * using the specified constraint. The {@link Entry#setValue} operation will * be verified with the constraint, along with add operations on the returned * collection. The {@code add} and {@code addAll} operations simply forward to * the underlying collection, which throws an {@link * UnsupportedOperationException} per the map and multimap specification. * * @param entries the entries to constrain * @param constraint the constraint for the entries * @return a constrained view of the specified entries */ private static Collection> constrainedEntries( Collection> entries, MapConstraint constraint) { if (entries instanceof Set) { return constrainedEntrySet((Set>) entries, constraint); } return new ConstrainedEntries(entries, constraint); } /** * Returns a constrained view of the specified set of entries, using the * specified constraint. The {@link Entry#setValue} operation will be verified * with the constraint, along with add operations on the returned set. The * {@code add} and {@code addAll} operations simply forward to the underlying * set, which throws an {@link UnsupportedOperationException} per the map and * multimap specification. * *

The returned multimap is not serializable. * * @param entries the entries to constrain * @param constraint the constraint for the entries * @return a constrained view of the specified entries */ private static Set> constrainedEntrySet( Set> entries, MapConstraint constraint) { return new ConstrainedEntrySet(entries, constraint); } /** @see MapConstraints#constrainedMap */ static class ConstrainedMap extends ForwardingMap { private final Map delegate; final MapConstraint constraint; private transient Set> entrySet; ConstrainedMap( Map delegate, MapConstraint constraint) { this.delegate = checkNotNull(delegate); this.constraint = checkNotNull(constraint); } @Override protected Map delegate() { return delegate; } @Override public Set> entrySet() { Set> result = entrySet; if (result == null) { entrySet = result = constrainedEntrySet(delegate.entrySet(), constraint); } return result; } @Override public V put(K key, V value) { constraint.checkKeyValue(key, value); return delegate.put(key, value); } @Override public void putAll(Map map) { delegate.putAll(checkMap(map, constraint)); } } /** * Returns a constrained view of the specified bimap, using the specified * constraint. Any operations that modify the bimap will have the associated * keys and values verified with the constraint. * *

The returned bimap is not serializable. * * @param map the bimap to constrain * @param constraint the constraint that validates added entries * @return a constrained view of the specified bimap */ public static BiMap constrainedBiMap( BiMap map, MapConstraint constraint) { return new ConstrainedBiMap(map, null, constraint); } /** @see MapConstraints#constrainedBiMap */ private static class ConstrainedBiMap extends ConstrainedMap implements BiMap { /* * We could switch to racy single-check lazy init and remove volatile, but * there's a downside. That's because this field is also written in the * constructor. Without volatile, the constructor's write of the existing * inverse BiMap could occur after inverse()'s read of the field's initial * null value, leading inverse() to overwrite the existing inverse with a * doubly indirect version. This wouldn't be catastrophic, but it's * something to keep in mind if we make the change. * * Note that UnmodifiableBiMap *does* use racy single-check lazy init. * TODO(cpovirk): pick one and standardize */ volatile BiMap inverse; ConstrainedBiMap(BiMap delegate, @Nullable BiMap inverse, MapConstraint constraint) { super(delegate, constraint); this.inverse = inverse; } @Override protected BiMap delegate() { return (BiMap) super.delegate(); } @Override public V forcePut(K key, V value) { constraint.checkKeyValue(key, value); return delegate().forcePut(key, value); } @Override public BiMap inverse() { if (inverse == null) { inverse = new ConstrainedBiMap(delegate().inverse(), this, new InverseConstraint(constraint)); } return inverse; } @Override public Set values() { return delegate().values(); } } /** @see MapConstraints#constrainedBiMap */ private static class InverseConstraint implements MapConstraint { final MapConstraint constraint; public InverseConstraint(MapConstraint constraint) { this.constraint = checkNotNull(constraint); } @Override public void checkKeyValue(K key, V value) { constraint.checkKeyValue(value, key); } } /** @see MapConstraints#constrainedMultimap */ private static class ConstrainedMultimap extends ForwardingMultimap implements Serializable { final MapConstraint constraint; final Multimap delegate; transient Collection> entries; transient Map> asMap; public ConstrainedMultimap(Multimap delegate, MapConstraint constraint) { this.delegate = checkNotNull(delegate); this.constraint = checkNotNull(constraint); } @Override protected Multimap delegate() { return delegate; } @Override public Map> asMap() { Map> result = asMap; if (result == null) { final Map> asMapDelegate = delegate.asMap(); asMap = result = new ForwardingMap>() { Set>> entrySet; Collection> values; @Override protected Map> delegate() { return asMapDelegate; } @Override public Set>> entrySet() { Set>> result = entrySet; if (result == null) { entrySet = result = constrainedAsMapEntries( asMapDelegate.entrySet(), constraint); } return result; } @SuppressWarnings("unchecked") @Override public Collection get(Object key) { try { Collection collection = ConstrainedMultimap.this.get((K) key); return collection.isEmpty() ? null : collection; } catch (ClassCastException e) { return null; // key wasn't a K } } @Override public Collection> values() { Collection> result = values; if (result == null) { values = result = new ConstrainedAsMapValues( delegate().values(), entrySet()); } return result; } @Override public boolean containsValue(Object o) { return values().contains(o); } }; } return result; } @Override public Collection> entries() { Collection> result = entries; if (result == null) { entries = result = constrainedEntries(delegate.entries(), constraint); } return result; } @Override public Collection get(final K key) { return Constraints.constrainedTypePreservingCollection( delegate.get(key), new Constraint() { @Override public V checkElement(V value) { constraint.checkKeyValue(key, value); return value; } }); } @Override public boolean put(K key, V value) { constraint.checkKeyValue(key, value); return delegate.put(key, value); } @Override public boolean putAll(K key, Iterable values) { return delegate.putAll(key, checkValues(key, values, constraint)); } @Override public boolean putAll( Multimap multimap) { boolean changed = false; for (Entry entry : multimap.entries()) { changed |= put(entry.getKey(), entry.getValue()); } return changed; } @Override public Collection replaceValues( K key, Iterable values) { return delegate.replaceValues(key, checkValues(key, values, constraint)); } } /** @see ConstrainedMultimap#asMap */ private static class ConstrainedAsMapValues extends ForwardingCollection> { final Collection> delegate; final Set>> entrySet; /** * @param entrySet map entries, linking each key with its corresponding * values, that already enforce the constraint */ ConstrainedAsMapValues(Collection> delegate, Set>> entrySet) { this.delegate = delegate; this.entrySet = entrySet; } @Override protected Collection> delegate() { return delegate; } @Override public Iterator> iterator() { final Iterator>> iterator = entrySet.iterator(); return new Iterator>() { @Override public boolean hasNext() { return iterator.hasNext(); } @Override public Collection next() { return iterator.next().getValue(); } @Override public void remove() { iterator.remove(); } }; } @Override public Object[] toArray() { return standardToArray(); } @Override public T[] toArray(T[] array) { return standardToArray(array); } @Override public boolean contains(Object o) { return standardContains(o); } @Override public boolean containsAll(Collection c) { return standardContainsAll(c); } @Override public boolean remove(Object o) { return standardRemove(o); } @Override public boolean removeAll(Collection c) { return standardRemoveAll(c); } @Override public boolean retainAll(Collection c) { return standardRetainAll(c); } } /** @see MapConstraints#constrainedEntries */ private static class ConstrainedEntries extends ForwardingCollection> { final MapConstraint constraint; final Collection> entries; ConstrainedEntries(Collection> entries, MapConstraint constraint) { this.entries = entries; this.constraint = constraint; } @Override protected Collection> delegate() { return entries; } @Override public Iterator> iterator() { final Iterator> iterator = entries.iterator(); return new ForwardingIterator>() { @Override public Entry next() { return constrainedEntry(iterator.next(), constraint); } @Override protected Iterator> delegate() { return iterator; } }; } // See Collections.CheckedMap.CheckedEntrySet for details on attacks. @Override public Object[] toArray() { return standardToArray(); } @Override public T[] toArray(T[] array) { return standardToArray(array); } @Override public boolean contains(Object o) { return Maps.containsEntryImpl(delegate(), o); } @Override public boolean containsAll(Collection c) { return standardContainsAll(c); } @Override public boolean remove(Object o) { return Maps.removeEntryImpl(delegate(), o); } @Override public boolean removeAll(Collection c) { return standardRemoveAll(c); } @Override public boolean retainAll(Collection c) { return standardRetainAll(c); } } /** @see MapConstraints#constrainedEntrySet */ static class ConstrainedEntrySet extends ConstrainedEntries implements Set> { ConstrainedEntrySet(Set> entries, MapConstraint constraint) { super(entries, constraint); } // See Collections.CheckedMap.CheckedEntrySet for details on attacks. @Override public boolean equals(@Nullable Object object) { return Sets.equalsImpl(this, object); } @Override public int hashCode() { return Sets.hashCodeImpl(this); } } /** @see MapConstraints#constrainedAsMapEntries */ static class ConstrainedAsMapEntries extends ForwardingSet>> { private final MapConstraint constraint; private final Set>> entries; ConstrainedAsMapEntries(Set>> entries, MapConstraint constraint) { this.entries = entries; this.constraint = constraint; } @Override protected Set>> delegate() { return entries; } @Override public Iterator>> iterator() { final Iterator>> iterator = entries.iterator(); return new ForwardingIterator>>() { @Override public Entry> next() { return constrainedAsMapEntry(iterator.next(), constraint); } @Override protected Iterator>> delegate() { return iterator; } }; } // See Collections.CheckedMap.CheckedEntrySet for details on attacks. @Override public Object[] toArray() { return standardToArray(); } @Override public T[] toArray(T[] array) { return standardToArray(array); } @Override public boolean contains(Object o) { return Maps.containsEntryImpl(delegate(), o); } @Override public boolean containsAll(Collection c) { return standardContainsAll(c); } @Override public boolean equals(@Nullable Object object) { return standardEquals(object); } @Override public int hashCode() { return standardHashCode(); } @Override public boolean remove(Object o) { return Maps.removeEntryImpl(delegate(), o); } @Override public boolean removeAll(Collection c) { return standardRemoveAll(c); } @Override public boolean retainAll(Collection c) { return standardRetainAll(c); } } private static class ConstrainedListMultimap extends ConstrainedMultimap implements ListMultimap { ConstrainedListMultimap(ListMultimap delegate, MapConstraint constraint) { super(delegate, constraint); } @Override public List get(K key) { return (List) super.get(key); } @Override public List removeAll(Object key) { return (List) super.removeAll(key); } @Override public List replaceValues( K key, Iterable values) { return (List) super.replaceValues(key, values); } } private static class ConstrainedSetMultimap extends ConstrainedMultimap implements SetMultimap { ConstrainedSetMultimap(SetMultimap delegate, MapConstraint constraint) { super(delegate, constraint); } @Override public Set get(K key) { return (Set) super.get(key); } @Override public Set> entries() { return (Set>) super.entries(); } @Override public Set removeAll(Object key) { return (Set) super.removeAll(key); } @Override public Set replaceValues( K key, Iterable values) { return (Set) super.replaceValues(key, values); } } private static class ConstrainedSortedSetMultimap extends ConstrainedSetMultimap implements SortedSetMultimap { ConstrainedSortedSetMultimap(SortedSetMultimap delegate, MapConstraint constraint) { super(delegate, constraint); } @Override public SortedSet get(K key) { return (SortedSet) super.get(key); } @Override public SortedSet removeAll(Object key) { return (SortedSet) super.removeAll(key); } @Override public SortedSet replaceValues( K key, Iterable values) { return (SortedSet) super.replaceValues(key, values); } @Override public Comparator valueComparator() { return ((SortedSetMultimap) delegate()).valueComparator(); } } private static Collection checkValues(K key, Iterable values, MapConstraint constraint) { Collection copy = Lists.newArrayList(values); for (V value : copy) { constraint.checkKeyValue(key, value); } return copy; } private static Map checkMap(Map map, MapConstraint constraint) { Map copy = new LinkedHashMap(map); for (Entry entry : copy.entrySet()) { constraint.checkKeyValue(entry.getKey(), entry.getValue()); } return copy; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy