use crate::abstractions::set::Set;
use crate::class::Class;
use crate::object::Object;
use jni::errors::Result;
use jni::objects::JValue;
use jni::sys::_jobject;
use jni::JNIEnv;
pub struct Map<'a> {
pub inner: Object<'a>,
pub k_class: Class<'a>,
pub v_class: Class<'a>,
env: &'a JNIEnv<'a>,
}
#[allow(clippy::from_over_into)]
impl<'a> Into<*mut _jobject> for Map<'a> {
fn into(self) -> *mut _jobject {
self.inner.inner.into_inner()
}
}
impl<'a> Drop for Map<'a> {
fn drop(&mut self) {
let _ = self.env.delete_local_ref(self.inner.inner);
}
}
impl<'a> Map<'a> {
pub fn new(
env: &'a JNIEnv<'a>,
object: Object<'a>,
k_class: Class<'a>,
v_class: Class<'a>,
) -> Self {
Self {
inner: object,
k_class,
v_class,
env,
}
}
pub fn hashmap(env: &'a JNIEnv<'a>, k_class: Class<'a>, v_class: Class<'a>) -> Result<Self> {
let hashmap = env.new_object("java/util/HashMap", "()V", &[])?;
Ok(Self {
inner: Object::new(env, hashmap, Class::HashMap(env)?),
k_class,
v_class,
env,
})
}
pub fn hashmap_with_capacity(
env: &'a JNIEnv<'a>,
k_class: Class<'a>,
v_class: Class<'a>,
initial_capacity: i32,
) -> Result<Self> {
let hashmap = env.new_object(
"java/util/HashMap",
"(I)V",
&[JValue::Int(initial_capacity)],
)?;
Ok(Self {
inner: Object::new(env, hashmap, Class::HashMap(env)?),
k_class,
v_class,
env,
})
}
pub fn hashmap_with_capacity_and_load_factor(
env: &'a JNIEnv<'a>,
k_class: Class<'a>,
v_class: Class<'a>,
initial_capacity: i32,
load_factor: f32,
) -> Result<Self> {
let hashmap = env.new_object(
"java/util/HashMap",
"(IF)V",
&[JValue::Int(initial_capacity), JValue::Float(load_factor)],
)?;
Ok(Self {
inner: Object::new(env, hashmap, Class::HashMap(env)?),
k_class,
v_class,
env,
})
}
pub fn put(&self, key: Object<'a>, value: Object<'a>) -> Result<Option<Object<'a>>> {
let prev_value = self.env.call_method(
self.inner.inner,
"put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
&[key.into(), value.into()],
)?;
let object = Object::new(self.env, prev_value.l()?, self.v_class.clone());
match object.inner.is_null() {
true => Ok(None),
false => Ok(Some(object)),
}
}
pub fn get(&self, key: &Object<'a>) -> Result<Option<Object<'a>>> {
let value = self.env.call_method(
self.inner.inner,
"get",
"(Ljava/lang/Object;)Ljava/lang/Object;",
&[key.into()],
)?;
let object = Object::new(self.env, value.l()?, self.v_class.clone());
match object.inner.is_null() {
true => Ok(None),
false => Ok(Some(object)),
}
}
pub fn is_empty(&self) -> Result<bool> {
let is_empty = self
.env
.call_method(self.inner.inner, "isEmpty", "()Z", &[])?;
is_empty.z()
}
pub fn size(&self) -> Result<i32> {
let size = self.env.call_method(self.inner.inner, "size", "()I", &[])?;
size.i()
}
pub fn contains_key(&self, key: &Object<'a>) -> Result<bool> {
let contains_key = self.env.call_method(
self.inner.inner,
"containsKey",
"(Ljava/lang/Object;)Z",
&[key.into()],
)?;
contains_key.z()
}
pub fn contains_value(&self, value: &Object<'a>) -> Result<bool> {
let contains_value = self.env.call_method(
self.inner.inner,
"containsValue",
"(Ljava/lang/Object;)Z",
&[value.into()],
)?;
contains_value.z()
}
pub fn remove(&self, key: &Object<'a>) -> Result<Option<Object<'a>>> {
let removed_value = self.env.call_method(
self.inner.inner,
"remove",
"(Ljava/lang/Object;)Ljava/lang/Object;",
&[key.into()],
)?;
let object = Object::new(self.env, removed_value.l()?, self.v_class.clone());
match object.inner.is_null() {
true => Ok(None),
false => Ok(Some(object)),
}
}
pub fn remove_if_mapped(&self, key: &Object<'a>, value: &Object<'a>) -> Result<bool> {
let removed = self.env.call_method(
self.inner.inner,
"remove",
"(Ljava/lang/Object;Ljava/lang/Object;)Z",
&[key.into(), value.into()],
)?;
removed.z()
}
pub fn entry_set(&self) -> Result<Set<'a>> {
let entry_set =
self.env
.call_method(self.inner.inner, "entrySet", "()Ljava/util/Set;", &[])?;
let object = Object::new(self.env, entry_set.l()?, Class::Set(self.env)?);
let set = Set::new(self.env, object, Class::MapEntry(self.env)?);
Ok(set)
}
pub fn clear(&self) -> Result<()> {
self.env
.call_method(self.inner.inner, "clear", "()V", &[])?;
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::test::JVM;
#[test]
fn hashmap() {
let jvm = JVM.lock().unwrap();
let env = jvm.attach_current_thread().unwrap();
let int_class = Class::Integer(&env).unwrap();
assert!(Map::hashmap(&env, int_class.clone(), int_class.clone()).is_ok());
assert!(Map::hashmap_with_capacity(&env, int_class.clone(), int_class.clone(), 32).is_ok());
assert!(Map::hashmap_with_capacity_and_load_factor(
&env,
int_class.clone(),
int_class,
32,
32.5
)
.is_ok());
}
#[test]
fn put() {
let jvm = JVM.lock().unwrap();
let env = jvm.attach_current_thread().unwrap();
let int_class = Class::Integer(&env).unwrap();
let map = Map::hashmap(&env, int_class.clone(), int_class).unwrap();
let put_result = map.put(
Object::new_integer_object(&env, 1).unwrap(),
Object::new_integer_object(&env, 10).unwrap(),
);
assert!(put_result.is_ok());
}
#[test]
fn get() {
let jvm = JVM.lock().unwrap();
let env = jvm.attach_current_thread().unwrap();
let int_class = Class::Integer(&env).unwrap();
let map = Map::hashmap(&env, int_class.clone(), int_class).unwrap();
let key = Object::new_integer_object(&env, 1).unwrap();
map.put(key.clone(), Object::new_integer_object(&env, 10).unwrap())
.unwrap();
let gotten = map.get(&key).unwrap();
assert!(gotten.is_some());
let gotten = gotten.unwrap().get_integer().unwrap();
assert_eq!(10, gotten);
}
#[test]
fn is_empty() {
let jvm = JVM.lock().unwrap();
let env = jvm.attach_current_thread().unwrap();
let int_class = Class::Integer(&env).unwrap();
let map = Map::hashmap(&env, int_class.clone(), int_class).unwrap();
let is_empty = map.is_empty();
assert!(is_empty.is_ok());
let is_empty = is_empty.unwrap();
assert!(is_empty);
}
#[test]
fn size() {
let jvm = JVM.lock().unwrap();
let env = jvm.attach_current_thread().unwrap();
let int_class = Class::Integer(&env).unwrap();
let map = Map::hashmap(&env, int_class.clone(), int_class).unwrap();
map.put(
Object::new_integer_object(&env, 1).unwrap(),
Object::new_integer_object(&env, 10).unwrap(),
)
.unwrap();
let size = map.size();
assert!(size.is_ok());
let size = size.unwrap();
assert_eq!(1, size);
}
#[test]
fn contains_key() {
let jvm = JVM.lock().unwrap();
let env = jvm.attach_current_thread().unwrap();
let int_class = Class::Integer(&env).unwrap();
let map = Map::hashmap(&env, int_class.clone(), int_class).unwrap();
let key = Object::new_integer_object(&env, 1).unwrap();
map.put(key.clone(), Object::new_integer_object(&env, 10).unwrap())
.unwrap();
let contains_key = map.contains_key(&key);
assert!(contains_key.is_ok());
let contains_key = contains_key.unwrap();
assert!(contains_key);
}
#[test]
fn contains_value() {
let jvm = JVM.lock().unwrap();
let env = jvm.attach_current_thread().unwrap();
let int_class = Class::Integer(&env).unwrap();
let map = Map::hashmap(&env, int_class.clone(), int_class).unwrap();
let value = Object::new_integer_object(&env, 10).unwrap();
map.put(Object::new_integer_object(&env, 1).unwrap(), value.clone())
.unwrap();
let contains_value = map.contains_value(&value);
assert!(contains_value.is_ok());
let contains_value = contains_value.unwrap();
assert!(contains_value);
}
#[test]
fn remove() {
let jvm = JVM.lock().unwrap();
let env = jvm.attach_current_thread().unwrap();
let int_class = Class::Integer(&env).unwrap();
let map = Map::hashmap(&env, int_class.clone(), int_class).unwrap();
let key = Object::new_integer_object(&env, 1).unwrap();
let value = Object::new_integer_object(&env, 10).unwrap();
map.put(key.clone(), value.clone()).unwrap();
let removed = map.remove(&key);
assert!(removed.is_ok());
let removed = removed.unwrap();
assert!(removed.is_some());
let removed = removed.unwrap();
assert!(value.equals(&removed).unwrap())
}
#[test]
fn entry_set() {
let jvm = JVM.lock().unwrap();
let env = jvm.attach_current_thread().unwrap();
let int_class = Class::Integer(&env).unwrap();
let map = Map::hashmap(&env, int_class.clone(), int_class).unwrap();
let entry_set = map.entry_set();
assert!(entry_set.is_ok());
}
#[test]
fn remove_if_mapped() {
let jvm = JVM.lock().unwrap();
let env = jvm.attach_current_thread().unwrap();
let int_class = Class::Integer(&env).unwrap();
let map = Map::hashmap(&env, int_class.clone(), int_class).unwrap();
let key = Object::new_integer_object(&env, 1).unwrap();
let value = Object::new_integer_object(&env, 10).unwrap();
map.put(key.clone(), value.clone()).unwrap();
assert_eq!(1, map.size().unwrap());
let other_value = Object::new_integer_object(&env, 25).unwrap();
assert!(map.remove_if_mapped(&key, &other_value).is_ok());
assert_eq!(1, map.size().unwrap());
assert!(map.remove_if_mapped(&key, &value).unwrap());
assert_eq!(0, map.size().unwrap());
}
#[test]
fn clear() {
let jvm = JVM.lock().unwrap();
let env = jvm.attach_current_thread().unwrap();
let int_class = Class::Integer(&env).unwrap();
let map = Map::hashmap(&env, int_class.clone(), int_class).unwrap();
map.put(
Object::new_integer_object(&env, 1).unwrap(),
Object::new_integer_object(&env, 10).unwrap(),
)
.unwrap();
let old_size = map.size().unwrap();
assert_eq!(1, old_size);
assert!(map.clear().is_ok());
let new_size = map.size().unwrap();
assert_eq!(0, new_size);
}
}