/*
* 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 org.glassfish.jersey.internal.guava;
import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.function.Function;
import java.util.function.Predicate;
import org.glassfish.jersey.internal.guava.Joiner.MapJoiner;
import static org.glassfish.jersey.internal.guava.Preconditions.checkArgument;
import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull;
import static org.glassfish.jersey.internal.guava.Predicates.compose;
/**
* Static utility methods pertaining to {@link Map} instances (including instances of
* {@link SortedMap}, {@link BiMap}, etc.). Also see this class's counterparts
* {@link Lists}, {@link Sets} and {@link Queues}.
* <p>
* <p>See the Guava User Guide article on <a href=
* "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Maps">
* {@code Maps}</a>.
*
* @author Kevin Bourrillion
* @author Mike Bostock
* @author Isaac Shum
* @author Louis Wasserman
* @since 2.0 (imported from Google Collections Library)
*/
public final class Maps {
private static final MapJoiner STANDARD_JOINER =
Collections2.STANDARD_JOINER.withKeyValueSeparator();
private Maps() {
}
@SuppressWarnings("unchecked")
private static <K> Function<Entry<K, ?>, K> keyFunction() {
return (Function) EntryFunction.KEY;
}
@SuppressWarnings("unchecked")
private static <V> Function<Entry<?, V>, V> valueFunction() {
return (Function) EntryFunction.VALUE;
}
private static <K, V> Iterator<K> keyIterator(Iterator<Entry<K, V>> entryIterator) {
return Iterators.transform(entryIterator, Maps.keyFunction());
}
static <K, V> Iterator<V> valueIterator(Iterator<Entry<K, V>> entryIterator) {
return Iterators.transform(entryIterator, Maps.valueFunction());
}
/**
* Creates a {@code HashMap} instance, with a high enough "initial capacity"
* that it <i>should</i> hold {@code expectedSize} elements without growth.
* This behavior cannot be broadly guaranteed, but it is observed to be true
* for OpenJDK 1.6. It also can't be guaranteed that the method isn't
* inadvertently <i>oversizing</i> the returned map.
*
* @param expectedSize the number of elements you expect to add to the
* returned map
* @return a new, empty {@code HashMap} with enough capacity to hold {@code
* expectedSize} elements without resizing
* @throws IllegalArgumentException if {@code expectedSize} is negative
*/
public static <K, V> HashMap<K, V> newHashMapWithExpectedSize(
int expectedSize) {
return new HashMap<K, V>(capacity(expectedSize));
}
/**
* Returns a capacity that is sufficient to keep the map from being resized as
* long as it grows no larger than expectedSize and the load factor is >= its
* default (0.75).
*/
static int capacity(int expectedSize) {
if (expectedSize < 3) {
CollectPreconditions.checkNonnegative(expectedSize, "expectedSize");
return expectedSize + 1;
}
if (expectedSize < Ints.MAX_POWER_OF_TWO) {
return expectedSize + expectedSize / 3;
}
return Integer.MAX_VALUE; // any large value
}
static <K, V> Iterator<Entry<K, V>> asMapEntryIterator(
Set<K> set, final Function<? super K, V> function) {
return new TransformedIterator<K, Entry<K, V>>(set.iterator()) {
@Override
Entry<K, V> transform(final K key) {
return immutableEntry(key, function.apply(key));
}
};
}
private static <E> Set<E> removeOnlySet(final Set<E> set) {
return new ForwardingSet<E>() {
@Override
protected Set<E> delegate() {
return set;
}
@Override
public boolean add(E element) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(Collection<? extends E> es) {
throw new UnsupportedOperationException();
}
};
}
private static <E> SortedSet<E> removeOnlySortedSet(final SortedSet<E> set) {
return new ForwardingSortedSet<E>() {
@Override
protected SortedSet<E> delegate() {
return set;
}
@Override
public boolean add(E element) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(Collection<? extends E> es) {
throw new UnsupportedOperationException();
}
@Override
public SortedSet<E> headSet(E toElement) {
return removeOnlySortedSet(super.headSet(toElement));
}
@Override
public SortedSet<E> subSet(E fromElement, E toElement) {
return removeOnlySortedSet(super.subSet(fromElement, toElement));
}
@Override
public SortedSet<E> tailSet(E fromElement) {
return removeOnlySortedSet(super.tailSet(fromElement));
}
};
}
/**
* Returns an immutable map entry with the specified key and value. The {@link
* Entry#setValue} operation throws an {@link UnsupportedOperationException}.
* <p>
* <p>The returned entry is serializable.
*
* @param key the key to be associated with the returned entry
* @param value the value to be associated with the returned entry
*/
public static <K, V> Entry<K, V> immutableEntry(
K key, V value) {
return new ImmutableEntry<K, V>(key, value);
}
static <K> Predicate<Entry<K, ?>> keyPredicateOnEntries(Predicate<? super K> keyPredicate) {
return compose(keyPredicate, Maps.<K>keyFunction());
}
static <V> Predicate<Entry<?, V>> valuePredicateOnEntries(Predicate<? super V> valuePredicate) {
return compose(valuePredicate, Maps.<V>valueFunction());
}
/**
* Delegates to {@link Map#get}. Returns {@code null} on {@code
* ClassCastException} and {@code NullPointerException}.
*/
static <V> V safeGet(Map<?, V> map, Object key) {
checkNotNull(map);
try {
return map.get(key);
} catch (ClassCastException e) {
return null;
} catch (NullPointerException e) {
return null;
}
}
/**
* Delegates to {@link Map#containsKey}. Returns {@code false} on {@code
* ClassCastException} and {@code NullPointerException}.
*/
static boolean safeContainsKey(Map<?, ?> map, Object key) {
checkNotNull(map);
try {
return map.containsKey(key);
} catch (ClassCastException e) {
return false;
} catch (NullPointerException e) {
return false;
}
}
/**
* Delegates to {@link Map#remove}. Returns {@code null} on {@code
* ClassCastException} and {@code NullPointerException}.
*/
static <V> V safeRemove(Map<?, V> map, Object key) {
checkNotNull(map);
try {
return map.remove(key);
} catch (ClassCastException e) {
return null;
} catch (NullPointerException e) {
return null;
}
}
private enum EntryFunction implements Function<Entry<?, ?>, Object> {
KEY {
@Override
public Object apply(Entry<?, ?> entry) {
return entry.getKey();
}
},
VALUE {
@Override
public Object apply(Entry<?, ?> entry) {
return entry.getValue();
}
}
}
private static class AsMapView<K, V> extends ImprovedAbstractMap<K, V> {
final Function<? super K, V> function;
private final Set<K> set;
AsMapView(Set<K> set, Function<? super K, V> function) {
this.set = checkNotNull(set);
this.function = checkNotNull(function);
}
Set<K> backingSet() {
return set;
}
@Override
public Set<K> createKeySet() {
return removeOnlySet(backingSet());
}
@Override
Collection<V> createValues() {
return Collections2.transform(set, function);
}
@Override
public int size() {
return backingSet().size();
}
@Override
public boolean containsKey(Object key) {
return backingSet().contains(key);
}
@Override
public V get(Object key) {
if (Collections2.safeContains(backingSet(), key)) {
@SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it
K k = (K) key;
return function.apply(k);
} else {
return null;
}
}
@Override
public V remove(Object key) {
if (backingSet().remove(key)) {
@SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it
K k = (K) key;
return function.apply(k);
} else {
return null;
}
}
@Override
public void clear() {
backingSet().clear();
}
@Override
protected Set<Entry<K, V>> createEntrySet() {
return new EntrySet<K, V>() {
@Override
Map<K, V> map() {
return Maps.AsMapView.this;
}
@Override
public Iterator<Entry<K, V>> iterator() {
return asMapEntryIterator(backingSet(), function);
}
};
}
}
/**
* {@code AbstractMap} extension that implements {@link #isEmpty()} as {@code
* entrySet().isEmpty()} instead of {@code size() == 0} to speed up
* implementations where {@code size()} is O(n), and it delegates the {@code
* isEmpty()} methods of its key set and value collection to this
* implementation.
*/
abstract static class ImprovedAbstractMap<K, V> extends AbstractMap<K, V> {
private transient Set<Entry<K, V>> entrySet;
private transient Set<K> keySet;
private transient Collection<V> values;
/**
* Creates the entry set to be returned by {@link #entrySet()}. This method
* is invoked at most once on a given map, at the time when {@code entrySet}
* is first called.
*/
abstract Set<Entry<K, V>> createEntrySet();
@Override
public Set<Entry<K, V>> entrySet() {
Set<Entry<K, V>> result = entrySet;
return (result == null) ? entrySet = createEntrySet() : result;
}
@Override
public Set<K> keySet() {
Set<K> result = keySet;
return (result == null) ? keySet = createKeySet() : result;
}
Set<K> createKeySet() {
return new KeySet<K, V>(this);
}
@Override
public Collection<V> values() {
Collection<V> result = values;
return (result == null) ? values = createValues() : result;
}
Collection<V> createValues() {
return new Values<K, V>(this);
}
}
static class KeySet<K, V> extends Sets.ImprovedAbstractSet<K> {
final Map<K, V> map;
KeySet(Map<K, V> map) {
this.map = checkNotNull(map);
}
Map<K, V> map() {
return map;
}
@Override
public Iterator<K> iterator() {
return keyIterator(map().entrySet().iterator());
}
@Override
public int size() {
return map().size();
}
@Override
public boolean isEmpty() {
return map().isEmpty();
}
@Override
public boolean contains(Object o) {
return map().containsKey(o);
}
@Override
public boolean remove(Object o) {
if (contains(o)) {
map().remove(o);
return true;
}
return false;
}
@Override
public void clear() {
map().clear();
}
}
static class Values<K, V> extends AbstractCollection<V> {
final Map<K, V> map;
Values(Map<K, V> map) {
this.map = checkNotNull(map);
}
final Map<K, V> map() {
return map;
}
@Override
public Iterator<V> iterator() {
return valueIterator(map().entrySet().iterator());
}
@Override
public boolean remove(Object o) {
try {
return super.remove(o);
} catch (UnsupportedOperationException e) {
for (Entry<K, V> entry : map().entrySet()) {
if (Objects.equals(o, entry.getValue())) {
map().remove(entry.getKey());
return true;
}
}
return false;
}
}
@Override
public boolean removeAll(Collection<?> c) {
try {
return super.removeAll(checkNotNull(c));
} catch (UnsupportedOperationException e) {
Set<K> toRemove = Sets.newHashSet();
for (Entry<K, V> entry : map().entrySet()) {
if (c.contains(entry.getValue())) {
toRemove.add(entry.getKey());
}
}
return map().keySet().removeAll(toRemove);
}
}
@Override
public boolean retainAll(Collection<?> c) {
try {
return super.retainAll(checkNotNull(c));
} catch (UnsupportedOperationException e) {
Set<K> toRetain = Sets.newHashSet();
for (Entry<K, V> entry : map().entrySet()) {
if (c.contains(entry.getValue())) {
toRetain.add(entry.getKey());
}
}
return map().keySet().retainAll(toRetain);
}
}
@Override
public int size() {
return map().size();
}
@Override
public boolean isEmpty() {
return map().isEmpty();
}
@Override
public boolean contains(Object o) {
return map().containsValue(o);
}
@Override
public void clear() {
map().clear();
}
}
abstract static class EntrySet<K, V>
extends Sets.ImprovedAbstractSet<Entry<K, V>> {
abstract Map<K, V> map();
@Override
public int size() {
return map().size();
}
@Override
public void clear() {
map().clear();
}
@Override
public boolean contains(Object o) {
if (o instanceof Entry) {
Entry<?, ?> entry = (Entry<?, ?>) o;
Object key = entry.getKey();
V value = Maps.safeGet(map(), key);
return Objects.equals(value, entry.getValue())
&& (value != null || map().containsKey(key));
}
return false;
}
@Override
public boolean isEmpty() {
return map().isEmpty();
}
@Override
public boolean remove(Object o) {
if (contains(o)) {
Entry<?, ?> entry = (Entry<?, ?>) o;
return map().keySet().remove(entry.getKey());
}
return false;
}
@Override
public boolean removeAll(Collection<?> c) {
try {
return super.removeAll(checkNotNull(c));
} catch (UnsupportedOperationException e) {
// if the iterators don't support remove
return Sets.removeAllImpl(this, c.iterator());
}
}
@Override
public boolean retainAll(Collection<?> c) {
try {
return super.retainAll(checkNotNull(c));
} catch (UnsupportedOperationException e) {
// if the iterators don't support remove
Set<Object> keys = Sets.newHashSetWithExpectedSize(c.size());
for (Object o : c) {
if (contains(o)) {
Entry<?, ?> entry = (Entry<?, ?>) o;
keys.add(entry.getKey());
}
}
return map().keySet().retainAll(keys);
}
}
}
}