package com.github.davidmoten.rtree;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.charset.Charset;
import com.esotericsoftware.kryo.Kryo;
import com.github.davidmoten.guavamini.Preconditions;
import com.github.davidmoten.rtree.fbs.SerializerFlatBuffers;
import com.github.davidmoten.rtree.geometry.Geometry;
import com.github.davidmoten.rtree.kryo.SerializerKryo;
import com.github.davidmoten.rx.Functions;
import rx.functions.Func0;
import rx.functions.Func1;
public final class Serializers {
private Serializers() {
// prevent instantiation
}
public static class SerializerBuilder {
private Method method = Method.FLATBUFFERS;
private SerializerBuilder() {
}
public <T> SerializerTypedBuilder<T> serializer(Func1<? super T, byte[]> serializer) {
return new SerializerTypedBuilder<T>(serializer, null, method);
}
public <T> SerializerTypedBuilder<T> deserializer(Func1<byte[], ? extends T> deserializer) {
return new SerializerTypedBuilder<T>(null, deserializer, method);
}
public <S extends Geometry> Serializer<String, S> string(Charset charset) {
Func1<String, byte[]> serializer = createStringSerializer(charset);
Func1<byte[], String> deserializer = createStringDeserializer(charset);
return new SerializerTypedBuilder<String>(serializer, deserializer, method).create();
}
@SuppressWarnings("unchecked")
public <T extends Serializable, S extends Geometry> Serializer<T, S> javaIo() {
Func1<T, byte[]> serializer = (Func1<T, byte[]>) javaIoSerializer();
Func1<byte[], T> deserializer = (Func1<byte[], T>) javaIoDeserializer();
return new SerializerTypedBuilder<T>(serializer, deserializer, method).create();
}
public <S extends Geometry> Serializer<String, S> utf8() {
return string(Charset.forName("UTF-8"));
}
public <S extends Geometry> Serializer<byte[], S> bytes() {
Func1<byte[], byte[]> serializer = Functions.identity();
Func1<byte[], byte[]> deserializer = Functions.identity();
return new SerializerTypedBuilder<byte[]>(serializer, deserializer, method).create();
}
public SerializerBuilder method(Method method) {
this.method = method;
return this;
}
}
public static final class SerializerTypedBuilder<T> {
private Func1<? super T, byte[]> serializer;
private Func1<byte[], ? extends T> deserializer;
private Method method;
private Func0<Kryo> kryoFactory = new Func0<Kryo>() {
@Override
public Kryo call() {
return new Kryo();
}
};
private SerializerTypedBuilder(Func1<? super T, byte[]> serializer,
Func1<byte[], ? extends T> deserializer, Method method) {
this.serializer = serializer;
this.deserializer = deserializer;
this.method = method;
}
public SerializerTypedBuilder<T> serializer(Func1<? super T, byte[]> serializer) {
this.serializer = serializer;
return this;
}
public SerializerTypedBuilder<T> deserializer(Func1<byte[], ? extends T> deserializer) {
this.deserializer = deserializer;
return this;
}
public SerializerTypedBuilder<T> method(Method method) {
// TODO remove this check when kryo ready
Preconditions.checkArgument(method != Method.KRYO,
"kryo serialization not implemented yet");
this.method = method;
return this;
}
// TODO enable when ready
private SerializerTypedBuilder<T> kryo(Func0<Kryo> kryoFactory) {
this.method = Method.KRYO;
this.kryoFactory = kryoFactory;
return this;
}
@SuppressWarnings("unchecked")
public <S extends Geometry> Serializer<T, S> create() {
if (method == Method.FLATBUFFERS) {
if (serializer == null) {
serializer = (Func1<T, byte[]>) javaIoSerializer();
}
if (deserializer == null) {
deserializer = (Func1<byte[], T>) javaIoDeserializer();
}
return SerializerFlatBuffers.create(serializer, deserializer);
} else {
return SerializerKryo.create(serializer, deserializer, kryoFactory);
}
}
}
public static <T, S extends Geometry> SerializerBuilder flatBuffers() {
return new SerializerBuilder().method(Method.FLATBUFFERS);
}
public enum Method {
FLATBUFFERS, KRYO;
}
private static Func1<String, byte[]> createStringSerializer(final Charset charset) {
return new Func1<String, byte[]>() {
@Override
public byte[] call(String s) {
return s.getBytes(charset);
}
};
}
private static <T> Func1<byte[], String> createStringDeserializer(final Charset charset) {
return new Func1<byte[], String>() {
@Override
public String call(byte[] bytes) {
return new String(bytes, charset);
}
};
}
private static Func1<Serializable, byte[]> javaIoSerializer() {
return new Func1<Serializable, byte[]>() {
@Override
public byte[] call(Serializable o) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(bytes);
oos.writeObject(o);
oos.close();
return bytes.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if (oos != null)
oos.close();
} catch (IOException e) {
// ignore
}
}
}
};
}
private static Func1<byte[], Serializable> javaIoDeserializer() {
return new Func1<byte[], Serializable>() {
@Override
public Serializable call(byte[] bytes) {
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(is);
return (Serializable) ois.readObject();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} finally {
if (ois != null)
try {
ois.close();
} catch (IOException e) {
// ignore
}
}
}
};
}
}