package com.kryptnostic.rhizome.hazelcast.serializers; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collection; import java.util.EnumSet; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Optional; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.io.Resources; import com.google.common.primitives.Ints; import com.hazelcast.nio.ObjectDataInput; import com.hazelcast.nio.ObjectDataOutput; import com.kryptnostic.rhizome.hazelcast.objects.OrderedUUIDSet; import com.kryptnostic.rhizome.hazelcast.objects.UUIDSet; public class RhizomeUtils { static final Logger logger = LoggerFactory.getLogger( RhizomeUtils.class ); public static class Serializers { public static <T extends Enum<T>> void serializeEnumSet( ObjectDataOutput out, Class<T> clazz, Set<T> set ) throws IOException { T[] enumConstants = clazz.getEnumConstants(); if ( enumConstants == null ) { throw new IllegalArgumentException( "This method may only be called for Enum classes" ); } for ( T t : enumConstants ) { out.writeBoolean( set.contains( t ) ); } } public static <T extends Enum<T>> EnumSet<T> deSerializeEnumSet( ObjectDataInput in, Class<T> clazz ) throws IOException { T[] enumConstants = clazz.getEnumConstants(); if ( enumConstants == null ) { throw new IllegalArgumentException( "This method may only be called for Enum classes" ); } EnumSet<T> elements = EnumSet.<T> noneOf( clazz ); for ( T t : enumConstants ) { if ( in.readBoolean() ) { elements.add( t ); } } return elements; } public static <T> void serializeOptional( ObjectDataOutput out, Optional<T> object, IoPerformingBiConsumer<ObjectDataOutput, T> serializer ) throws IOException { out.writeBoolean( object.isPresent() ); if ( object.isPresent() ) { T value = object.get(); serializer.accept( out, value ); } } public static <T> Optional<T> deserializeToOptional( ObjectDataInput in, IoPerformingFunction<ObjectDataInput, T> deserializer ) throws IOException { Optional<T> object = Optional.absent(); if ( in.readBoolean() ) { object = Optional.of( deserializer.apply( in ) ); } return object; } } public static class Sets { public static OrderedUUIDSet newOrderedUUIDSetWithExpectedSize( int expected ) { return new OrderedUUIDSet( expectedSize( expected ) ); } public static UUIDSet newUUIDSetWithExpectedSize( int expected ) { return new UUIDSet( expectedSize( expected ) ); } public static int expectedSize( int expectedSize ) { if ( expectedSize < 0 ) { throw new IllegalArgumentException( "expectedSize cannot be negative but was: " + expectedSize ); } if ( expectedSize < 3 ) { return expectedSize + 1; } if ( expectedSize < Ints.MAX_POWER_OF_TWO ) { return expectedSize + expectedSize / 3; } return Integer.MAX_VALUE; } } public static class Streams { public static void writeStringStringMap( ObjectDataOutput out, Map<String, String> object ) throws IOException { int size = object.size(); Set<String> keys = object.keySet(); Collection<String> vals = object.values(); RhizomeUtils.Streams.writeStringArray( out, keys.toArray( new String[ size ] ) ); RhizomeUtils.Streams.writeStringArray( out, vals.toArray( new String[ size ] ) ); } public static Map<String, String> readStringStringMap( ObjectDataInput in ) throws IOException { String[] keys = RhizomeUtils.Streams.readStringArray( in ); String[] vals = RhizomeUtils.Streams.readStringArray( in ); Map<String, String> map = Maps.newHashMapWithExpectedSize( keys.length ); for ( int i = 0; i < keys.length; i++ ) { map.put( keys[ i ], vals[ i ] ); } return map; } public static void writeStringArray( ObjectDataOutput out, String[] strings ) throws IOException { out.writeInt( strings.length ); for ( String string : strings ) { out.writeUTF( string ); } } public static String[] readStringArray( ObjectDataInput in ) throws IOException { int size = in.readInt(); String[] strings = new String[ size ]; for ( int i = 0; i < size; i++ ) { strings[ i ] = in.readUTF(); } return strings; } public static void writeByteArray( ObjectOutput out, byte[] bytes ) throws IOException { out.writeInt( bytes.length ); out.write( bytes, 0, bytes.length ); } public static byte[] readByteArray( ObjectInput in ) throws IOException { int size = in.readInt(); byte[] result = new byte[ size ]; in.readFully( result ); return result; } public static String loadResourceToString( final String path ) { try { URL resource = Resources.getResource( path ); return Resources.toString( resource, StandardCharsets.UTF_8 ); } catch ( IOException | IllegalArgumentException e ) { logger.error( "Failed to load resource from " + path, e ); return null; } } } public static class Pods { public static Class<?>[] concatenate( Class<?>[]... podSets ) { Iterable<Class<?>> concatenatedPods = Iterables.<Class<?>> concat( Iterables.transform( Arrays.<Class<?>[]> asList( podSets ), podSet -> Arrays.asList( podSet ) ) ); return Iterables.toArray( concatenatedPods, Class.class ); } } }