package openmods.serializable.providers; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; import com.google.common.reflect.TypeToken; 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.TypeUtils; import openmods.serializable.IGenericSerializerProvider; import openmods.serializable.SerializerRegistry; import openmods.utils.ByteUtils; import openmods.utils.io.IStreamSerializer; import openmods.utils.io.InputBitStream; import openmods.utils.io.OutputBitStream; import openmods.utils.io.StreamUtils; public class MapSerializerProvider implements IGenericSerializerProvider { @Override public IStreamSerializer<?> getSerializer(Type type) { TypeToken<?> typeToken = TypeToken.of(type); if (TypeUtils.MAP_TOKEN.isAssignableFrom(typeToken)) { final TypeToken<?> keyType = typeToken.resolveType(TypeUtils.MAP_KEY_PARAM); final TypeToken<?> valueType = typeToken.resolveType(TypeUtils.MAP_VALUE_PARAM); final IStreamSerializer<Object> keySerializer = getSerializer(keyType); final IStreamSerializer<Object> valueSerializer = getSerializer(valueType); return new IStreamSerializer<Map<Object, Object>>() { @Override public Map<Object, Object> readFromStream(DataInput input) throws IOException { final int length = ByteUtils.readVLI(input); Map<Object, Object> result = Maps.newHashMap(); if (length > 0) { final int nullBitsSize = StreamUtils.bitsToBytes(length * 2); final byte[] nullBits = StreamUtils.readBytes(input, nullBitsSize); final InputBitStream nullBitStream = InputBitStream.create(nullBits); for (int i = 0; i < length; i++) { Object key = null; if (nullBitStream.readBit()) key = keySerializer.readFromStream(input); Object value = null; if (nullBitStream.readBit()) value = valueSerializer.readFromStream(input); result.put(key, value); } } return result; } @Override public void writeToStream(Map<Object, Object> o, DataOutput output) throws IOException { final int length = o.size(); ByteUtils.writeVLI(output, length); if (length > 0) { final ByteArrayDataOutput nullBits = ByteStreams.newDataOutput(); final OutputBitStream nullBitsStream = OutputBitStream.create(nullBits); List<Map.Entry<Object, Object>> entries = ImmutableList.copyOf(o.entrySet()); for (Map.Entry<Object, Object> e : entries) { nullBitsStream.writeBit(e.getKey() != null); nullBitsStream.writeBit(e.getValue() != null); } nullBitsStream.flush(); output.write(nullBits.toByteArray()); for (Map.Entry<Object, Object> e : entries) { writeValue(e.getKey(), keySerializer, output); writeValue(e.getValue(), valueSerializer, output); } } } private void writeValue(Object value, IStreamSerializer<Object> serializer, DataOutput output) throws IOException { if (value != null) serializer.writeToStream(value, output); } }; } return null; } private static IStreamSerializer<Object> getSerializer(final TypeToken<?> type) { final IStreamSerializer<Object> keySerializer = SerializerRegistry.instance.findSerializer(type.getType()); Preconditions.checkNotNull(keySerializer, "Can't find serializer for %s", type); return keySerializer; } }