diff options
Diffstat (limited to 'src/wrapper/objects/jlist.rs')
-rw-r--r-- | src/wrapper/objects/jlist.rs | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/src/wrapper/objects/jlist.rs b/src/wrapper/objects/jlist.rs new file mode 100644 index 0000000..34b15ef --- /dev/null +++ b/src/wrapper/objects/jlist.rs @@ -0,0 +1,202 @@ +use crate::{ + errors::*, + objects::{JMethodID, JObject}, + signature::{JavaType, Primitive}, + sys::jint, + JNIEnv, +}; + +/// Wrapper for JObjects that implement `java/util/List`. Provides methods to get, +/// add, and remove elements. +/// +/// Looks up the class and method ids on creation rather than for every method +/// call. +pub struct JList<'a: 'b, 'b> { + internal: JObject<'a>, + get: JMethodID<'a>, + add: JMethodID<'a>, + add_idx: JMethodID<'a>, + remove: JMethodID<'a>, + size: JMethodID<'a>, + env: &'b JNIEnv<'a>, +} + +impl<'a: 'b, 'b> ::std::ops::Deref for JList<'a, 'b> { + type Target = JObject<'a>; + + fn deref(&self) -> &Self::Target { + &self.internal + } +} + +impl<'a: 'b, 'b> From<JList<'a, 'b>> for JObject<'a> { + fn from(other: JList<'a, 'b>) -> JObject<'a> { + other.internal + } +} + +impl<'a: 'b, 'b> JList<'a, 'b> { + /// Create a map from the environment and an object. This looks up the + /// necessary class and method ids to call all of the methods on it so that + /// exra work doesn't need to be done on every method call. + pub fn from_env(env: &'b JNIEnv<'a>, obj: JObject<'a>) -> Result<JList<'a, 'b>> { + let class = env.auto_local(env.find_class("java/util/List")?); + + let get = env.get_method_id(&class, "get", "(I)Ljava/lang/Object;")?; + let add = env.get_method_id(&class, "add", "(Ljava/lang/Object;)Z")?; + let add_idx = env.get_method_id(&class, "add", "(ILjava/lang/Object;)V")?; + let remove = env.get_method_id(&class, "remove", "(I)Ljava/lang/Object;")?; + let size = env.get_method_id(&class, "size", "()I")?; + + Ok(JList { + internal: obj, + get, + add, + add_idx, + remove, + size, + env, + }) + } + + /// Look up the value for a key. Returns `Some` if it's found and `None` if + /// a null pointer would be returned. + pub fn get(&self, idx: jint) -> Result<Option<JObject<'a>>> { + let result = self.env.call_method_unchecked( + self.internal, + self.get, + JavaType::Object("java/lang/Object".into()), + &[idx.into()], + ); + + match result { + Ok(val) => Ok(Some(val.l()?)), + Err(e) => match e { + Error::NullPtr(_) => Ok(None), + _ => Err(e), + }, + } + } + + /// Append an element to the list + pub fn add(&self, value: JObject<'a>) -> Result<()> { + let result = self.env.call_method_unchecked( + self.internal, + self.add, + JavaType::Primitive(Primitive::Boolean), + &[value.into()], + ); + + let _ = result?; + Ok(()) + } + + /// Insert an element at a specific index + pub fn insert(&self, idx: jint, value: JObject<'a>) -> Result<()> { + let result = self.env.call_method_unchecked( + self.internal, + self.add_idx, + JavaType::Primitive(Primitive::Void), + &[idx.into(), value.into()], + ); + + let _ = result?; + Ok(()) + } + + /// Remove an element from the list by index + pub fn remove(&self, idx: jint) -> Result<Option<JObject<'a>>> { + let result = self.env.call_method_unchecked( + self.internal, + self.remove, + JavaType::Object("java/lang/Object".into()), + &[idx.into()], + ); + + match result { + Ok(val) => Ok(Some(val.l()?)), + Err(e) => match e { + Error::NullPtr(_) => Ok(None), + _ => Err(e), + }, + } + } + + /// Get the size of the list + pub fn size(&self) -> Result<jint> { + let result = self.env.call_method_unchecked( + self.internal, + self.size, + JavaType::Primitive(Primitive::Int), + &[], + ); + + result.and_then(|v| v.i()) + } + + /// Pop the last element from the list + /// + /// Note that this calls `size()` to determine the last index. + pub fn pop(&self) -> Result<Option<JObject<'a>>> { + let size = self.size()?; + if size == 0 { + return Ok(None); + } + + let result = self.env.call_method_unchecked( + self.internal, + self.remove, + JavaType::Object("java/lang/Object".into()), + &[(size - 1).into()], + ); + + match result { + Ok(val) => Ok(Some(val.l()?)), + Err(e) => match e { + Error::NullPtr(_) => Ok(None), + _ => Err(e), + }, + } + } + + /// Get key/value iterator for the map. This is done by getting the + /// `EntrySet` from java and iterating over it. + pub fn iter(&self) -> Result<JListIter<'a, 'b, '_>> { + Ok(JListIter { + list: &self, + current: 0, + size: self.size()?, + }) + } +} + +/// An iterator over the keys and values in a map. +/// +/// TODO: make the iterator implementation for java iterators its own thing +/// and generic enough to use elsewhere. +pub struct JListIter<'a: 'b, 'b: 'c, 'c> { + list: &'c JList<'a, 'b>, + current: jint, + size: jint, +} + +impl<'a: 'b, 'b: 'c, 'c> Iterator for JListIter<'a, 'b, 'c> { + type Item = JObject<'a>; + + fn next(&mut self) -> Option<Self::Item> { + if self.current == self.size { + return None; + } + let res = self.list.get(self.current); + match res { + Ok(elem) => { + self.current += 1; + elem + } + Err(_) => { + self.current = self.size; + None + } + } + } +} |