use std::os::raw::{c_char, c_void};
use crate::errors::{Error, ExtractResult};
use crate::Metadata;
use jni::errors::jni_error_code_to_result;
use jni::objects::{JByteBuffer, JObject, JObjectArray, JString, JValue, JValueOwned};
use jni::{sys, JNIEnv, JavaVM};
use std::collections::HashMap;
pub fn jni_new_direct_buffer<'local>(
env: &mut JNIEnv<'local>,
data: *mut u8,
len: usize,
) -> ExtractResult<JByteBuffer<'local>> {
let direct_byte_buffer = unsafe { env.new_direct_byte_buffer(data, len) }
.map_err(|_e| Error::JniEnvCall("Failed to create direct byte buffer"))?;
Ok(direct_byte_buffer)
}
pub fn jni_call_static_method<'local>(
env: &mut JNIEnv<'local>,
class: &str,
method: &str,
signature: &str,
args: &[JValue],
) -> ExtractResult<JValueOwned<'local>> {
let call_result = env.call_static_method(class, method, signature, args);
match call_result {
Ok(result) => Ok(result),
Err(error) => match error {
jni::errors::Error::JavaException => {
jni_check_exception(env)?;
Err(Error::JniError(error))
}
_ => Err(Error::JniError(error)),
},
}
}
pub fn jni_call_method<'local>(
env: &mut JNIEnv<'local>,
obj: &JObject<'local>,
method: &str,
signature: &str,
args: &[JValue],
) -> ExtractResult<JValueOwned<'local>> {
let call_result = env.call_method(obj, method, signature, args);
match call_result {
Ok(result) => Ok(result),
Err(error) => match error {
jni::errors::Error::JavaException => {
jni_check_exception(env)?;
Err(Error::JniError(error))
}
_ => Err(Error::JniError(error)),
},
}
}
pub fn jni_new_string<'local>(env: &mut JNIEnv<'local>, s: &str) -> ExtractResult<JString<'local>> {
match env.new_string(s) {
Ok(s) => Ok(s),
Err(_) => Err(Error::JniEnvCall("Couldn't create Java String")),
}
}
pub fn jni_new_string_as_jvalue<'local>(
env: &mut JNIEnv<'local>,
s: &str,
) -> ExtractResult<JValueOwned<'local>> {
let jstring = jni_new_string(env, s)?;
Ok(JValueOwned::from(jstring))
}
pub fn jni_jobject_to_string<'local>(
env: &mut JNIEnv<'local>,
jobject: JObject<'local>,
) -> ExtractResult<String> {
let jstring_output = JString::from(jobject);
let javastr_output = unsafe { env.get_string_unchecked(&jstring_output)? };
let output_str = javastr_output.to_string_lossy();
Ok(output_str.to_string())
}
pub fn jni_jobject_array_to_vec<'local>(
env: &mut JNIEnv<'local>,
array: JObject<'local>,
) -> ExtractResult<Vec<String>> {
let j_array_string = JObjectArray::from(array);
let j_array_length = env.get_array_length(&j_array_string)?;
let mut vec = Vec::with_capacity(j_array_length as usize);
for i in 0..j_array_length {
let elem_obj = env.get_object_array_element(&j_array_string, i)?;
let elem_str = jni_jobject_to_string(env, elem_obj)?;
vec.push(elem_str);
}
Ok(vec)
}
pub fn jni_tika_metadata_to_rust_metadata<'local>(
env: &mut JNIEnv<'local>,
j_tika_metadata_object: JObject<'local>,
) -> ExtractResult<Metadata> {
let j_keys_names = env
.call_method(
&j_tika_metadata_object,
"names",
"()[Ljava/lang/String;",
&[],
)?
.l()?;
let keys_names = jni_jobject_array_to_vec(env, j_keys_names)?;
let mut metadata = HashMap::new();
for key_name in keys_names.iter() {
let j_key_name = jni_new_string_as_jvalue(env, key_name)?;
let j_obj_array_name_metadata = env
.call_method(
&j_tika_metadata_object,
"getValues",
"(Ljava/lang/String;)[Ljava/lang/String;",
&[(&j_key_name).into()],
)?
.l()?;
let key_metadata = jni_jobject_array_to_vec(env, j_obj_array_name_metadata)?;
metadata.insert(key_name.to_string(), key_metadata);
}
Ok(metadata)
}
pub fn jni_check_exception(env: &mut JNIEnv) -> ExtractResult<bool> {
if env.exception_check()? {
env.exception_describe()?;
env.exception_clear()?;
return Ok(true);
}
Ok(false)
}
pub fn create_vm_isolate() -> JavaVM {
unsafe {
let vm_options: Vec<sys::JavaVMOption> = vec![
sys::JavaVMOption {
optionString: "-Djava.library.path=.".as_ptr() as *mut c_char,
extraInfo: std::ptr::null_mut(),
},
sys::JavaVMOption {
optionString: "Djava.awt.headless=true".as_ptr() as *mut c_char,
extraInfo: std::ptr::null_mut(),
},
];
let mut args = sys::JavaVMInitArgs {
version: sys::JNI_VERSION_1_8,
nOptions: vm_options.len() as sys::jint,
options: vm_options.as_ptr() as *mut sys::JavaVMOption,
ignoreUnrecognized: sys::JNI_TRUE,
};
let mut ptr: *mut sys::JavaVM = std::ptr::null_mut();
let mut env: *mut sys::JNIEnv = std::ptr::null_mut();
let jni_res = sys::JNI_CreateJavaVM(
&mut ptr as *mut _,
&mut env as *mut *mut sys::JNIEnv as *mut *mut c_void,
&mut args as *mut sys::JavaVMInitArgs as *mut c_void,
);
jni_error_code_to_result(jni_res).unwrap_or_else(|e| {
panic!("Failed creating the graal native vm: {:?}", e);
});
JavaVM::from_raw(ptr).unwrap_or_else(|e| {
panic!("Failed creating the graal native from pointer: {:?}", e);
})
}
}