/**
* Copyright 2008 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 org.waveprotocol.wave.model.util;
import org.waveprotocol.wave.model.util.ReadableStringMap.ProcV;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Set;
/**
* Utilities related to StringMap, StringSet, and CollectionFactory.
*
* @author ohler@google.com (Christian Ohler)
*/
public class CollectionUtils {
private CollectionUtils() {
}
public static final DataDomain<ReadableStringSet, StringSet> STRING_SET_DOMAIN =
new DataDomain<ReadableStringSet, StringSet>() {
@Override
public void compose(StringSet target, ReadableStringSet changes, ReadableStringSet base) {
target.clear();
target.addAll(base);
target.addAll(changes);
}
@Override
public StringSet empty() {
return createStringSet();
}
@Override
public ReadableStringSet readOnlyView(StringSet modifiable) {
return modifiable;
}
};
public static final DataDomain<ReadableStringMap<Object>, StringMap<Object>> STRING_MAP_DOMAIN =
new DataDomain<ReadableStringMap<Object>, StringMap<Object>>() {
@Override
public void compose(StringMap<Object> target, ReadableStringMap<Object> changes,
ReadableStringMap<Object> base) {
target.clear();
target.putAll(base);
target.putAll(changes);
}
@Override
public StringMap<Object> empty() {
return createStringMap();
}
@Override
public ReadableStringMap<Object> readOnlyView(StringMap<Object> modifiable) {
return modifiable;
}
};
@SuppressWarnings("unchecked")
public static <T> DataDomain<StringMap<T>, StringMap<T>> stringMapDomain() {
return (DataDomain) STRING_MAP_DOMAIN;
}
@SuppressWarnings("unchecked")
public static <T> DataDomain<Set<T>, Set<T>> hashSetDomain() {
return (DataDomain) HASH_SET_DOMAIN;
}
public static final DataDomain<Set<Object>, Set<Object>> HASH_SET_DOMAIN =
new DataDomain<Set<Object>, Set<Object>>() {
@Override
public void compose(Set<Object> target, Set<Object> changes, Set<Object> base) {
target.clear();
target.addAll(changes);
target.addAll(base);
}
@Override
public Set<Object> empty() {
return new HashSet<Object>();
}
@Override
public Set<Object> readOnlyView(Set<Object> modifiable) {
return Collections.unmodifiableSet(modifiable);
}
};
/**
* An adapter that turns a java.util.Map<String, V> into a StringMap<V>.
*
* @author ohler@google.com (Christian Ohler)
*
* @param <V> type of values in the map
*/
private static final class StringMapAdapter<V> implements StringMap<V> {
private final Map<String, V> backend;
private StringMapAdapter(Map<String, V> backend) {
Preconditions.checkNotNull(backend, "Attempt to adapt a null map");
this.backend = backend;
}
@Override
public void putAll(ReadableStringMap<V> pairsToAdd) {
// TODO(ohler): check instanceof here and implement a fallback.
backend.putAll(((StringMapAdapter<V>) pairsToAdd).backend);
}
@Override
public void putAll(Map<String, V> sourceMap) {
Preconditions.checkArgument(!sourceMap.containsKey(null),
"Source map must not contain a null key");
backend.putAll(sourceMap);
}
@Override
public void clear() {
backend.clear();
}
@Override
public void put(String key, V value) {
Preconditions.checkNotNull(key, "StringMap cannot contain null keys");
backend.put(key, value);
}
@Override
public void remove(String key) {
Preconditions.checkNotNull(key, "StringMap cannot contain null keys");
backend.remove(key);
}
@Override
public boolean containsKey(String key) {
Preconditions.checkNotNull(key, "StringMap cannot contain null keys");
return backend.containsKey(key);
}
@Override
public V getExisting(String key) {
Preconditions.checkNotNull(key, "StringMap cannot contain null keys");
if (!backend.containsKey(key)) {
// Not using Preconditions.checkState to avoid unecessary string concatenation
throw new IllegalStateException("getExisting: Key '" + key + "' is not in map");
}
return backend.get(key);
}
@Override
public V get(String key) {
Preconditions.checkNotNull(key, "StringMap cannot contain null keys");
return backend.get(key);
}
@Override
public V get(String key, V defaultValue) {
Preconditions.checkNotNull(key, "StringMap cannot contain null keys");
if (backend.containsKey(key)) {
return backend.get(key);
} else {
return defaultValue;
}
}
@Override
public boolean isEmpty() {
return backend.isEmpty();
}
@Override
public void each(ProcV<? super V> callback) {
for (Map.Entry<String, V> entry : backend.entrySet()) {
callback.apply(entry.getKey(), entry.getValue());
}
}
@Override
public void filter(EntryFilter<? super V> filter) {
for (Iterator<Map.Entry<String, V>> iterator = backend.entrySet().iterator();
iterator.hasNext();) {
Map.Entry<String, V> entry = iterator.next();
if (filter.apply(entry.getKey(), entry.getValue())) {
// entry stays
} else {
iterator.remove();
}
}
}
@Override
public int countEntries() {
return backend.size();
}
@Override
public String someKey() {
return isEmpty() ? null : backend.keySet().iterator().next();
}
@Override
public ReadableStringSet keySet() {
return new StringSetAdapter(backend.keySet());
}
@Override
public String toString() {
return backend.toString();
}
// NOTE(patcoleman): equals() and hashCode() should not be implemented in this adaptor, as
// they are unsupported in the javascript collections.
}
/**
* An adapter that turns a java.util.Map<Double, V> into a NumberMap<V>.
*
* @param <V> type of values in the map
*/
private static final class NumberMapAdapter<V> implements NumberMap<V> {
private final Map<Double, V> backend;
private NumberMapAdapter(Map<Double, V> backend) {
Preconditions.checkNotNull(backend, "Attempt to adapt a null map");
this.backend = backend;
}
@Override
public void putAll(ReadableNumberMap<V> pairsToAdd) {
// TODO(ohler): check instanceof here and implement a fallback.
backend.putAll(((NumberMapAdapter<V>) pairsToAdd).backend);
}
@Override
public void putAll(Map<Double, V> sourceMap) {
backend.putAll(sourceMap);
}
@Override
public void clear() {
backend.clear();
}
@Override
public void put(double key, V value) {
backend.put(key, value);
}
@Override
public void remove(double key) {
backend.remove(key);
}
@Override
public boolean containsKey(double key) {
return backend.containsKey(key);
}
@Override
public V getExisting(double key) {
assert backend.containsKey(key);
return backend.get(key);
}
@Override
public V get(double key) {
return backend.get(key);
}
@Override
public V get(double key, V defaultValue) {
if (backend.containsKey(key)) {
return backend.get(key);
} else {
return defaultValue;
}
}
@Override
public boolean isEmpty() {
return backend.isEmpty();
}
@Override
public void each(ProcV<V> callback) {
for (Map.Entry<Double, V> entry : backend.entrySet()) {
callback.apply(entry.getKey(), entry.getValue());
}
}
@Override
public void filter(EntryFilter<V> filter) {
for (Iterator<Map.Entry<Double, V>> iterator = backend.entrySet().iterator();
iterator.hasNext();) {
Map.Entry<Double, V> entry = iterator.next();
if (filter.apply(entry.getKey(), entry.getValue())) {
// entry stays
} else {
iterator.remove();
}
}
}
@Override
public int countEntries() {
return backend.size();
}
@Override
public String toString() {
return backend.toString();
}
// NOTE(patcoleman): equals() and hashCode() should not be implemented in this adaptor, as
// they are unsupported in the javascript collections.
}
/**
* An adapter that turns a java.util.Map<Integer, V> into an IntMap<V>.
*
* @param <V> type of values in the map
*/
private static final class IntMapAdapter<V> implements IntMap<V> {
private final Map<Integer, V> backend;
private IntMapAdapter(Map<Integer, V> backend) {
Preconditions.checkNotNull(backend, "Attempt to adapt a null map");
this.backend = backend;
}
@Override
public void putAll(ReadableIntMap<V> pairsToAdd) {
// TODO(ohler): check instanceof here and implement a fallback.
backend.putAll(((IntMapAdapter<V>) pairsToAdd).backend);
}
@Override
public void putAll(Map<Integer, V> sourceMap) {
backend.putAll(sourceMap);
}
@Override
public void clear() {
backend.clear();
}
@Override
public void put(int key, V value) {
backend.put(key, value);
}
@Override
public void remove(int key) {
backend.remove(key);
}
@Override
public boolean containsKey(int key) {
return backend.containsKey(key);
}
@Override
public V getExisting(int key) {
assert backend.containsKey(key);
return backend.get(key);
}
@Override
public V get(int key) {
return backend.get(key);
}
@Override
public V get(int key, V defaultValue) {
if (backend.containsKey(key)) {
return backend.get(key);
} else {
return defaultValue;
}
}
@Override
public boolean isEmpty() {
return backend.isEmpty();
}
@Override
public void each(ProcV<V> callback) {
for (Map.Entry<Integer, V> entry : backend.entrySet()) {
callback.apply(entry.getKey(), entry.getValue());
}
}
@Override
public void filter(EntryFilter<V> filter) {
for (Iterator<Map.Entry<Integer, V>> iterator = backend.entrySet().iterator();
iterator.hasNext();) {
Map.Entry<Integer, V> entry = iterator.next();
if (filter.apply(entry.getKey(), entry.getValue())) {
// entry stays
} else {
iterator.remove();
}
}
}
@Override
public int countEntries() {
return backend.size();
}
@Override
public String toString() {
return backend.toString();
}
// NOTE(patcoleman): equals() and hashCode() should not be implemented in this adaptor, as
// they are unsupported in the javascript collections.
}
/**
* An adapter that turns a java.util.Set<String> into a StringSet.
*
* @author ohler@google.com (Christian Ohler)
*/
private static class StringSetAdapter implements StringSet {
private final Set<String> backend;
private StringSetAdapter(Set<String> backend) {
Preconditions.checkNotNull(backend, "Attempt to adapt a null set");
this.backend = backend;
}
@Override
public void add(String s) {
Preconditions.checkNotNull(s, "StringSet cannot contain null values");
backend.add(s);
}
@Override
public void clear() {
backend.clear();
}
@Override
public boolean contains(String s) {
Preconditions.checkNotNull(s, "StringSet cannot contain null values");
return backend.contains(s);
}
@Override
public void remove(String s) {
Preconditions.checkNotNull(s, "StringSet cannot contain null values");
backend.remove(s);
}
@Override
public boolean isEmpty() {
return backend.isEmpty();
}
@Override
public void each(ReadableStringSet.Proc callback) {
for (String s : backend) {
callback.apply(s);
}
}
@Override
public boolean isSubsetOf(Set<String> set) {
return set.containsAll(backend);
}
@Override
public boolean isSubsetOf(final ReadableStringSet other) {
for (String s : backend) {
if (!other.contains(s)) {
return false;
}
}
return true;
}
@Override
public void addAll(ReadableStringSet set) {
backend.addAll(((StringSetAdapter) set).backend);
}
@Override
public void removeAll(ReadableStringSet set) {
backend.removeAll(((StringSetAdapter) set).backend);
}
@Override
public void filter(StringPredicate filter) {
for (Iterator<String> iterator = backend.iterator(); iterator.hasNext();) {
String x = iterator.next();
if (filter.apply(x)) {
// entry stays
} else {
iterator.remove();
}
}
}
@Override
public String someElement() {
return isEmpty() ? null : backend.iterator().next();
}
@Override
public String toString() {
return backend.toString();
}
@Override
public int countEntries() {
return backend.size();
}
}
/**
* An adapter that wraps a {@link IdentityHashMap}, presenting it as an
* {@link IdentitySet}.
*/
private static class IdentitySetAdapter<T> implements IdentitySet<T> {
private final Map<T, T> backend = new IdentityHashMap<T, T>();
private IdentitySetAdapter() {
}
@Override
public void add(T x) {
Preconditions.checkNotNull(x, "IdentitySet cannot contain null values");
// Note: Boxed primitives, and String, are disallowed. There are special
// purpose maps for those key types, and the equality semantics between
// the boxed primitives of Javascript and Java are dubious at best.
if (x instanceof String || x instanceof Integer || x instanceof Double || x instanceof Long
|| x instanceof Boolean) {
throw new UnsupportedOperationException(
"Should NOT use boxed primitives with IdentitySet");
}
backend.put(x, x);
}
@Override
public void clear() {
backend.clear();
}
@Override
public boolean contains(T s) {
Preconditions.checkNotNull(s, "IdentitySet cannot contain null values");
return backend.containsKey(s);
}
@Override
public void remove(T s) {
Preconditions.checkNotNull(s, "IdentitySet cannot contain null values");
backend.remove(s);
}
@Override
public boolean isEmpty() {
return backend.isEmpty();
}
@Override
public T someElement() {
for (T e : backend.keySet()) {
return e;
}
return null;
}
@Override
public void each(Proc<? super T> procedure) {
for (T s : backend.keySet()) {
procedure.apply(s);
}
}
@Override
public String toString() {
return backend.toString();
}
@Override
public int countEntries() {
return backend.size();
}
}
private static class NumberPriorityQueueAdapter implements NumberPriorityQueue {
private final Queue<Double> queue;
private NumberPriorityQueueAdapter(Queue<Double> queue) {
this.queue = queue;
}
@Override
public boolean offer(double e) {
return queue.offer(e);
}
@Override
public double peek() {
return queue.peek();
}
@Override
public double poll() {
return queue.poll();
}
@Override
public int size() {
return queue.size();
}
}
/**
* An adapter that wraps a java.util.IdentityHashMap<K, V> into an
* IdentityMap<K, V>. Note that this is a simple map, so 'identity' is defined
* by the hashCode/equals of K instances.
*
* @param <K> type of keys in the map.
* @param <V> type of values in the map
*/
private static class IdentityHashMapAdapter<K, V> implements IdentityMap<K, V> {
private final Map<K, V> backend = new IdentityHashMap<K, V>();
private IdentityHashMapAdapter() {
}
@Override
public V get(K key) {
return backend.get(key);
}
@Override
public boolean has(K key) {
return backend.containsKey(key);
}
@Override
public void put(K key, V value) {
// Note: Boxed primitives, and String, are disallowed. See explanation in
// IdentitySetAdapter.
if (key instanceof String || key instanceof Integer || key instanceof Double
|| key instanceof Long || key instanceof Boolean) {
throw new UnsupportedOperationException(
"Should NOT use boxed primitives as key with identity map");
}
backend.put(key, value);
}
@Override
public void remove(K key) {
removeAndReturn(key);
}
@Override
public V removeAndReturn(K key) {
return backend.remove(key);
}
@Override
public void clear() {
backend.clear();
}
@Override
public boolean isEmpty() {
return backend.isEmpty();
}
@Override
public void each(ProcV<? super K, ? super V> proc) {
for (Map.Entry<K, V> entry : backend.entrySet()) {
proc.apply(entry.getKey(), entry.getValue());
}
}
@Override
public <R> R reduce(R initial, Reduce<? super K, ? super V, R> proc) {
R reduction = initial;
for (Map.Entry<K, V> entry : backend.entrySet()) {
reduction = proc.apply(reduction, entry.getKey(), entry.getValue());
}
return reduction;
}
@Override
public String toString() {
return backend.toString();
}
@Override
public int countEntries() {
return backend.size();
}
// NOTE(patcoleman): equals() and hashCode() should not be implemented in this adaptor, as
// they are unsupported in the javascript collections.
}
/**
* An implementation of CollectionFactory based on java.util.HashSet and
* java.util.HashMap.
*
* @author ohler@google.com (Christian Ohler)
*/
private static class HashCollectionFactory implements CollectionFactory {
@Override
public <V> StringMap<V> createStringMap() {
return CollectionUtils.adaptStringMap(new HashMap<String, V>());
}
@Override
public <V> NumberMap<V> createNumberMap() {
return CollectionUtils.adaptNumberMap(new HashMap<Double, V>());
}
@Override
public <V> IntMap<V> createIntMap() {
return CollectionUtils.adaptIntMap(new HashMap<Integer, V>());
}
@Override
public StringSet createStringSet() {
return CollectionUtils.adaptStringSet(new HashSet<String>());
}
@Override
public <T> IdentitySet<T> createIdentitySet() {
return new IdentitySetAdapter<T>();
}
@Override
public <E> Queue<E> createQueue() {
return new LinkedList<E>();
}
@Override
public NumberPriorityQueue createPriorityQueue() {
return CollectionUtils.adaptNumberPriorityQueue(new PriorityQueue<Double>());
}
@Override
public <K, V> IdentityMap<K, V> createIdentityMap() {
return new IdentityHashMapAdapter<K, V>();
}
}
private static final HashCollectionFactory HASH_COLLECTION_FACTORY =
new HashCollectionFactory();
private static CollectionFactory defaultCollectionFactory = HASH_COLLECTION_FACTORY;
/**
* Implements a persistently empty string map that throws exceptions on
* attempt to add keys.
*/
private static final class EmptyStringMap<V> implements StringMap<V> {
@Override
public void clear() {
// Success as the map is already empty.
}
@Override
public void filter(StringMap.EntryFilter<? super V> filter) {
}
@Override
public void put(String key, V value) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(ReadableStringMap<V> pairsToAdd) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(Map<String, V> sourceMap) {
throw new UnsupportedOperationException();
}
@Override
public void remove(String key) {
}
@Override
public boolean containsKey(String key) {
return false;
}
@Override
public int countEntries() {
return 0;
}
@Override
public void each(org.waveprotocol.wave.model.util.ReadableStringMap.ProcV<? super V> callback) {
}
@Override
public V get(String key, V defaultValue) {
return null;
}
@Override
public V get(String key) {
return null;
}
@Override
public V getExisting(String key) {
throw new UnsupportedOperationException();
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public String someKey() {
return null;
}
@Override
public ReadableStringSet keySet() {
// TODO(danilatos/ohler): Implement an immutable EMPTY_SET
return CollectionUtils.createStringSet();
}
}
private static final EmptyStringMap<Object> EMPTY_MAP = new EmptyStringMap<Object>();
private static final IdentityMap<Object, Object> EMPTY = new IdentityMap<Object, Object>() {
@Override
public void clear() {
}
@Override
public int countEntries() {
return 0;
}
@Override
public void each(ProcV<Object, Object> proc) {
}
@Override
public Object get(Object key) {
return null;
}
@Override
public boolean has(Object key) {
return false;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public void put(Object key, Object value) {
throw new UnsupportedOperationException();
}
@Override
public <R> R reduce(R initial, Reduce<Object, Object, R> proc) {
return initial;
}
@Override
public void remove(Object key) {
throw new UnsupportedOperationException();
}
@Override
public Object removeAndReturn(Object key) {
throw new UnsupportedOperationException();
}
};
//
// Plain old collections.
//
/**
* Creates an empty {@code HashSet}.
*/
public static <E> HashSet<E> newHashSet() {
return new HashSet<E>();
}
/**
* Creates a {@code HashSet} instance containing the given elements.
*
* @param elements the elements that the set should contain
* @return a newly created {@code HashSet} containing those elements.
*/
public static <E> HashSet<E> newHashSet(E... elements) {
int capacity = Math.max((int) (elements.length / .75f) + 1, 16);
HashSet<E> set = new HashSet<E>(capacity);
Collections.addAll(set, elements);
return set;
}
/**
* Creates a {@code HashSet} instance containing the given elements.
*
* @param elements the elements that the set should contain
* @return a newly created {@code HashSet} containing those elements.
*/
public static <E> HashSet<E> newHashSet(Collection<? extends E> elements) {
return new HashSet<E>(elements);
}
/**
* Creates an empty immutable set.
*
* @return a newly created set containing those elements.
*/
public static <E> Set<E> immutableSet() {
// TODO(anorth): optimise to a truly immutable set.
return Collections.unmodifiableSet(CollectionUtils.<E>newHashSet());
}
/**
* Creates an immutable set containing the given elements.
*
* @param elements the elements that the set should contain
* @return a newly created set containing those elements.
*/
public static <E> Set<E> immutableSet(Collection<? extends E> elements) {
// TODO(anorth): optimise to a truly immutable set.
return Collections.unmodifiableSet(newHashSet(elements));
}
/**
* Creates an immutable set containing the given elements.
*
* @param elements the elements that the set should contain
* @return a newly created set containing those elements.
*/
public static <E> Set<E> immutableSet(E... elements) {
// TODO(anorth): optimise to a truly immutable set.
return Collections.unmodifiableSet(newHashSet(elements));
}
/** Creates an empty {@link HashMap}. */
public static <K, V> HashMap<K, V> newHashMap() {
return new HashMap<K, V>();
}
/**
* Creates a {@link HashMap} containing the elements in the given map.
*/
public static <K, V> HashMap<K, V> newHashMap(Map<? extends K, ? extends V> map) {
return new HashMap<K, V>(map);
}
/** Creates a new immutable map with one entry. */
public static <K, V> Map<K, V> immutableMap(K k1, V v1) {
// TODO(anorth): optimise to a truly immutable map.
return Collections.singletonMap(k1, v1);
}
/** Creates a new immutable map with the given entries. */
public static <K, V> Map<K, V> immutableMap(K k1, V v1, K k2, V v2) {
Map<K, V> map = newHashMap();
map.put(k1, v1);
map.put(k2, v2);
return Collections.unmodifiableMap(map);
}
/** Creates a new, empty linked list. */
public static <T> LinkedList<T> newLinkedList() {
return new LinkedList<T>();
}
/** Creates a new linked list containing elements provided by an iterable. */
public static <T> LinkedList<T> newLinkedList(Iterable<? extends T> elements) {
LinkedList<T> list = newLinkedList();
for (T e : elements) {
list.add(e);
}
return list;
}
/** Creates a new linked list containing the provided elements. */
public static <T> LinkedList<T> newLinkedList(T... elements) {
return newLinkedList(Arrays.asList(elements));
}
/** Creates a new, empty array list. */
public static <T> ArrayList<T> newArrayList() {
return new ArrayList<T>();
}
/** Creates a new array list containing elements provided by an iterable. */
public static <T> ArrayList<T> newArrayList(Iterable<? extends T> elements) {
ArrayList<T> list = newArrayList();
for (T e : elements) {
list.add(e);
}
return list;
}
/** Creates a new array list containing the provided elements. */
public static <T> ArrayList<T> newArrayList(T... elements) {
return newArrayList(Arrays.asList(elements));
}
//
// String-based collections.
//
/**
* Sets the default collection factory.
*
* This is used in the GWT client initialization code to plug in the JSO-based
* collection factory. There shouldn't be any need to call this from other
* places.
*/
public static void setDefaultCollectionFactory(CollectionFactory f) {
defaultCollectionFactory = f;
}
/**
* Returns a CollectionFactory based on HashSet and HashMap from java.util.
*
* Note: getCollectionFactory() is probably a better choice.
*/
public static CollectionFactory getHashCollectionFactory() {
return HASH_COLLECTION_FACTORY;
}
/**
* Returns the default CollectionFactory.
*/
public static CollectionFactory getCollectionFactory() {
return defaultCollectionFactory;
}
/**
* Creates a new StringMap using the default collection factory.
*/
public static <V> StringMap<V> createStringMap() {
return CollectionUtils.getCollectionFactory().createStringMap();
}
/**
* @returns an immutable empty map object. Always reuses the same object, does
* not create new ones.
*/
@SuppressWarnings("unchecked")
public static <V> StringMap<V> emptyMap() {
return (StringMap<V>) EMPTY_MAP;
}
/**
* @returns an immutable empty map object. Always reuses the same object, does
* not create new ones.
*/
@SuppressWarnings("unchecked")
public static <K, V> IdentityMap<K, V> emptyIdentityMap() {
return (IdentityMap<K, V>) EMPTY;
}
/**
* Creates a new NumberMap using the default collection factory.
*/
public static <V> NumberMap<V> createNumberMap() {
return CollectionUtils.getCollectionFactory().createNumberMap();
}
/**
* Creates a new NumberMap using the default collection factory.
*/
public static <V> IntMap<V> createIntMap() {
return CollectionUtils.getCollectionFactory().createIntMap();
}
/**
* Creates a new queue using the default collection factory.
*/
public static <V> Queue<V> createQueue() {
return CollectionUtils.getCollectionFactory().createQueue();
}
/**
* Creates a new priority queue using the default collection factory.
*/
public static NumberPriorityQueue createPriorityQueue() {
return CollectionUtils.getCollectionFactory().createPriorityQueue();
}
/**
* Creates a new IdentityMap using the default collection factory.
*/
public static <K, V> IdentityMap<K, V> createIdentityMap() {
return CollectionUtils.getCollectionFactory().createIdentityMap();
}
/**
* Creates a new IdentitySet using the default collection factory.
*/
public static <V> IdentitySet<V> createIdentitySet() {
return CollectionUtils.getCollectionFactory().createIdentitySet();
}
/**
* Creates a new, immutable, singleton IdentitySet.
*/
public static <V> ReadableIdentitySet<V> singleton(final V value) {
Preconditions.checkNotNull(value, "Can not create singleton of null");
return new ReadableIdentitySet<V>() {
@Override
public boolean contains(V s) {
// Note that == is used, not .equals(), because this is an identity set.
return value == s;
}
@Override
public int countEntries() {
return 1;
}
@Override
public void each(Proc<? super V> procedure) {
procedure.apply(value);
}
@Override
public V someElement() {
return value;
}
@Override
public boolean isEmpty() {
return false;
}
};
}
/**
* Creates a new StringSet using the default collection factory.
*/
public static StringSet createStringSet() {
return getCollectionFactory().createStringSet();
}
public static <V> StringMap<V> copyStringMap(ReadableStringMap<V> m) {
StringMap<V> copy = createStringMap();
copy.putAll(m);
return copy;
}
public static StringSet copyStringSet(ReadableStringSet s) {
StringSet copy = createStringSet();
copy.addAll(s);
return copy;
}
/**
* Adds all entries from the source map to the target map.
*
* @return the target map, for convenience
*/
public static <V, M extends Map<String, V>> M copyToJavaMap(ReadableStringMap<V> source,
final M target) {
source.each(new StringMap.ProcV<V>() {
@Override
public void apply(String key, V value) {
target.put(key, value);
}
});
return target;
}
/**
* Adds all entries from the source map to the target map. NOTE(patcoleman):
* please only call from assertions/testing code. Ideally everything should be
* ignorant of the java.util.Map implementations as the collection API here
* becomes more useful.
*
* @return java.util.Map version of our IdentityMap
*/
public static <K, V> Map<K, V> copyToJavaIdentityMapForTesting(IdentityMap<K, V> source) {
final Map<K, V> result = new IdentityHashMap<K, V>();
source.each(new IdentityMap.ProcV<K, V>() {
@Override
public void apply(K key, V value) {
result.put(key, value);
}
});
return result;
}
/**
* Creates a new java set with the same contents as the source StringSet.
*/
public static <V> Map<String, V> newJavaMap(ReadableStringMap<V> source) {
return copyToJavaMap(source, new HashMap<String, V>());
}
/**
* Adds all elements from the source set to the target collection.
*
* @return the target collection, for convenience
*/
public static <C extends Collection<String>> C copyToJavaCollection(
ReadableStringSet source, final C target) {
source.each(new StringSet.Proc() {
@Override
public void apply(String element) {
target.add(element);
}
});
return target;
}
/**
* Adds all values from the source map to the target collection.
*
* @return the target collection, for convenience
*/
public static <T, C extends Collection<T>> C copyValuesToJavaCollection(
ReadableStringMap<T> source, final C target) {
source.each(new StringMap.ProcV<T>() {
@Override
public void apply(String key, T value) {
target.add(value);
}
});
return target;
}
/**
* Creates a new java set with the same contents as the source StringSet.
*/
public static Set<String> newJavaSet(ReadableStringSet source) {
return copyToJavaCollection(source, new HashSet<String>());
}
/**
* Creates a new java list with the same contents as the source StringSet.
*/
public static List<String> newJavaList(ReadableStringSet source) {
return copyToJavaCollection(source, new ArrayList<String>());
}
/**
* Creates a new java list with the same contents as the values of the source
* StringMap.
*/
public static <T> List<T> newJavaList(ReadableStringMap<T> source) {
return copyValuesToJavaCollection(source, new ArrayList<T>());
}
/**
* Returns a StringMap view of the specified map.
*/
public static <V> StringMap<V> adaptStringMap(Map<String, V> a) {
return new StringMapAdapter<V>(a);
}
/**
* Returns a StringMap view of the specified map.
*/
public static <V> NumberMap<V> adaptNumberMap(Map<Double, V> a) {
return new NumberMapAdapter<V>(a);
}
/**
* Returns a StringMap view of the specified map.
*/
public static <V> IntMap<V> adaptIntMap(Map<Integer, V> a) {
return new IntMapAdapter<V>(a);
}
/**
* Returns a StringSet view of the specified set.
*/
public static StringSet adaptStringSet(Set<String> a) {
return new StringSetAdapter(a);
}
/**
* Returns a NumberPriorityQueue adaptor of a regular java.util.PriorityQueue
*/
public static NumberPriorityQueue adaptNumberPriorityQueue(PriorityQueue<Double> priorityQueue) {
return new NumberPriorityQueueAdapter(priorityQueue);
}
/**
* Returns a StringSet copy of the specified set.
*/
public static StringSet newStringSet(Set<String> a) {
StringSet s = createStringSet();
for (String value : a) {
s.add(value);
}
return s;
}
/**
* Returns a StringSet consisting of the specified values, removing duplicates
*/
public static StringSet newStringSet(String... values) {
StringSet s = createStringSet();
for (String value : values) {
s.add(value);
}
return s;
}
/**
* Returns a StringMap consisting of the specified key-value pairs
*/
public static StringMap<String> newStringMap(String... pairs) {
Preconditions.checkArgument(pairs.length % 2 == 0, "newStringMap: pairs must have even length");
StringMap<String> m = createStringMap();
for (int i = 0; i < pairs.length; i += 2) {
m.put(pairs[i], pairs[i + 1]);
}
return m;
}
/**
* Returns a list containing all the values of the given string map. The result
* will be a copy not backed by the map so it is safe to modify the map while
* concurrently iterating the list.
*/
public static <T> List<T> valueList(ReadableStringMap<T> map) {
final List<T> result = newArrayList();
map.each(new ProcV<T>() {
public void apply(String key, T value) {
result.add(value);
}
});
return result;
}
/**
* Joins an array of strings with the given separator
*/
public static String join(char separator, String first, String... rest) {
StringBuilder ret = new StringBuilder(first);
for (int i = 0; i < rest.length; i++) {
ret.append(separator);
ret.append(rest[i]);
}
return ret.toString();
}
/**
* Joins an array of strings with the given separator
*/
public static String join(char separator, String... parts) {
StringBuilder ret = new StringBuilder();
if (parts.length > 0) {
ret.append(parts[0]);
}
for (int i = 1; i < parts.length; i++) {
ret.append(separator);
ret.append(parts[i]);
}
return ret.toString();
}
/**
* Joins an array of strings.
*/
public static String join(String... parts) {
StringBuilder ret = new StringBuilder();
if (parts.length > 0) {
ret.append(parts[0]);
}
for (int i = 1; i < parts.length; i++) {
ret.append(parts[i]);
}
return ret.toString();
}
public static String repeat(char component, int repeat) {
Preconditions.checkArgument(repeat >= 0, "Cannot have negative repeat");
char[] chars = new char[repeat];
Arrays.fill(chars, component);
return String.valueOf(chars);
}
}