use jni::errors::{Error, Result};
use jni::objects::{JList, JObject, JString, JValue};
use jni::sys::{jboolean, jbooleanArray, jbyteArray, jchar, jobject};
use jni::JNIEnv;
use crate::convert::unchecked::{FromJavaValue, IntoJavaValue};
use crate::convert::{JavaValue, Signature};
pub use robusta_codegen::{TryFromJavaValue, TryIntoJavaValue};
pub trait TryIntoJavaValue<'env>: Signature {
type Target: JavaValue<'env>;
const SIG_TYPE: &'static str = <Self as Signature>::SIG_TYPE;
fn try_into(self, env: &JNIEnv<'env>) -> Result<Self::Target>;
}
pub trait TryFromJavaValue<'env: 'borrow, 'borrow>
where
Self: Sized + Signature,
{
type Source: JavaValue<'env>;
const SIG_TYPE: &'static str = <Self as Signature>::SIG_TYPE;
fn try_from(s: Self::Source, env: &'borrow JNIEnv<'env>) -> Result<Self>;
}
impl<'env, T> TryIntoJavaValue<'env> for T
where
T: JavaValue<'env> + Signature,
{
type Target = T;
fn try_into(self, env: &JNIEnv<'env>) -> Result<Self::Target> {
Ok(IntoJavaValue::into(self, env))
}
}
impl<'env: 'borrow, 'borrow, T> TryFromJavaValue<'env, 'borrow> for T
where
T: JavaValue<'env> + Signature,
{
type Source = T;
fn try_from(s: Self::Source, env: &'borrow JNIEnv<'env>) -> Result<Self> {
Ok(FromJavaValue::from(s, env))
}
}
impl<'env> TryIntoJavaValue<'env> for String {
type Target = JString<'env>;
const SIG_TYPE: &'static str = "Ljava/lang/String;";
fn try_into(self, env: &JNIEnv<'env>) -> Result<Self::Target> {
env.new_string(self)
}
}
impl<'env: 'borrow, 'borrow> TryFromJavaValue<'env, 'borrow> for String {
type Source = JString<'env>;
fn try_from(s: Self::Source, env: &'borrow JNIEnv<'env>) -> Result<Self> {
env.get_string(s).map(Into::into)
}
}
impl<'env> TryIntoJavaValue<'env> for bool {
type Target = jboolean;
fn try_into(self, _env: &JNIEnv<'env>) -> Result<Self::Target> {
Ok(IntoJavaValue::into(self, _env))
}
}
impl<'env: 'borrow, 'borrow> TryFromJavaValue<'env, 'borrow> for bool {
type Source = jboolean;
fn try_from(s: Self::Source, _env: &JNIEnv<'env>) -> Result<Self> {
Ok(FromJavaValue::from(s, _env))
}
}
impl<'env> TryIntoJavaValue<'env> for char {
type Target = jchar;
fn try_into(self, _env: &JNIEnv<'env>) -> Result<Self::Target> {
Ok(IntoJavaValue::into(self, _env))
}
}
impl<'env: 'borrow, 'borrow> TryFromJavaValue<'env, 'borrow> for char {
type Source = jchar;
fn try_from(s: Self::Source, _env: &JNIEnv<'env>) -> Result<Self> {
let res = std::char::decode_utf16(std::iter::once(s)).next();
match res {
Some(Ok(c)) => Ok(c),
Some(Err(_)) | None => Err(Error::WrongJValueType("char", "jchar")),
}
}
}
impl Signature for Box<[bool]> {
const SIG_TYPE: &'static str = "[Z";
}
impl<'env> TryIntoJavaValue<'env> for Box<[bool]> {
type Target = jbooleanArray;
fn try_into(self, env: &JNIEnv<'env>) -> Result<Self::Target> {
let len = self.len();
let buf: Vec<_> = self.iter().map(|&b| Into::into(b)).collect();
let raw = env.new_boolean_array(len as i32)?;
env.set_boolean_array_region(raw, 0, &buf)?;
Ok(raw)
}
}
impl<'env: 'borrow, 'borrow> TryFromJavaValue<'env, 'borrow> for Box<[bool]> {
type Source = jbooleanArray;
fn try_from(s: Self::Source, env: &'borrow JNIEnv<'env>) -> Result<Self> {
let len = env.get_array_length(s)?;
let mut buf = Vec::with_capacity(len as usize).into_boxed_slice();
env.get_boolean_array_region(s, 0, &mut *buf)?;
buf.iter()
.map(|&b| TryFromJavaValue::try_from(b, &env))
.collect()
}
}
impl<'env, T> TryIntoJavaValue<'env> for Vec<T>
where
T: TryIntoJavaValue<'env>,
{
type Target = jobject;
fn try_into(self, env: &JNIEnv<'env>) -> Result<Self::Target> {
let obj = env.new_object(
"java/util/ArrayList",
"(I)V",
&[JValue::Int(self.len() as i32)],
)?;
let list = JList::from_env(&env, obj)?;
let _: Result<Vec<_>> = self
.into_iter()
.map::<Result<_>, _>(|el| {
Ok(JavaValue::autobox(
TryIntoJavaValue::try_into(el, &env)?,
&env,
))
})
.map(|el| Ok(list.add(el?)))
.collect();
Ok(list.into_inner())
}
}
impl<'env: 'borrow, 'borrow, T, U> TryFromJavaValue<'env, 'borrow> for Vec<T>
where
T: TryFromJavaValue<'env, 'borrow, Source = U>,
U: JavaValue<'env>,
{
type Source = JObject<'env>;
fn try_from(s: Self::Source, env: &'borrow JNIEnv<'env>) -> Result<Self> {
let list = JList::from_env(env, s)?;
list.iter()?
.map(|el| T::try_from(U::unbox(el, env), env))
.collect()
}
}
impl Signature for Box<[u8]> {
const SIG_TYPE: &'static str = "[B";
}
impl<'env> TryIntoJavaValue<'env> for Box<[u8]> {
type Target = jbyteArray;
fn try_into(self, env: &JNIEnv<'env>) -> Result<Self::Target> {
env.byte_array_from_slice(self.as_ref())
}
}
impl<'env: 'borrow, 'borrow> TryFromJavaValue<'env, 'borrow> for Box<[u8]> {
type Source = jbyteArray;
fn try_from(s: Self::Source, env: &'borrow JNIEnv<'env>) -> Result<Box<[u8]>> {
let buf = env.convert_byte_array(s)?;
let boxed_slice = buf.into_boxed_slice();
Ok(boxed_slice)
}
}
impl<'env, T> TryIntoJavaValue<'env> for jni::errors::Result<T>
where
T: TryIntoJavaValue<'env>,
{
type Target = <T as TryIntoJavaValue<'env>>::Target;
fn try_into(self, env: &JNIEnv<'env>) -> Result<Self::Target> {
self.and_then(|s| TryIntoJavaValue::try_into(s, env))
}
}