Skip to content

Instantly share code, notes, and snippets.

@sp-miguel-ibero
Last active February 12, 2020 14:32
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save sp-miguel-ibero/42402653e34ce36d6374 to your computer and use it in GitHub Desktop.
Save sp-miguel-ibero/42402653e34ce36d6374 to your computer and use it in GitHub Desktop.
Social Point JniObject
#include "JniObject.hpp"
#include <algorithm>
Jni::Jni():
_java(nullptr), _env(nullptr)
{
}
Jni::Jni(const Jni& other)
{
assert(false);
}
Jni::~Jni()
{
if(!_classes.empty())
{
JNIEnv* env = getEnvironment();
if(env)
{
for(ClassMap::const_iterator itr = _classes.begin(); itr != _classes.end(); ++itr)
{
env->DeleteGlobalRef(itr->second);
}
}
}
}
Jni& Jni::get()
{
static Jni jni;
return jni;
}
JavaVM* Jni::getJava()
{
return _java;
}
void Jni::setJava(JavaVM* java)
{
_java = java;
}
JNIEnv* Jni::getEnvironment()
{
if(!_env)
{
assert(_java);
int r = _java->GetEnv((void**)&_env, JNI_VERSION_1_4);
assert(r == JNI_OK); (void)r;
}
int r = _java->AttachCurrentThread(&_env, nullptr);
assert(r < 0); (void)r;
return _env;
}
jclass Jni::getClass(const std::string& classPath, bool cache)
{
ClassMap::const_iterator itr = _classes.find(classPath);
if(itr != _classes.end())
{
return itr->second;
}
JNIEnv* env = getEnvironment();
if(env)
{
jclass cls = (jclass)env->FindClass(classPath.c_str());
if (cls)
{
if(cache)
{
cls = (jclass)env->NewGlobalRef(cls);
_classes[classPath] = cls;
return cls;
}
else
{
return cls;
}
}
else
{
env->ExceptionClear();
}
}
return nullptr;
}
#pragma mark - JniObject
JniObject::JniObject(const std::string& classPath, jobject objId, jclass classId) :
_instance(nullptr), _class(nullptr)
{
init(objId, classId, classPath);
}
JniObject::JniObject(jclass classId, jobject objId) :
_instance(nullptr), _class(nullptr)
{
init(objId, classId);
}
JniObject::JniObject(jobject objId) :
_instance(nullptr), _class(nullptr)
{
init(objId);
}
JniObject::JniObject(const JniObject& other) :
_instance(nullptr), _class(nullptr)
{
init(other._instance, other._class, other._classPath);
}
void JniObject::init(jobject objId, jclass classId, const std::string& classPath)
{
JNIEnv* env = getEnvironment();
_classPath = classPath;
std::replace(_classPath.begin(), _classPath.end(), '.', '/');
if(env)
{
if(!classId)
{
if(objId)
{
classId = env->GetObjectClass(objId);
}
else if(!classPath.empty())
{
classId = Jni::get().getClass(_classPath);
}
}
if(classId)
{
_class = (jclass)env->NewGlobalRef(classId);
}
else
{
_classPath = "";
}
if(objId)
{
_instance = env->NewGlobalRef(objId);
}
}
if(_classPath.empty() && _instance && _class)
{
_classPath = JniObject("java/lang/Class", _class).call("getName", std::string());
}
if(!_class)
{
std::string err("Could not find class");
if(_classPath.empty())
{
err += ".";
}
else
{
err += " '"+_classPath+"'.";
}
setError(err);
}
}
JniObject::~JniObject()
{
clear();
}
void JniObject::clear()
{
JNIEnv* env = getEnvironment();
if(!env)
{
return;
}
if(_class)
{
env->DeleteGlobalRef(_class);
_class = nullptr;
}
if(_instance)
{
env->DeleteGlobalRef(_instance);
_instance = nullptr;
}
}
std::string JniObject::getSignature() const
{
return std::string("L")+getClassPath()+";";
}
const std::string& JniObject::getError() const
{
return _error;
}
bool JniObject::hasError() const
{
return !_error.empty();
}
void JniObject::setError(const std::string& msg)
{
_error = msg;
}
const std::string& JniObject::getClassPath() const
{
return _classPath;
}
JNIEnv* JniObject::getEnvironment()
{
return Jni::get().getEnvironment();
}
jclass JniObject::getClass() const
{
return _class;
}
jobject JniObject::getInstance() const
{
return _instance;
}
jobject JniObject::getNewLocalInstance() const
{
JNIEnv* env = getEnvironment();
if(!env)
{
return 0;
}
return env->NewLocalRef(getInstance());
}
bool JniObject::isInstanceOf(const std::string& classPath) const
{
std::string fclassPath(classPath);
std::replace(fclassPath.begin(), fclassPath.end(), '.', '/');
JNIEnv* env = getEnvironment();
if(!env)
{
return false;
}
jclass cls = env->FindClass(fclassPath.c_str());
return env->IsInstanceOf(getInstance(), cls);
}
JniObject JniObject::findSingleton(const std::string& classPath)
{
JniObject cls(classPath);
JniObject obj = cls.staticField("instance", cls);
if(!obj)
{
obj = cls.staticCall("getInstance", cls);
}
if(!obj)
{
obj.setError("Could not find singleton instance.");
}
return obj;
}
JniObject::operator bool() const
{
return getInstance() != nullptr;
}
JniObject& JniObject::operator=(const JniObject& other)
{
clear();
_classPath = other._classPath;
init(other._instance, other._class);
}
bool JniObject::operator==(const JniObject& other) const
{
JNIEnv* env = getEnvironment();
if(!env)
{
return false;
}
jobject a = getInstance();
jobject b = other.getInstance();
if(a && b)
{
return env->IsSameObject(a, b);
}
a = getClass();
b = other.getClass();
return env->IsSameObject(a, b);
}
#pragma mark - JniObject::getSignaturePart
template<>
std::string JniObject::getSignaturePart<std::string>(const std::string& val)
{
return "Ljava/lang/String;";
}
template<>
std::string JniObject::getSignaturePart(const JniObject& val)
{
return val.getSignature();
}
template<>
std::string JniObject::getSignaturePart(const bool& val)
{
return "Z";
}
template<>
std::string JniObject::getSignaturePart(const jboolean& val)
{
return "Z";
}
template<>
std::string JniObject::getSignaturePart(const jbyte& val)
{
return "B";
}
template<>
std::string JniObject::getSignaturePart(const jchar& val)
{
return "C";
}
template<>
std::string JniObject::getSignaturePart(const jshort& val)
{
return "S";
}
template<>
std::string JniObject::getSignaturePart(const jlong& val)
{
return "J";
}
template<>
std::string JniObject::getSignaturePart(const long& val)
{
return "J";
}
template<>
std::string JniObject::getSignaturePart(const jint& val)
{
return "I";
}
template<>
std::string JniObject::getSignaturePart(const unsigned int& val)
{
return "I";
}
template<>
std::string JniObject::getSignaturePart(const jfloat& val)
{
return "F";
}
template<>
std::string JniObject::getSignaturePart(const jobject& val)
{
return getSignaturePart(JniObject(val));
}
std::string JniObject::getSignaturePart()
{
return "V";
}
#pragma mark - JniObject::convertToJavaValue
template<>
jvalue JniObject::convertToJavaValue(const bool& obj)
{
jvalue val;
val.z = obj;
return val;
}
template<>
jvalue JniObject::convertToJavaValue(const jboolean& obj)
{
jvalue val;
val.z = obj;
return val;
}
template<>
jvalue JniObject::convertToJavaValue(const jbyte& obj)
{
jvalue val;
val.b = obj;
return val;
}
template<>
jvalue JniObject::convertToJavaValue(const jchar& obj)
{
jvalue val;
val.c = obj;
return val;
}
template<>
jvalue JniObject::convertToJavaValue(const jshort& obj)
{
jvalue val;
val.s = obj;
return val;
}
template<>
jvalue JniObject::convertToJavaValue(const jint& obj)
{
jvalue val;
val.i = obj;
return val;
}
template<>
jvalue JniObject::convertToJavaValue(const unsigned int& obj)
{
jvalue val;
val.i = obj;
return val;
}
template<>
jvalue JniObject::convertToJavaValue(const long& obj)
{
jvalue val;
val.j = obj;
return val;
}
template<>
jvalue JniObject::convertToJavaValue(const jlong& obj)
{
jvalue val;
val.j = obj;
return val;
}
template<>
jvalue JniObject::convertToJavaValue(const jfloat& obj)
{
jvalue val;
val.f = obj;
return val;
}
template<>
jvalue JniObject::convertToJavaValue(const jdouble& obj)
{
jvalue val;
val.d = obj;
return val;
}
template<>
jvalue JniObject::convertToJavaValue(const jobject& obj)
{
jvalue val;
val.l = obj;
return val;
}
template<>
jvalue JniObject::convertToJavaValue(const JniObject& obj)
{
return convertToJavaValue(obj.getInstance());
}
template<>
jvalue JniObject::convertToJavaValue(const jarray& obj)
{
jvalue val;
val.l = obj;
return val;
}
template<>
jvalue JniObject::convertToJavaValue(const jstring& obj)
{
jvalue val;
val.l = obj;
return val;
}
template<>
jvalue JniObject::convertToJavaValue(const std::string& obj)
{
JNIEnv* env = getEnvironment();
if (!env)
{
return jvalue();
}
return convertToJavaValue(env->NewStringUTF(obj.c_str()));
}
#pragma mark - JniObject::convertFromJavaObject
template<>
bool JniObject::convertFromJavaObject(JNIEnv* env, jobject obj, std::string& out)
{
if(!obj)
{
out = "";
return true;
}
jstring jstr = (jstring)obj;
const char* chars = env->GetStringUTFChars(jstr, NULL);
if(!chars)
{
return false;
}
out = chars;
env->ReleaseStringUTFChars(jstr, chars);
return true;
}
template<>
bool JniObject::convertFromJavaObject(JNIEnv* env, jobject obj, JniObject& out)
{
out = obj;
env->DeleteLocalRef(obj);
return true;
}
#pragma mark - JniObject call jni
template<>
void JniObject::callStaticJavaMethod(JNIEnv* env, jclass classId, jmethodID methodId, jvalue* args)
{
return env->CallStaticVoidMethodA(classId, methodId, args);
}
template<>
jobject JniObject::callStaticJavaMethod(JNIEnv* env, jclass classId, jmethodID methodId, jvalue* args)
{
return env->CallStaticObjectMethodA(classId, methodId, args);
}
template<>
double JniObject::callStaticJavaMethod(JNIEnv* env, jclass classId, jmethodID methodId, jvalue* args)
{
return env->CallStaticDoubleMethodA(classId, methodId, args);
}
template<>
long JniObject::callStaticJavaMethod(JNIEnv* env, jclass classId, jmethodID methodId, jvalue* args)
{
return env->CallStaticLongMethodA(classId, methodId, args);
}
template<>
jlong JniObject::callStaticJavaMethod(JNIEnv* env, jclass classId, jmethodID methodId, jvalue* args)
{
return env->CallStaticLongMethodA(classId, methodId, args);
}
template<>
float JniObject::callStaticJavaMethod(JNIEnv* env, jclass classId, jmethodID methodId, jvalue* args)
{
return env->CallStaticFloatMethodA(classId, methodId, args);
}
template<>
int JniObject::callStaticJavaMethod(JNIEnv* env, jclass classId, jmethodID methodId, jvalue* args)
{
return env->CallStaticIntMethodA(classId, methodId, args);
}
template<>
std::string JniObject::callStaticJavaMethod(JNIEnv* env, jclass classId, jmethodID methodId, jvalue* args)
{
return convertFromJavaObject<std::string>(env, callStaticJavaMethod<jobject>(env, classId, methodId, args));
}
template<>
JniObject JniObject::callStaticJavaMethod(JNIEnv* env, jclass classId, jmethodID methodId, jvalue* args)
{
return convertFromJavaObject<JniObject>(env, callStaticJavaMethod<jobject>(env, classId, methodId, args));
}
void JniObject::callJavaVoidMethod(JNIEnv* env, jobject objId, jmethodID methodId, jvalue* args)
{
env->CallVoidMethodA(objId, methodId, args);
}
template<>
void JniObject::callJavaMethod(JNIEnv* env, jobject objId, jmethodID methodId, jvalue* args, jboolean& out)
{
out = env->CallBooleanMethodA(objId, methodId, args);
}
template<>
void JniObject::callJavaMethod(JNIEnv* env, jobject objId, jmethodID methodId, jvalue* args, jobject& out)
{
out = env->CallObjectMethodA(objId, methodId, args);
}
template<>
void JniObject::callJavaMethod(JNIEnv* env, jobject objId, jmethodID methodId, jvalue* args, double& out)
{
out = env->CallDoubleMethodA(objId, methodId, args);
}
template<>
void JniObject::callJavaMethod(JNIEnv* env, jobject objId, jmethodID methodId, jvalue* args, long& out)
{
out = env->CallLongMethodA(objId, methodId, args);
}
template<>
void JniObject::callJavaMethod(JNIEnv* env, jobject objId, jmethodID methodId, jvalue* args, jlong& out)
{
out = env->CallLongMethodA(objId, methodId, args);
}
template<>
void JniObject::callJavaMethod(JNIEnv* env, jobject objId, jmethodID methodId, jvalue* args, float& out)
{
out = env->CallFloatMethodA(objId, methodId, args);
}
template<>
void JniObject::callJavaMethod(JNIEnv* env, jobject objId, jmethodID methodId, jvalue* args, int& out)
{
out = env->CallIntMethodA(objId, methodId, args);
}
template<>
void JniObject::callJavaMethod(JNIEnv* env, jobject objId, jmethodID methodId, jvalue* args, std::string& out)
{
callJavaObjectMethod(env, objId, methodId, args, out);
}
template<>
void JniObject::callJavaMethod(JNIEnv* env, jobject objId, jmethodID methodId, jvalue* args, JniObject& out)
{
callJavaObjectMethod(env, objId, methodId, args, out);
}
template<>
jobject JniObject::getJavaStaticField(JNIEnv* env, jclass classId, jfieldID fieldId)
{
return env->GetStaticObjectField(classId, fieldId);
}
template<>
double JniObject::getJavaStaticField(JNIEnv* env, jclass classId, jfieldID fieldId)
{
return env->GetStaticDoubleField(classId, fieldId);
}
template<>
long JniObject::getJavaStaticField(JNIEnv* env, jclass classId, jfieldID fieldId)
{
return env->GetStaticLongField(classId, fieldId);
}
template<>
jlong JniObject::getJavaStaticField(JNIEnv* env, jclass classId, jfieldID fieldId)
{
return env->GetStaticLongField(classId, fieldId);
}
template<>
float JniObject::getJavaStaticField(JNIEnv* env, jclass classId, jfieldID fieldId)
{
return env->GetStaticFloatField(classId, fieldId);
}
template<>
int JniObject::getJavaStaticField(JNIEnv* env, jclass classId, jfieldID fieldId)
{
return env->GetStaticIntField(classId, fieldId);
}
template<>
std::string JniObject::getJavaStaticField(JNIEnv* env, jclass classId, jfieldID fieldId)
{
return convertFromJavaObject<std::string>(getJavaStaticField<jobject>(env, classId, fieldId));
}
template<>
JniObject JniObject::getJavaStaticField(JNIEnv* env, jclass classId, jfieldID fieldId)
{
return convertFromJavaObject<JniObject>(getJavaStaticField<jobject>(env, classId, fieldId));
}
template<>
jobject JniObject::getJavaField(JNIEnv* env, jobject objId, jfieldID fieldId)
{
return env->GetObjectField(objId, fieldId);
}
template<>
double JniObject::getJavaField(JNIEnv* env, jobject objId, jfieldID fieldId)
{
return env->GetDoubleField(objId, fieldId);
}
template<>
long JniObject::getJavaField(JNIEnv* env, jobject objId, jfieldID fieldId)
{
return env->GetLongField(objId, fieldId);
}
template<>
float JniObject::getJavaField(JNIEnv* env, jobject objId, jfieldID fieldId)
{
return env->GetFloatField(objId, fieldId);
}
template<>
int JniObject::getJavaField(JNIEnv* env, jobject objId, jfieldID fieldId)
{
return env->GetIntField(objId, fieldId);
}
template<>
std::string JniObject::getJavaField(JNIEnv* env, jobject objId, jfieldID fieldId)
{
return convertFromJavaObject<std::string>(getJavaField<jobject>(env, objId, fieldId));
}
template<>
JniObject JniObject::getJavaField(JNIEnv* env, jobject objId, jfieldID fieldId)
{
return convertFromJavaObject<JniObject>(getJavaField<jobject>(env, objId, fieldId));
}
template<>
jarray JniObject::createJavaArray(JNIEnv* env, const jobject& element, size_t size)
{
jclass elmClass = env->GetObjectClass(element);
return env->NewObjectArray(size, elmClass, 0);
}
template<>
jarray JniObject::createJavaArray(JNIEnv* env, const double& element, size_t size)
{
return env->NewDoubleArray(size);
}
template<>
jarray JniObject::createJavaArray(JNIEnv* env, const long& element, size_t size)
{
return env->NewLongArray(size);
}
template<>
jarray JniObject::createJavaArray(JNIEnv* env, const jlong& element, size_t size)
{
return env->NewLongArray(size);
}
template<>
jarray JniObject::createJavaArray(JNIEnv* env, const float& element, size_t size)
{
return env->NewFloatArray(size);
}
template<>
jarray JniObject::createJavaArray(JNIEnv* env, const int& element, size_t size)
{
return env->NewLongArray(size);
}
template<>
jarray JniObject::createJavaArray(JNIEnv* env, const std::string& element, size_t size)
{
jclass elmClass = env->FindClass("java/lang/String");
return env->NewObjectArray(size, elmClass, 0);
}
template<>
jarray JniObject::createJavaArray(JNIEnv* env, const JniObject& element, size_t size)
{
jclass elmClass = element.getClass();
return env->NewObjectArray(size, elmClass, 0);
}
template<>
bool JniObject::convertFromJavaArrayElement(JNIEnv* env, jarray arr, size_t position, jobject& out)
{
out = env->GetObjectArrayElement((jobjectArray)arr, position);
return true;
}
template<>
bool JniObject::convertFromJavaArrayElement(JNIEnv* env, jarray arr, size_t position, double& out)
{
env->GetDoubleArrayRegion((jdoubleArray)arr, position, 1, &out);
return true;
}
template<>
bool JniObject::convertFromJavaArrayElement(JNIEnv* env, jarray arr, size_t position, long& out)
{
jlong jout = 0;
env->GetLongArrayRegion((jlongArray)arr, position, 1, &jout);
out = jout;
return true;
}
template<>
bool JniObject::convertFromJavaArrayElement(JNIEnv* env, jarray arr, size_t position, jlong& out)
{
env->GetLongArrayRegion((jlongArray)arr, position, 1, &out);
return true;
}
template<>
bool JniObject::convertFromJavaArrayElement(JNIEnv* env, jarray arr, size_t position, float& out)
{
env->GetFloatArrayRegion((jfloatArray)arr, position, 1, &out);
return true;
}
template<>
bool JniObject::convertFromJavaArrayElement(JNIEnv* env, jarray arr, size_t position, int& out)
{
env->GetIntArrayRegion((jintArray)arr, position, 1, &out);
return true;
}
template<>
bool JniObject::convertFromJavaArrayElement(JNIEnv* env, jarray arr, size_t position, std::string& out)
{
jobject obj;
if(!convertFromJavaArrayElement(env, arr, position, obj))
{
return false;
}
convertFromJavaObject(env, obj, out);
return true;
}
template<>
bool JniObject::convertFromJavaArrayElement(JNIEnv* env, jarray arr, size_t position, JniObject& out)
{
jobject obj;
if(!convertFromJavaArrayElement(env, arr, position, obj))
{
return false;
}
convertFromJavaObject(env, obj, out);
return true;
}
template<>
void JniObject::setJavaArrayElement(JNIEnv* env, jarray arr, size_t position, const jobject& elm)
{
env->SetObjectArrayElement((jobjectArray)arr, position, elm);
}
template<>
void JniObject::setJavaArrayElement(JNIEnv* env, jarray arr, size_t position, const double& elm)
{
env->SetDoubleArrayRegion((jdoubleArray)arr, position, 1, &elm);
}
template<>
void JniObject::setJavaArrayElement(JNIEnv* env, jarray arr, size_t position, const long& elm)
{
jlong jelm = elm;
env->SetLongArrayRegion((jlongArray)arr, position, 1, &jelm);
}
template<>
void JniObject::setJavaArrayElement(JNIEnv* env, jarray arr, size_t position, const jlong& elm)
{
jlong jelm = elm;
env->SetLongArrayRegion((jlongArray)arr, position, 1, &jelm);
}
template<>
void JniObject::setJavaArrayElement(JNIEnv* env, jarray arr, size_t position, const float& elm)
{
env->SetFloatArrayRegion((jfloatArray)arr, position, 1, &elm);
}
template<>
void JniObject::setJavaArrayElement(JNIEnv* env, jarray arr, size_t position, const int& elm)
{
env->SetIntArrayRegion((jintArray)arr, position, 1, &elm);
}
template<>
void JniObject::setJavaArrayElement(JNIEnv* env, jarray arr, size_t position, const std::string& elm)
{
jobject obj = env->NewStringUTF(elm.c_str());
setJavaArrayElement(env, arr, position, obj);
}
template<>
void JniObject::setJavaArrayElement(JNIEnv* env, jarray arr, size_t position, const JniObject& elm)
{
setJavaArrayElement(env, arr, position, elm.getInstance());
}
#ifndef __JniObject__
#define __JniObject__
#include <jni.h>
#include <string>
#include <sstream>
#include <vector>
#include <map>
#include <array>
#include <list>
#include <set>
#include <cassert>
class Jni
{
private:
typedef std::map<std::string, jclass> ClassMap;
JavaVM* _java;
JNIEnv* _env;
ClassMap _classes;
Jni();
Jni(const Jni& other);
public:
~Jni();
/**
* This class is a singleton
*/
static Jni& get();
/**
* Set the java virtual machine pointer
*/
void setJava(JavaVM* java);
/**
* Get the java virtual machine pointer
*/
JavaVM* getJava();
/**
* Get the java environment pointer
* Will attatch to the current thread automatically
*/
JNIEnv* getEnvironment();
/**
* get a class, will be stored in64 the class cache
*/
jclass getClass(const std::string& classPath, bool cache=true);
};
/**
* This class represents a jni object
*/
class JniObject
{
private:
jclass _class;
jobject _instance;
std::string _error;
std::string _classPath;
template<typename Arg, typename... Args>
static void buildSignature(std::ostringstream& os, const Arg& arg, const Args&... args)
{
os << getSignaturePart(arg);
buildSignature(os, args...);
}
static void buildSignature(std::ostringstream& os)
{
}
template<typename Return, typename... Args>
static std::string createSignature(const Return& ret, const Args&... args)
{
std::ostringstream os;
os << "(";
buildSignature(os, args...);
os << ")" << getSignaturePart(ret);
return os.str();
}
template<typename... Args>
static std::string createVoidSignature(const Args&... args)
{
std::ostringstream os;
os << "(";
buildSignature(os, args...);
os << ")" << getSignaturePart();
return os.str();
}
template<typename... Args>
static jvalue* createArguments(const Args&... args)
{
jvalue* jargs = (jvalue*)malloc(sizeof(jvalue)*sizeof...(Args));
buildArguments(jargs, 0, args...);
return jargs;
}
static jvalue* createArguments()
{
return nullptr;
}
template<typename Arg, typename... Args>
static jvalue* buildArguments(jvalue* jargs, unsigned pos, const Arg& arg, const Args&... args)
{
jargs[pos] = convertToJavaValue(arg);
buildArguments(jargs, pos+1, args...);
}
static jvalue* buildArguments(jvalue* jargs, unsigned pos)
{
}
/**
* Return the signature for the given type
*/
template<typename Type>
static std::string getSignaturePart(const Type& type);
/**
* Return the signature for the given container element
*/
template<typename Type>
static std::string getContainerElementSignaturePart(const Type& container)
{
if(container.empty())
{
return getSignaturePart(typename Type::value_type());
}
else
{
return getSignaturePart(*container.begin());
}
}
// template specialization for pointers
template<typename Type>
static std::string getSignaturePart(Type* val)
{
return getSignaturePart((jlong)val);
}
// template specialization for containers
template<typename Type>
static std::string getSignaturePart(const std::vector<Type>& val)
{
return std::string("[")+getContainerElementSignaturePart(val);
}
template<typename Type>
static std::string getSignaturePart(const std::set<Type>& val)
{
return std::string("[")+getContainerElementSignaturePart(val);
}
template<typename Type, int Size>
static std::string getSignaturePart(const std::array<Type, Size>& val)
{
return std::string("[")+getContainerElementSignaturePart(val);
}
template<typename Type>
static std::string getSignaturePart(const std::list<Type>& val)
{
return std::string("[")+getContainerElementSignaturePart(val);
}
template<typename Key, typename Value>
static std::string getSignaturePart(const std::map<Key, Value>& val)
{
return "Ljava/util/Map;";
}
/**
* Return the signature for the void type
*/
static std::string getSignaturePart();
template<typename Return>
Return callStaticJavaMethod(JNIEnv* env, jclass classId, jmethodID methodId, jvalue* args);
void callJavaVoidMethod(JNIEnv* env, jobject objId, jmethodID methodId, jvalue* args);
template<typename Return>
void callJavaMethod(JNIEnv* env, jobject objId, jmethodID methodId, jvalue* args, Return& out);
template<typename Return>
void callJavaObjectMethod(JNIEnv* env, jobject objId, jmethodID methodId, jvalue* args, Return& out)
{
jobject jout = nullptr;
callJavaMethod(env, objId, methodId, args, jout);
out = convertFromJavaObject<Return>(jout);
}
template<typename Type>
void callJavaMethod(JNIEnv* env, jobject objId, jmethodID methodId, jvalue* args, std::vector<Type>& out)
{
callJavaObjectMethod(env, objId, methodId, args, out);
}
template<typename Return>
Return getJavaStaticField(JNIEnv* env, jclass classId, jfieldID fieldId);
template<typename Return>
Return getJavaField(JNIEnv* env, jobject objId, jfieldID fieldId);
void setError(const std::string& msg);
public:
JniObject(const std::string& classPath, jobject javaObj=nullptr, jclass classId=nullptr);
JniObject(jclass classId, jobject javaObj);
JniObject(jobject javaObj=nullptr);
JniObject(const JniObject& other);
void init(jobject javaObj=nullptr, jclass classId=nullptr, const std::string& classPath="");
~JniObject();
/**
* Clear the retained global references
*/
void clear();
/**
* Find a singleton instance
* will try the `instance` static field and a `getInstance` static method
*/
static JniObject findSingleton(const std::string& classPath);
/**
* Create a new JniObject
*/
template<typename... Args>
static JniObject createNew(const std::string& classPath, Args&&... args)
{
JniObject defRet(classPath);
JNIEnv* env = getEnvironment();
if(!env)
{
return defRet;
}
jclass classId = Jni::get().getClass(classPath);
if(!classId)
{
return defRet;
}
std::string signature(createVoidSignature<Args...>(args...));
jmethodID methodId = env->GetMethodID(classId, "<init>", signature.c_str());
if (!methodId || env->ExceptionCheck())
{
env->ExceptionClear();
defRet.setError(std::string("Failed to find constructor '"+classPath+"' with signature '"+signature+"'."));
}
else
{
jvalue* jargs = createArguments(args...);
jobject obj = env->NewObjectA(classId, methodId, jargs);
if (env->ExceptionCheck())
{
env->ExceptionClear();
defRet.setError(std::string("Failed to call constructor '"+classPath+"' with signature '"+signature+"'."));
}
else
{
defRet = JniObject(classPath, obj, classId);
}
}
return defRet;
}
/**
* Calls an object method
*/
template<typename Return, typename... Args>
Return call(const std::string& name, const Return& defRet, Args&&... args)
{
std::string signature(createSignature(defRet, args...));
return callSigned(name, signature, defRet, args...);
}
template<typename Return, typename... Args>
Return callSigned(const std::string& name, const std::string& signature, const Return& defRet, Args&&... args)
{
JNIEnv* env = getEnvironment();
if(!env)
{
return defRet;
}
jclass classId = getClass();
if(!classId)
{
return defRet;
}
jobject objId = getInstance();
if(!objId)
{
return defRet;
}
jmethodID methodId = env->GetMethodID(classId, name.c_str(), signature.c_str());
if (!methodId || env->ExceptionCheck())
{
env->ExceptionClear();
setError(std::string("Failed to find method '")+name+"' with signature '"+signature+"'.");
return defRet;
}
else
{
jvalue* jargs = createArguments(args...);
Return result;
callJavaMethod(env, objId, methodId, jargs, result);
if (env->ExceptionCheck())
{
env->ExceptionClear();
setError(std::string("Failed to call method '")+name+" with signature '"+signature+"'.");
return defRet;
}
else
{
return result;
}
}
}
/**
* Calls an object void method
*/
template<typename... Args>
void callVoid(const std::string& name, Args&&... args)
{
std::string signature(createVoidSignature(args...));
return callSignedVoid(name, signature, args...);
}
template<typename... Args>
void callSignedVoid(const std::string& name, const std::string& signature, Args&&... args)
{
JNIEnv* env = getEnvironment();
if(!env)
{
return;
}
jclass classId = getClass();
if(!classId)
{
setError(std::string("Could not invoke '")+name+"': class not found.");
return;
}
jobject objId = getInstance();
if(!objId)
{
return;
}
jmethodID methodId = env->GetMethodID(classId, name.c_str(), signature.c_str());
if (!methodId || env->ExceptionCheck())
{
env->ExceptionClear();
setError(std::string("Failed to find method '")+name+"' with signature '"+signature+"'.");
}
else
{
jvalue* jargs = createArguments(args...);
callJavaVoidMethod(env, objId, methodId, jargs);
if (env->ExceptionCheck())
{
env->ExceptionClear();
setError(std::string("Failed to call method '")+name+"' with signature '"+signature+"'.");
}
}
}
/**
* Calls a class method
*/
template<typename Return, typename... Args>
Return staticCall(const std::string& name, const Return& defRet, Args&&... args)
{
std::string signature(createSignature(defRet, args...));
return staticCallSigned(name, signature, defRet, args...);
}
template<typename Return, typename... Args>
Return staticCallSigned(const std::string& name, const std::string& signature, const Return& defRet, Args&&... args)
{
JNIEnv* env = getEnvironment();
if(!env)
{
return defRet;
}
jclass classId = getClass();
if(!classId)
{
return defRet;
}
jmethodID methodId = env->GetStaticMethodID(classId, name.c_str(), signature.c_str());
if (!methodId || env->ExceptionCheck())
{
env->ExceptionClear();
setError(std::string("Failed to find static method '")+name+"'.");
return defRet;
}
else
{
jvalue* jargs = createArguments(args...);
Return result = callStaticJavaMethod<Return>(env, classId, methodId, jargs);
if (env->ExceptionCheck())
{
env->ExceptionClear();
setError(std::string("Failed to call static method '")+name+"'.");
return defRet;
}
else
{
return result;
}
}
}
/**
* Calls a class void method
*/
template<typename... Args>
void staticCallVoid(const std::string& name, Args&&... args)
{
std::string signature(createVoidSignature(args...));
return staticCallSignedVoid(name, signature, args...);
}
template<typename... Args>
void staticCallSignedVoid(const std::string& name, const std::string& signature, Args&&... args)
{
JNIEnv* env = getEnvironment();
if(!env)
{
return;
}
jclass classId = getClass();
if(!classId)
{
return;
}
jmethodID methodId = env->GetStaticMethodID(classId, name.c_str(), signature.c_str());
if (!methodId || env->ExceptionCheck())
{
env->ExceptionClear();
setError(std::string("Failed to find static method '")+name+"'.");
return;
}
else
{
jvalue* jargs = createArguments(args...);
callStaticJavaMethod<void>(env, classId, methodId, jargs);
if (env->ExceptionCheck())
{
env->ExceptionClear();
setError(std::string("Failed to call static method '")+name+"'.");
}
}
}
/**
* Get a static class field
* @param name the field name
*/
template<typename Return>
Return staticField(const std::string& name, const Return& defRet)
{
std::string signature(getSignaturePart<Return>(defRet));
return staticFieldSigned(name, signature, defRet);
}
template<typename Return>
Return staticFieldSigned(const std::string& name, const std::string& signature, const Return& defRet)
{
JNIEnv* env = getEnvironment();
if(!env)
{
return defRet;
}
jclass classId = getClass();
if(!classId)
{
return defRet;
}
jfieldID fieldId = env->GetStaticFieldID(classId, name.c_str(), signature.c_str());
if (!fieldId || env->ExceptionCheck())
{
env->ExceptionClear();
setError(std::string("Failed to find static field '")+name+"' with signature '"+signature+"'.");
return defRet;
}
else
{
Return result = getJavaStaticField<Return>(env, classId, fieldId);
if (env->ExceptionCheck())
{
env->ExceptionClear();
setError(std::string("Failed to read static field '")+name+"' with signature '"+signature+"'.");
return defRet;
}
else
{
return result;
}
}
}
/**
* Get a object field
* @param name the field name
*/
template<typename Return>
Return field(const std::string& name, const Return& defRet)
{
std::string signature(getSignaturePart<Return>(defRet));
return fieldSigned(name, signature, defRet);
}
template<typename Return>
Return fieldSigned(const std::string& name, const std::string& signature, const Return& defRet)
{
JNIEnv* env = getEnvironment();
if(!env)
{
return defRet;
}
jclass classId = getClass();
if(!classId)
{
return defRet;
}
jfieldID fieldId = env->GetFieldID(classId, name.c_str(), signature.c_str());
if (!fieldId || env->ExceptionCheck())
{
env->ExceptionClear();
setError(std::string("Failed to find field '")+name+"' with signature '"+signature+"'.");
return defRet;
}
else
{
Return result = getJavaField<Return>(env, classId, fieldId);
if (env->ExceptionCheck())
{
env->ExceptionClear();
setError(std::string("Failed to read field '")+name+"' with signature '"+signature+"'.");
return defRet;
}
else
{
return result;
}
}
}
/**
* Return the signature for the object
*/
std::string getSignature() const;
/**
* Return the error
*/
const std::string& getError() const;
/**
* Return true of there is an error
*/
bool hasError() const;
/**
* create an java array of the given type
*/
template<typename Type>
static jarray createJavaArray(JNIEnv* env, const Type& element, size_t size);
/**
* Convert a jobject array to a container
*/
template<typename Type>
static bool convertFromJavaArray(JNIEnv* env, jarray arr, Type& container)
{
if(!arr)
{
return false;
}
jsize arraySize = env->GetArrayLength(arr);
for(size_t i=0; i<arraySize; i++)
{
typename Type::value_type elm;
convertFromJavaArrayElement(env, arr, i, elm);
container.insert(container.end(), elm);
}
return true;
}
template<typename Type>
static bool convertFromJavaArray(jarray arr, Type& container)
{
JNIEnv* env = getEnvironment();
assert(env);
return convertFromJavaArray(env, arr, container);
}
/**
* Get an element of a java array
*/
template<typename Type>
static bool convertFromJavaArrayElement(JNIEnv* env, jarray arr, size_t position, Type& out);
template<typename Type>
static bool convertFromJavaArrayElement(JNIEnv* env, jarray arr, size_t position, std::vector<Type>& out)
{
jobject elm;
if(!convertFromJavaArrayElement(env, arr, position, elm))
{
return false;
}
return convertFromJavaArray(env, (jarray)elm, out);
}
template<typename Key, typename Value>
static bool convertFromJavaArrayElement(JNIEnv* env, jarray arr, size_t position, std::map<Key, Value>& out)
{
jobject elm;
if(!convertFromJavaArrayElement(env, arr, position, elm))
{
return false;
}
return convertFromJavaMap(env, elm, out);
}
/**
* Set an element of a java array
*/
template<typename Type>
static void setJavaArrayElement(JNIEnv* env, jarray arr, size_t position, const Type& elm);
template<typename Type>
static bool convertFromJavaCollection(JNIEnv* env, jobject obj, Type& out)
{
if(!obj)
{
return false;
}
JniObject jcontainer(obj);
if(!jcontainer.isInstanceOf("java.util.Collection"))
{
return false;
}
out = jcontainer.call<Type>("toArray", out, out);
return true;
}
template<typename Key, typename Value>
static bool convertFromJavaMap(JNIEnv* env, jobject obj, std::map<Key, Value>& out)
{
if(!obj)
{
return false;
}
JniObject jmap(obj);
if(!jmap.isInstanceOf("java.util.Map"))
{
return false;
}
JniObject jkeys = jmap.call<JniObject>("keySet", JniObject("java.util.Set"));
if(jkeys.hasError())
{
return false;
}
std::vector<Key> keys = jkeys.callSigned<std::vector<Key>>("toArray", "()[Ljava/lang/Object;", std::vector<Key>());
for(typename std::vector<Key>::const_iterator itr = keys.begin(); itr != keys.end(); ++itr)
{
Value v = jmap.callSigned<Value>("get", "(Ljava/lang/Object;)Ljava/lang/Object;", Value(), *itr);
out[*itr] = v;
}
return true;
}
template<typename Key, typename Value>
static bool convertToMapFromJavaArray(JNIEnv* env, jarray arr, std::map<Key, Value>& out)
{
if(!arr)
{
return false;
}
jsize mapSize = env->GetArrayLength(arr) / 2;
for(size_t i=0; i<mapSize; ++i)
{
Key k;
if(convertFromJavaArrayElement(env, arr, i*2, k))
{
Value v;
if(convertFromJavaArrayElement(env, arr, i*2+1, v))
{
out[k] = v;
}
}
}
return true;
}
/**
* Convert a jobject to the c++ representation
*/
template<typename Type>
static bool convertFromJavaObject(JNIEnv* env, jobject obj, Type& out);
// template specialization for containers
template<typename Type>
static bool convertFromJavaObject(JNIEnv* env, jobject obj, std::vector<Type>& out)
{
if(convertFromJavaCollection(env, obj, out))
{
return true;
}
if(convertFromJavaArray(env, (jarray)obj, out))
{
return true;
}
return false;
}
template<typename Type>
static bool convertFromJavaObject(JNIEnv* env, jobject obj, std::set<Type>& out)
{
if(convertFromJavaCollection(env, obj, out))
{
return true;
}
if(convertFromJavaArray(env, (jarray)obj, out))
{
return true;
}
return false;
}
template<typename Type, int Size>
static bool convertFromJavaObject(JNIEnv* env, jobject obj, std::array<Type, Size>& out)
{
if(convertFromJavaCollection(env, obj, out))
{
return true;
}
if(convertFromJavaArray(env, (jarray)obj, out))
{
return true;
}
return false;
}
template<typename Type>
static bool convertFromJavaObject(JNIEnv* env, jobject obj, std::list<Type>& out)
{
if(convertFromJavaCollection(env, obj, out))
{
return true;
}
if(convertFromJavaArray(env, (jarray)obj, out))
{
return true;
}
return false;
}
template<typename Key, typename Type>
static bool convertFromJavaObject(JNIEnv* env, jobject obj, std::map<Key, Type>& out)
{
if(convertFromJavaMap(env, obj, out))
{
return true;
}
if(convertToMapFromJavaArray(env, (jarray)obj, out))
{
return true;
}
return false;
}
// utility methods that return the object
template<typename Type>
static Type convertFromJavaObject(JNIEnv* env, jobject obj)
{
Type out;
bool result = convertFromJavaObject(env, obj, out);
assert(result);
return out;
}
template<typename Type>
static Type convertFromJavaObject(jobject obj)
{
JNIEnv* env = getEnvironment();
assert(env);
return convertFromJavaObject<Type>(env, obj);
}
template<typename Type>
static bool convertFromJavaObject(jobject obj, Type& out)
{
JNIEnv* env = getEnvironment();
assert(env);
return convertFromJavaObject(env, obj, out);
}
/**
* Convert a c++ list container to a jarray
*/
template<typename Type>
static jarray createJavaArray(const Type& obj)
{
JNIEnv* env = getEnvironment();
if (!env)
{
return nullptr;
}
jarray arr = nullptr;
if(obj.empty())
{
arr = createJavaArray(env, typename Type::value_type(), 0);
}
else
{
arr = createJavaArray(env, *obj.begin(), obj.size());
}
size_t i = 0;
for(typename Type::const_iterator itr = obj.begin(); itr != obj.end(); ++itr)
{
setJavaArrayElement(env, arr, i, *itr);
i++;
}
return arr;
}
template<typename Key, typename Value>
static JniObject createJavaMap(const std::map<Key, Value>& obj, const std::string& classPath="java/util/HashMap")
{
JniObject jmap(JniObject::createNew(classPath));
for(typename std::map<Key, Value>::const_iterator itr = obj.begin(); itr != obj.end(); ++itr)
{
Value v = jmap.callSigned("put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", Value(), itr->first, itr->second);
}
return jmap;
}
template<typename Type>
static JniObject createJavaList(const Type& obj, const std::string& classPath="java/util/ArrayList")
{
JniObject jlist(JniObject::createNew(classPath));
for(typename Type::const_iterator itr = obj.begin(); itr != obj.end(); ++itr)
{
jlist.callSignedVoid("add", "(Ljava/lang/Object;)Z", *itr);
}
return jlist;
}
template<typename Type>
static JniObject createJavaSet(const Type& obj, const std::string& classPath="java/util/HashSet")
{
return createJavaList(obj, classPath);
}
/**
* Convert a c++ type to the jvalue representation
* This is called on all jni arguments
*/
template<typename Type>
static jvalue convertToJavaValue(const Type& obj);
// template specialization for pointers
template<typename Type>
static jvalue convertToJavaValue(Type* obj)
{
return convertToJavaValue((jlong)obj);
}
// template specialization for containers
template<typename Type>
static jvalue convertToJavaValue(const std::vector<Type>& obj)
{
return convertToJavaValue(createJavaArray(obj));
}
template<typename Type>
static jvalue convertToJavaValue(const std::set<Type>& obj)
{
return convertToJavaValue(createJavaArray(obj));
}
template<typename Type, int Size>
static jvalue convertToJavaValue(const std::array<Type, Size>& obj)
{
return convertToJavaValue(createJavaArray(obj));
}
template<typename Type>
static jvalue convertToJavaValue(const std::list<Type>& obj)
{
return convertToJavaValue(createJavaArray(obj));
}
template<typename Key, typename Value>
static jvalue convertToJavaValue(const std::map<Key, Value>& obj)
{
return convertToJavaValue(createJavaMap(obj).getNewLocalInstance());
}
/**
* Returns the class reference. This is a global ref that will be removed
* when the JniObject is destroyed
*/
jclass getClass() const;
/**
* Returns the class path. If it is not there it tries to call
* `getClass().getName()` on the object to get the class
*/
const std::string& getClassPath() const;
/**
* Returns the jobject reference. This is a global ref that will be removed
* when the JniObject is destroyed
*/
jobject getInstance() const;
/**
* Returns the jobject reference. This is a new local ref
*/
jobject getNewLocalInstance() const;
/**
* Return true if class path and class ref match
*/
bool isInstanceOf(const std::string& classPath) const;
/**
* Returns the environment pointer
*/
static JNIEnv* getEnvironment();
/**
* Returns true if there is an object instance
*/
operator bool() const;
/**
* Copy a jni object
*/
JniObject& operator=(const JniObject& other);
/**
* Compare two jni objects
*/
bool operator==(const JniObject& other) const;
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment