/*
* Copyright 2008-2017 the original author or 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 griffon.util;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import static java.util.Objects.requireNonNull;
/**
* <p>Utility class that simplifies creating collections in Java.</p>
* <p><strong>Creating Maps</strong><br/>
* <pre>
* Map<String, Object> m = map()
* .e("foo", foo)
* .e("bar", bar);
* </pre></p>
*
* <p><strong>Creating Lists</strong><br/>
* <pre>
* List<String> l = list()
* .e("foo")
* .e("bar");
* </pre></p>
*
* <p><strong>Creating Maps</strong><br/>
* <pre>
* Set<String> s = set()
* .e("foo")
* .e("bar");
* </pre></p>
*
* @author Andres Almiray
* @since 2.0.0
*/
public final class CollectionUtils {
private static final String ERROR_MAP_NULL = "Argument 'map' must not be null";
public static <T> List<T> reverse(List<T> input) {
List<T> output = new ArrayList<>(input);
Collections.reverse(output);
return output;
}
public static <T> List<T> reverse(Collection<T> input) {
List<T> output = new ArrayList<>(input);
Collections.reverse(output);
return output;
}
@SuppressWarnings({"rawtypes", "unchecked"})
public static <K, V> Map newMap(Object... keysAndValues) {
if (keysAndValues == null) {
return Collections.emptyMap();
}
if (keysAndValues.length % 2 == 1) {
throw new IllegalArgumentException("Must have an even number of keys and values");
}
Map<K, V> map = new HashMap<>();
for (int i = 0; i < keysAndValues.length; i += 2) {
map.put((K) keysAndValues[i], (V) keysAndValues[i + 1]);
}
return map;
}
@SafeVarargs
public static <T> Set<T> newSet(T... values) {
if (values == null) {
return Collections.emptySet();
}
return new HashSet<>(Arrays.asList(values));
}
@SafeVarargs
public static <T> List<T> newList(T... values) {
if (values == null) {
return Collections.emptyList();
}
return new ArrayList<>(Arrays.asList(values));
}
public static <K, V> MapBuilder<K, V> map() {
return map(new LinkedHashMap<K, V>());
}
public static <K, V> MapBuilder<K, V> map(Map<K, V> delegate) {
return new MapBuilder<>(delegate);
}
public static <E> ListBuilder<E> list() {
return list(new ArrayList<E>());
}
public static <E> ListBuilder<E> list(List<E> delegate) {
return new ListBuilder<>(delegate);
}
public static <E> SetBuilder<E> set() {
return set(new HashSet<E>());
}
public static <E> SetBuilder<E> set(Set<E> delegate) {
return new SetBuilder<>(delegate);
}
/**
* Returns an adapted Map as a Properties instance.
* <p>
* The Map is used live, which means changes made to it will affect the
* Properties instance directly.
*
* @param map the Map instance to adapt as a Properties instance
*
* @return a new Properties instance backed by the supplied Map.
*
* @since 2.1.0
*/
@Nonnull
public static Properties toProperties(@Nonnull Map<String, Object> map) {
requireNonNull(map, ERROR_MAP_NULL);
return new MapToPropertiesAdapter(map);
}
/**
* Creates a Properties instances based on the given Map.
*
* @param map the Map instance to convert as a Properties instance
*
* @return a new Properties instance based by the supplied Map.
*
* @since 2.10.0
*/
@Nonnull
public static Properties toPropertiesDeep(@Nonnull Map<String, Object> map) {
requireNonNull(map, ERROR_MAP_NULL);
Properties properties = new Properties();
for (Map.Entry<String, Object> e : map.entrySet()) {
createKey(properties, e.getKey(), e.getValue());
}
return properties;
}
@SuppressWarnings("unchecked")
private static void createKey(@Nonnull Properties properties, @Nonnull String key, @Nonnull Object value) {
if (value instanceof Map) {
Map<String, Object> map = (Map<String, Object>) value;
for (Map.Entry<String, Object> e : map.entrySet()) {
createKey(properties, key + "." + e.getKey(), e.getValue());
}
} else {
properties.put(key, value);
}
}
public static class MapBuilder<K, V> implements Map<K, V> {
private final Map<K, V> delegate;
public MapBuilder(Map<K, V> delegate) {
this.delegate = delegate;
}
public MapBuilder<K, V> e(K k, V v) {
delegate.put(k, v);
return this;
}
public int size() {
return delegate.size();
}
public boolean isEmpty() {
return delegate.isEmpty();
}
public boolean containsKey(Object o) {
return delegate.containsKey(o);
}
public boolean containsValue(Object o) {
return delegate.containsValue(o);
}
public V get(Object o) {
return delegate.get(o);
}
public V put(K k, V v) {
return delegate.put(k, v);
}
public V remove(Object o) {
return delegate.remove(o);
}
public void putAll(Map<? extends K, ? extends V> map) {
delegate.putAll(map);
}
public void clear() {
delegate.clear();
}
public Set<K> keySet() {
return delegate.keySet();
}
public Collection<V> values() {
return delegate.values();
}
public Set<Entry<K, V>> entrySet() {
return delegate.entrySet();
}
@Override
public boolean equals(Object o) {
return delegate.equals(o);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public String toString() {
return delegate.toString();
}
}
public static class ListBuilder<E> implements List<E> {
private final List<E> delegate;
public ListBuilder(List<E> delegate) {
this.delegate = delegate;
}
public ListBuilder<E> e(E e) {
delegate.add(e);
return this;
}
public int size() {
return delegate.size();
}
public boolean isEmpty() {
return delegate.isEmpty();
}
public boolean contains(Object o) {
return delegate.contains(o);
}
public Iterator<E> iterator() {
return delegate.iterator();
}
public Object[] toArray() {
return delegate.toArray();
}
public <T> T[] toArray(T[] ts) {
return delegate.toArray(ts);
}
public boolean add(E e) {
return delegate.add(e);
}
public boolean remove(Object o) {
return delegate.remove(o);
}
public boolean containsAll(Collection<?> objects) {
return delegate.containsAll(objects);
}
public boolean addAll(Collection<? extends E> es) {
return delegate.addAll(es);
}
public boolean addAll(int i, Collection<? extends E> es) {
return delegate.addAll(i, es);
}
public boolean removeAll(Collection<?> objects) {
return delegate.removeAll(objects);
}
public boolean retainAll(Collection<?> objects) {
return delegate.retainAll(objects);
}
public void clear() {
delegate.clear();
}
@Override
public boolean equals(Object o) {
return delegate.equals(o);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public String toString() {
return delegate.toString();
}
public E get(int i) {
return delegate.get(i);
}
public E set(int i, E e) {
return delegate.set(i, e);
}
public void add(int i, E e) {
delegate.add(i, e);
}
public E remove(int i) {
return delegate.remove(i);
}
public int indexOf(Object o) {
return delegate.indexOf(o);
}
public int lastIndexOf(Object o) {
return delegate.lastIndexOf(o);
}
public ListIterator<E> listIterator() {
return delegate.listIterator();
}
public ListIterator<E> listIterator(int i) {
return delegate.listIterator(i);
}
public List<E> subList(int i, int i1) {
return delegate.subList(i, i1);
}
}
public static class SetBuilder<E> implements Set<E> {
private final Set<E> delegate;
public SetBuilder(Set<E> delegate) {
this.delegate = delegate;
}
public SetBuilder<E> e(E e) {
delegate.add(e);
return this;
}
public int size() {
return delegate.size();
}
public boolean isEmpty() {
return delegate.isEmpty();
}
public boolean contains(Object o) {
return delegate.contains(o);
}
public Iterator<E> iterator() {
return delegate.iterator();
}
public Object[] toArray() {
return delegate.toArray();
}
public <T> T[] toArray(T[] ts) {
return delegate.toArray(ts);
}
public boolean add(E e) {
return delegate.add(e);
}
public boolean remove(Object o) {
return delegate.remove(o);
}
public boolean containsAll(Collection<?> objects) {
return delegate.containsAll(objects);
}
public boolean addAll(Collection<? extends E> es) {
return delegate.addAll(es);
}
public boolean retainAll(Collection<?> objects) {
return delegate.retainAll(objects);
}
public boolean removeAll(Collection<?> objects) {
return delegate.removeAll(objects);
}
public void clear() {
delegate.clear();
}
@Override
public boolean equals(Object o) {
return delegate.equals(o);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public String toString() {
return delegate.toString();
}
}
private static class MapToPropertiesAdapter extends Properties {
private final Map<String, Object> map;
private MapToPropertiesAdapter(@Nonnull Map<String, Object> map) {
this.map = map;
}
@Override
public synchronized Object setProperty(String key, String value) {
return map.put(key, value);
}
@Override
public String getProperty(String key) {
Object value = map.get(key);
return value != null ? String.valueOf(value) : null;
}
@Override
public String getProperty(String key, String defaultValue) {
Object value = map.get(key);
return value != null ? String.valueOf(value) : defaultValue;
}
@Override
public Enumeration<?> propertyNames() {
return keys();
}
@Override
public Set<String> stringPropertyNames() {
return map.keySet();
}
@Override
public synchronized int size() {
return map.size();
}
@Override
public synchronized boolean isEmpty() {
return map.isEmpty();
}
@Override
public synchronized Enumeration<Object> keys() {
return new Enumeration<Object>() {
private Iterator<String> keys = new ArrayList<>(map.keySet()).iterator();
@Override
public boolean hasMoreElements() {
return keys.hasNext();
}
@Override
public String nextElement() {
return keys.next();
}
};
}
@Override
public synchronized Enumeration<Object> elements() {
return new Enumeration<Object>() {
private Iterator<Object> values = new ArrayList<>(map.values()).iterator();
@Override
public boolean hasMoreElements() {
return values.hasNext();
}
@Override
public Object nextElement() {
return values.next();
}
};
}
@Override
public synchronized boolean contains(Object value) {
return map.containsValue(value);
}
@Override
public boolean containsValue(Object value) {
return map.containsValue(value);
}
@Override
public synchronized boolean containsKey(Object key) {
return map.containsKey(key);
}
@Override
public synchronized Object get(Object key) {
return map.get(key);
}
@Override
public synchronized Object put(Object key, Object value) {
return map.put(String.valueOf(key), value);
}
@Override
public synchronized Object remove(Object key) {
return map.remove(key);
}
@Override
public synchronized void putAll(Map<?, ?> t) {
map.putAll((Map<String, Object>) t);
}
@Override
public synchronized void clear() {
map.clear();
}
@Override
public Set<Object> keySet() {
return new LinkedHashSet<Object>(map.keySet());
}
@Override
public Set<Map.Entry<Object, Object>> entrySet() {
Set<Map.Entry<Object, Object>> set = new LinkedHashSet<>((Set) map.entrySet());
return new LinkedHashSet<>(set);
}
@Override
public Collection<Object> values() {
return map.values();
}
@Override
public synchronized Object clone() {
Map<String, Object> m = new LinkedHashMap<>(map);
return new MapToPropertiesAdapter(m);
}
}
}