From a990859db622bdaed3e9772dda22e2e274ea3e61 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Fri, 28 Aug 2015 12:35:04 -0700 Subject: [PATCH] Initial commit of Java wrappers. The JNI bindings are built directly into the shared library. In the gradle build, all built shared libraries are embedded into the generated jar. Java bindings may be disabled via -DWITHOUT_JAVA (cmake) or -PskipJava=true (gradle). TODO: - getEntryInfo() and RPC are not yet implemented. - The cmake build doesn't integrate the built objects into the jar. - The Java client and server tests are not built (but have been manually tested). This has not yet been tested on Windows. --- CMakeLists.txt | 34 + build.gradle | 146 ++ java/lib/NetworkTablesJNI.cpp | 1178 +++++++++++++++++ .../wpilibj/networktables/ConnectionInfo.java | 17 + .../wpilibj/networktables/NetworkTable.java | 735 ++++++++++ .../NetworkTableKeyNotDefined.java | 22 + .../networktables/NetworkTablesJNI.java | 145 ++ .../networktables/PersistentException.java | 18 + .../edu/wpi/first/wpilibj/tables/IRemote.java | 37 + .../tables/IRemoteConnectionListener.java | 20 + .../edu/wpi/first/wpilibj/tables/ITable.java | 292 ++++ .../first/wpilibj/tables/ITableListener.java | 19 + .../tables/TableKeyNotDefinedException.java | 20 + java/test/Client.java | 39 + java/test/Server.java | 26 + ntcore.def | 59 + 16 files changed, 2807 insertions(+) create mode 100644 java/lib/NetworkTablesJNI.cpp create mode 100644 java/src/edu/wpi/first/wpilibj/networktables/ConnectionInfo.java create mode 100644 java/src/edu/wpi/first/wpilibj/networktables/NetworkTable.java create mode 100644 java/src/edu/wpi/first/wpilibj/networktables/NetworkTableKeyNotDefined.java create mode 100644 java/src/edu/wpi/first/wpilibj/networktables/NetworkTablesJNI.java create mode 100644 java/src/edu/wpi/first/wpilibj/networktables/PersistentException.java create mode 100644 java/src/edu/wpi/first/wpilibj/tables/IRemote.java create mode 100644 java/src/edu/wpi/first/wpilibj/tables/IRemoteConnectionListener.java create mode 100644 java/src/edu/wpi/first/wpilibj/tables/ITable.java create mode 100644 java/src/edu/wpi/first/wpilibj/tables/ITableListener.java create mode 100644 java/src/edu/wpi/first/wpilibj/tables/TableKeyNotDefinedException.java create mode 100644 java/test/Client.java create mode 100644 java/test/Server.java diff --git a/CMakeLists.txt b/CMakeLists.txt index d8c47f1851..4512af415f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,40 @@ fix_default_compiler_settings_() file(GLOB_RECURSE SRC_FILES src/*.cpp) include_directories(include src) + +# Java bindings +if (NOT WITHOUT_JAVA) + find_package(Java) + find_package(JNI) + include(UseJava) + set(CMAKE_JAVA_COMPILE_FLAGS "-Xlint:unchecked") + + include_directories(${JNI_INCLUDE_DIRS}) + list(APPEND SRC_FILES java/lib/NetworkTablesJNI.cpp) + + file(GLOB_RECURSE JAVA_SOURCES java/src/*.java) + set(CMAKE_JNI_TARGET true) + add_jar(networktables ${JAVA_SOURCES}) + + # Generate JNI headers + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/edu_wpi_first_wpilibj_networktables_NetworkTablesJNI.h" + DEPENDS networktables java/src/edu/wpi/first/wpilibj/networktables/NetworkTablesJNI.java + COMMAND "${Java_JAVAH_EXECUTABLE}" + -jni + -o "${CMAKE_CURRENT_BINARY_DIR}/edu_wpi_first_wpilibj_networktables_NetworkTablesJNI.h" + -classpath "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/networktables.dir/" + edu.wpi.first.wpilibj.networktables.NetworkTablesJNI + ) + include_directories("${CMAKE_CURRENT_BINARY_DIR}") + set_source_files_properties( + java/lib/NetworkTablesJNI.cpp + OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/edu_wpi_first_wpilibj_networktables_NetworkTablesJNI.h" + ) + + #add_subdirectory(java/test) +endif() + if (WIN32) add_library(ntcore SHARED ${SRC_FILES} ntcore.def) else() diff --git a/build.gradle b/build.gradle index e1b9400974..76aafaa5c6 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,17 @@ import org.apache.tools.ant.taskdefs.condition.Os +if (!project.hasProperty('skipJava')) { +apply plugin: 'java' +} apply plugin: 'cpp' apply plugin: 'visual-studio' +def armjdkDownloadSite = 'http://www.oracle.com/technetwork/java/javase/downloads/jdk8-arm-downloads-2187472.html' +def armjdkFolder = 'jdk-linux-arm-vfp-sflt' +def armjdkVersion = 'jdk1.8.0_33' +def armjdkLocation = System.getProperty("user.home") + File.separator + armjdkFolder + File.separator + armjdkVersion +def generatedJNIHeaderLoc = 'build/include' + model { toolChains { gcc(Gcc) { @@ -38,9 +47,11 @@ model { // deprecated function, remove this line (this will cause calling deprecated functions // to be treated as a warning rather than an error). args << '-Wno-error=deprecated-declarations' + args << '-m32' } linker.withArguments { args -> args << '-rdynamic' + args << '-m32' } } target('x64') { @@ -95,14 +106,55 @@ model { targetPlatform 'arm' targetPlatform 'x86' targetPlatform 'x64' + binaries.all { + tasks.withType(CppCompile) { + if (!project.hasProperty('skipJava')) { + if (targetPlatform == platforms.arm) { + dependsOn verifyArmJre + + // JDK is included for jni.h. We also need the arm-linux specific headers. + // This does not need to change when compiling on Windows + // The JNI headers are put into the build/include directory by the jniHeaders task + cppCompiler.args '-I', "${armjdkLocation}/include" + cppCompiler.args '-I', "${armjdkLocation}/include/linux" + } else { + def jdkLocation = org.gradle.internal.jvm.Jvm.current().javaHome + cppCompiler.args '-I', "${jdkLocation}/include" + + if (targetPlatform.operatingSystem.macOsX) { + cppCompiler.args '-I', "${jdkLocation}/include/darwin" + } else if (targetPlatform.operatingSystem.linux) { + cppCompiler.args '-I', "${jdkLocation}/include/linux" + } else if (targetPlatform.operatingSystem.windows) { + cppCompiler.args '-I', "${jdkLocation}/include/win32" + } else if (targetPlatform.operatingSystem.freeBSD) { + cppCompiler.args '-I', "${jdkLocation}/include/freebsd" + } + } + + jniHeadersNetworkTables.outputs.files.each { file -> + cppCompiler.args '-I', file.getPath() + } + dependsOn jniHeadersNetworkTables + } + } + } sources { cpp { source { srcDirs = ["src"] + if (!project.hasProperty('skipJava')) { + srcDirs "java/lib" + } includes = ["**/*.cpp"] } exportedHeaders { srcDirs = ["include"] + if (!project.hasProperty('skipJava')) { + jniHeadersNetworkTables.outputs.files.each { file -> + srcDirs file.getPath() + } + } includes = ["**/*.h"] } } @@ -110,3 +162,97 @@ model { } } } + +if (!project.hasProperty('skipJava')) { + +compileJava { + options.compilerArgs << "-Xlint:unchecked" +} + +sourceSets { + main { + java { + srcDirs = ["java/src"] + } + } +} + +jar { + description = 'Generates NetworkTables jar, with the JNI shared libraries embedded' + dependsOn { armNtcoreSharedLibrary } + dependsOn { x64NtcoreSharedLibrary } + dependsOn { x86NtcoreSharedLibrary } + dependsOn { classes } + binaries.withType(SharedLibraryBinary) { binary -> + from(file(binary.sharedLibraryFile)) { + if (binary.targetPlatform == platforms.arm) { + into "Linux/arm" + } else if (binary.targetPlatform.operatingSystem.name == "Linux") { + if (binary.targetPlatform.architecture.name == "x86-64") { + into "Linux/amd64" + } else { + into "Linux/" + binary.targetPlatform.architecture.name + } + } else { + into binary.targetPlatform.operatingSystem.name + "/" + binary.targetPlatform.architecture.name + } + } + } +} + +task networktablesJavaSource(type: Jar, dependsOn: classes) { + description = 'Generates the source jar for NetworkTables java' + group = 'WPILib' + classifier = 'classes' + from sourceSets.main.allJava +} + +task networktablesJavadoc(type: Jar, dependsOn: javadoc) { + description = 'Generates the javadoc jar for NetworkTables java' + group = 'WPILib' + classifier = 'javadoc' + from javadoc.destinationDir +} + +/** + * Generates the JNI headers + */ +task jniHeadersNetworkTables { + description = 'Generates JNI headers from edu.wpi.first.wpilibj.networktables.*' + group = 'WPILib' + def outputFolder = file(generatedJNIHeaderLoc) + inputs.files sourceSets.main.output + outputs.file outputFolder + doLast { + outputFolder.mkdirs() + exec { + executable org.gradle.internal.jvm.Jvm.current().getExecutable('javah') + args '-d', outputFolder + args '-classpath', sourceSets.main.output.classesDir + args 'edu.wpi.first.wpilibj.networktables.NetworkTablesJNI' + } + } +} + +clean { + delete generatedJNIHeaderLoc +} + +// Ensures that the ARM JNI headers have been downloaded and are in the correct location for generating the JNI build +task verifyArmJre { + description = 'Verifies that the ARM JDK is downloaded in the user directory' + group = 'WPILib' + def outputFolder = new File(armjdkLocation) + outputs.file outputFolder + doLast { + if (!outputFolder.exists() && !outputFolder.isDirectory()) { + def errorMessage = 'The ARM JDK was not found. Please install the JDK in the following location:' + + System.lineSeparator() + armjdkLocation + + System.lineSeparator() + 'You can download the JDK here:' + + System.lineSeparator() + armjdkDownloadSite + throw new GradleException(errorMessage) + } + } +} + +} // skipJava diff --git a/java/lib/NetworkTablesJNI.cpp b/java/lib/NetworkTablesJNI.cpp new file mode 100644 index 0000000000..9b7f7edc4d --- /dev/null +++ b/java/lib/NetworkTablesJNI.cpp @@ -0,0 +1,1178 @@ +#include +#include +#include + +#include "edu_wpi_first_wpilibj_networktables_NetworkTablesJNI.h" +#include "ntcore.h" + +// +// Globals and load/unload +// + +// Used for callback. +static JavaVM *jvm; +static jclass booleanCls = nullptr; +static jclass doubleCls = nullptr; +static jclass stringCls = nullptr; +static jclass connectionInfoCls = nullptr; +static jclass keyNotDefinedEx = nullptr; +static jclass persistentEx = nullptr; + +jint JNI_OnLoad(JavaVM *vm, void *reserved) { + jvm = vm; + + JNIEnv *env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) + return JNI_ERR; + + // Cache references to classes + jclass local; + + local = env->FindClass("java/lang/Boolean"); + if (!local) return JNI_ERR; + booleanCls = static_cast(env->NewGlobalRef(local)); + if (!booleanCls) return JNI_ERR; + env->DeleteLocalRef(local); + + local = env->FindClass("java/lang/Double"); + if (!local) return JNI_ERR; + doubleCls = static_cast(env->NewGlobalRef(local)); + if (!doubleCls) return JNI_ERR; + env->DeleteLocalRef(local); + + local = env->FindClass("java/lang/String"); + if (!local) return JNI_ERR; + stringCls = static_cast(env->NewGlobalRef(local)); + if (!stringCls) return JNI_ERR; + env->DeleteLocalRef(local); + + local = env->FindClass("edu/wpi/first/wpilibj/networktables/ConnectionInfo"); + if (!local) return JNI_ERR; + connectionInfoCls = static_cast(env->NewGlobalRef(local)); + if (!connectionInfoCls) return JNI_ERR; + env->DeleteLocalRef(local); + + local = + env->FindClass("edu/wpi/first/wpilibj/networktables/NetworkTableKeyNotDefined"); + keyNotDefinedEx = static_cast(env->NewGlobalRef(local)); + if (!keyNotDefinedEx) return JNI_ERR; + env->DeleteLocalRef(local); + + local = + env->FindClass("edu/wpi/first/wpilibj/networktables/PersistentException"); + persistentEx = static_cast(env->NewGlobalRef(local)); + if (!persistentEx) return JNI_ERR; + env->DeleteLocalRef(local); + + return JNI_VERSION_1_6; +} + +void JNI_OnUnload(JavaVM *vm, void *reserved) { + JNIEnv *env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) + return; + // Delete global references + if (booleanCls) env->DeleteGlobalRef(booleanCls); + if (doubleCls) env->DeleteGlobalRef(doubleCls); + if (stringCls) env->DeleteGlobalRef(stringCls); + if (connectionInfoCls) env->DeleteGlobalRef(connectionInfoCls); + if (keyNotDefinedEx) env->DeleteGlobalRef(keyNotDefinedEx); + if (persistentEx) env->DeleteGlobalRef(persistentEx); +} + +// +// Helper class to automatically clean up a local reference +// +template +class JavaLocal { + public: + JavaLocal(JNIEnv *env, T obj) : m_env(env), m_obj(obj) {} + ~JavaLocal() { + if (m_obj) m_env->DeleteLocalRef(m_obj); + } + operator T() { return m_obj; } + T obj() { return m_obj; } + + private: + JNIEnv *m_env; + T m_obj; +}; + +// +// Helper class to create and clean up a global reference +// +template +class JavaGlobal { + public: + JavaGlobal(JNIEnv *env, T obj) + : m_obj(static_cast(env->NewGlobalRef(obj))) {} + ~JavaGlobal() { + JNIEnv *env; + if (jvm->AttachCurrentThread(reinterpret_cast(&env), nullptr) != + JNI_OK) + return; + env->DeleteGlobalRef(m_obj); + jvm->DetachCurrentThread(); + } + operator T() { return m_obj; } + T obj() { return m_obj; } + + private: + T m_obj; +}; + +// +// Helper class to create and clean up a weak global reference +// +template +class JavaWeakGlobal { + public: + JavaWeakGlobal(JNIEnv *env, T obj) + : m_obj(static_cast(env->NewWeakGlobalRef(obj))) {} + ~JavaWeakGlobal() { + JNIEnv *env; + if (jvm->AttachCurrentThread(reinterpret_cast(&env), nullptr) != + JNI_OK) + return; + env->DeleteWeakGlobalRef(m_obj); + jvm->DetachCurrentThread(); + } + JavaLocal obj(JNIEnv *env) { + return JavaLocal(env, env->NewLocalRef(m_obj)); + } + + private: + T m_obj; +}; + +// +// Conversions from Java objects to C++ +// + +class JavaStringRef { + public: + JavaStringRef(JNIEnv *env, jstring str) + : m_env(env), + m_jstr(str), + m_str(env->GetStringUTFChars(str, nullptr)) {} + ~JavaStringRef() { m_env->ReleaseStringUTFChars(m_jstr, m_str); } + + operator nt::StringRef() const { return nt::StringRef(m_str); } + nt::StringRef str() const { return nt::StringRef(m_str); } + const char *c_str() const { return m_str; } + + private: + JNIEnv *m_env; + jstring m_jstr; + const char *m_str; +}; + +class JavaByteRef { + public: + JavaByteRef(JNIEnv *env, jbyteArray jarr) + : m_env(env), + m_jarr(jarr), + m_elements(env->GetByteArrayElements(jarr, nullptr)), + m_size(env->GetArrayLength(jarr)) {} + ~JavaByteRef() { + m_env->ReleaseByteArrayElements(m_jarr, m_elements, JNI_ABORT); + } + + operator nt::StringRef() const { + return nt::StringRef(reinterpret_cast(m_elements), m_size); + } + + private: + JNIEnv *m_env; + jbyteArray m_jarr; + jbyte *m_elements; + size_t m_size; +}; + +std::shared_ptr FromJavaRaw(JNIEnv *env, jbyteArray jarr) { + size_t len = env->GetArrayLength(jarr); + jbyte *elements = + static_cast(env->GetPrimitiveArrayCritical(jarr, nullptr)); + if (!elements) return nullptr; + auto rv = nt::Value::MakeRaw( + nt::StringRef(reinterpret_cast(elements), len)); + env->ReleasePrimitiveArrayCritical(jarr, elements, JNI_ABORT); + return rv; +} + +std::shared_ptr FromJavaRpc(JNIEnv *env, jbyteArray jarr) { + size_t len = env->GetArrayLength(jarr); + jbyte *elements = + static_cast(env->GetPrimitiveArrayCritical(jarr, nullptr)); + if (!elements) return nullptr; + auto rv = nt::Value::MakeRpc( + nt::StringRef(reinterpret_cast(elements), len)); + env->ReleasePrimitiveArrayCritical(jarr, elements, JNI_ABORT); + return rv; +} + +std::shared_ptr FromJavaBooleanArray(JNIEnv *env, + jbooleanArray jarr) { + size_t len = env->GetArrayLength(jarr); + std::vector arr; + arr.reserve(len); + jboolean *elements = + static_cast(env->GetPrimitiveArrayCritical(jarr, nullptr)); + if (!elements) return nullptr; + for (size_t i = 0; i < len; ++i) arr.push_back(elements[i]); + env->ReleasePrimitiveArrayCritical(jarr, elements, JNI_ABORT); + return nt::Value::MakeBooleanArray(arr); +} + +std::shared_ptr FromJavaDoubleArray(JNIEnv *env, jdoubleArray jarr) { + size_t len = env->GetArrayLength(jarr); + jdouble *elements = + static_cast(env->GetPrimitiveArrayCritical(jarr, nullptr)); + if (!elements) return nullptr; + auto rv = nt::Value::MakeDoubleArray(nt::ArrayRef(elements, len)); + env->ReleasePrimitiveArrayCritical(jarr, elements, JNI_ABORT); + return rv; +} + +std::shared_ptr FromJavaStringArray(JNIEnv *env, jobjectArray jarr) { + size_t len = env->GetArrayLength(jarr); + std::vector arr; + arr.reserve(len); + for (size_t i = 0; i < len; ++i) { + JavaLocal elem( + env, static_cast(env->GetObjectArrayElement(jarr, i))); + if (!elem) return nullptr; + arr.push_back(JavaStringRef(env, elem).str()); + } + return nt::Value::MakeStringArray(std::move(arr)); +} + +// +// Conversions from C++ to Java objects +// + +static inline jstring ToJavaString(JNIEnv *env, nt::StringRef str) { + // fastpath if string already null terminated; if not, need to make a copy + if (str.data()[str.size()] == '\0') + return env->NewStringUTF(str.data()); + else + return env->NewStringUTF(str.str().c_str()); +} + +static jbyteArray ToJavaByteArray(JNIEnv *env, nt::StringRef str) { + jbyteArray jarr = env->NewByteArray(str.size()); + if (!jarr) return nullptr; + env->SetByteArrayRegion(jarr, 0, str.size(), + reinterpret_cast(str.data())); + return jarr; +} + +static jbooleanArray ToJavaBooleanArray(JNIEnv *env, nt::ArrayRef arr) +{ + jbooleanArray jarr = env->NewBooleanArray(arr.size()); + if (!jarr) return nullptr; + jboolean *elements = + static_cast(env->GetPrimitiveArrayCritical(jarr, nullptr)); + if (!elements) return nullptr; + for (size_t i = 0; i < arr.size(); ++i) + elements[i] = arr[i]; + env->ReleasePrimitiveArrayCritical(jarr, elements, 0); + return jarr; +} + +static jdoubleArray ToJavaDoubleArray(JNIEnv *env, nt::ArrayRef arr) +{ + jdoubleArray jarr = env->NewDoubleArray(arr.size()); + if (!jarr) return nullptr; + env->SetDoubleArrayRegion(jarr, 0, arr.size(), arr.data()); + return jarr; +} + +static jobjectArray ToJavaStringArray(JNIEnv *env, + nt::ArrayRef arr) { + jobjectArray jarr = env->NewObjectArray(arr.size(), stringCls, nullptr); + if (!jarr) return nullptr; + for (size_t i = 0; i < arr.size(); ++i) { + JavaLocal elem(env, env->NewStringUTF(arr[i].c_str())); + env->SetObjectArrayElement(jarr, i, elem.obj()); + } + return jarr; +} + +static jobject ToJavaObject(JNIEnv *env, const nt::Value& value) { + static jmethodID booleanConstructor = nullptr; + static jmethodID doubleConstructor = nullptr; + if (!booleanConstructor) + booleanConstructor = env->GetMethodID(booleanCls, "", "(Z)V"); + if (!doubleConstructor) + doubleConstructor = env->GetMethodID(doubleCls, "", "(D)V"); + + switch (value.type()) { + case NT_BOOLEAN: + return env->NewObject(booleanCls, booleanConstructor, + (jboolean)(value.GetBoolean() ? 1 : 0)); + case NT_DOUBLE: + return env->NewObject(doubleCls, doubleConstructor, + (jdouble)value.GetDouble()); + case NT_STRING: + return ToJavaString(env, value.GetString()); + case NT_RAW: + return ToJavaByteArray(env, value.GetRaw()); + case NT_BOOLEAN_ARRAY: + return ToJavaBooleanArray(env, value.GetBooleanArray()); + case NT_DOUBLE_ARRAY: + return ToJavaDoubleArray(env, value.GetDoubleArray()); + case NT_STRING_ARRAY: + return ToJavaStringArray(env, value.GetStringArray()); + case NT_RPC: + return ToJavaByteArray(env, value.GetRpc()); + default: + return nullptr; + } +} + +static jobject ToJavaObject(JNIEnv *env, const nt::ConnectionInfo &info) { + static jmethodID constructor = + env->GetMethodID(connectionInfoCls, "", + "(Ljava/lang/String;Ljava/lang/String;IJI)V"); + JavaLocal remote_id(env, ToJavaString(env, info.remote_id)); + JavaLocal remote_name(env, ToJavaString(env, info.remote_name)); + return env->NewObject(connectionInfoCls, constructor, remote_id.obj(), + remote_name.obj(), (jint)info.remote_port, + (jlong)info.last_update, (jint)info.protocol_version); +} + +// +// Exception throwers +// + +static void ThrowTableKeyNotDefined(JNIEnv *env, jstring key) { + static jmethodID constructor = nullptr; + if (!constructor) + constructor = + env->GetMethodID(keyNotDefinedEx, "", "(Ljava/lang/String;)V"); + jobject exception = env->NewObject(keyNotDefinedEx, constructor, key); + env->Throw(static_cast(exception)); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: containsKey + * Signature: (Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_containsKey + (JNIEnv *env, jclass, jstring key) +{ + auto val = nt::GetEntryValue(JavaStringRef(env, key)); + if (!val) return false; + return true; +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getType + * Signature: (Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getType + (JNIEnv *env, jclass, jstring key) +{ + auto val = nt::GetEntryValue(JavaStringRef(env, key)); + if (!val) return NT_UNASSIGNED; + return val->type(); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: putBoolean + * Signature: (Ljava/lang/String;Z)Z + */ +JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putBoolean + (JNIEnv *env, jclass, jstring key, jboolean value) +{ + return nt::SetEntryValue(JavaStringRef(env, key), + nt::Value::MakeBoolean(value)); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: putDouble + * Signature: (Ljava/lang/String;D)Z + */ +JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putDouble + (JNIEnv *env, jclass, jstring key, jdouble value) +{ + return nt::SetEntryValue(JavaStringRef(env, key), + nt::Value::MakeDouble(value)); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: putString + * Signature: (Ljava/lang/String;Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putString + (JNIEnv *env, jclass, jstring key, jstring value) +{ + return nt::SetEntryValue(JavaStringRef(env, key), + nt::Value::MakeString(JavaStringRef(env, value))); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: putRaw + * Signature: (Ljava/lang/String;[B)Z + */ +JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putRaw + (JNIEnv *env, jclass, jstring key, jbyteArray value) +{ + auto v = FromJavaRaw(env, value); + if (!v) return false; + return nt::SetEntryValue(JavaStringRef(env, key), v); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: putBooleanArray + * Signature: (Ljava/lang/String;[Z)Z + */ +JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putBooleanArray + (JNIEnv *env, jclass, jstring key, jbooleanArray value) +{ + auto v = FromJavaBooleanArray(env, value); + if (!v) return false; + return nt::SetEntryValue(JavaStringRef(env, key), v); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: putDoubleArray + * Signature: (Ljava/lang/String;[D)Z + */ +JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putDoubleArray + (JNIEnv *env, jclass, jstring key, jdoubleArray value) +{ + auto v = FromJavaDoubleArray(env, value); + if (!v) return false; + return nt::SetEntryValue(JavaStringRef(env, key), v); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: putStringArray + * Signature: (Ljava/lang/String;[Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putStringArray + (JNIEnv *env, jclass, jstring key, jobjectArray value) +{ + auto v = FromJavaStringArray(env, value); + if (!v) return false; + return nt::SetEntryValue(JavaStringRef(env, key), v); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: forcePutBoolean + * Signature: (Ljava/lang/String;Z)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutBoolean + (JNIEnv *env, jclass, jstring key, jboolean value) +{ + nt::SetEntryTypeValue(JavaStringRef(env, key), nt::Value::MakeBoolean(value)); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: forcePutDouble + * Signature: (Ljava/lang/String;D)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutDouble + (JNIEnv *env, jclass, jstring key, jdouble value) +{ + nt::SetEntryTypeValue(JavaStringRef(env, key), nt::Value::MakeDouble(value)); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: forcePutString + * Signature: (Ljava/lang/String;Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutString + (JNIEnv *env, jclass, jstring key, jstring value) +{ + nt::SetEntryTypeValue(JavaStringRef(env, key), + nt::Value::MakeString(JavaStringRef(env, value))); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: forcePutRaw + * Signature: (Ljava/lang/String;[B)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutRaw + (JNIEnv *env, jclass, jstring key, jbyteArray value) +{ + auto v = FromJavaRaw(env, value); + if (!v) return; + nt::SetEntryTypeValue(JavaStringRef(env, key), v); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: forcePutBooleanArray + * Signature: (Ljava/lang/String;[Z)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutBooleanArray + (JNIEnv *env, jclass, jstring key, jbooleanArray value) +{ + auto v = FromJavaBooleanArray(env, value); + if (!v) return; + nt::SetEntryTypeValue(JavaStringRef(env, key), v); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: forcePutDoubleArray + * Signature: (Ljava/lang/String;[D)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutDoubleArray + (JNIEnv *env, jclass, jstring key, jdoubleArray value) +{ + auto v = FromJavaDoubleArray(env, value); + if (!v) return; + nt::SetEntryTypeValue(JavaStringRef(env, key), v); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: forcePutStringArray + * Signature: (Ljava/lang/String;[Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutStringArray + (JNIEnv *env, jclass, jstring key, jobjectArray value) +{ + auto v = FromJavaStringArray(env, value); + if (!v) return; + nt::SetEntryTypeValue(JavaStringRef(env, key), v); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getBoolean + * Signature: (Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getBoolean__Ljava_lang_String_2 + (JNIEnv *env, jclass, jstring key) +{ + auto val = nt::GetEntryValue(JavaStringRef(env, key)); + if (!val || !val->IsBoolean()) { + ThrowTableKeyNotDefined(env, key); + return false; + } + return val->GetBoolean(); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getDouble + * Signature: (Ljava/lang/String;)D + */ +JNIEXPORT jdouble JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getDouble__Ljava_lang_String_2 + (JNIEnv *env, jclass, jstring key) +{ + auto val = nt::GetEntryValue(JavaStringRef(env, key)); + if (!val || !val->IsDouble()) { + ThrowTableKeyNotDefined(env, key); + return 0; + } + return val->GetDouble(); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getString + * Signature: (Ljava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getString__Ljava_lang_String_2 + (JNIEnv *env, jclass, jstring key) +{ + auto val = nt::GetEntryValue(JavaStringRef(env, key)); + if (!val || !val->IsString()) { + ThrowTableKeyNotDefined(env, key); + return nullptr; + } + return ToJavaString(env, val->GetString()); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getRaw + * Signature: (Ljava/lang/String;)[B + */ +JNIEXPORT jbyteArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getRaw__Ljava_lang_String_2 + (JNIEnv *env, jclass, jstring key) +{ + auto val = nt::GetEntryValue(JavaStringRef(env, key)); + if (!val || !val->IsRaw()) { + ThrowTableKeyNotDefined(env, key); + return nullptr; + } + return ToJavaByteArray(env, val->GetRaw()); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getBooleanArray + * Signature: (Ljava/lang/String;)[Z + */ +JNIEXPORT jbooleanArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getBooleanArray__Ljava_lang_String_2 + (JNIEnv *env, jclass, jstring key) +{ + auto val = nt::GetEntryValue(JavaStringRef(env, key)); + if (!val || !val->IsBooleanArray()) { + ThrowTableKeyNotDefined(env, key); + return nullptr; + } + return ToJavaBooleanArray(env, val->GetBooleanArray()); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getDoubleArray + * Signature: (Ljava/lang/String;)[D + */ +JNIEXPORT jdoubleArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getDoubleArray__Ljava_lang_String_2 + (JNIEnv *env, jclass, jstring key) +{ + auto val = nt::GetEntryValue(JavaStringRef(env, key)); + if (!val || !val->IsDoubleArray()) { + ThrowTableKeyNotDefined(env, key); + return nullptr; + } + return ToJavaDoubleArray(env, val->GetDoubleArray()); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getStringArray + * Signature: (Ljava/lang/String;)[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getStringArray__Ljava_lang_String_2 + (JNIEnv *env, jclass, jstring key) +{ + auto val = nt::GetEntryValue(JavaStringRef(env, key)); + if (!val || !val->IsStringArray()) { + ThrowTableKeyNotDefined(env, key); + return nullptr; + } + return ToJavaStringArray(env, val->GetStringArray()); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getBoolean + * Signature: (Ljava/lang/String;Z)Z + */ +JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getBoolean__Ljava_lang_String_2Z + (JNIEnv *env, jclass, jstring key, jboolean defaultValue) +{ + auto val = nt::GetEntryValue(JavaStringRef(env, key)); + if (!val || !val->IsBoolean()) return defaultValue; + return val->GetBoolean(); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getDouble + * Signature: (Ljava/lang/String;D)D + */ +JNIEXPORT jdouble JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getDouble__Ljava_lang_String_2D + (JNIEnv *env, jclass, jstring key, jdouble defaultValue) +{ + auto val = nt::GetEntryValue(JavaStringRef(env, key)); + if (!val || !val->IsDouble()) return defaultValue; + return val->GetDouble(); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getString + * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getString__Ljava_lang_String_2Ljava_lang_String_2 + (JNIEnv *env, jclass, jstring key, jstring defaultValue) +{ + auto val = nt::GetEntryValue(JavaStringRef(env, key)); + if (!val || !val->IsString()) return defaultValue; + return ToJavaString(env, val->GetString()); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getRaw + * Signature: (Ljava/lang/String;[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getRaw__Ljava_lang_String_2_3B + (JNIEnv *env, jclass, jstring key, jbyteArray defaultValue) +{ + auto val = nt::GetEntryValue(JavaStringRef(env, key)); + if (!val || !val->IsRaw()) return defaultValue; + return ToJavaByteArray(env, val->GetRaw()); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getBooleanArray + * Signature: (Ljava/lang/String;[Z)[Z + */ +JNIEXPORT jbooleanArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getBooleanArray__Ljava_lang_String_2_3Z + (JNIEnv *env, jclass, jstring key, jbooleanArray defaultValue) +{ + auto val = nt::GetEntryValue(JavaStringRef(env, key)); + if (!val || !val->IsBooleanArray()) return defaultValue; + return ToJavaBooleanArray(env, val->GetBooleanArray()); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getDoubleArray + * Signature: (Ljava/lang/String;[D)[D + */ +JNIEXPORT jdoubleArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getDoubleArray__Ljava_lang_String_2_3D + (JNIEnv *env, jclass, jstring key, jdoubleArray defaultValue) +{ + auto val = nt::GetEntryValue(JavaStringRef(env, key)); + if (!val || !val->IsDoubleArray()) return defaultValue; + return ToJavaDoubleArray(env, val->GetDoubleArray()); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getStringArray + * Signature: (Ljava/lang/String;[Ljava/lang/String;)[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getStringArray__Ljava_lang_String_2_3Ljava_lang_String_2 + (JNIEnv *env, jclass, jstring key, jobjectArray defaultValue) +{ + auto val = nt::GetEntryValue(JavaStringRef(env, key)); + if (!val || !val->IsStringArray()) return defaultValue; + return ToJavaStringArray(env, val->GetStringArray()); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: setEntryFlags + * Signature: (Ljava/lang/String;I)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setEntryFlags + (JNIEnv *env, jclass, jstring key, jint flags) +{ + nt::SetEntryFlags(JavaStringRef(env, key), flags); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getEntryFlags + * Signature: (Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getEntryFlags + (JNIEnv *env, jclass, jstring key) +{ + return nt::GetEntryFlags(JavaStringRef(env, key)); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: deleteEntry + * Signature: (Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_deleteEntry + (JNIEnv *env, jclass, jstring key) +{ + nt::DeleteEntry(JavaStringRef(env, key)); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: deleteAllEntries + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_deleteAllEntries + (JNIEnv *, jclass) +{ + nt::DeleteAllEntries(); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: flush + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_flush + (JNIEnv *, jclass) +{ + nt::Flush(); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: addEntryListener + * Signature: (Ljava/lang/String;Ledu/wpi/first/wpilibj/networktables/NetworkTablesJNI/EntryListenerFunction;Z)I + */ +JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_addEntryListener + (JNIEnv *envouter, jclass, jstring prefix, jobject listener, + jboolean immediateNotify) +{ + // the shared pointer to the weak global will keep it around until the + // entry listener is destroyed + auto listener_global = + std::make_shared>(envouter, listener); + + // cls is a temporary here; cannot be used within callback functor + jclass cls = envouter->GetObjectClass(listener); + if (!cls) return 0; + + // method ids, on the other hand, are safe to retain + jmethodID mid = envouter->GetMethodID( + cls, "apply", "(ILjava/lang/String;Ljava/lang/Object;Z)V"); + if (!mid) return 0; + + return nt::AddEntryListener( + JavaStringRef(envouter, prefix), + [=](unsigned int uid, nt::StringRef name, + std::shared_ptr value, bool is_new) { + // need to attach as we're coming from a separate thread here + JNIEnv *env; + if (jvm->AttachCurrentThread(reinterpret_cast(&env), + nullptr) != JNI_OK) + return; + + // get the handler + auto handler = listener_global->obj(env); + if (!handler) { + // can happen due to weak reference + jvm->DetachCurrentThread(); + return; + } + + // convert the value into the appropriate Java type + jobject jobj = ToJavaObject(env, *value); + + if (!jobj) { + jvm->DetachCurrentThread(); + return; + } + + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + jvm->DetachCurrentThread(); + return; + } + env->CallVoidMethod(handler, mid, (jint)uid, ToJavaString(env, name), + jobj, (jboolean)(is_new ? 1 : 0)); + if (env->ExceptionCheck()) env->ExceptionDescribe(); + + jvm->DetachCurrentThread(); + }, + immediateNotify); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: removeEntryListener + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_removeEntryListener + (JNIEnv *, jclass, jint entryListenerUid) +{ + nt::RemoveEntryListener(entryListenerUid); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: addConnectionListener + * Signature: (Ledu/wpi/first/wpilibj/networktables/NetworkTablesJNI/ConnectionListenerFunction;Z)I + */ +JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_addConnectionListener + (JNIEnv *envouter, jclass, jobject listener, jboolean immediateNotify) +{ + // the shared pointer to the weak global will keep it around until the + // entry listener is destroyed + auto listener_global = + std::make_shared>(envouter, listener); + + // cls is a temporary here; cannot be used within callback functor + jclass cls = envouter->GetObjectClass(listener); + if (!cls) return 0; + + // method ids, on the other hand, are safe to retain + jmethodID mid = envouter->GetMethodID( + cls, "apply", "(IZLedu/wpi/first/wpilibj/networktables/ConnectionInfo;)V"); + if (!mid) return 0; + + return nt::AddConnectionListener( + [=](unsigned int uid, bool connected, const nt::ConnectionInfo& conn) { + // need to attach as we're coming from a separate thread here + JNIEnv *env; + if (jvm->AttachCurrentThread(reinterpret_cast(&env), + nullptr) != JNI_OK) + return; + + // get the handler + auto handler = listener_global->obj(env); + if (!handler) { + // can happen due to weak reference + jvm->DetachCurrentThread(); + return; + } + + // convert into the appropriate Java type + jobject jobj = ToJavaObject(env, conn); + + if (!jobj) { + jvm->DetachCurrentThread(); + return; + } + + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + jvm->DetachCurrentThread(); + return; + } + env->CallVoidMethod(handler, mid, (jint)uid, + (jboolean)(connected ? 1 : 0), jobj); + if (env->ExceptionCheck()) env->ExceptionDescribe(); + + jvm->DetachCurrentThread(); + }, + immediateNotify); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: removeConnectionListener + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_removeConnectionListener + (JNIEnv *, jclass, jint connListenerUid) +{ + nt::RemoveConnectionListener(connListenerUid); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getRpc + * Signature: (Ljava/lang/String;)[B + */ +JNIEXPORT jbyteArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getRpc__Ljava_lang_String_2 + (JNIEnv *env, jclass, jstring key) +{ + auto val = nt::GetEntryValue(JavaStringRef(env, key)); + if (!val || !val->IsRpc()) { + ThrowTableKeyNotDefined(env, key); + return nullptr; + } + return ToJavaByteArray(env, val->GetRpc()); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getRpc + * Signature: (Ljava/lang/String;[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getRpc__Ljava_lang_String_2_3B + (JNIEnv *env, jclass, jstring key, jbyteArray defaultValue) +{ + auto val = nt::GetEntryValue(JavaStringRef(env, key)); + if (!val || !val->IsRpc()) return defaultValue; + return ToJavaByteArray(env, val->GetRpc()); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: callRpc + * Signature: (Ljava/lang/String;[B)I + */ +JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_callRpc + (JNIEnv *env, jclass, jstring key, jbyteArray params) +{ + return nt::CallRpc(JavaStringRef(env, key), JavaByteRef(env, params)); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: setNetworkIdentity + * Signature: (Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setNetworkIdentity + (JNIEnv *env, jclass, jstring name) +{ + nt::SetNetworkIdentity(JavaStringRef(env, name)); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: startServer + * Signature: (Ljava/lang/String;Ljava/lang/String;I)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_startServer + (JNIEnv *env, jclass, jstring persistFilename, jstring listenAddress, + jint port) +{ + nt::StartServer(JavaStringRef(env, persistFilename), + JavaStringRef(env, listenAddress).c_str(), port); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: stopServer + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_stopServer + (JNIEnv *, jclass) +{ + nt::StopServer(); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: startClient + * Signature: (Ljava/lang/String;I)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_startClient + (JNIEnv *env, jclass, jstring serverName, jint port) +{ + nt::StartClient(JavaStringRef(env, serverName).c_str(), port); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: stopClient + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_stopClient + (JNIEnv *, jclass) +{ + nt::StopClient(); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: setUpdateRate + * Signature: (D)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setUpdateRate + (JNIEnv *, jclass, jdouble interval) +{ + nt::SetUpdateRate(interval); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: getConnections + * Signature: ()[Ledu/wpi/first/wpilibj/networktables/ConnectionInfo; + */ +JNIEXPORT jobjectArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getConnections + (JNIEnv *env, jclass) +{ + auto arr = nt::GetConnections(); + jobjectArray jarr = + env->NewObjectArray(arr.size(), connectionInfoCls, nullptr); + if (!jarr) return nullptr; + for (size_t i = 0; i < arr.size(); ++i) { + JavaLocal jelem(env, ToJavaObject(env, arr[i])); + env->SetObjectArrayElement(jarr, i, jelem); + } + return jarr; +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: savePersistent + * Signature: (Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_savePersistent + (JNIEnv *env, jclass, jstring filename) +{ + const char *err = nt::SavePersistent(JavaStringRef(env, filename)); + if (err) env->ThrowNew(persistentEx, err); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: loadPersistent + * Signature: (Ljava/lang/String;)[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_loadPersistent + (JNIEnv *env, jclass, jstring filename) +{ + std::vector warns; + const char *err = nt::LoadPersistent(JavaStringRef(env, filename), + [&](size_t line, const char *msg) { + std::ostringstream oss; + oss << line << ": " << msg; + warns.push_back(oss.str()); + }); + if (err) { + env->ThrowNew(persistentEx, err); + return nullptr; + } + return ToJavaStringArray(env, warns); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: now + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_now + (JNIEnv *, jclass) +{ + return nt::Now(); +} + +/* + * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI + * Method: setLogger + * Signature: (Ledu/wpi/first/wpilibj/networktables/NetworkTablesJNI/LoggerFunction;I)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setLogger + (JNIEnv *envouter, jclass, jobject func, jint minLevel) +{ + // the shared pointer to the global will keep it around until the + // a new logger is set + auto func_global = std::make_shared>(envouter, func); + + // cls is a temporary here; cannot be used within callback functor + jclass cls = envouter->GetObjectClass(func); + if (!cls) return; + + // method ids, on the other hand, are safe to retain + jmethodID mid = envouter->GetMethodID( + cls, "apply", "(ILjava/lang/String;ILjava/lang/String;)V"); + if (!mid) return; + + return nt::SetLogger( + [=](unsigned int level, const char *file, unsigned int line, + const char *msg) { + // need to attach as we're coming from a separate thread here + JNIEnv *env; + if (jvm->AttachCurrentThread(reinterpret_cast(&env), + nullptr) != JNI_OK) + return; + + // get the handler + auto handler = func_global->obj(); + if (!handler) { + // shouldn't happen, but ignore if it does + jvm->DetachCurrentThread(); + return; + } + + env->CallVoidMethod(handler, mid, (jint)level, ToJavaString(env, file), + (jint)line, ToJavaString(env, msg)); + if (env->ExceptionCheck()) env->ExceptionDescribe(); + + jvm->DetachCurrentThread(); + }, + minLevel); +} diff --git a/java/src/edu/wpi/first/wpilibj/networktables/ConnectionInfo.java b/java/src/edu/wpi/first/wpilibj/networktables/ConnectionInfo.java new file mode 100644 index 0000000000..d9f0e1d6a8 --- /dev/null +++ b/java/src/edu/wpi/first/wpilibj/networktables/ConnectionInfo.java @@ -0,0 +1,17 @@ +package edu.wpi.first.wpilibj.networktables; + +public class ConnectionInfo { + final String remote_id; + final String remote_name; + final int remote_port; + final long last_update; + final int protocol_version; + + ConnectionInfo(String remote_id, String remote_name, int remote_port, long last_update, int protocol_version) { + this.remote_id = remote_id; + this.remote_name = remote_name; + this.remote_port = remote_port; + this.last_update = last_update; + this.protocol_version = protocol_version; + } +} diff --git a/java/src/edu/wpi/first/wpilibj/networktables/NetworkTable.java b/java/src/edu/wpi/first/wpilibj/networktables/NetworkTable.java new file mode 100644 index 0000000000..5a06ebed66 --- /dev/null +++ b/java/src/edu/wpi/first/wpilibj/networktables/NetworkTable.java @@ -0,0 +1,735 @@ +package edu.wpi.first.wpilibj.networktables; + +import edu.wpi.first.wpilibj.tables.*; +import java.io.*; +import java.util.*; + +/** + * A network table that knows its subtable path. + * + */ +public class NetworkTable implements ITable, IRemote { + /** + * The path separator for sub-tables and keys + * + */ + public static final char PATH_SEPARATOR = '/'; + /** + * The default port that network tables operates on + */ + public static final int DEFAULT_PORT = 1735; + + private static boolean client = false; + private static boolean running = false; + private static int port = DEFAULT_PORT; + private static String ipAddress = null; + private static String persistentFilename = "networktables.ini"; + + private synchronized static void checkInit() { + if (running) + throw new IllegalStateException( + "Network tables has already been initialized"); + } + + /** + * initializes network tables + */ + public synchronized static void initialize() { + checkInit(); + if (client) + NetworkTablesJNI.startClient(ipAddress, port); + else + NetworkTablesJNI.startServer(persistentFilename, ipAddress, port); + running = true; + } + + /** + * set that network tables should be a server + * This must be called before initalize or getTable + */ + public synchronized static void setServerMode() { + checkInit(); + client = false; + } + + /** + * set that network tables should be a client + * This must be called before initalize or getTable + */ + public synchronized static void setClientMode() { + checkInit(); + client = true; + } + + /** + * set the team the robot is configured for (this will set the ip address that + * network tables will connect to in client mode) + * This must be called before initalize or getTable + * @param team the team number + */ + public synchronized static void setTeam(int team) { + setIPAddress("10." + (team / 100) + "." + (team % 100) + ".2"); + } + + /** + * @param address the adress that network tables will connect to in client + * mode + */ + public synchronized static void setIPAddress(final String address) { + checkInit(); + ipAddress = address; + } + + /** + * @param port the port number that network tables will connect to in client + * mode or listen to in server mode + */ + public synchronized static void setPort(int aport) { + checkInit(); + port = aport; + } + + /** + * Gets the table with the specified key. If the table does not exist, a new + *table will be created.
+ * This will automatically initialize network tables if it has not been + *already + * + * @param key + * the key name + * @return the network table requested + */ + public synchronized static NetworkTable getTable(String key) { + if (!running) + initialize(); + if (key.isEmpty()) + return new NetworkTable(key); + return new NetworkTable(PATH_SEPARATOR + key); + } + + private final String path; + + NetworkTable(String path) { + this.path = path; + } + public String toString() { return "NetworkTable: " + path; } + + public boolean isConnected() { + ConnectionInfo[] conns = NetworkTablesJNI.getConnections(); + return conns.length > 0; + } + + public boolean isServer() { + return !client; + } + + private class ListenerBase { + public int uid; + } + + private class ConnectionListenerAdapter extends ListenerBase implements NetworkTablesJNI.ConnectionListenerFunction { + private final IRemote targetSource; + private final IRemoteConnectionListener targetListener; + + public ConnectionListenerAdapter(IRemote targetSource, IRemoteConnectionListener targetListener) { + this.targetSource = targetSource; + this.targetListener = targetListener; + } + + public void apply(int uid, boolean connected, ConnectionInfo conn) { + if (connected) + targetListener.connected(targetSource); + else + targetListener.disconnected(targetSource); + } + } + + private final Hashtable connectionListenerMap = new Hashtable(); + public synchronized void addConnectionListener(IRemoteConnectionListener listener, + boolean immediateNotify) { + ConnectionListenerAdapter adapter = connectionListenerMap.get(listener); + if (adapter != null) + throw new IllegalStateException("Cannot add the same listener twice"); + adapter = new ConnectionListenerAdapter(this, listener); + adapter.uid = NetworkTablesJNI.addConnectionListener(adapter, immediateNotify); + connectionListenerMap.put(listener, adapter); + } + + public synchronized void removeConnectionListener(IRemoteConnectionListener listener) { + ConnectionListenerAdapter adapter = connectionListenerMap.get(listener); + if (adapter != null) { + NetworkTablesJNI.removeConnectionListener(adapter.uid); + connectionListenerMap.remove(listener); + } + } + + public void addTableListener(ITableListener listener) { + addTableListener(listener, false); + } + + private class TableListenerAdapter extends ListenerBase implements NetworkTablesJNI.EntryListenerFunction { + private final int prefixLen; + private final ITable targetSource; + private final ITableListener targetListener; + + public TableListenerAdapter(int prefixLen, ITable targetSource, ITableListener targetListener) { + this.prefixLen = prefixLen; + this.targetSource = targetSource; + this.targetListener = targetListener; + } + + public void apply(int uid, String key, Object value, boolean isNew) { + String relativeKey = key.substring(prefixLen); + if (relativeKey.indexOf(PATH_SEPARATOR) != -1) + return; + targetListener.valueChanged(targetSource, relativeKey, value, isNew); + } + } + + private final Hashtable> listenerMap = new Hashtable>(); + public synchronized void addTableListener(ITableListener listener, + boolean immediateNotify) { + List adapters = listenerMap.get(listener); + if (adapters == null) { + adapters = new ArrayList(); + listenerMap.put(listener, adapters); + } + TableListenerAdapter adapter = + new TableListenerAdapter(path.length() + 1, this, listener); + adapter.uid = NetworkTablesJNI.addEntryListener(path + PATH_SEPARATOR, adapter, immediateNotify); + adapters.add(adapter); + } + + private class KeyListenerAdapter extends ListenerBase implements NetworkTablesJNI.EntryListenerFunction { + private final String relativeKey; + private final String fullKey; + private final ITable targetSource; + private final ITableListener targetListener; + + public KeyListenerAdapter(String relativeKey, String fullKey, ITable targetSource, ITableListener targetListener) { + this.relativeKey = relativeKey; + this.fullKey = fullKey; + this.targetSource = targetSource; + this.targetListener = targetListener; + } + + public void apply(int uid, String key, Object value, boolean isNew) { + if (!key.equals(fullKey)) + return; + targetListener.valueChanged(targetSource, relativeKey, value, isNew); + } + } + + public synchronized void addTableListener(String key, ITableListener listener, + boolean immediateNotify) { + List adapters = listenerMap.get(listener); + if (adapters == null) { + adapters = new ArrayList(); + listenerMap.put(listener, adapters); + } + String fullKey = path + PATH_SEPARATOR + key; + KeyListenerAdapter adapter = + new KeyListenerAdapter(key, fullKey, this, listener); + adapter.uid = NetworkTablesJNI.addEntryListener(fullKey, adapter, immediateNotify); + adapters.add(adapter); + } + + private class SubListenerAdapter extends ListenerBase implements NetworkTablesJNI.EntryListenerFunction { + private final int prefixLen; + private final ITable targetSource; + private final ITableListener targetListener; + private final Set notifiedTables = new HashSet(); + + public SubListenerAdapter(int prefixLen, ITable targetSource, ITableListener targetListener) { + this.prefixLen = prefixLen; + this.targetSource = targetSource; + this.targetListener = targetListener; + } + + public void apply(int uid, String key, Object value, boolean isNew) { + String relativeKey = key.substring(prefixLen); + int endSubTable = relativeKey.indexOf(PATH_SEPARATOR); + if (endSubTable == -1) + return; + String subTableKey = relativeKey.substring(0, endSubTable); + if (notifiedTables.contains(subTableKey)) + return; + notifiedTables.add(subTableKey); + targetListener.valueChanged(targetSource, subTableKey, targetSource.getSubTable(subTableKey), true); + } + } + + public synchronized void addSubTableListener(final ITableListener listener) { + List adapters = listenerMap.get(listener); + if (adapters == null) { + adapters = new ArrayList(); + listenerMap.put(listener, adapters); + } + SubListenerAdapter adapter = + new SubListenerAdapter(path.length() + 1, this, listener); + adapter.uid = NetworkTablesJNI.addEntryListener(path + PATH_SEPARATOR, adapter, true); + adapters.add(adapter); + } + + public synchronized void removeTableListener(ITableListener listener) { + List adapters = listenerMap.get(listener); + if (adapters != null) { + for (int i = 0; i < adapters.size(); ++i) + NetworkTablesJNI.removeEntryListener(adapters.get(i).uid); + adapters.clear(); + } + } + + /** + * Returns the table at the specified key. If there is no table at the + * specified key, it will create a new table + * + * @param key + * the key name + * @return the networktable to be returned + */ + public ITable getSubTable(String key) { + return new NetworkTable(path + PATH_SEPARATOR + key); + } + + /** + * Checks the table and tells if it contains the specified key + * + * @param key + * the key to be checked + */ + public boolean containsKey(String key) { + return NetworkTablesJNI.containsKey(path + PATH_SEPARATOR + key); + } + + public boolean containsSubTable(String key) { + String subtablePrefix = path + key + PATH_SEPARATOR; + //List keys = node.getEntryStore().keys(); + //for (int i = 0; i < keys.size(); ++i) { + // if (((String)keys.get(i)).startsWith(subtablePrefix)) + // return true; + //} + return false; + } + + /** + * Maps the specified key to the specified value in this table. The key can + * not be null. The value can be retrieved by calling the get method with a + * key that is equal to the original key. + * + * @param key + * the key + * @param value + * the value + */ + public void putNumber(String key, double value) { + NetworkTablesJNI.putDouble(path + PATH_SEPARATOR + key, value); + } + + /** + * Returns the key that the name maps to. + * + * @param key + * the key name + * @return the key + * @throws TableKeyNotDefinedException + * if the specified key is null + */ + public double getNumber(String key) throws TableKeyNotDefinedException { + return NetworkTablesJNI.getDouble(path + PATH_SEPARATOR + key); + } + + /** + * Returns the key that the name maps to. If the key is null, it will return + * the default value + * + * @param key + * the key name + * @param defaultValue + * the default value if the key is null + * @return the key + */ + public double getNumber(String key, double defaultValue) { + return NetworkTablesJNI.getDouble(path + PATH_SEPARATOR + key, defaultValue); + } + + /** + * Maps the specified key to the specified value in this table. The key can + * not be null. The value can be retrieved by calling the get method with a + * key that is equal to the original key. + * + * @param key + * the key + * @param value + * the value + */ + public void putString(String key, String value) { + NetworkTablesJNI.putString(path + PATH_SEPARATOR + key, value); + } + + /** + * Returns the key that the name maps to. + * + * @param key + * the key name + * @return the key + * @throws TableKeyNotDefinedException + * if the specified key is null + */ + public String getString(String key) throws TableKeyNotDefinedException { + return NetworkTablesJNI.getString(path + PATH_SEPARATOR + key); + } + + /** + * Returns the key that the name maps to. If the key is null, it will return + * the default value + * + * @param key + * the key name + * @param defaultValue + * the default value if the key is null + * @return the key + */ + public String getString(String key, String defaultValue) { + return NetworkTablesJNI.getString(path + PATH_SEPARATOR + key, defaultValue); + } + + /** + * Maps the specified key to the specified value in this table. The key can + * not be null. The value can be retrieved by calling the get method with a + * key that is equal to the original key. + * + * @param key + * the key + * @param value + * the value + */ + public void putBoolean(String key, boolean value) { + NetworkTablesJNI.putBoolean(path + PATH_SEPARATOR + key, value); + } + + /** + * Returns the key that the name maps to. + * + * @param key + * the key name + * @return the key + * @throws TableKeyNotDefinedException + * if the specified key is null + */ + public boolean getBoolean(String key) throws TableKeyNotDefinedException { + return NetworkTablesJNI.getBoolean(path + PATH_SEPARATOR + key); + } + + /** + * Returns the key that the name maps to. If the key is null, it will return + * the default value + * + * @param key + * the key name + * @param defaultValue + * the default value if the key is null + * @return the key + */ + public boolean getBoolean(String key, boolean defaultValue) { + return NetworkTablesJNI.getBoolean(path + PATH_SEPARATOR + key, defaultValue); + } + + /** + * Maps the specified key to the specified value in this table. The key can + * not be null. The value can be retrieved by calling the get method with a + * key that is equal to the original key. + * + * @param key + * the key + * @param value + * the value + */ + public void putBooleanArray(String key, boolean[] value) { + NetworkTablesJNI.putBooleanArray(path + PATH_SEPARATOR + key, value); + } + + /** + * Returns the key that the name maps to. + * + * @param key + * the key name + * @return the key + * @throws TableKeyNotDefinedException + * if the specified key is null + */ + public boolean[] getBooleanArray(String key) throws TableKeyNotDefinedException { + return NetworkTablesJNI.getBooleanArray(path + PATH_SEPARATOR + key); + } + + /** + * Returns the key that the name maps to. If the key is null, it will return + * the default value + * + * @param key + * the key name + * @param defaultValue + * the default value if the key is null + * @return the key + */ + public boolean[] getBooleanArray(String key, boolean[] defaultValue) { + return NetworkTablesJNI.getBooleanArray(path + PATH_SEPARATOR + key, defaultValue); + } + + /** + * Maps the specified key to the specified value in this table. The key can + * not be null. The value can be retrieved by calling the get method with a + * key that is equal to the original key. + * + * @param key + * the key + * @param value + * the value + */ + public void putNumberArray(String key, double[] value) { + NetworkTablesJNI.putDoubleArray(path + PATH_SEPARATOR + key, value); + } + + /** + * Returns the key that the name maps to. + * + * @param key + * the key name + * @return the key + * @throws TableKeyNotDefinedException + * if the specified key is null + */ + public double[] getNumberArray(String key) throws TableKeyNotDefinedException { + return NetworkTablesJNI.getDoubleArray(path + PATH_SEPARATOR + key); + } + + /** + * Returns the key that the name maps to. If the key is null, it will return + * the default value + * + * @param key + * the key name + * @param defaultValue + * the default value if the key is null + * @return the key + */ + public double[] getNumberArray(String key, double[] defaultValue) { + return NetworkTablesJNI.getDoubleArray(path + PATH_SEPARATOR + key, defaultValue); + } + + /** + * Maps the specified key to the specified value in this table. The key can + * not be null. The value can be retrieved by calling the get method with a + * key that is equal to the original key. + * + * @param key + * the key + * @param value + * the value + */ + public void putStringArray(String key, String[] value) { + NetworkTablesJNI.putStringArray(path + PATH_SEPARATOR + key, value); + } + + /** + * Returns the key that the name maps to. + * + * @param key + * the key name + * @return the key + * @throws TableKeyNotDefinedException + * if the specified key is null + */ + public String[] getStringArray(String key) throws TableKeyNotDefinedException { + return NetworkTablesJNI.getStringArray(path + PATH_SEPARATOR + key); + } + + /** + * Returns the key that the name maps to. If the key is null, it will return + * the default value + * + * @param key + * the key name + * @param defaultValue + * the default value if the key is null + * @return the key + */ + public String[] getStringArray(String key, String[] defaultValue) { + return NetworkTablesJNI.getStringArray(path + PATH_SEPARATOR + key, defaultValue); + } + + /** + * Maps the specified key to the specified value in this table. The key can + * not be null. The value can be retrieved by calling the get method with a + * key that is equal to the original key. + * + * @param key the key name + * @param value the value to be put + */ + public void putValue(String key, Object value) { + if (value instanceof Boolean) + NetworkTablesJNI.putBoolean(path + PATH_SEPARATOR + key, ((Boolean)value).booleanValue()); + else if (value instanceof Double) + NetworkTablesJNI.putDouble(path + PATH_SEPARATOR + key, ((Double)value).doubleValue()); + else if (value instanceof String) + NetworkTablesJNI.putString(path + PATH_SEPARATOR + key, (String)value); + else if (value instanceof byte[]) + NetworkTablesJNI.putRaw(path + PATH_SEPARATOR + key, (byte[])value); + else if (value instanceof boolean[]) + NetworkTablesJNI.putBooleanArray(path + PATH_SEPARATOR + key, (boolean[])value); + else if (value instanceof double[]) + NetworkTablesJNI.putDoubleArray(path + PATH_SEPARATOR + key, (double[])value); + else if (value instanceof String[]) + NetworkTablesJNI.putStringArray(path + PATH_SEPARATOR + key, (String[])value); + } + + /** + * Returns the key that the name maps to. + * NOTE: If the value is a double, it will return a Double object, + * not a primitive. To get the primitive, use getDouble + * + * @param key + * the key name + * @return the key + * @throws TableKeyNotDefinedException + * if the specified key is null + */ + public Object getValue(String key) throws TableKeyNotDefinedException { + return NetworkTablesJNI.getValue(path + PATH_SEPARATOR + key); + } + + /** + * Returns the key that the name maps to. If the key is null, it will return + * the default value + * NOTE: If the value is a double, it will return a Double object, + * not a primitive. To get the primitive, use getDouble + * + * @param key + * the key name + * @param defaultValue + * the default value if the key is null + * @return the key + */ + public Object getValue(String key, Object defaultValue) { + return NetworkTablesJNI.getValue(path + PATH_SEPARATOR + key, defaultValue); + } + + /** The persistent flag value. */ + public static final int PERSISTENT = 1; + + /** + * Sets flags on the specified key in this table. The key can + * not be null. + * + * @param key the key name + * @param flags the flags to set + */ + public void setFlags(String key, int flags) { + NetworkTablesJNI.setEntryFlags(path + PATH_SEPARATOR + key, flags); + } + + /** + * Returns the flags for the specified key. + * + * @param key + * the key name + * @return the flags, or 0 if the key is not defined + */ + public int getFlags(String key) { + return NetworkTablesJNI.getEntryFlags(path + PATH_SEPARATOR + key); + } + + /* + * Deprecated Methods + */ + /** + * @deprecated + * Maps the specified key to the specified value in this table. + * The key can not be null. + * The value can be retrieved by calling the get method with a key that is + * equal to the original key. + * @param key the key + * @param value the value + * @throws IllegalArgumentException if key is null + */ + public void putInt(String key, int value) { putNumber(key, value); } + + /** + * @deprecated + * Returns the value at the specified key. + * @param key the key + * @return the value + * @throws TableKeyNotDefinedException if there is no value mapped to by the + * key + * @throws IllegalArgumentException if the value mapped to by the key is not + * an int + * @throws IllegalArgumentException if the key is null + */ + public int getInt(String key) throws TableKeyNotDefinedException { + return (int)getNumber(key); + } + + /** + * @deprecated + * Returns the value at the specified key. + * @param key the key + * @param defaultValue the value returned if the key is undefined + * @return the value + * @throws NetworkTableKeyNotDefined if there is no value mapped to by the key + * @throws IllegalArgumentException if the value mapped to by the key is not + * an int + * @throws IllegalArgumentException if the key is null + */ + public int getInt(String key, int defaultValue) throws TableKeyNotDefinedException { + try { + return (int)getNumber(key); + } catch (NoSuchElementException ex) { + return defaultValue; + } + } + + /** + * @deprecated + * Maps the specified key to the specified value in this table. + * The key can not be null. + * The value can be retrieved by calling the get method with a key that is + * equal to the original key. + * @param key the key + * @param value the value + * @throws IllegalArgumentException if key is null + */ + public void putDouble(String key, double value) { + putNumber(key, value); + } + + /** + * @deprecated + * Returns the value at the specified key. + * @param key the key + * @return the value + * @throws NoSuchEleNetworkTableKeyNotDefinedmentException if there is no + * value mapped to by the key + * @throws IllegalArgumentException if the value mapped to by the key is not a + * double + * @throws IllegalArgumentException if the key is null + */ + public double getDouble(String key) throws TableKeyNotDefinedException { + return getNumber(key); + } + + /** + * @deprecated + * Returns the value at the specified key. + * @param key the key + * @param defaultValue the value returned if the key is undefined + * @return the value + * @throws NoSuchEleNetworkTableKeyNotDefinedmentException if there is no + * value mapped to by the key + * @throws IllegalArgumentException if the value mapped to by the key is not a + * double + * @throws IllegalArgumentException if the key is null + */ + public double getDouble(String key, double defaultValue) { + return getNumber(key, defaultValue); + } +} diff --git a/java/src/edu/wpi/first/wpilibj/networktables/NetworkTableKeyNotDefined.java b/java/src/edu/wpi/first/wpilibj/networktables/NetworkTableKeyNotDefined.java new file mode 100644 index 0000000000..d654cfce58 --- /dev/null +++ b/java/src/edu/wpi/first/wpilibj/networktables/NetworkTableKeyNotDefined.java @@ -0,0 +1,22 @@ +package edu.wpi.first.wpilibj.networktables; + +import edu.wpi.first.wpilibj.tables.TableKeyNotDefinedException; + +/** + * An exception throw when the lookup a a key-value fails in a {@link NetworkTable} + * + * @deprecated to provide backwards compatability for new api + * + * @author Mitchell + * + */ +public class NetworkTableKeyNotDefined extends TableKeyNotDefinedException { + + /** + * @param key the key that was not defined in the table + */ + public NetworkTableKeyNotDefined(String key) { + super(key); + } + +} diff --git a/java/src/edu/wpi/first/wpilibj/networktables/NetworkTablesJNI.java b/java/src/edu/wpi/first/wpilibj/networktables/NetworkTablesJNI.java new file mode 100644 index 0000000000..45dabc35a1 --- /dev/null +++ b/java/src/edu/wpi/first/wpilibj/networktables/NetworkTablesJNI.java @@ -0,0 +1,145 @@ +package edu.wpi.first.wpilibj.networktables; + +import edu.wpi.first.wpilibj.tables.*; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +public class NetworkTablesJNI { + static boolean libraryLoaded = false; + static File jniLibrary = null; + static { + if (!libraryLoaded) { + try { + String osname = System.getProperty("os.name"); + String resname = "/" + osname + "/" + System.getProperty("os.arch") + "/"; + System.out.println("platform: " + resname); + if (osname.startsWith("Windows")) + resname += "ntcore.dll"; + else + resname += "libntcore.so"; + InputStream is = NetworkTablesJNI.class.getResourceAsStream(resname); + if (is != null) { + // create temporary file + if (System.getProperty("os.name").startsWith("Windows")) + jniLibrary = File.createTempFile("NetworkTablesJNI", ".dll"); + else + jniLibrary = File.createTempFile("libNetworkTablesJNI", ".so"); + // flag for delete on exit + jniLibrary.deleteOnExit(); + OutputStream os = new FileOutputStream(jniLibrary); + + byte[] buffer = new byte[1024]; + int readBytes; + try { + while ((readBytes = is.read(buffer)) != -1) { + os.write(buffer, 0, readBytes); + } + } finally { + os.close(); + is.close(); + } + + System.load(jniLibrary.getAbsolutePath()); + } else { + if (osname.startsWith("Windows")) + System.loadLibrary("ntcore.dll"); + else + System.loadLibrary("libntcore.so"); + } + } catch (IOException ex) { + ex.printStackTrace(); + System.exit(1); + } + libraryLoaded = true; + } + } + + public static native boolean containsKey(String key); + public static native int getType(String key); + + public static native boolean putBoolean(String key, boolean value); + public static native boolean putDouble(String key, double value); + public static native boolean putString(String key, String value); + public static native boolean putRaw(String key, byte[] value); + public static native boolean putBooleanArray(String key, boolean[] value); + public static native boolean putDoubleArray(String key, double[] value); + public static native boolean putStringArray(String key, String[] value); + + public static native void forcePutBoolean(String key, boolean value); + public static native void forcePutDouble(String key, double value); + public static native void forcePutString(String key, String value); + public static native void forcePutRaw(String key, byte[] value); + public static native void forcePutBooleanArray(String key, boolean[] value); + public static native void forcePutDoubleArray(String key, double[] value); + public static native void forcePutStringArray(String key, String[] value); + + public static native Object getValue(String key) throws TableKeyNotDefinedException; + public static native boolean getBoolean(String key) throws TableKeyNotDefinedException; + public static native double getDouble(String key) throws TableKeyNotDefinedException; + public static native String getString(String key) throws TableKeyNotDefinedException; + public static native byte[] getRaw(String key) throws TableKeyNotDefinedException; + public static native boolean[] getBooleanArray(String key) throws TableKeyNotDefinedException; + public static native double[] getDoubleArray(String key) throws TableKeyNotDefinedException; + public static native String[] getStringArray(String key) throws TableKeyNotDefinedException; + + public static native Object getValue(String key, Object defaultValue); + public static native boolean getBoolean(String key, boolean defaultValue); + public static native double getDouble(String key, double defaultValue); + public static native String getString(String key, String defaultValue); + public static native byte[] getRaw(String key, byte[] defaultValue); + public static native boolean[] getBooleanArray(String key, boolean[] defaultValue); + public static native double[] getDoubleArray(String key, double[] defaultValue); + public static native String[] getStringArray(String key, String[] defaultValue); + + public static native void setEntryFlags(String key, int flags); + public static native int getEntryFlags(String key); + + public static native void deleteEntry(String key); + public static native void deleteAllEntries(); + + // public static native EntryInfo[] getEntryInfo(String prefix, int types); + + public static native void flush(); + + public interface EntryListenerFunction { + void apply(int uid, String key, Object value, boolean isNew); + } + public static native int addEntryListener(String prefix, EntryListenerFunction listener, boolean immediateNotify); + public static native void removeEntryListener(int entryListenerUid); + + public interface ConnectionListenerFunction { + void apply(int uid, boolean connected, ConnectionInfo conn); + } + public static native int addConnectionListener(ConnectionListenerFunction listener, boolean immediateNotify); + public static native void removeConnectionListener(int connListenerUid); + + // public static native void createRpc(String key, byte[] def, IRpc rpc); + public static native byte[] getRpc(String key) throws TableKeyNotDefinedException; + public static native byte[] getRpc(String key, byte[] defaultValue); + public static native int callRpc(String key, byte[] params); + // public static native byte[] getRpcResultBlocking(int callUid); + // public static native byte[] getRpcResultNonblocking(int callUid) throws RpcNoResponseException; + + public static native void setNetworkIdentity(String name); + public static native void startServer(String persistFilename, String listenAddress, int port); + public static native void stopServer(); + public static native void startClient(String serverName, int port); + public static native void stopClient(); + public static native void setUpdateRate(double interval); + + public static native ConnectionInfo[] getConnections(); + + public static native void savePersistent(String filename) throws PersistentException; + public static native String[] loadPersistent(String filename) throws PersistentException; // returns warnings + + public static native long now(); + + public interface LoggerFunction { + void apply(int level, String file, int line, String msg); + } + public static native void setLogger(LoggerFunction func, int minLevel); +} diff --git a/java/src/edu/wpi/first/wpilibj/networktables/PersistentException.java b/java/src/edu/wpi/first/wpilibj/networktables/PersistentException.java new file mode 100644 index 0000000000..8d521fbbb0 --- /dev/null +++ b/java/src/edu/wpi/first/wpilibj/networktables/PersistentException.java @@ -0,0 +1,18 @@ +package edu.wpi.first.wpilibj.networktables; + +import java.io.IOException; + +/** + * An exception thrown when persistent load/save fails in a {@link NetworkTable} + * + */ +public class PersistentException extends IOException { + + /** + * @param message The error message + */ + public PersistentException(String message) { + super(message); + } + +} diff --git a/java/src/edu/wpi/first/wpilibj/tables/IRemote.java b/java/src/edu/wpi/first/wpilibj/tables/IRemote.java new file mode 100644 index 0000000000..a79f740473 --- /dev/null +++ b/java/src/edu/wpi/first/wpilibj/tables/IRemote.java @@ -0,0 +1,37 @@ +package edu.wpi.first.wpilibj.tables; + + +/** + * Represents an object that has a remote connection + * + * @author Mitchell + * + */ +public interface IRemote { + /** + * Register an object to listen for connection and disconnection events + * + * @param listener the listener to be register + * @param immediateNotify if the listener object should be notified of the current connection state + */ + public void addConnectionListener(IRemoteConnectionListener listener, boolean immediateNotify); + + /** + * Unregister a listener from connection events + * + * @param listener the listener to be unregistered + */ + public void removeConnectionListener(IRemoteConnectionListener listener); + + /** + * Get the current state of the objects connection + * @return the current connection state + */ + public boolean isConnected(); + + /** + * If the object is acting as a server + * @return if the object is a server + */ + public boolean isServer(); +} diff --git a/java/src/edu/wpi/first/wpilibj/tables/IRemoteConnectionListener.java b/java/src/edu/wpi/first/wpilibj/tables/IRemoteConnectionListener.java new file mode 100644 index 0000000000..cab9b1c3ec --- /dev/null +++ b/java/src/edu/wpi/first/wpilibj/tables/IRemoteConnectionListener.java @@ -0,0 +1,20 @@ +package edu.wpi.first.wpilibj.tables; + +/** + * A listener that listens for connection changes in a {@link IRemote} object + * + * @author Mitchell + * + */ +public interface IRemoteConnectionListener { + /** + * Called when an IRemote is connected + * @param remote the object that connected + */ + public void connected(IRemote remote); + /** + * Called when an IRemote is disconnected + * @param remote the object that disconnected + */ + public void disconnected(IRemote remote); +} diff --git a/java/src/edu/wpi/first/wpilibj/tables/ITable.java b/java/src/edu/wpi/first/wpilibj/tables/ITable.java new file mode 100644 index 0000000000..122ddcc8e9 --- /dev/null +++ b/java/src/edu/wpi/first/wpilibj/tables/ITable.java @@ -0,0 +1,292 @@ +package edu.wpi.first.wpilibj.tables; + +import java.util.NoSuchElementException; + + +/** + * A table whose values can be read and written to + * + * @author Mitchell + * + */ +public interface ITable { + + /** + * @param key the key to search for + * @return true if the table as a value assigned to the given key + */ + public boolean containsKey(String key); + + /** + * @param key the key to search for + * @return true if there is a subtable with the key which contains at least + * one key/subtable of its own + */ + public boolean containsSubTable(String key); + + /** + * @param key the name of the table relative to this one + * @return a sub table relative to this one + */ + public ITable getSubTable(String key); + + /** + * Gets the value associated with a key as an object + * @param key the key of the value to look up + * @return the value associated with the given key + * @throws TableKeyNotDefinedException if there is no value associated with + * the given key + */ + public Object getValue(String key) throws TableKeyNotDefinedException; + + /** + * Put a value in the table + * @param key the key to be assigned to + * @param value the value that will be assigned + * @throws IllegalArgumentException when the value is not supported by the + * table + */ + public void putValue(String key, Object value) + throws IllegalArgumentException; + + /** + * Put a number in the table + * @param key the key to be assigned to + * @param value the value that will be assigned + */ + public void putNumber(String key, double value); + /** + * @param key the key to look up + * @return the value associated with the given key + * @throws TableKeyNotDefinedException if there is no value associated with + * the given key + */ + public double getNumber(String key) throws TableKeyNotDefinedException; + /** + * @param key the key to look up + * @param defaultValue the value to be returned if no value is found + * @return the value associated with the given key or the given default value + * if there is no value associated with the key + */ + public double getNumber(String key, double defaultValue); + + /** + * Put a string in the table + * @param key the key to be assigned to + * @param value the value that will be assigned + */ + public void putString(String key, String value); + /** + * @param key the key to look up + * @return the value associated with the given key + * @throws TableKeyNotDefinedException if there is no value associated with + * the given key + */ + public String getString(String key) throws TableKeyNotDefinedException; + /** + * @param key the key to look up + * @param defaultValue the value to be returned if no value is found + * @return the value associated with the given key or the given default value + * if there is no value associated with the key + */ + public String getString(String key, String defaultValue); + + /** + * Put a boolean in the table + * @param key the key to be assigned to + * @param value the value that will be assigned + */ + public void putBoolean(String key, boolean value); + /** + * @param key the key to look up + * @return the value associated with the given key + * @throws TableKeyNotDefinedException if there is no value associated with + * the given key + */ + public boolean getBoolean(String key) throws TableKeyNotDefinedException; + /** + * @param key the key to look up + * @param defaultValue the value to be returned if no value is found + * @return the value associated with the given key or the given default value + * if there is no value associated with the key + */ + public boolean getBoolean(String key, boolean defaultValue); + + /** + * Put a boolean array in the table + * @param key the key to be assigned to + * @param value the value that will be assigned + */ + public void putBooleanArray(String key, boolean[] value); + /** + * @param key the key to look up + * @return the value associated with the given key + * @throws TableKeyNotDefinedException if there is no value associated with + * the given key + */ + public boolean[] getBooleanArray(String key) throws TableKeyNotDefinedException; + /** + * @param key the key to look up + * @param defaultValue the value to be returned if no value is found + * @return the value associated with the given key or the given default value + * if there is no value associated with the key + */ + public boolean[] getBooleanArray(String key, boolean[] defaultValue); + + /** + * Put a number array in the table + * @param key the key to be assigned to + * @param value the value that will be assigned + */ + public void putNumberArray(String key, double[] value); + /** + * @param key the key to look up + * @return the value associated with the given key + * @throws TableKeyNotDefinedException if there is no value associated with + * the given key + */ + public double[] getNumberArray(String key) throws TableKeyNotDefinedException; + /** + * @param key the key to look up + * @param defaultValue the value to be returned if no value is found + * @return the value associated with the given key or the given default value + * if there is no value associated with the key + */ + public double[] getNumberArray(String key, double[] defaultValue); + + /** + * Put a string array in the table + * @param key the key to be assigned to + * @param value the value that will be assigned + */ + public void putStringArray(String key, String[] value); + /** + * @param key the key to look up + * @return the value associated with the given key + * @throws TableKeyNotDefinedException if there is no value associated with + * the given key + */ + public String[] getStringArray(String key) throws TableKeyNotDefinedException; + /** + * @param key the key to look up + * @param defaultValue the value to be returned if no value is found + * @return the value associated with the given key or the given default value + * if there is no value associated with the key + */ + public String[] getStringArray(String key, String[] defaultValue); + + /** + * Add a listener for changes to the table + * @param listener the listener to add + */ + public void addTableListener(ITableListener listener); + /** + * Add a listener for changes to the table + * @param listener the listener to add + * @param immediateNotify if true then this listener will be notified of all + * current entries (marked as new) + */ + public void addTableListener(ITableListener listener, + boolean immediateNotify); + + /** + * Add a listener for changes to a specific key the table + * @param key the key to listen for + * @param listener the listener to add + * @param immediateNotify if true then this listener will be notified of all + * current entries (marked as new) + */ + public void addTableListener(String key, ITableListener listener, + boolean immediateNotify); + /** + * This will immediately notify the listener of all current sub tables + * @param listener + */ + public void addSubTableListener(final ITableListener listener); + /** + * Remove a listener from receiving table events + * @param listener the listener to be removed + */ + public void removeTableListener(ITableListener listener); + + /* + * Depricated Methods + */ + /** + * @deprecated + * Maps the specified key to the specified value in this table. + * The key can not be null. + * The value can be retrieved by calling the get method with a key that is + * equal to the original key. + * @param key the key + * @param value the value + * @throws IllegalArgumentException if key is null + */ + public void putInt(String key, int value); + + /** + * @deprecated + * Returns the value at the specified key. + * @param key the key + * @return the value + * @throws TableKeyNotDefinedException if there is no value mapped to by the + * key + * @throws IllegalArgumentException if the value mapped to by the key is not + * an int + * @throws IllegalArgumentException if the key is null + */ + public int getInt(String key) throws TableKeyNotDefinedException; + + /** + * @deprecated + * Returns the value at the specified key. + * @param key the key + * @param defaultValue the value returned if the key is undefined + * @return the value + * @throws NetworkTableKeyNotDefined if there is no value mapped to by the key + * @throws IllegalArgumentException if the value mapped to by the key is not + * an int + * @throws IllegalArgumentException if the key is null + */ + public int getInt(String key, int defaultValue) + throws TableKeyNotDefinedException; + + /** + * @deprecated + * Maps the specified key to the specified value in this table. + * The key can not be null. + * The value can be retrieved by calling the get method with a key that is + * equal to the original key. + * @param key the key + * @param value the value + * @throws IllegalArgumentException if key is null + */ + public void putDouble(String key, double value); + + /** + * @deprecated + * Returns the value at the specified key. + * @param key the key + * @return the value + * @throws NoSuchEleNetworkTableKeyNotDefinedmentException if there is no + * value mapped to by the key + * @throws IllegalArgumentException if the value mapped to by the key is not a + * double + * @throws IllegalArgumentException if the key is null + */ + public double getDouble(String key) throws TableKeyNotDefinedException; + + /** + * @deprecated + * Returns the value at the specified key. + * @param key the key + * @param defaultValue the value returned if the key is undefined + * @return the value + * @throws NoSuchEleNetworkTableKeyNotDefinedmentException if there is no + * value mapped to by the key + * @throws IllegalArgumentException if the value mapped to by the key is not a + * double + * @throws IllegalArgumentException if the key is null + */ + public double getDouble(String key, double defaultValue); +} diff --git a/java/src/edu/wpi/first/wpilibj/tables/ITableListener.java b/java/src/edu/wpi/first/wpilibj/tables/ITableListener.java new file mode 100644 index 0000000000..f53528f180 --- /dev/null +++ b/java/src/edu/wpi/first/wpilibj/tables/ITableListener.java @@ -0,0 +1,19 @@ +package edu.wpi.first.wpilibj.tables; + +/** + * A listener that listens to changes in values in a {@link ITable} + * + * @author Mitchell + * + */ +public interface ITableListener { + /** + * Called when a key-value pair is changed in a {@link ITable} + * WARNING: If a new key-value is put in this method value changed will immediatly be called which could lead to recursive code + * @param source the table the key-value pair exists in + * @param key the key associated with the value that changed + * @param value the new value + * @param isNew true if the key did not previously exist in the table, otherwise it is false + */ + public void valueChanged(ITable source, String key, Object value, boolean isNew); +} diff --git a/java/src/edu/wpi/first/wpilibj/tables/TableKeyNotDefinedException.java b/java/src/edu/wpi/first/wpilibj/tables/TableKeyNotDefinedException.java new file mode 100644 index 0000000000..4ca3a54189 --- /dev/null +++ b/java/src/edu/wpi/first/wpilibj/tables/TableKeyNotDefinedException.java @@ -0,0 +1,20 @@ +package edu.wpi.first.wpilibj.tables; + +import java.util.NoSuchElementException; + +/** + * An exception throw when the lookup a a key-value fails in a {@link ITable} + * + * @author Mitchell + * + */ +public class TableKeyNotDefinedException extends NoSuchElementException { + + /** + * @param key the key that was not defined in the table + */ + public TableKeyNotDefinedException(String key) { + super("Unknown Table Key: "+key); + } + +} diff --git a/java/test/Client.java b/java/test/Client.java new file mode 100644 index 0000000000..7f18690ca7 --- /dev/null +++ b/java/test/Client.java @@ -0,0 +1,39 @@ +import edu.wpi.first.wpilibj.networktables.*; +import edu.wpi.first.wpilibj.tables.*; + +public class Client { + private static class MyLogger implements NetworkTablesJNI.LoggerFunction { + public void apply(int level, String file, int line, String msg) { + System.err.println(msg); + } + } + + public static void main(String[] args) { + NetworkTablesJNI.setLogger(new MyLogger(), 0); + NetworkTable.setIPAddress("127.0.0.1"); + NetworkTable.setPort(10000); + NetworkTable.setClientMode(); + NetworkTable nt = NetworkTable.getTable(""); + try { Thread.sleep(2000); } catch (InterruptedException e) {} + try { + System.out.println("Got foo: " + nt.getNumber("foo")); + } catch(TableKeyNotDefinedException ex) { + } + nt.putBoolean("bar", false); + nt.setFlags("bar", NetworkTable.PERSISTENT); + nt.putBoolean("bar2", true); + nt.putBoolean("bar2", false); + nt.putBoolean("bar2", true); + nt.putString("str", "hello world"); + double[] nums = new double[3]; + nums[0] = 0.5; + nums[1] = 1.2; + nums[2] = 3.0; + nt.putNumberArray("numarray", nums); + String[] strs = new String[2]; + strs[0] = "Hello"; + strs[1] = "World"; + nt.putStringArray("strarray", strs); + try { Thread.sleep(10000); } catch (InterruptedException e) {} + } +} diff --git a/java/test/Server.java b/java/test/Server.java new file mode 100644 index 0000000000..33844fb92e --- /dev/null +++ b/java/test/Server.java @@ -0,0 +1,26 @@ +import edu.wpi.first.wpilibj.networktables.*; +import edu.wpi.first.wpilibj.tables.*; + +public class Server { + private static class MyLogger implements NetworkTablesJNI.LoggerFunction { + public void apply(int level, String file, int line, String msg) { + System.err.println(msg); + } + } + + public static void main(String[] args) { + NetworkTablesJNI.setLogger(new MyLogger(), 0); + NetworkTable.setIPAddress("127.0.0.1"); + NetworkTable.setPort(10000); + NetworkTable.setServerMode(); + NetworkTable nt = NetworkTable.getTable(""); + try { Thread.sleep(1000); } catch (InterruptedException e) {} + nt.putNumber("foo", 0.5); + nt.setFlags("foo", NetworkTable.PERSISTENT); + nt.putNumber("foo2", 0.5); + nt.putNumber("foo2", 0.7); + nt.putNumber("foo2", 0.6); + nt.putNumber("foo2", 0.5); + try { Thread.sleep(10000); } catch (InterruptedException e) {} + } +} diff --git a/ntcore.def b/ntcore.def index f370f139cc..97bbbd1a92 100644 --- a/ntcore.def +++ b/ntcore.def @@ -41,3 +41,62 @@ NT_PackRpcValues @42 NT_UnpackRpcValues @43 NT_DisposeRpcDefinition @44 NT_DisposeRpcCallInfo @45 + +; JNI functions +JNI_OnLoad +JNI_OnUnload +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_containsKey +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getType +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putBoolean +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putDouble +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putString +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putRaw +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putBooleanArray +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putDoubleArray +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putStringArray +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutBoolean +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutDouble +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutString +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutRaw +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutBooleanArray +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutDoubleArray +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutStringArray +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getValue__Ljava_lang_String_2 +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getBoolean__Ljava_lang_String_2 +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getDouble__Ljava_lang_String_2 +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getString__Ljava_lang_String_2 +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getRaw__Ljava_lang_String_2 +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getBooleanArray__Ljava_lang_String_2 +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getDoubleArray__Ljava_lang_String_2 +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getStringArray__Ljava_lang_String_2 +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getValue__Ljava_lang_String_2Ljava_lang_Object_2 +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getBoolean__Ljava_lang_String_2Z +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getDouble__Ljava_lang_String_2D +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getString__Ljava_lang_String_2Ljava_lang_String_2 +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getRaw__Ljava_lang_String_2_3B +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getBooleanArray__Ljava_lang_String_2_3Z +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getDoubleArray__Ljava_lang_String_2_3D +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getStringArray__Ljava_lang_String_2_3Ljava_lang_String_2 +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setEntryFlags +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getEntryFlags +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_deleteEntry +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_deleteAllEntries +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_flush +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_addEntryListener +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_removeEntryListener +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_addConnectionListener +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_removeConnectionListener +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getRpc__Ljava_lang_String_2 +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getRpc__Ljava_lang_String_2_3B +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_callRpc +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setNetworkIdentity +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_startServer +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_stopServer +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_startClient +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_stopClient +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setUpdateRate +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getConnections +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_savePersistent +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_loadPersistent +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_now +Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setLogger