Add Java bindings.

This commit is contained in:
Martin Ling 2014-04-27 09:48:18 +01:00
parent f774095496
commit 9fcf4d0b5b
7 changed files with 418 additions and 1 deletions

7
.gitignore vendored
View File

@ -61,6 +61,13 @@ bindings/python/sigrok/core/lowlevel_wrap.c
bindings/python/sigrok/core/classes.py
bindings/python/sigrok/core/classes_wrap.cpp
# Files generated by building Java bindings
*.class
bindings/java/org/sigrok/core/classes/*.java
bindings/java/org/sigrok/core/classes/classes_wrap.cxx
bindings/java/libsigrok_java_core_classes.so
bindings/java/sigrok-core.jar
# Files generated by the testsuite
test-suite.log
tests/*.log

View File

@ -427,6 +427,50 @@ CLEAN_EXTRA += python-clean
endif
if BINDINGS_JAVA
JDIR = bindings/java
JPKG = org/sigrok/core/classes
JINT = $(JDIR)/$(JPKG)/classes.i
JSRC = $(JDIR)/$(JPKG)/classes_wrap.cxx
JLIB = $(JDIR)/libsigrok_java_core_classes.so
JJAR = $(JDIR)/sigrok-core.jar
java-build: $(JJAR) $(JLIB)
$(JSRC): $(JINT) bindings/swig/classes.i
swig -c++ -java -package org.sigrok.core.classes \
-Iinclude -Ibindings/cxx/include \
-outdir $(JDIR)/$(JPKG) $(JINT)
$(JJAR): $(JSRC) $(JDIR)/$(JPKG)/*.java
$(JAVAC) -sourcepath $(JDIR) $(JDIR)/$(JPKG)/*.java
jar cf $(JJAR) -C $(JDIR) $(JPKG)
$(JLIB): $(JSRC) bindings/cxx/libsigrokxx.la
$(CXX) $(CXXFLAGS) -L.libs -Lbindings/cxx/.libs \
-fno-strict-aliasing -fPIC -shared \
$(JDIR)/$(JPKG)/classes_wrap.cxx -lsigrokxx \
-o $(JLIB)
java-install:
$(INSTALL) -d $(libdir)/jni
$(INSTALL) $(JLIB) -t $(libdir)/jni
$(INSTALL) -d $(datadir)/java
$(INSTALL) $(JJAR) -t $(datadir)/java
java-clean:
rm $(JSRC)
rm $(JDIR)/$(JPKG)/*.class
rm $(JJAR)
rm $(JLIB)
BUILD_EXTRA += java-build
INSTALL_EXTRA += java-install
CLEAN_EXTRA += java-clean
endif
all-local: $(BUILD_EXTRA)
install-exec-local: $(INSTALL_EXTRA)
clean-extra: $(CLEAN_EXTRA)

View File

@ -0,0 +1,6 @@
package org.sigrok.core.classes;
public interface DatafeedCallback
{
public void run(Device device, Packet packet);
}

View File

@ -0,0 +1,6 @@
package org.sigrok.core.classes;
public interface LogCallback
{
public void run(LogLevel loglevel, String message);
}

View File

@ -0,0 +1,6 @@
package org.sigrok.core.classes;
public interface SourceCallback
{
public void run(int revents);
}

View File

@ -0,0 +1,325 @@
%module classes
/* Automatically load JNI library. */
%pragma(java) jniclasscode=%{
static {
System.loadLibrary("sigrok_java_core_classes");
}
%}
/* Map Java FileDescriptor objects to int fds */
%typemap(jni) int fd "jobject"
%typemap(jtype) int fd "java.io.FileDescriptor"
%typemap(jstype) int fd "java.io.FileDescriptor"
%typemap(javain) int fd "$javainput"
%typemap(in) int fd {
jclass FileDescriptor = jenv->FindClass("java/io/FileDescriptor");
jfieldID fd = jenv->GetFieldID(FileDescriptor, "fd", "I");
$1 = jenv->GetIntField($input, fd);
}
/* Map Glib::VariantBase to a Variant class in Java */
%rename(Variant) VariantBase;
namespace Glib {
class VariantBase {};
}
/* Map between std::vector and java.util.Vector */
%define VECTOR(CValue, JValue)
%typemap(jni) std::vector< CValue > "jobject"
%typemap(jtype) std::vector< CValue > "java.util.Vector<JValue>"
%typemap(jstype) std::vector< CValue > "java.util.Vector<JValue>"
%typemap(javain,
pre=" $javaclassname temp$javainput = $javaclassname.convertVector($javainput);",
pgcppname="temp$javainput")
std::vector< CValue > "$javaclassname.getCPtr(temp$javainput)"
%typemap(javacode) std::vector< CValue > %{
static $javaclassname convertVector(java.util.Vector<JValue> in)
{
$javaclassname out = new $javaclassname();
for (JValue value : in)
out.add(value);
return out;
}
%}
%typemap(javaout) std::vector< CValue > {
return (java.util.Vector<JValue>)$jnicall;
}
%typemap(out) std::vector< CValue > {
jclass Vector = jenv->FindClass("java/util/Vector");
jmethodID Vector_init = jenv->GetMethodID(Vector, "<init>", "()V");
jmethodID Vector_add = jenv->GetMethodID(Vector, "add",
"(Ljava/lang/Object;)Z");
jclass Value = jenv->FindClass("org/sigrok/core/classes/" #JValue);
jmethodID Value_init = jenv->GetMethodID(Value, "<init>", "(JZ)V");
$result = jenv->NewObject(Vector, Vector_init);
jlong value;
for (auto entry : $1)
{
*(CValue **) &value = new CValue(entry);
jenv->CallObjectMethod($result, Vector_add,
jenv->NewObject(Value, Value_init, value, true));
}
}
%enddef
VECTOR(std::shared_ptr<sigrok::Channel>, Channel)
VECTOR(std::shared_ptr<sigrok::HardwareDevice>, HardwareDevice)
/* Common macro for mapping between std::map and java.util.Map */
%define MAP_COMMON(CKey, CValue, JKey, JValue)
%typemap(jstype) std::map< CKey, CValue >
"java.util.Map<JKey, JValue>"
%typemap(javain,
pre=" $javaclassname temp$javainput = $javaclassname.convertMap($javainput);",
pgcppname="temp$javainput")
std::map< CKey, CValue > "$javaclassname.getCPtr(temp$javainput)"
%typemap(javacode) std::map< CKey, CValue > %{
static $javaclassname convertMap(java.util.Map<JKey,JValue> in)
{
$javaclassname out = new $javaclassname();
for (java.util.Map.Entry<JKey, JValue> entry : in.entrySet())
out.set(entry.getKey(), entry.getValue());
return out;
}
%}
%typemap(javaout) std::map< CKey, CValue > {
return (java.util.Map<JKey, JValue>)$jnicall;
}
%enddef
/* Specialisation for string->string maps. */
MAP_COMMON(std::string, std::string, String, String)
%typemap(out) std::map<std::string, std::string> {
jclass HashMap = jenv->FindClass("java/util/HashMap");
jmethodID init = jenv->GetMethodID(HashMap, "<init>", "()V");
jmethodID put = jenv->GetMethodID(HashMap, "put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
$result = jenv->NewObject(HashMap, init);
for (auto entry : $1)
jenv->CallObjectMethod($result, put,
jenv->NewStringUTF(entry.first.c_str()),
jenv->NewStringUTF(entry.second.c_str()));
}
/* Specialisation macro for string->shared_ptr maps. */
%define STRING_TO_SHARED_PTR_MAP(ClassName)
%typemap(jni) std::map<std::string, std::shared_ptr<sigrok::ClassName> >
"jobject"
%typemap(jtype) std::map<std::string, std::shared_ptr<sigrok::ClassName> >
"java.util.Map<String,ClassName>"
MAP_COMMON(std::string, std::shared_ptr<sigrok::ClassName>, String, ClassName)
%typemap(out) std::map<std::string, std::shared_ptr<sigrok::ClassName> > {
jclass HashMap = jenv->FindClass("java/util/HashMap");
jmethodID HashMap_init = jenv->GetMethodID(HashMap, "<init>", "()V");
jmethodID HashMap_put = jenv->GetMethodID(HashMap, "put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
jclass Value = jenv->FindClass("org/sigrok/core/classes/" #ClassName);
jmethodID Value_init = jenv->GetMethodID(Value, "<init>", "(JZ)V");
$result = jenv->NewObject(HashMap, HashMap_init);
jlong value;
for (auto entry : $1)
{
*(std::shared_ptr< sigrok::ClassName > **)&value =
new std::shared_ptr< sigrok::ClassName>(entry.second);
jenv->CallObjectMethod($result, HashMap_put,
jenv->NewStringUTF(entry.first.c_str()),
jenv->NewObject(Value, Value_init, value, true));
}
}
%enddef
STRING_TO_SHARED_PTR_MAP(Driver)
STRING_TO_SHARED_PTR_MAP(InputFormat)
STRING_TO_SHARED_PTR_MAP(OutputFormat)
/* Specialisation for ConfigKey->Variant maps */
MAP_COMMON(const sigrok::ConfigKey *, Glib::VariantBase, ConfigKey, Variant)
%typemap(out) std::map<const sigrok::ConfigKey *, Glib::VariantBase> {
jclass HashMap = jenv->FindClass("java/util/HashMap");
jmethodID HashMap_init = jenv->GetMethodID(HashMap, "<init>", "()V");
jmethodID HashMap_put = jenv->GetMethodID(HashMap, "put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
jclass ConfigKey = jenv->FindClass("org/sigrok/core/classes/ConfigKey");
jmethodID ConfigKey_init = jenv->GetMethodID(ConfigKey, "<init>", "(JZ)V");
jclass Variant = jenv->FindClass("org/sigrok/core/classes/Variant");
jmethodID Variant_init = jenv->GetMethodID(Variant, "<init>", "(JZ)V");
$result = jenv->NewObject(HashMap, HashMap_init);
jlong key;
jlong value;
for (auto entry : $1)
{
*(const sigrok::ConfigKey **) &key = entry.first;
*(Glib::VariantBase **) &value = new Glib::VariantBase(entry.second);
jenv->CallObjectMethod($result, HashMap_put,
jenv->NewObject(ConfigKey, ConfigKey_init, key, false));
jenv->NewObject(Variant, Variant_init, value, true));
}
}
/* Support Driver.scan() with no arguments. */
%extend sigrok::Driver {
std::vector<std::shared_ptr<sigrok::HardwareDevice> > scan()
{
std::map<const sigrok::ConfigKey *, Glib::VariantBase> options;
return $self->scan(options);
}
}
/* Support OutputFormat.create_output(device) with no options. */
%extend sigrok::OutputFormat {
std::shared_ptr<sigrok::Output> create_output(
std::shared_ptr<sigrok::Device> device)
{
std::map<std::string, std::string> options;
return $self->create_output(device, options);
}
}
/* Pass JNIEnv parameter to C++ extension methods requiring it. */
%typemap(in, numinputs=0) JNIEnv * %{
$1 = jenv;
%}
/* Support Java log callbacks. */
%inline {
typedef jobject jlogcallback;
}
%typemap(jni) jlogcallback "jlogcallback"
%typemap(jtype) jlogcallback "LogCallback"
%typemap(jstype) jlogcallback "LogCallback"
%typemap(javain) jlogcallback "$javainput"
%extend sigrok::Context
{
void add_log_callback(JNIEnv *env, jlogcallback obj)
{
jclass obj_class = env->GetObjectClass(obj);
jmethodID method = env->GetMethodID(obj_class, "run",
"(Lorg/sigrok/core/classes/LogLevel;Ljava/lang/String;)V");
jclass LogLevel = (jclass) env->NewGlobalRef(
env->FindClass("org/sigrok/core/classes/LogLevel"));
jmethodID LogLevel_init = env->GetMethodID(LogLevel, "<init>", "(JZ)V");
jobject obj_ref = env->NewGlobalRef(obj);
$self->set_log_callback([=] (
const sigrok::LogLevel *loglevel,
std::string message)
{
jlong loglevel_addr;
*(const sigrok::LogLevel **) &loglevel_addr = loglevel;
jobject loglevel_obj = env->NewObject(
LogLevel, LogLevel_init, loglevel_addr, false);
jobject message_obj = env->NewStringUTF(message.c_str());
env->CallVoidMethod(obj_ref, method, loglevel_obj, message_obj);
if (env->ExceptionCheck())
throw sigrok::Error(SR_ERR);
});
}
}
/* Support Java datafeed callbacks. */
%inline {
typedef jobject jdatafeedcallback;
}
%typemap(jni) jdatafeedcallback "jdatafeedcallback"
%typemap(jtype) jdatafeedcallback "DatafeedCallback"
%typemap(jstype) jdatafeedcallback "DatafeedCallback"
%typemap(javain) jdatafeedcallback "$javainput"
%extend sigrok::Session
{
void add_datafeed_callback(JNIEnv *env, jdatafeedcallback obj)
{
jclass obj_class = env->GetObjectClass(obj);
jmethodID method = env->GetMethodID(obj_class, "run",
"(Lorg/sigrok/core/classes/Device;Lorg/sigrok/core/classes/Packet;)V");
jclass Device = (jclass) env->NewGlobalRef(
env->FindClass("org/sigrok/core/classes/Device"));
jmethodID Device_init = env->GetMethodID(Device, "<init>", "(JZ)V");
jclass Packet = (jclass) env->NewGlobalRef(
env->FindClass("org/sigrok/core/classes/Packet"));
jmethodID Packet_init = env->GetMethodID(Packet, "<init>", "(JZ)V");
jobject obj_ref = env->NewGlobalRef(obj);
$self->add_datafeed_callback([=] (
std::shared_ptr<sigrok::Device> device,
std::shared_ptr<sigrok::Packet> packet)
{
jlong device_addr;
jlong packet_addr;
*(std::shared_ptr<sigrok::Device> **) &device_addr =
new std::shared_ptr<sigrok::Device>(device);
*(std::shared_ptr<sigrok::Packet> **) &packet_addr =
new std::shared_ptr<sigrok::Packet>(packet);
jobject device_obj = env->NewObject(
Device, Device_init, device_addr, true);
jobject packet_obj = env->NewObject(
Packet, Packet_init, packet_addr, true);
env->CallVoidMethod(obj_ref, method, device_obj, packet_obj);
if (env->ExceptionCheck())
throw sigrok::Error(SR_ERR);
});
}
}
/* Support Java event source callbacks. */
%inline {
typedef jobject jsourcecallback;
}
%typemap(jni) jsourcecallback "jsourcecallback"
%typemap(jtype) jsourcecallback "SourceCallback"
%typemap(jstype) jsourcecallback "SourceCallback"
%typemap(javain) jsourcecallback "$javainput"
%extend sigrok::EventSource
{
std::shared_ptr<sigrok::EventSource> create(
int fd, Glib::IOCondition events, int timeout,
JNIEnv *env, jsourcecallback obj)
{
(void) $self;
jclass obj_class = env->GetObjectClass(obj);
jmethodID method = env->GetMethodID(obj_class, "run", "(I)V");
jobject obj_ref = env->NewGlobalRef(obj);
return sigrok::EventSource::create(fd, events, timeout, [=] (int revents)
{
bool result = env->CallBooleanMethod(obj_ref, method, revents);
if (env->ExceptionCheck())
throw sigrok::Error(SR_ERR);
return result;
});
}
}
%include "bindings/swig/classes.i"

View File

@ -166,6 +166,11 @@ AC_ARG_ENABLE(python,
[build Python bindings [default=yes]]),
[BINDINGS_PYTHON="$enableval"], [BINDINGS_PYTHON="yes"])
AC_ARG_ENABLE(java,
AC_HELP_STRING([--enable-java],
[build Java bindings [default=yes]]),
[BINDINGS_PYTHON="$enableval"], [BINDINGS_JAVA="yes"])
# Check if the C++ compiler supports the C++11 standard.
AX_CXX_COMPILE_STDCXX_11(,[optional])
@ -174,10 +179,11 @@ if test "x$HAVE_CXX11" != "x1"; then
BINDINGS_CXX="no"
fi
# Python bindings depend on C++ bindings.
# Python and Java bindings depend on C++ bindings.
if test "x$BINDINGS_CXX" != "xyes"; then
BINDINGS_PYTHON="no"
BINDINGS_JAVA="no"
fi
# Checks for libraries.
@ -362,6 +368,20 @@ AC_SUBST(SR_PKGLIBS)
CFLAGS="$CFLAGS -I./include/libsigrok $LIB_CFLAGS"
CXXFLAGS="$CXXFLAGS -I./include -I./bindings/cxx/include $LIB_CFLAGS"
# Find Java compiler and JNI includes for Java bindings.
AC_CHECK_PROG([HAVE_JAVAC], [javac], [yes], [no])
if test "x$HAVE_JAVAC" = "xyes"; then
AX_PROG_JAVAC
AX_JNI_INCLUDE_DIR
for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS; do
CXXFLAGS="$CXXFLAGS -I$JNI_INCLUDE_DIR"
done
else
BINDINGS_JAVA="no"
fi
# Now set AM_CONDITIONALs and AC_DEFINEs for the enabled/disabled drivers.
AM_CONDITIONAL(HW_AGILENT_DMM, test x$HW_AGILENT_DMM = xyes)
@ -553,6 +573,8 @@ AM_CONDITIONAL(BINDINGS_CXX, test x$BINDINGS_CXX = xyes)
AM_CONDITIONAL(BINDINGS_PYTHON, test x$BINDINGS_PYTHON = xyes)
AM_CONDITIONAL(BINDINGS_JAVA, test x$BINDINGS_JAVA = xyes)
# Checks for header files.
# These are already checked: inttypes.h stdint.h stdlib.h string.h unistd.h.
@ -609,4 +631,5 @@ echo -e "\nEnabled hardware drivers:\n${driver_summary}"
echo -e "\nEnabled language bindings:\n"
echo " - C++............................. $BINDINGS_CXX"
echo " - Python.......................... $BINDINGS_PYTHON"
echo " - Java............................ $BINDINGS_JAVA"
echo