diff --git a/src/dorkbox/serializers/ArraysAsListSerializer.java b/src/dorkbox/serializers/ArraysAsListSerializer.java index 1911f97..1c7ebf7 100644 --- a/src/dorkbox/serializers/ArraysAsListSerializer.java +++ b/src/dorkbox/serializers/ArraysAsListSerializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2010 Martin Grotzke + * Copyright 2021 dorkbox, llc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -8,24 +8,22 @@ * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package dorkbox.serializers; +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.List; + import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; -import java.lang.reflect.Array; -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.List; - /** * A kryo {@link Serializer} for lists created via {@link Arrays#asList(Object...)}. *

@@ -35,85 +33,102 @@ import java.util.List; * * @author Martin Grotzke */ -public class ArraysAsListSerializer extends Serializer> { +public +class ArraysAsListSerializer extends Serializer> { - private Field _arrayField; - - public ArraysAsListSerializer() { - try { - _arrayField = Class.forName( "java.util.Arrays$ArrayList" ).getDeclaredField( "a" ); - _arrayField.setAccessible( true ); - } catch ( final Exception e ) { - throw new RuntimeException( e ); - } + public + ArraysAsListSerializer() { } @Override - public List read(final Kryo kryo, final Input input, final Class> type) { + public + List read(final Kryo kryo, final Input input, final Class> type) { final int length = input.readInt(true); - Class componentType = kryo.readClass( input ).getType(); + Class componentType = kryo.readClass(input) + .getType(); if (componentType.isPrimitive()) { componentType = getPrimitiveWrapperClass(componentType); } try { - final Object items = Array.newInstance( componentType, length ); - for( int i = 0; i < length; i++ ) { - Array.set(items, i, kryo.readClassAndObject( input )); + final Object items = Array.newInstance(componentType, length); + for (int i = 0; i < length; i++) { + Array.set(items, i, kryo.readClassAndObject(input)); } - return Arrays.asList( (Object[])items ); - } catch ( final Exception e ) { - throw new RuntimeException( e ); + return Arrays.asList((Object[]) items); + } catch (final Exception e) { + throw new RuntimeException(e); } } @Override - public void write(final Kryo kryo, final Output output, final List obj) { + public + void write(final Kryo kryo, final Output output, final List obj) { try { - final Object[] array = (Object[]) _arrayField.get( obj ); - output.writeInt(array.length, true); - final Class componentType = array.getClass().getComponentType(); - kryo.writeClass( output, componentType ); - for( final Object item : array ) { - kryo.writeClassAndObject( output, item ); + int size = obj.size(); + output.writeInt(size, true); + + if (size == 0) { + final Class componentType = obj.toArray() + .getClass() + .getComponentType(); + kryo.writeClass(output, componentType); } - } catch ( final RuntimeException e ) { - // Don't eat and wrap RuntimeExceptions because the ObjectBuffer.write... - // handles SerializationException specifically (resizing the buffer)... - throw e; - } catch ( final Exception e ) { - throw new RuntimeException( e ); + else { + kryo.writeClass(output, + obj.get(0) + .getClass()); + for (final Object item : obj) { + kryo.writeClassAndObject(output, item); + } + } + + } catch (final RuntimeException e) { + // Don't eat and wrap RuntimeExceptions because the ObjectBuffer.write... + // handles SerializationException specifically (resizing the buffer)... + throw e; + } catch (final Exception e) { + throw new RuntimeException(e); } } @Override - public List copy(Kryo kryo, List original) { + public + List copy(Kryo kryo, List original) { try { - final Object[] array = (Object[]) _arrayField.get(original); - kryo.reference(array); - Object[] arrayCopy = kryo.copy(array); + Object[] object = original.toArray(); + kryo.reference(object); + Object[] arrayCopy = kryo.copy(object); return Arrays.asList(arrayCopy); } catch (final Exception e) { throw new RuntimeException(e); } } - private static Class getPrimitiveWrapperClass(final Class c) { + private static + Class getPrimitiveWrapperClass(final Class c) { if (c.isPrimitive()) { if (c.equals(Long.TYPE)) { return Long.class; - } else if (c.equals(Integer.TYPE)) { + } + else if (c.equals(Integer.TYPE)) { return Integer.class; - } else if (c.equals(Double.TYPE)) { + } + else if (c.equals(Double.TYPE)) { return Double.class; - } else if (c.equals(Float.TYPE)) { + } + else if (c.equals(Float.TYPE)) { return Float.class; - } else if (c.equals(Boolean.TYPE)) { + } + else if (c.equals(Boolean.TYPE)) { return Boolean.class; - } else if (c.equals(Character.TYPE)) { + } + else if (c.equals(Character.TYPE)) { return Character.class; - } else if (c.equals(Short.TYPE)) { + } + else if (c.equals(Short.TYPE)) { return Short.class; - } else if (c.equals(Byte.TYPE)) { + } + else if (c.equals(Byte.TYPE)) { return Byte.class; } } diff --git a/src/dorkbox/serializers/EnumMapSerializer.java b/src/dorkbox/serializers/EnumMapSerializer.java index cdf17a5..03131ff 100644 --- a/src/dorkbox/serializers/EnumMapSerializer.java +++ b/src/dorkbox/serializers/EnumMapSerializer.java @@ -16,95 +16,76 @@ */ package dorkbox.serializers; -import static com.esotericsoftware.minlog.Log.TRACE; -import static com.esotericsoftware.minlog.Log.trace; - -import java.lang.reflect.Field; import java.util.EnumMap; import java.util.Map; import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; /** * A serializer for {@link EnumMap}s. - * + * * @author Martin Grotzke */ -public class EnumMapSerializer extends Serializer, ?>> { - - private static final Field TYPE_FIELD; - - static { - try { - TYPE_FIELD = EnumMap.class.getDeclaredField( "keyType" ); - TYPE_FIELD.setAccessible( true ); - } catch ( final Exception e ) { - throw new RuntimeException( "The EnumMap class seems to have changed, could not access expected field.", e ); - } - } +public +class EnumMapSerializer extends Serializer, ?>> { - // 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(); + static { + } @Override @SuppressWarnings({"unchecked", "rawtypes"}) - public EnumMap, ?> copy(final Kryo kryo, final EnumMap, ?> original) { + public + EnumMap, ?> copy(final Kryo kryo, final EnumMap, ?> 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 EnumMap copy = new EnumMap(original); for (final Map.Entry entry : original.entrySet()) { - copy.put((Enum)entry.getKey(), kryo.copy(entry.getValue())); + copy.put((Enum) entry.getKey(), kryo.copy(entry.getValue())); } return copy; - } - - @SuppressWarnings( { "unchecked", "rawtypes" } ) - private EnumMap, ?> create(final Kryo kryo, final Input input, - final Class, ?>> type) { - final Class> keyType = kryo.readClass( input ).getType(); - return new EnumMap( keyType ); } - + @Override - @SuppressWarnings({ "rawtypes", "unchecked" }) - public EnumMap, ?> read(final Kryo kryo, final Input input, - final Class, ?>> type) { - kryo.reference(FAKE_REFERENCE); - final EnumMap, ?> result = create(kryo, input, type); - final Class> keyType = getKeyType( result ); + @SuppressWarnings({"rawtypes", "unchecked"}) + public + EnumMap, ?> read(final Kryo kryo, final Input input, final Class, ?>> type) { + final Class> keyType = kryo.readClass(input) + .getType(); + final EnumMap, ?> result = new EnumMap(keyType); final Enum[] enumConstants = keyType.getEnumConstants(); final EnumMap rawResult = result; final int size = input.readInt(true); - for ( int i = 0; i < size; i++ ) { + for (int i = 0; i < size; i++) { final int ordinal = input.readVarInt(true); final Enum key = enumConstants[ordinal]; - final Object value = kryo.readClassAndObject( input ); - rawResult.put( key, value ); + final Object value = kryo.readClassAndObject(input); + rawResult.put(key, value); } return result; } @Override - public void write(final Kryo kryo, final Output output, final EnumMap, ?> map) { - kryo.writeClass( output, getKeyType( map ) ); - output.writeInt(map.size(), true); - for ( final Map.Entry,?> entry : map.entrySet() ) { - output.writeVarInt(entry.getKey().ordinal(), true); - kryo.writeClassAndObject(output, entry.getValue()); - } - if ( TRACE ) trace( "kryo", "Wrote EnumMap: " + map ); - } - - @SuppressWarnings("unchecked") - private Class> getKeyType( final EnumMap map ) { - try { - return (Class>)TYPE_FIELD.get( map ); - } catch ( final Exception e ) { - throw new RuntimeException( "Could not access keys field.", e ); + public + void write(final Kryo kryo, final Output output, final EnumMap, ?> map) { + if (map.isEmpty()) { + throw new KryoException("An EnumMap must not be empty to be serialized, the type can not be safely inferred."); + } else { + @SuppressWarnings("unchecked") + Class> keyType = (Class>) map.keySet() + .iterator() + .next() + .getDeclaringClass(); + kryo.writeClass(output, keyType); + output.writeInt(map.size(), true); + for (final Map.Entry, ?> entry : map.entrySet()) { + output.writeVarInt(entry.getKey() + .ordinal(), true); + kryo.writeClassAndObject(output, entry.getValue()); + } } } } diff --git a/src/dorkbox/serializers/EnumSetSerializer.java b/src/dorkbox/serializers/EnumSetSerializer.java index e4c4df8..f81f013 100644 --- a/src/dorkbox/serializers/EnumSetSerializer.java +++ b/src/dorkbox/serializers/EnumSetSerializer.java @@ -16,69 +16,63 @@ */ package dorkbox.serializers; -import static com.esotericsoftware.minlog.Log.TRACE; -import static com.esotericsoftware.minlog.Log.trace; - -import java.lang.reflect.Field; import java.util.EnumSet; import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; /** * A serializer for {@link EnumSet}s. - * - * @author Martin Grotzke */ -@SuppressWarnings( { "unchecked", "rawtypes" } ) -public class EnumSetSerializer extends Serializer>> { - - private static final Field TYPE_FIELD; - - static { - try { - TYPE_FIELD = EnumSet.class.getDeclaredField( "elementType" ); - TYPE_FIELD.setAccessible( true ); - } catch ( final Exception e ) { - throw new RuntimeException( "The EnumSet class seems to have changed, could not access expected field.", e ); - } - } +@SuppressWarnings({"unchecked", "rawtypes"}) +public +class EnumSetSerializer extends Serializer>> { @Override - public EnumSet> copy (final Kryo kryo, final EnumSet> original) { + public + EnumSet> copy(final Kryo kryo, final EnumSet> original) { return original.clone(); } @Override - public EnumSet read(final Kryo kryo, final Input input, final Class>> type) { - final Class elementType = kryo.readClass( input ).getType(); - final EnumSet result = EnumSet.noneOf( elementType ); + public + EnumSet read(final Kryo kryo, final Input input, final Class>> type) { + final Class elementType = kryo.readClass(input) + .getType(); + final EnumSet result = EnumSet.noneOf(elementType); final int size = input.readInt(true); final Enum[] enumConstants = elementType.getEnumConstants(); - for ( int i = 0; i < size; i++ ) { - result.add( enumConstants[input.readInt(true)] ); + for (int i = 0; i < size; i++) { + result.add(enumConstants[input.readInt(true)]); } return result; } @Override - public void write(final Kryo kryo, final Output output, final EnumSet> set) { - kryo.writeClass( output, getElementType( set ) ); - output.writeInt( set.size(), true ); - for (final Enum item : set) { - output.writeInt(item.ordinal(), true); + public + void write(final Kryo kryo, final Output output, final EnumSet> set) { + if (set.isEmpty()) { + EnumSet> tmp = EnumSet.complementOf(set); + if (tmp.isEmpty()) throw new KryoException("An EnumSet must have a defined Enum to be serialized."); + Class> type = (Class>) tmp.iterator() + .next() + .getDeclaringClass(); + kryo.writeClass(output, type); + output.writeInt(0, true); } + else { + Class> type = (Class>) set.iterator() + .next() + .getDeclaringClass(); + kryo.writeClass(output, type); + output.writeInt(set.size(), true); - if ( TRACE ) trace( "kryo", "Wrote EnumSet: " + set ); - } - - private Class> getElementType( final EnumSet> set ) { - try { - return (Class)TYPE_FIELD.get( set ); - } catch ( final Exception e ) { - throw new RuntimeException( "Could not access keys field.", e ); + for (final Enum item : set) { + output.writeInt(item.ordinal(), true); + } } } }