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

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

Go to download

Google Collections Library is a suite of new collections and collection-related goodness for Java 5.0

The newest version!
/*
 * Copyright (C) 2007 Google Inc.
 *
 * 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 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.SortedSet;
import java.util.Map.Entry;
import java.util.Set;

/**
 * Factory and utilities pertaining to the {@code MapConstraint} interface.
 *
 * 

Constraints and collections returned by this class are serializable. * * @see Constraints * @author Mike Bostock */ public final class MapConstraints { private MapConstraints() {} /** * A constraint that verifies that neither the key nor the value is null. If * either is null, a {@link NullPointerException} is thrown. */ public static final MapConstraint NOT_NULL = NotNullMapConstraint.INSTANCE; // enum singleton pattern private enum NotNullMapConstraint implements MapConstraint { INSTANCE; public void checkKeyValue(Object key, Object value) { checkNotNull(key); checkNotNull(value); } @Override public String toString() { return "Not null"; } } /** * Returns a constraint that verifies that the key is an instance of {@code * keyType} and that the value is an instance of {@code valueType}. A {@link * ClassCastException} is thrown otherwise. In addition, the constraint throws * a {@link ClassCastException} is thrown when the key or value is * {@code null}. * * @param keyType the required type for keys * @param valueType the required type for values * @return a constraint which verifies the type of keys and values */ static MapConstraint classConstraint( Class keyType, Class valueType) { return new ClassMapConstraint(keyType, valueType); } /** @see MapConstraints#classConstraint */ private static class ClassMapConstraint implements MapConstraint, Serializable { final Class keyType; final Class valueType; ClassMapConstraint(Class keyType, Class valueType) { this.keyType = checkNotNull(keyType); this.valueType = checkNotNull(valueType); } public void checkKeyValue(Object key, Object value) { keyType.cast(checkNotNull(key)); valueType.cast(checkNotNull(value)); } @Override public String toString() { return "key " + keyType + ", value " + valueType; } static final long serialVersionUID = 5170999662998754707L; } /** * 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. * * @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. * * @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. * * @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. * * @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. * * @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( Entry entry, final MapConstraint constraint) { checkNotNull(entry); checkNotNull(constraint); return new ForwardingMapEntry(entry) { // not Serializable @Override public V setValue(V value) { constraint.checkKeyValue(getKey(), value); return super.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( Entry> entry, final MapConstraint constraint) { checkNotNull(entry); checkNotNull(constraint); return new ForwardingMapEntry>(entry) { // not Serializable @Override public Collection getValue() { return Constraints.constrainedTypePreservingCollection( super.getValue(), new Constraint() { public void checkElement(V value) { constraint.checkKeyValue(getKey(), 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 */ @SuppressWarnings("unchecked") 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. * * @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 { final MapConstraint constraint; private transient volatile Set> entrySet; ConstrainedMap( Map delegate, MapConstraint constraint) { super(delegate); this.constraint = checkNotNull(constraint); } @Override public Set> entrySet() { if (entrySet == null) { entrySet = constrainedEntrySet(super.entrySet(), constraint); } return entrySet; } @Override public V put(K key, V value) { constraint.checkKeyValue(key, value); return super.put(key, value); } @Override public void putAll(Map map) { super.putAll(checkMap(map, constraint)); } private static final long serialVersionUID = 2187468218878274045L; } /** * 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. * * @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 { transient volatile BiMap inverse; ConstrainedBiMap(BiMap delegate, BiMap inverse, MapConstraint constraint) { super(delegate, constraint); this.inverse = inverse; } @SuppressWarnings("unchecked") @Override protected BiMap delegate() { return (BiMap) super.delegate(); } public V forcePut(K key, V value) { constraint.checkKeyValue(key, value); return delegate().forcePut(key, value); } public BiMap inverse() { if (inverse == null) { inverse = new ConstrainedBiMap(delegate().inverse(), this, new InverseConstraint(constraint)); } return inverse; } @Override public Set values() { return delegate().values(); } private static final long serialVersionUID = 0; } /** @see MapConstraints#constrainedBiMap */ private static class InverseConstraint implements MapConstraint, Serializable { final MapConstraint constraint; public InverseConstraint(MapConstraint constraint) { this.constraint = checkNotNull(constraint); } public void checkKeyValue(K key, V value) { constraint.checkKeyValue(value, key); } private static final long serialVersionUID = 4461050635804577699L; } /** @see MapConstraints#constrainedMultimap */ private static class ConstrainedMultimap extends ForwardingMultimap { final MapConstraint constraint; transient volatile Collection> entries; transient volatile Map> asMap; public ConstrainedMultimap(Multimap delegate, MapConstraint constraint) { super(delegate); this.constraint = constraint; } @Override public Map> asMap() { if (asMap == null) { asMap = new ForwardingMap>(delegate().asMap()) { volatile Set>> entrySet; volatile Collection> values; @Override public Set>> entrySet() { if (entrySet == null) { entrySet = constrainedAsMapEntries( super.entrySet(), constraint); } return entrySet; } @SuppressWarnings("unchecked") @Override public Collection get(Object key) { Collection collection = ConstrainedMultimap.this.get((K) key); return collection.isEmpty() ? null : collection; } @Override public Collection> values() { if (values == null) { values = new ConstrainedAsMapValues( delegate().values(), entrySet()); } return values; } @Override public boolean containsValue(Object o) { return values().contains(o); } }; } return asMap; } @Override public Collection> entries() { if (entries == null) { entries = constrainedEntries(super.entries(), constraint); } return entries; } @Override public Collection get(final K key) { return Constraints.constrainedTypePreservingCollection( super.get(key), new Constraint() { public void checkElement(V value) { constraint.checkKeyValue(key, value); } }); } @Override public boolean put(K key, V value) { constraint.checkKeyValue(key, value); return super.put(key, value); } @Override public void putAll(K key, Iterable values) { super.putAll(key, checkValues(key, values, constraint)); } @Override public void putAll(Multimap multimap) { super.putAll(checkMultimap(multimap, constraint)); } @Override public Collection replaceValues( K key, Iterable values) { return super.replaceValues(key, checkValues(key, values, constraint)); } private static final long serialVersionUID = 1022236989881570422L; } /** @see ConstrainedMultimap#asMap */ private static class ConstrainedAsMapValues extends NonSerializableForwardingCollection> { final Set>> entrySet; /** * @param entrySet map entries, linking each key with its corresponding * values, that already enforce the constraint */ ConstrainedAsMapValues(Collection> delegate, Set>> entrySet) { super(delegate); this.entrySet = entrySet; } @Override public Iterator> iterator() { final Iterator>> iterator = entrySet.iterator(); return new Iterator>() { public boolean hasNext() { return iterator.hasNext(); } public Collection next() { return iterator.next().getValue(); } public void remove() { iterator.remove(); } }; } @Override public Object[] toArray() { return ForwardingCollection.toArrayImpl(this); } @Override public T[] toArray(T[] array) { return ForwardingCollection.toArrayImpl(this, array); } @Override public boolean contains(Object o) { return ForwardingCollection.containsImpl(this, o); } @Override public boolean containsAll(Collection c) { return ForwardingCollection.containsAllImpl(this, c); } @Override public boolean remove(Object o) { return ForwardingCollection.removeImpl(this, o); } @Override public boolean removeAll(Collection c) { return ForwardingCollection.removeAllImpl(this, c); } @Override public boolean retainAll(Collection c) { return ForwardingCollection.retainAllImpl(this, c); } } /** @see MapConstraints#constrainedEntries */ private static class ConstrainedEntries // not Serializable extends NonSerializableForwardingCollection> { final MapConstraint constraint; ConstrainedEntries(Collection> entries, MapConstraint constraint) { super(entries); this.constraint = constraint; } @Override public Iterator> iterator() { return new ForwardingIterator>(super.iterator()) { @Override public Entry next() { return constrainedEntry(super.next(), constraint); } }; } // See Collections.CheckedMap.CheckedEntrySet for details on attacks. @Override public Object[] toArray() { return ForwardingCollection.toArrayImpl(this); } @Override public T[] toArray(T[] array) { return ForwardingCollection.toArrayImpl(this, array); } @Override public boolean contains(Object o) { return Maps.containsEntryImpl(delegate(), o); } @Override public boolean containsAll(Collection c) { return ForwardingCollection.containsAllImpl(this, c); } @Override public boolean remove(Object o) { return Maps.removeEntryImpl(delegate(), o); } @Override public boolean removeAll(Collection c) { return ForwardingCollection.removeAllImpl(this, c); } @Override public boolean retainAll(Collection c) { return ForwardingCollection.retainAllImpl(this, c); } } /** @see MapConstraints#constrainedEntrySet */ static class ConstrainedEntrySet // not Serializable extends ConstrainedEntries implements Set> { ConstrainedEntrySet(Set> entries, MapConstraint constraint) { super(entries, constraint); } // See Collections.CheckedMap.CheckedEntrySet for details on attacks. @Override public boolean equals(Object o) { return ForwardingSet.equalsImpl(this, o); } @Override public int hashCode() { return ForwardingSet.hashCodeImpl(this); } } /** @see MapConstraints#constrainedAsMapEntries */ static class ConstrainedAsMapEntries // not Serializable extends NonSerializableForwardingSet>> { private final MapConstraint constraint; ConstrainedAsMapEntries(Set>> entries, MapConstraint constraint) { super(entries); this.constraint = constraint; } @Override public Iterator>> iterator() { return new ForwardingIterator>>(super.iterator()) { @Override public Entry> next() { return constrainedAsMapEntry(super.next(), constraint); } }; } // See Collections.CheckedMap.CheckedEntrySet for details on attacks. @Override public Object[] toArray() { return ForwardingSet.toArrayImpl(this); } @Override public T[] toArray(T[] array) { return ForwardingSet.toArrayImpl(this, array); } @Override public boolean contains(Object o) { return Maps.containsEntryImpl(delegate(), o); } @Override public boolean containsAll(Collection c) { return ForwardingSet.containsAllImpl(this, c); } @Override public boolean equals(Object o) { return ForwardingSet.equalsImpl(this, o); } @Override public int hashCode() { return ForwardingSet.hashCodeImpl(this); } @Override public boolean remove(Object o) { return Maps.removeEntryImpl(delegate(), o); } @Override public boolean removeAll(Collection c) { return ForwardingSet.removeAllImpl(this, c); } @Override public boolean retainAll(Collection c) { return ForwardingSet.retainAllImpl(this, 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 final long serialVersionUID = 0; } 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 final long serialVersionUID = 0; } 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); } public Comparator valueComparator() { return ((SortedSetMultimap) delegate()).valueComparator(); } private static final long serialVersionUID = 0; } 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; } private static Multimap checkMultimap( Multimap map, MapConstraint constraint) { Multimap copy = new LinkedListMultimap(map); for (Entry entry : copy.entries()) { constraint.checkKeyValue(entry.getKey(), entry.getValue()); } return copy; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy