#include #include #include #include #include #include #include #include #include "edu_wpi_first_wpilibj_networktables_NetworkTablesJNI.h" #include "ntcore.h" #include "support/atomic_static.h" #include "support/SafeThread.h" #include "llvm/ConvertUTF.h" #include "llvm/SmallString.h" #include "llvm/SmallVector.h" // // Globals and load/unload // // Used for callback. static JavaVM *jvm = nullptr; static jclass booleanCls = nullptr; static jclass doubleCls = nullptr; static jclass stringCls = nullptr; static jclass connectionInfoCls = nullptr; static jclass entryInfoCls = nullptr; static jclass keyNotDefinedEx = nullptr; static jclass persistentEx = nullptr; static jclass illegalArgEx = nullptr; // Thread-attached environment for listener callbacks. static JNIEnv *listenerEnv = nullptr; static void ListenerOnStart() { if (!jvm) return; JNIEnv *env; JavaVMAttachArgs args; args.version = JNI_VERSION_1_2; args.name = const_cast("NTListener"); args.group = nullptr; if (jvm->AttachCurrentThreadAsDaemon(reinterpret_cast(&env), &args) != JNI_OK) return; if (!env || !env->functions) return; listenerEnv = env; } static void ListenerOnExit() { listenerEnv = nullptr; if (!jvm) return; jvm->DetachCurrentThread(); } extern "C" { JNIEXPORT jint JNICALL 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/EntryInfo"); if (!local) return JNI_ERR; entryInfoCls = static_cast(env->NewGlobalRef(local)); if (!entryInfoCls) 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); local = env->FindClass("java/lang/IllegalArgumentException"); if (!local) return JNI_ERR; illegalArgEx = static_cast(env->NewGlobalRef(local)); if (!illegalArgEx) return JNI_ERR; env->DeleteLocalRef(local); // Initial configuration of listener start/exit nt::SetListenerOnStart(ListenerOnStart); nt::SetListenerOnExit(ListenerOnExit); return JNI_VERSION_1_6; } JNIEXPORT void JNICALL 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 (entryInfoCls) env->DeleteGlobalRef(entryInfoCls); if (keyNotDefinedEx) env->DeleteGlobalRef(keyNotDefinedEx); if (persistentEx) env->DeleteGlobalRef(persistentEx); if (illegalArgEx) env->DeleteGlobalRef(illegalArgEx); jvm = nullptr; } } // extern "C" // // 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() { if (!jvm || nt::NotifierDestroyed()) return; JNIEnv *env; bool attached = false; // don't attach and de-attach if already attached to a thread. if (jvm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) == JNI_EDETACHED) { if (jvm->AttachCurrentThread(reinterpret_cast(&env), nullptr) != JNI_OK) return; attached = true; } if (!env || !env->functions) return; env->DeleteGlobalRef(m_obj); if (attached) 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() { if (!jvm || nt::NotifierDestroyed()) return; JNIEnv *env; bool attached = false; // don't attach and de-attach if already attached to a thread. if (jvm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) == JNI_EDETACHED) { if (jvm->AttachCurrentThread(reinterpret_cast(&env), nullptr) != JNI_OK) return; attached = true; } if (!env || !env->functions) return; env->DeleteWeakGlobalRef(m_obj); if (attached) 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) { jsize size = env->GetStringLength(str); const jchar *chars = env->GetStringChars(str, nullptr); llvm::convertUTF16ToUTF8String(llvm::makeArrayRef(chars, size), m_str); env->ReleaseStringChars(str, chars); } operator nt::StringRef() const { return m_str; } nt::StringRef str() const { return m_str; } const char* c_str() const { return m_str.data(); } private: llvm::SmallString<128> 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; }; class JavaByteRefBB { public: JavaByteRefBB(JNIEnv *env, jobject bb, int len) : m_elements(env->GetDirectBufferAddress(bb)), m_size(len) {} operator nt::StringRef() const { return nt::StringRef(reinterpret_cast(m_elements), m_size); } private: void *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 FromJavaRawBB(JNIEnv *env, jobject jbb, int len) { void* elements = env->GetDirectBufferAddress(jbb); if (!elements) return nullptr; auto rv = nt::Value::MakeRaw( nt::StringRef(reinterpret_cast(elements), len)); 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) { llvm::SmallVector chars; llvm::convertUTF8ToUTF16String(str, chars); return env->NewString(chars.begin(), chars.size()); } 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_ip(env, ToJavaString(env, info.remote_ip)); return env->NewObject(connectionInfoCls, constructor, remote_id.obj(), remote_ip.obj(), (jint)info.remote_port, (jlong)info.last_update, (jint)info.protocol_version); } static jobject ToJavaObject(JNIEnv *env, const nt::EntryInfo &info) { static jmethodID constructor = env->GetMethodID(entryInfoCls, "", "(Ljava/lang/String;IIJ)V"); JavaLocal name(env, ToJavaString(env, info.name)); return env->NewObject(entryInfoCls, constructor, name.obj(), (jint)info.type, (jint)info.flags, (jlong)info.last_change); } // // 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)); } extern "C" { /* * 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 != JNI_FALSE)); } /* * 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__Ljava_lang_String_2_3B (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: putRaw * Signature: (Ljava/lang/String;Ljava/nio/ByteBuffer;I)Z */ JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putRaw__Ljava_lang_String_2Ljava_nio_ByteBuffer_2I (JNIEnv *env, jclass, jstring key, jobject value, jint len) { auto v = FromJavaRawBB(env, value, len); 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 != JNI_FALSE)); } /* * 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__Ljava_lang_String_2_3B (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: forcePutRaw * Signature: (Ljava/lang/String;Ljava/nio/ByteBuffer;I)V */ JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutRaw__Ljava_lang_String_2Ljava_nio_ByteBuffer_2I (JNIEnv *env, jclass, jstring key, jobject value, jint len) { auto v = FromJavaRawBB(env, value, len); 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: getValue * Signature: (Ljava/lang/String;)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getValue__Ljava_lang_String_2 (JNIEnv *env, jclass, jstring key) { auto val = nt::GetEntryValue(JavaStringRef(env, key)); if (!val) { ThrowTableKeyNotDefined(env, key); return nullptr; } return ToJavaObject(env, *val); } /* * 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: getValue * Signature: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getValue__Ljava_lang_String_2Ljava_lang_Object_2 (JNIEnv *env, jclass, jstring key, jobject defaultValue) { auto val = nt::GetEntryValue(JavaStringRef(env, key)); if (!val) return defaultValue; return ToJavaObject(env, *val); } /* * 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: setDefaultBoolean * Signature: (Ljava/lang/String;Z)Z */ JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setDefaultBoolean (JNIEnv *env, jclass, jstring key, jboolean defaultValue) { return nt::SetDefaultEntryValue(JavaStringRef(env, key), nt::Value::MakeBoolean(defaultValue != JNI_FALSE)); } /* * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI * Method: setDefaultDouble * Signature: (Ljava/lang/String;D)Z */ JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setDefaultDouble (JNIEnv *env, jclass, jstring key, jdouble defaultValue) { return nt::SetDefaultEntryValue(JavaStringRef(env, key), nt::Value::MakeDouble(defaultValue)); } /* * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI * Method: setDefaultString * Signature: (Ljava/lang/String;Ljava/lang/String;)Z */ JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setDefaultString (JNIEnv *env, jclass, jstring key, jstring defaultValue) { return nt::SetDefaultEntryValue(JavaStringRef(env, key), nt::Value::MakeString(JavaStringRef(env, defaultValue))); } /* * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI * Method: setDefaultRaw * Signature: (Ljava/lang/String;[B)Z */ JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setDefaultRaw (JNIEnv *env, jclass, jstring key, jbyteArray defaultValue) { auto v = FromJavaRaw(env, defaultValue); return nt::SetDefaultEntryValue(JavaStringRef(env, key), v); } /* * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI * Method: setDefaultBooleanArray * Signature: (Ljava/lang/String;[Z)Z */ JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setDefaultBooleanArray (JNIEnv *env, jclass, jstring key, jbooleanArray defaultValue) { auto v = FromJavaBooleanArray(env, defaultValue); return nt::SetDefaultEntryValue(JavaStringRef(env, key), v); } /* * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI * Method: setDefaultDoubleArray * Signature: (Ljava/lang/String;[D)Z */ JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setDefaultDoubleArray (JNIEnv *env, jclass, jstring key, jdoubleArray defaultValue) { auto v = FromJavaDoubleArray(env, defaultValue); return nt::SetDefaultEntryValue(JavaStringRef(env, key), v); } /* * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI * Method: setDefaultStringArray * Signature: (Ljava/lang/String;[Ljava/lang/String;)Z */ JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setDefaultStringArray (JNIEnv *env, jclass, jstring key, jobjectArray defaultValue) { auto v = FromJavaStringArray(env, defaultValue); return nt::SetDefaultEntryValue(JavaStringRef(env, key), v); } /* * 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: getEntries * Signature: (Ljava/lang/String;I)[Ledu/wpi/first/wpilibj/networktables/EntryInfo; */ JNIEXPORT jobjectArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getEntries (JNIEnv *env, jclass, jstring prefix, jint types) { auto arr = nt::GetEntryInfo(JavaStringRef(env, prefix), types); jobjectArray jarr = env->NewObjectArray(arr.size(), entryInfoCls, 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: 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, jint flags) { // 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;I)V"); if (!mid) return 0; return nt::AddEntryListener( JavaStringRef(envouter, prefix), [=](unsigned int uid, nt::StringRef name, std::shared_ptr value, unsigned int flags_) { JNIEnv *env = listenerEnv; if (!env || !env->functions) return; // get the handler auto handler = listener_global->obj(); // convert the value into the appropriate Java type JavaLocal jobj(env, ToJavaObject(env, *value)); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); return; } if (!jobj) return; JavaLocal jname(env, ToJavaString(env, name)); env->CallVoidMethod(handler, mid, (jint)uid, jname.obj(), jobj.obj(), (jint)(flags_)); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); } }, flags); } /* * 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) { JNIEnv *env = listenerEnv; if (!env || !env->functions) return; // get the handler auto handler = listener_global->obj(); //if (!handler) goto done; // can happen due to weak reference // convert into the appropriate Java type JavaLocal jobj(env, ToJavaObject(env, conn)); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); return; } if (!jobj) return; env->CallVoidMethod(handler, mid, (jint)uid, (jboolean)(connected ? 1 : 0), jobj.obj()); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); } }, immediateNotify != JNI_FALSE); } /* * 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__Ljava_lang_String_2_3B (JNIEnv *env, jclass, jstring key, jbyteArray params) { return nt::CallRpc(JavaStringRef(env, key), JavaByteRef(env, params)); } /* * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI * Method: callRpc * Signature: (Ljava/lang/String;Ljava/nio/ByteBuffer;I)I */ JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_callRpc__Ljava_lang_String_2Ljava_nio_ByteBuffer_2I (JNIEnv *env, jclass, jstring key, jobject params, jint params_len) { return nt::CallRpc(JavaStringRef(env, key), JavaByteRefBB(env, params, params_len)); } /* * 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__Ljava_lang_String_2I (JNIEnv *env, jclass, jstring serverName, jint port) { nt::StartClient(JavaStringRef(env, serverName).c_str(), port); } /* * 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___3Ljava_lang_String_2_3I (JNIEnv *env, jclass, jobjectArray serverNames, jintArray ports) { int len = env->GetArrayLength(serverNames); if (len != env->GetArrayLength(ports)) { env->ThrowNew(illegalArgEx, "serverNames and ports arrays must be the same size"); return; } jint* portInts = env->GetIntArrayElements(ports, nullptr); if (!portInts) return; std::vector names; std::vector> servers; names.reserve(len); servers.reserve(len); for (int i = 0; i < len; ++i) { JavaLocal elem( env, static_cast(env->GetObjectArrayElement(serverNames, i))); if (!elem) { env->ThrowNew(illegalArgEx, "null string in serverNames"); return; } names.emplace_back(JavaStringRef(env, elem).str()); servers.emplace_back(std::make_pair(nt::StringRef(names.back()), portInts[i])); } env->ReleaseIntArrayElements(ports, portInts, JNI_ABORT); nt::StartClient(servers); } /* * 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(); } } // extern "C" // Thread where log callbacks are actually performed. // // JNI's AttachCurrentThread() creates a Java Thread object on every // invocation, which is both time inefficient and causes issues with Eclipse // (which tries to keep a thread list up-to-date and thus gets swamped). // // Instead, this class attaches just once. When a hardware notification // occurs, a condition variable wakes up this thread and this thread actually // makes the call into Java. class LoggerThreadJNI : public wpi::SafeThread { public: void Main(); struct LogMessage { LogMessage(unsigned int level_, const char* file_, unsigned int line_, const char* msg_) : level(level_), file(file_), line(line_), msg(msg_) {} unsigned int level; const char* file; unsigned int line; std::string msg; }; std::queue m_queue; jobject m_func = nullptr; jmethodID m_mid; }; class LoggerJNI : public wpi::SafeThreadOwner { public: static LoggerJNI& GetInstance() { ATOMIC_STATIC(LoggerJNI, instance); return instance; } void SetFunc(JNIEnv* env, jobject func, jmethodID mid); void Log(unsigned int level, const char* file, unsigned int line, const char* msg); private: ATOMIC_STATIC_DECL(LoggerJNI) }; ATOMIC_STATIC_INIT(LoggerJNI) void LoggerJNI::SetFunc(JNIEnv* env, jobject func, jmethodID mid) { auto thr = GetThread(); if (!thr) return; // free global reference if (thr->m_func) env->DeleteGlobalRef(thr->m_func); // create global reference thr->m_func = env->NewGlobalRef(func); thr->m_mid = mid; } void LoggerJNI::Log(unsigned int level, const char *file, unsigned int line, const char *msg) { auto thr = GetThread(); if (!thr) return; thr->m_queue.emplace(level, file, line, msg); thr->m_cond.notify_one(); } void LoggerThreadJNI::Main() { JNIEnv *env; JavaVMAttachArgs args; args.version = JNI_VERSION_1_2; args.name = const_cast("NTLogger"); args.group = nullptr; jint rs = jvm->AttachCurrentThreadAsDaemon((void**)&env, &args); if (rs != JNI_OK) return; std::unique_lock lock(m_mutex); while (m_active) { m_cond.wait(lock, [&] { return !(m_active && m_queue.empty()); }); if (!m_active) break; while (!m_queue.empty()) { if (!m_active) break; auto item = std::move(m_queue.front()); m_queue.pop(); auto func = m_func; auto mid = m_mid; lock.unlock(); // don't hold mutex during callback execution { JavaLocal file(env, ToJavaString(env, item.file)); JavaLocal msg(env, ToJavaString(env, item.msg)); env->CallVoidMethod(func, mid, (jint)item.level, file.obj(), (jint)item.line, msg.obj()); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); } } lock.lock(); } } if (jvm) jvm->DetachCurrentThread(); } extern "C" { /* * 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 *env, jclass, jobject func, jint minLevel) { // cls is a temporary here; cannot be used within callback functor jclass cls = env->GetObjectClass(func); if (!cls) return; // method ids, on the other hand, are safe to retain jmethodID mid = env->GetMethodID( cls, "apply", "(ILjava/lang/String;ILjava/lang/String;)V"); if (!mid) return; auto& logger = LoggerJNI::GetInstance(); logger.Start(); logger.SetFunc(env, func, mid); nt::SetLogger( [](unsigned int level, const char *file, unsigned int line, const char *msg) { LoggerJNI::GetInstance().Log(level, file, line, msg); }, minLevel); } } // extern "C"