44import java .lang .annotation .Annotation ;
55import java .lang .reflect .Constructor ;
66import java .lang .reflect .InvocationTargetException ;
7+ import java .lang .reflect .Method ;
8+ import java .lang .reflect .Modifier ;
79import java .lang .reflect .ParameterizedType ;
810import java .math .BigInteger ;
911import java .net .InetAddress ;
@@ -38,6 +40,8 @@ class Decoder {
3840
3941 private final ConcurrentHashMap <Class <?>, CachedConstructor <?>> constructors ;
4042
43+ private final ConcurrentHashMap <Class <?>, CachedCreator > creators ;
44+
4145 private final InetAddress lookupIp ;
4246 private final Network lookupNetwork ;
4347
@@ -47,6 +51,7 @@ class Decoder {
4751 buffer ,
4852 pointerBase ,
4953 new ConcurrentHashMap <>(),
54+ new ConcurrentHashMap <>(),
5055 null ,
5156 null
5257 );
@@ -63,6 +68,7 @@ class Decoder {
6368 buffer ,
6469 pointerBase ,
6570 constructors ,
71+ new ConcurrentHashMap <>(),
6672 null ,
6773 null
6874 );
@@ -73,13 +79,15 @@ class Decoder {
7379 Buffer buffer ,
7480 long pointerBase ,
7581 ConcurrentHashMap <Class <?>, CachedConstructor <?>> constructors ,
82+ ConcurrentHashMap <Class <?>, CachedCreator > creators ,
7683 InetAddress lookupIp ,
7784 Network lookupNetwork
7885 ) {
7986 this .cache = cache ;
8087 this .pointerBase = pointerBase ;
8188 this .buffer = buffer ;
8289 this .constructors = constructors ;
90+ this .creators = creators ;
8391 this .lookupIp = lookupIp ;
8492 this .lookupNetwork = lookupNetwork ;
8593 }
@@ -217,9 +225,11 @@ private <T> Object decodeByType(
217225 }
218226 return this .decodeArray (size , cls , elementClass );
219227 case BOOLEAN :
220- return Decoder .decodeBoolean (size );
228+ Boolean bool = Decoder .decodeBoolean (size );
229+ return convertValue (bool , cls );
221230 case UTF8_STRING :
222- return this .decodeString (size );
231+ String str = this .decodeString (size );
232+ return convertValue (str , cls );
223233 case DOUBLE :
224234 return this .decodeDouble (size );
225235 case FLOAT :
@@ -653,6 +663,7 @@ private <T> Object decodeMapIntoObject(int size, Class<T> cls)
653663 private boolean shouldInstantiateFromContext (Class <?> parameterType ) {
654664 if (parameterType == null
655665 || parameterType .isPrimitive ()
666+ || parameterType .isEnum ()
656667 || isSimpleType (parameterType )
657668 || Map .class .isAssignableFrom (parameterType )
658669 || List .class .isAssignableFrom (parameterType )) {
@@ -870,6 +881,74 @@ private static void validateInjectionTarget(
870881 }
871882 }
872883
884+ /**
885+ * Converts a decoded value to the target type using a creator method if available.
886+ * If no creator method is found, returns the original value.
887+ */
888+ private Object convertValue (Object value , Class <?> targetType ) {
889+ if (value == null || targetType == null
890+ || targetType == Object .class
891+ || targetType .isInstance (value )) {
892+ return value ;
893+ }
894+
895+ CachedCreator creator = getCachedCreator (targetType );
896+ if (creator == null ) {
897+ return value ;
898+ }
899+
900+ if (!creator .parameterType ().isInstance (value )) {
901+ return value ;
902+ }
903+
904+ try {
905+ return creator .method ().invoke (null , value );
906+ } catch (IllegalAccessException | InvocationTargetException e ) {
907+ throw new DeserializationException (
908+ "Error invoking creator method " + creator .method ().getName ()
909+ + " on class " + targetType .getName (), e );
910+ }
911+ }
912+
913+ private CachedCreator getCachedCreator (Class <?> cls ) {
914+ CachedCreator cached = this .creators .get (cls );
915+ if (cached != null ) {
916+ return cached ;
917+ }
918+
919+ CachedCreator creator = findCreatorMethod (cls );
920+ if (creator != null ) {
921+ this .creators .putIfAbsent (cls , creator );
922+ }
923+ return creator ;
924+ }
925+
926+ private static CachedCreator findCreatorMethod (Class <?> cls ) {
927+ Method [] methods = cls .getDeclaredMethods ();
928+ for (Method method : methods ) {
929+ if (!method .isAnnotationPresent (MaxMindDbCreator .class )) {
930+ continue ;
931+ }
932+ if (!Modifier .isStatic (method .getModifiers ())) {
933+ throw new DeserializationException (
934+ "Creator method " + method .getName () + " on class " + cls .getName ()
935+ + " must be static." );
936+ }
937+ if (method .getParameterCount () != 1 ) {
938+ throw new DeserializationException (
939+ "Creator method " + method .getName () + " on class " + cls .getName ()
940+ + " must have exactly one parameter." );
941+ }
942+ if (!cls .isAssignableFrom (method .getReturnType ())) {
943+ throw new DeserializationException (
944+ "Creator method " + method .getName () + " on class " + cls .getName ()
945+ + " must return " + cls .getName () + " or a subtype." );
946+ }
947+ return new CachedCreator (method , method .getParameterTypes ()[0 ]);
948+ }
949+ return null ;
950+ }
951+
873952 private static Object parseDefault (String value , Class <?> target ) {
874953 try {
875954 if (target .equals (Boolean .TYPE ) || target .equals (Boolean .class )) {
0 commit comments