aboutsummaryrefslogtreecommitdiff
path: root/src/wrapper/objects/jlist.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/wrapper/objects/jlist.rs')
-rw-r--r--src/wrapper/objects/jlist.rs202
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
+ }
+ }
+ }
+}