package openmods.serializable;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import openmods.reflection.ConstructorAccess;
import openmods.reflection.TypeUtils;
import openmods.serializable.providers.ArraySerializerProvider;
import openmods.serializable.providers.ClassSerializerProvider;
import openmods.serializable.providers.EnumSerializerProvider;
import openmods.serializable.providers.ListSerializerProvider;
import openmods.serializable.providers.MapSerializerProvider;
import openmods.serializable.providers.SetSerializerProvider;
import openmods.utils.io.IStreamReader;
import openmods.utils.io.IStreamSerializer;
import openmods.utils.io.IStreamWriter;
import openmods.utils.io.TypeRW;
public class SerializerRegistry {
public static final SerializerRegistry instance = new SerializerRegistry();
private final Map<Class<?>, IStreamSerializer<?>> serializers = Maps.newHashMap(TypeRW.STREAM_SERIALIZERS);
private final List<ISerializerProvider> providers = Lists.newArrayList();
private final List<IGenericSerializerProvider> genericProviders = Lists.newArrayList();
{
providers.add(new EnumSerializerProvider());
providers.add(new ArraySerializerProvider());
providers.add(new ClassSerializerProvider());
genericProviders.add(new ListSerializerProvider());
genericProviders.add(new SetSerializerProvider());
genericProviders.add(new MapSerializerProvider());
}
@SuppressWarnings("unchecked")
private static <T> Class<? extends T> resolve(Class<?> intf, Class<?> concrete) {
final Class<?> rawType = TypeUtils.getTypeParameter(intf, concrete).getRawType();
return (Class<? extends T>)rawType;
}
public <T> void register(Class<? extends T> target, IStreamSerializer<T> serializer) {
Preconditions.checkArgument(target != Object.class, "Can't register serializer for Object");
final IStreamSerializer<?> prev = serializers.put(target, serializer);
Preconditions.checkState(prev == null, "Duplicate serializer for %s", target);
}
public <T> void register(IStreamSerializer<T> serializer) {
Class<? extends T> cls = resolve(IStreamSerializer.class, serializer.getClass());
register(cls, serializer);
}
public <T extends IStreamWriteable & IStreamReadable> void registerSerializable(Class<? extends T> cls, IInstanceFactory<T> factory) {
register(cls, SerializerAdapters.createFromFactory(factory));
}
public <T extends IStreamWriteable & IStreamReadable> void registerSerializable(IInstanceFactory<T> factory) {
Class<? extends T> cls = resolve(IInstanceFactory.class, factory.getClass());
registerSerializable(cls, factory);
}
public <T extends IStreamWriteable & IStreamReadable> void registerSerializable(Class<T> cls) {
IInstanceFactory<T> factory = ConstructorAccess.create(cls);
registerSerializable(cls, factory);
}
public <T extends IStreamWriteable> void registerWriteable(Class<? extends T> cls, IStreamReader<T> reader) {
register(cls, SerializerAdapters.createFromReader(reader));
}
public <T extends IStreamWriteable> void registerWriteable(IStreamReader<T> reader) {
Class<? extends T> cls = resolve(IStreamReader.class, reader.getClass());
registerWriteable(cls, reader);
}
public void registerProvider(ISerializerProvider provider) {
Preconditions.checkNotNull(provider);
providers.add(provider);
}
private IStreamSerializer<?> findClassSerializer(Class<?> cls, IStreamSerializer<?> serializer) {
for (ISerializerProvider provider : providers) {
serializer = provider.getSerializer(cls);
if (serializer != null) {
serializers.put(cls, serializer);
return serializer;
}
}
return null;
}
@SuppressWarnings("unchecked")
public <T> IStreamSerializer<T> findSerializer(Class<? extends T> cls) {
IStreamSerializer<?> serializer = serializers.get(cls);
if (serializer == null) serializer = findClassSerializer(cls, serializer);
return (IStreamSerializer<T>)serializer;
}
public IStreamSerializer<Object> findSerializer(Type type) {
if (type instanceof Class) return findSerializer((Class<?>)type);
return findGenericSerializer(type);
}
@SuppressWarnings("unchecked")
protected IStreamSerializer<Object> findGenericSerializer(Type type) {
for (IGenericSerializerProvider provider : genericProviders) {
IStreamSerializer<?> serializer = provider.getSerializer(type);
if (serializer != null) return (IStreamSerializer<Object>)serializer;
}
return null;
}
public <T> T createFromStream(DataInput input, Class<? extends T> cls) throws IOException {
IStreamReader<T> reader = findSerializer(cls);
Preconditions.checkNotNull(reader, "Can't find reader for %s", cls);
return reader.readFromStream(input);
}
public Object createFromStream(DataInput input, Type type) throws IOException {
IStreamReader<?> reader = findSerializer(type);
Preconditions.checkNotNull(reader, "Can't find reader for %s", type);
return reader.readFromStream(input);
}
public <T> void writeToStream(DataOutput output, Class<? extends T> cls, T target) throws IOException {
Preconditions.checkNotNull(target);
IStreamWriter<T> writer = findSerializer(cls);
Preconditions.checkNotNull(writer, "Can't find writer for %s", cls);
writer.writeToStream(target, output);
}
public void writeToStream(DataOutput output, Type type, Object target) throws IOException {
Preconditions.checkNotNull(target);
IStreamWriter<Object> writer = findSerializer(type);
Preconditions.checkNotNull(writer, "Can't find writer for %s", type);
writer.writeToStream(target, output);
}
}