I would like to have the conversion made by JNA automatically. Right now I'm following the solution from the second answer in a very similar question and JNA's own EnumConverter utility class. There's one crucial difference, my enum has a constructor argument.
My code defining the TypeConverter
:
public class SentinelStatusConverter implements TypeConverter {
@Override
public SentinelStatus fromNative(Object nativeValue, FromNativeContext context) {
Integer code = (Integer) nativeValue;
return SentinelStatus.fromCode(code);
}
@Override
public Integer toNative(Object value, ToNativeContext context) {
SentinelStatus status = (SentinelStatus) value;
return Integer.valueOf(status.getCode());
}
@Override
public Class<Integer> nativeType() {
return Integer.class;
}
}
public class SentinelTypeMapper extends DefaultTypeMapper {
public SentinelTypeMapper() {
addTypeConverter(SentinelStatus.class, new SentinelStatusConverter());
}
}
Here's code directly registering the native C library along with my custom TypeMapper
. C functions return an int
which I want to map automatically into a SentinelStatus
enum:
public class SentinelLibrary {
static {
Map<String, Object> options = new HashMap<String, Object>();
options.put(Library.OPTION_TYPE_MAPPER, new SentinelTypeMapper());
Native.register(NativeLibrary.getInstance("libnamelib", options));
}
public static native SentinelStatus hasp_get_sessioninfo(
NativeLong sessionHandle,
String query,
PointerByReference info);
}
SentinelStatus
is an enum
like so:
public enum SentinelStatus {
HASP_STATUS_OK(0),
HASP_SOME_ERROR(13),
...
HASP_NOT_IMPL(1831);
private final int code;
SentinelStatus(final int code) { this.code = code; }
public int getCode() { return this.code; }
public static SentinelStatus fromCode(final int code) {
for (SentinelStatus status : EnumSet.allOf(SentinelStatus.class)) {
if (code == status.getCode()) {
return status;
}
}
return SentinelStatus.HASP_NOT_IMPL;
}
}
With this JNA mapping and converter I get an error whenever I try to load the SentinelLibrary
class:
java.lang.ExceptionInInitializerError
...
Caused by: java.lang.IllegalArgumentException: Unsupported Structure field type class package.name.SentinelStatus
at com.sun.jna.Structure$FFIType.get(Structure.java:1851)
at com.sun.jna.Structure$FFIType.get(Structure.java:1806)
at com.sun.jna.Native.register(Native.java:1438)
at com.sun.jna.Native.register(Native.java:1165)
at package.name.SentinelLibrary.<clinit>(line with Native.register() call)
I've read the documentation and there weren't any restriction as to the mapped class or type. Only the NativeMapped
interface required the implementer to provide a public no-argument constructor.
Is it possible to map a C integer to an enum this way?
UPDATE:
After further rummaging through JNA code, I've added this field to the SentinelStatus
enum:
public final static TypeMapper TYPE_MAPPER = new SentinelTypeMapper();
Now SentinelLibrary
gets loaded without errors. But all methods returning the enum, return null
with error printed to stderr
:
JNA: unrecognized return type, size 4