package net.varkhan.base.conversion.serializer.composite;
import net.varkhan.base.conversion.serializer.DecodingException;
import net.varkhan.base.conversion.serializer.EncodingException;
import net.varkhan.base.conversion.serializer.Serializer;
import net.varkhan.base.conversion.serializer.primitives.NullSerializer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;
/**
* <b>Type union serializer</b>.
* <p/>
* Serializes an union of types.
* <p/>
*
* @author varkhan
* @date 1/30/11
* @time 4:59 AM
*/
@SuppressWarnings( { "UnusedDeclaration" })
public class UnionSerializer<U,C> implements Serializer<U,C> {
protected static final int MAX_TYPES=255;
private int size=0;
@SuppressWarnings( { "unchecked" })
private final Class<? extends U>[] types=new Class[MAX_TYPES];
@SuppressWarnings( { "unchecked" })
private final Serializer<U,C>[] serializers=new Serializer[MAX_TYPES];
public UnionSerializer() { }
@SuppressWarnings( { "unchecked", "RedundantCast" })
public <T extends U> int setSerializer(Class<T> klass, Serializer<T,C> serializer) {
if(size>=MAX_TYPES)
throw new IllegalArgumentException("Can not add serializer for type "+klass.getName()+": type table is full");
types[size]=klass;
serializers[size]=(Serializer<U,C>) serializer;
return ++size;
}
@SuppressWarnings( { "unchecked", "RedundantCast" })
public <T extends U> Serializer<T,C> getSerializer(Class<? extends T> klass) {
for(int i=0;i<size;i++) {
if(types[i].isAssignableFrom(klass)) {
return (Serializer<T,C>) serializers[i];
}
}
return null;
}
@SuppressWarnings( { "unchecked", "RedundantCast" })
public <T extends U> Serializer<T,C> getSerializer(int id) {
if(id==0) return NullSerializer.instance();
if(id>size) return null;
return (Serializer<T,C>) serializers[id-1];
}
public U decode(InputStream stm, C ctx) {
try {
int r=stm.read();
if(r<0) throw new DecodingException();
if(r==0) return null;
if(r>size) throw new DecodingException("Unknown type ID "+r);
return serializers[r-1].decode(stm, ctx);
}
catch(IOException e) {
throw new DecodingException(e);
}
}
public U decode(ByteBuffer buf, C ctx) {
try {
byte r=buf.get();
if(r==0) return null;
if(r>size) throw new DecodingException("Unknown type ID "+r);
return serializers[r-1].decode(buf, ctx);
}
catch(BufferUnderflowException e) {
throw new DecodingException(e);
}
catch(ReadOnlyBufferException e) {
throw new DecodingException(e);
}
}
public U decode(byte[] dat, long pos, long len, C ctx) {
try {
byte r=dat[(int) pos];
if(r==0) return null;
if(r>size) throw new DecodingException("Unknown type ID "+r);
return serializers[r-1].decode(dat, pos+1, len-1, ctx);
}
catch(ArrayIndexOutOfBoundsException e) {
throw new DecodingException(e);
}
}
public long encode(U obj, OutputStream stm, C ctx) {
try {
if(obj==null) {
stm.write(0);
return 1;
}
for(int i=0;i<size;i++) {
if(types[i].isAssignableFrom(obj.getClass())) {
stm.write(i+1);
return 1+serializers[i].encode(obj, stm, ctx);
}
}
throw new EncodingException("Unknown type "+obj.getClass().getName());
}
catch(IOException e) {
throw new EncodingException(e);
}
}
public long encode(U obj, ByteBuffer buf, C ctx) {
try {
if(obj==null) {
buf.put((byte) 0);
return 1;
}
for(int i=0;i<size;i++) {
if(types[i].isAssignableFrom(obj.getClass())) {
buf.put((byte) (i+1));
return 1+serializers[i].encode(obj, buf, ctx);
}
}
throw new EncodingException("Unknown type "+obj.getClass().getName());
}
catch(BufferOverflowException e) {
throw new EncodingException(e);
}
catch(ReadOnlyBufferException e) {
throw new EncodingException(e);
}
}
public long encode(U obj, byte[] dat, long pos, long len, C ctx) {
try {
if(obj==null) {
dat[(int) pos]=0;
return 1;
}
for(int i=0;i<size;i++) {
if(types[i].isAssignableFrom(obj.getClass())) {
dat[(int) pos]=(byte) (i+1);
return 1+serializers[i].encode(obj, dat, pos, len, ctx);
}
}
throw new EncodingException("Unknown type "+obj.getClass().getName());
}
catch(ArrayIndexOutOfBoundsException e) {
throw new EncodingException(e);
}
}
public long length(U obj, C ctx) {
if(obj==null) return 1;
for(int i=0;i<size;i++) {
if(types[i].isAssignableFrom(obj.getClass())) {
return 1+serializers[i].length(obj, ctx);
}
}
throw new EncodingException("Unknown type "+obj.getClass().getName());
}
}