package kryo; import static com.esotericsoftware.minlog.Log.TRACE; import static com.esotericsoftware.minlog.Log.trace; import java.lang.reflect.Field; import Roguelike.Util.FastEnumMap; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; /** * A serializer for {@link FastEnumMap}s. * * @author <a href="mailto:martin.grotzke@javakaffee.de">Martin Grotzke</a> */ public class FastEnumMapSerializer extends Serializer<FastEnumMap<? extends Enum<?>, ?>> { private static final Field TYPE_FIELD; static { try { TYPE_FIELD = FastEnumMap.class.getDeclaredField( "keyType" ); TYPE_FIELD.setAccessible( true ); } catch ( final Exception e ) { throw new RuntimeException( "The FastEnumMap class seems to have changed, could not access expected field.", e ); } } // Workaround reference reading, this should be removed sometimes. See also // https://groups.google.com/d/msg/kryo-users/Eu5V4bxCfws/k-8UQ22y59AJ private static final Object FAKE_REFERENCE = new Object(); @Override @SuppressWarnings( { "unchecked", "rawtypes" } ) public FastEnumMap<? extends Enum<?>, ?> copy( final Kryo kryo, final FastEnumMap<? extends Enum<?>, ?> original ) { // Make a shallow copy to copy the private key type of the original map // without using reflection. // This will work for empty original maps as well. final FastEnumMap copy = new FastEnumMap( original ); for ( int i = 0; i < copy.numItems(); i++ ) { copy.put( i, kryo.copy( original.get( i ) ) ); } return copy; } @SuppressWarnings( { "unchecked", "rawtypes" } ) private FastEnumMap<? extends Enum<?>, ?> create( final Kryo kryo, final Input input, final Class<FastEnumMap<? extends Enum<?>, ?>> type ) { final Class<? extends Enum<?>> keyType = kryo.readClass( input ).getType(); return new FastEnumMap( keyType ); } @Override @SuppressWarnings( { "rawtypes", "unchecked" } ) public FastEnumMap<? extends Enum<?>, ?> read( final Kryo kryo, final Input input, final Class<FastEnumMap<? extends Enum<?>, ?>> type ) { kryo.reference( FAKE_REFERENCE ); final FastEnumMap<? extends Enum<?>, ?> result = create( kryo, input, type ); final Class<Enum<?>> keyType = getKeyType( result ); final Enum<?>[] enumConstants = keyType.getEnumConstants(); final FastEnumMap rawResult = result; final int size = input.readInt( true ); for ( int i = 0; i < size; i++ ) { final int ordinal = input.readInt( true ); final Enum<?> key = enumConstants[ordinal]; final Object value = kryo.readClassAndObject( input ); rawResult.put( key, value ); } return result; } @Override public void write( final Kryo kryo, final Output output, final FastEnumMap<? extends Enum<?>, ?> map ) { kryo.writeClass( output, getKeyType( map ) ); output.writeInt( map.size, true ); for ( int i = 0; i < map.numItems(); i++ ) { Object val = map.get( i ); if ( val != null ) { output.writeInt( i, true ); kryo.writeClassAndObject( output, val ); } } if ( TRACE ) trace( "kryo", "Wrote FastEnumMap: " + map ); } @SuppressWarnings( "unchecked" ) private Class<Enum<?>> getKeyType( final FastEnumMap<?, ?> map ) { try { return (Class<Enum<?>>) TYPE_FIELD.get( map ); } catch ( final Exception e ) { throw new RuntimeException( "Could not access keys field.", e ); } } }