com.sun.jna.Library Maven / Gradle / Ivy
Show all versions of platform Show documentation
/* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package com.sun.jna;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
/** Derive from this interface for all native library definitions.
*
* Define an instance of your library like this:
*
* MyNativeLibrary INSTANCE = (MyNativeLibrary)
* Native.loadLibrary("mylib", MyNativeLibrary.class);
*
*
* By convention, method names are identical to the native names, although you
* can map java names to different native names by providing a
* {@link FunctionMapper} as a value for key {@link #OPTION_FUNCTION_MAPPER}
* in the options map passed to the
* {@link Native#loadLibrary(String, Class, Map)} call.
*
* Although the names for structures and structure fields may be chosen
* arbitrarily, they should correspond as closely as possible to the native
* definitions. The same is true for parameter names.
*
* This interface supports multiple, concurrent invocations of any library
* methods on the Java side. Check your library documentation for its
* multi-threading requirements on the native side. If a library is not safe
* for simultaneous multi-threaded access, consider using
* {@link Native#synchronizedLibrary} to prevent simultaneous multi-threaded
* access to the native code.
*
* Optional fields
* Interface options will be automatically propagated to structures defined
* within the library provided a call to
* {@link Native#loadLibrary(String,Class,Map)} is made prior to instantiating
* any of those structures. One common way of ensuring this is to declare
* an INSTANCE field in the interface which holds the
* loadLibrary
result.
*
* OPTIONS (an instance of {@link Map}),
* TYPE_MAPPER (an instance of {@link TypeMapper}) and
* STRUCTURE_ALIGNMENT (one of the alignment types defined in
* {@link Structure}) may also be defined. If no instance of the interface
* has been instantiated, these fields will be used to determine customization
* settings for structures and methods defined within the interface.
*
*
* @author Todd Fast, [email protected]
* @author Timothy Wall, [email protected]
*/
public interface Library {
/** Option key for a {@link TypeMapper} for the library. */
String OPTION_TYPE_MAPPER = "type-mapper";
/** Option key for a {@link FunctionMapper} for the library. */
String OPTION_FUNCTION_MAPPER = "function-mapper";
/** Option key for an {@link InvocationMapper} for the library. */
String OPTION_INVOCATION_MAPPER = "invocation-mapper";
/** Option key for structure alignment type ({@link Integer}), which should
* be one of the predefined alignment types in {@link Structure}.
*/
String OPTION_STRUCTURE_ALIGNMENT = "structure-alignment";
/** Option key for a boolean flag to allow any Java class instance as a
parameter. If no type mapper is found, the object is passed as a
pointer.
NOTE: This is for use with raw JNI interactions via the
JNIEnv data structure.
*/
String OPTION_ALLOW_OBJECTS = "allow-objects";
/** Calling convention for the entire library. */
String OPTION_CALLING_CONVENTION = "calling-convention";
static class Handler implements InvocationHandler {
static final Method OBJECT_TOSTRING;
static final Method OBJECT_HASHCODE;
static final Method OBJECT_EQUALS;
static {
try {
OBJECT_TOSTRING = Object.class.getMethod("toString", new Class[0]);
OBJECT_HASHCODE= Object.class.getMethod("hashCode", new Class[0]);
OBJECT_EQUALS = Object.class.getMethod("equals", new Class[] { Object.class });
}
catch (Exception e) {
throw new Error("Error retrieving Object.toString() method");
}
}
private static class FunctionNameMap implements FunctionMapper {
private final Map map;
public FunctionNameMap(Map map) {
this.map = new HashMap(map);
}
public String getFunctionName(NativeLibrary library, Method method) {
String name = method.getName();
if (map.containsKey(name)) {
return (String)map.get(name);
}
return name;
}
}
private final NativeLibrary nativeLibrary;
private final Class interfaceClass;
// Library invocation options
private final Map options;
private FunctionMapper functionMapper;
private final InvocationMapper invocationMapper;
private final Map functions = new WeakHashMap();
public Handler(String libname, Class interfaceClass, Map options) {
if (libname != null && "".equals(libname.trim())) {
throw new IllegalArgumentException("Invalid library name \""
+ libname + "\"");
}
this.interfaceClass = interfaceClass;
options = new HashMap(options);
int callingConvention =
AltCallingConvention.class.isAssignableFrom(interfaceClass)
? Function.ALT_CONVENTION : Function.C_CONVENTION;
if (options.get(OPTION_CALLING_CONVENTION) == null) {
options.put(OPTION_CALLING_CONVENTION,
new Integer(callingConvention));
}
this.options = options;
this.nativeLibrary = NativeLibrary.getInstance(libname, options);
functionMapper = (FunctionMapper)options.get(OPTION_FUNCTION_MAPPER);
if (functionMapper == null) {
// backward compatibility; passed-in map is itself the name map
functionMapper = new FunctionNameMap(options);
}
invocationMapper = (InvocationMapper)options.get(OPTION_INVOCATION_MAPPER);
}
public NativeLibrary getNativeLibrary() {
return nativeLibrary;
}
public String getLibraryName() {
return nativeLibrary.getName();
}
public Class getInterfaceClass() {
return interfaceClass;
}
private static class FunctionInfo {
InvocationHandler handler;
Function function;
boolean isVarArgs;
Map options;
}
public Object invoke(Object proxy, Method method, Object[] inArgs)
throws Throwable {
// Intercept Object methods
if (OBJECT_TOSTRING.equals(method)) {
return "Proxy interface to " + nativeLibrary;
}
else if (OBJECT_HASHCODE.equals(method)) {
return new Integer(hashCode());
}
else if (OBJECT_EQUALS.equals(method)) {
Object o = inArgs[0];
if (o != null && Proxy.isProxyClass(o.getClass())) {
return Function.valueOf(Proxy.getInvocationHandler(o) == this);
}
return Boolean.FALSE;
}
FunctionInfo f = null;
synchronized(functions) {
f = (FunctionInfo)functions.get(method);
if (f == null) {
f = new FunctionInfo();
f.isVarArgs = Function.isVarArgs(method);
if (invocationMapper != null) {
f.handler = invocationMapper.getInvocationHandler(nativeLibrary, method);
}
if (f.handler == null) {
// Find the function to invoke
String methodName =
functionMapper.getFunctionName(nativeLibrary, method);
if (methodName == null) {
// Just in case the function mapper screwed up
methodName = method.getName();
}
f.function = nativeLibrary.getFunction(methodName, method);
f.options = new HashMap(this.options);
f.options.put(Function.OPTION_INVOKING_METHOD, method);
}
functions.put(method, f);
}
}
if (f.isVarArgs) {
inArgs = Function.concatenateVarArgs(inArgs);
}
if (f.handler != null) {
return f.handler.invoke(proxy, method, inArgs);
}
return f.function.invoke(method.getReturnType(), inArgs, f.options);
}
}
}