package openmods.utils;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.reflect.TypeToken;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.SortedMap;
import java.util.SortedSet;
import openmods.reflection.TypeUtils;
import openmods.utils.io.IStreamReader;
import openmods.utils.io.IStreamWriter;
public class CollectionUtils {
public static final Random rnd = new Random();
public static <T> T getFirst(Collection<T> collection) {
Preconditions.checkArgument(!collection.isEmpty(), "Collection cannot be empty");
return collection.iterator().next();
}
public static <T> T getRandom(Collection<T> collection) {
return getRandom(collection, rnd);
}
public static <T> T getRandom(Collection<T> collection, Random rand) {
final int size = collection.size();
Preconditions.checkArgument(size > 0, "Can't select from empty collection");
if (size == 1) return getFirst(collection);
int randomIndex = rnd.nextInt(size);
int i = 0;
for (T obj : collection) {
if (i == randomIndex) return obj;
i = i + 1;
}
return null;
}
public static <T> T getRandom(List<T> list) {
return getRandom(list, rnd);
}
public static <T> T getRandom(List<T> list, Random rand) {
final int size = list.size();
Preconditions.checkArgument(size > 0, "Can't select from empty list");
if (size == 0) return null;
if (size == 1) return list.get(0);
int randomIndex = rnd.nextInt(list.size());
return list.get(randomIndex);
}
public static <T> T getWeightedRandom(Map<T, Integer> collection) {
int totalWeight = 0;
Collection<Integer> values = collection.values();
for (Integer i : values) {
totalWeight += i;
}
int r = rnd.nextInt(totalWeight);
for (Entry<T, Integer> entry : collection.entrySet()) {
r -= entry.getValue();
if (r <= 0) { return entry.getKey(); }
}
return null;
}
public static void readSortedIdList(DataInput input, Collection<Integer> output) {
int elemCount = ByteUtils.readVLI(input);
int currentId = 0;
for (int i = 0; i < elemCount; i++) {
currentId += ByteUtils.readVLI(input);
output.add(currentId);
}
}
public static void writeSortedIdList(DataOutput output, SortedSet<Integer> idList) {
ByteUtils.writeVLI(output, idList.size());
int currentId = 0;
for (Integer id : idList) {
int delta = id - currentId;
ByteUtils.writeVLI(output, delta);
currentId = id;
}
}
public static <D> void readSortedIdMap(DataInput input, Map<Integer, D> output, IStreamReader<D> reader) {
int elemCount = ByteUtils.readVLI(input);
int currentId = 0;
try {
for (int i = 0; i < elemCount; i++) {
currentId += ByteUtils.readVLI(input);
D data = reader.readFromStream(input);
output.put(currentId, data);
}
} catch (IOException e) {
Throwables.propagate(e);
}
}
public static <D> void writeSortedIdMap(DataOutput output, SortedMap<Integer, D> input, IStreamWriter<D> writer) {
ByteUtils.writeVLI(output, input.size());
int currentId = 0;
try {
for (Map.Entry<Integer, D> e : input.entrySet()) {
final int id = e.getKey();
final int delta = id - currentId;
ByteUtils.writeVLI(output, delta);
writer.writeToStream(e.getValue(), output);
currentId = id;
}
} catch (IOException e) {
Throwables.propagate(e);
}
}
private static <A, B> Object allocateArray(Function<A, B> transformer, final int length) {
final TypeToken<?> token = TypeToken.of(transformer.getClass());
final TypeToken<?> typeB = token.resolveType(TypeUtils.FUNCTION_B_PARAM);
return Array.newInstance(typeB.getRawType(), length);
}
@SuppressWarnings("unchecked")
public static <A, B> B[] transform(A[] input, Function<A, B> transformer) {
final Object result = allocateArray(transformer, input.length);
for (int i = 0; i < input.length; i++) {
final B o = transformer.apply(input[i]);
Array.set(result, i, o);
}
return (B[])result;
}
@SuppressWarnings("unchecked")
public static <A, B> B[] transform(Collection<A> input, Function<A, B> transformer) {
final Object result = allocateArray(transformer, input.size());
int i = 0;
for (A a : input) {
final B o = transformer.apply(a);
Array.set(result, i++, o);
}
return (B[])result;
}
public static <K, V> void putOnce(Map<K, V> map, K key, V value) {
final V prev = map.put(key, value);
Preconditions.checkState(prev == null, "Duplicate value on key %s: %s -> %s", key, prev, value);
}
}