jni/wrapper/objects/
jlist.rs

1use crate::{
2    errors::*,
3    objects::{AutoLocal, JClass, JMethodID, JObject, JValue},
4    signature::{Primitive, ReturnType},
5    sys::jint,
6    JNIEnv,
7};
8
9use std::marker::PhantomData;
10
11/// Wrapper for JObjects that implement `java/util/List`. Provides methods to get,
12/// add, and remove elements.
13///
14/// Looks up the class and method ids on creation rather than for every method
15/// call.
16pub struct JList<'local, 'other_local_1: 'obj_ref, 'obj_ref> {
17    internal: &'obj_ref JObject<'other_local_1>,
18    _phantom_class: PhantomData<AutoLocal<'local, JClass<'local>>>,
19    get: JMethodID,
20    add: JMethodID,
21    add_idx: JMethodID,
22    remove: JMethodID,
23    size: JMethodID,
24}
25
26impl<'local, 'other_local_1: 'obj_ref, 'obj_ref> AsRef<JList<'local, 'other_local_1, 'obj_ref>>
27    for JList<'local, 'other_local_1, 'obj_ref>
28{
29    fn as_ref(&self) -> &JList<'local, 'other_local_1, 'obj_ref> {
30        self
31    }
32}
33
34impl<'local, 'other_local_1: 'obj_ref, 'obj_ref> AsRef<JObject<'other_local_1>>
35    for JList<'local, 'other_local_1, 'obj_ref>
36{
37    fn as_ref(&self) -> &JObject<'other_local_1> {
38        self.internal
39    }
40}
41
42impl<'local, 'other_local_1: 'obj_ref, 'obj_ref> JList<'local, 'other_local_1, 'obj_ref> {
43    /// Create a map from the environment and an object. This looks up the
44    /// necessary class and method ids to call all of the methods on it so that
45    /// exra work doesn't need to be done on every method call.
46    pub fn from_env(
47        env: &mut JNIEnv<'local>,
48        obj: &'obj_ref JObject<'other_local_1>,
49    ) -> Result<JList<'local, 'other_local_1, 'obj_ref>> {
50        let class = AutoLocal::new(env.find_class("java/util/List")?, env);
51
52        let get = env.get_method_id(&class, "get", "(I)Ljava/lang/Object;")?;
53        let add = env.get_method_id(&class, "add", "(Ljava/lang/Object;)Z")?;
54        let add_idx = env.get_method_id(&class, "add", "(ILjava/lang/Object;)V")?;
55        let remove = env.get_method_id(&class, "remove", "(I)Ljava/lang/Object;")?;
56        let size = env.get_method_id(&class, "size", "()I")?;
57
58        Ok(JList {
59            internal: obj,
60            _phantom_class: PhantomData,
61            get,
62            add,
63            add_idx,
64            remove,
65            size,
66        })
67    }
68
69    /// Look up the value for a key. Returns `Some` if it's found and `None` if
70    /// a null pointer would be returned.
71    pub fn get<'other_local_2>(
72        &self,
73        env: &mut JNIEnv<'other_local_2>,
74        idx: jint,
75    ) -> Result<Option<JObject<'other_local_2>>> {
76        // SAFETY: We keep the class loaded, and fetched the method ID for this function.
77        // Provided argument is statically known as a JObject/null, rather than another primitive type.
78        let result = unsafe {
79            env.call_method_unchecked(
80                self.internal,
81                self.get,
82                ReturnType::Object,
83                &[JValue::from(idx).as_jni()],
84            )
85        };
86
87        match result {
88            Ok(val) => Ok(Some(val.l()?)),
89            Err(e) => match e {
90                Error::NullPtr(_) => Ok(None),
91                _ => Err(e),
92            },
93        }
94    }
95
96    /// Append an element to the list
97    pub fn add(&self, env: &mut JNIEnv, value: &JObject) -> Result<()> {
98        // SAFETY: We keep the class loaded, and fetched the method ID for this function.
99        // Provided argument is statically known as a JObject/null, rather than another primitive type.
100        let result = unsafe {
101            env.call_method_unchecked(
102                self.internal,
103                self.add,
104                ReturnType::Primitive(Primitive::Boolean),
105                &[JValue::from(value).as_jni()],
106            )
107        };
108
109        let _ = result?;
110        Ok(())
111    }
112
113    /// Insert an element at a specific index
114    pub fn insert(&self, env: &mut JNIEnv, idx: jint, value: &JObject) -> Result<()> {
115        // SAFETY: We keep the class loaded, and fetched the method ID for this function.
116        // Provided argument is statically known as a JObject/null, rather than another primitive type.
117        let result = unsafe {
118            env.call_method_unchecked(
119                self.internal,
120                self.add_idx,
121                ReturnType::Primitive(Primitive::Void),
122                &[JValue::from(idx).as_jni(), JValue::from(value).as_jni()],
123            )
124        };
125
126        let _ = result?;
127        Ok(())
128    }
129
130    /// Remove an element from the list by index
131    pub fn remove<'other_local_2>(
132        &self,
133        env: &mut JNIEnv<'other_local_2>,
134        idx: jint,
135    ) -> Result<Option<JObject<'other_local_2>>> {
136        // SAFETY: We keep the class loaded, and fetched the method ID for this function.
137        // Provided argument is statically known as a int, rather than any other java type.
138        let result = unsafe {
139            env.call_method_unchecked(
140                self.internal,
141                self.remove,
142                ReturnType::Object,
143                &[JValue::from(idx).as_jni()],
144            )
145        };
146
147        match result {
148            Ok(val) => Ok(Some(val.l()?)),
149            Err(e) => match e {
150                Error::NullPtr(_) => Ok(None),
151                _ => Err(e),
152            },
153        }
154    }
155
156    /// Get the size of the list
157    pub fn size(&self, env: &mut JNIEnv) -> Result<jint> {
158        // SAFETY: We keep the class loaded, and fetched the method ID for this function.
159        let result = unsafe {
160            env.call_method_unchecked(
161                self.internal,
162                self.size,
163                ReturnType::Primitive(Primitive::Int),
164                &[],
165            )
166        };
167
168        result.and_then(|v| v.i())
169    }
170
171    /// Pop the last element from the list
172    ///
173    /// Note that this calls `size()` to determine the last index.
174    pub fn pop<'other_local_2>(
175        &self,
176        env: &mut JNIEnv<'other_local_2>,
177    ) -> Result<Option<JObject<'other_local_2>>> {
178        let size = self.size(env)?;
179        if size == 0 {
180            return Ok(None);
181        }
182
183        // SAFETY: We keep the class loaded, and fetched the method ID for this function.
184        // Provided argument is statically known as a int.
185        let result = unsafe {
186            env.call_method_unchecked(
187                self.internal,
188                self.remove,
189                ReturnType::Object,
190                &[JValue::from(size - 1).as_jni()],
191            )
192        };
193
194        match result {
195            Ok(val) => Ok(Some(val.l()?)),
196            Err(e) => match e {
197                Error::NullPtr(_) => Ok(None),
198                _ => Err(e),
199            },
200        }
201    }
202
203    /// Get key/value iterator for the map. This is done by getting the
204    /// `EntrySet` from java and iterating over it.
205    ///
206    /// The returned iterator does not implement [`std::iter::Iterator`] and
207    /// cannot be used with a `for` loop. This is because its `next` method
208    /// uses a `&mut JNIEnv` to call the Java iterator. Use a `while let` loop
209    /// instead:
210    ///
211    /// ```rust,no_run
212    /// # use jni::{errors::Result, JNIEnv, objects::{AutoLocal, JList, JObject}};
213    /// #
214    /// # fn example(env: &mut JNIEnv, list: JList) -> Result<()> {
215    /// let mut iterator = list.iter(env)?;
216    ///
217    /// while let Some(obj) = iterator.next(env)? {
218    ///     let obj: AutoLocal<JObject> = env.auto_local(obj);
219    ///
220    ///     // Do something with `obj` here.
221    /// }
222    /// # Ok(())
223    /// # }
224    /// ```
225    ///
226    /// Each call to `next` creates a new local reference. To prevent excessive
227    /// memory usage or overflow error, the local reference should be deleted
228    /// using [`JNIEnv::delete_local_ref`] or [`JNIEnv::auto_local`] before the
229    /// next loop iteration. Alternatively, if the list is known to have a
230    /// small, predictable size, the loop could be wrapped in
231    /// [`JNIEnv::with_local_frame`] to delete all of the local references at
232    /// once.
233    pub fn iter<'list>(
234        &'list self,
235        env: &mut JNIEnv,
236    ) -> Result<JListIter<'list, 'local, 'obj_ref, 'other_local_1>> {
237        Ok(JListIter {
238            list: self,
239            current: 0,
240            size: self.size(env)?,
241        })
242    }
243}
244
245/// An iterator over the keys and values in a `java.util.List`. See
246/// [`JList::iter`] for more information.
247///
248/// TODO: make the iterator implementation for java iterators its own thing
249/// and generic enough to use elsewhere.
250pub struct JListIter<'list, 'local, 'other_local_1: 'obj_ref, 'obj_ref> {
251    list: &'list JList<'local, 'other_local_1, 'obj_ref>,
252    current: jint,
253    size: jint,
254}
255
256impl<'list, 'local, 'other_local_1: 'obj_ref, 'obj_ref>
257    JListIter<'list, 'local, 'other_local_1, 'obj_ref>
258{
259    /// Advances the iterator and returns the next object in the
260    /// `java.util.List`, or `None` if there are no more objects.
261    ///
262    /// See [`JList::iter`] for more information.
263    ///
264    /// This method creates a new local reference. To prevent excessive memory
265    /// usage or overflow error, the local reference should be deleted using
266    /// [`JNIEnv::delete_local_ref`] or [`JNIEnv::auto_local`] before the next
267    /// loop iteration. Alternatively, if the list is known to have a small,
268    /// predictable size, the loop could be wrapped in
269    /// [`JNIEnv::with_local_frame`] to delete all of the local references at
270    /// once.
271    ///
272    /// This method returns:
273    ///
274    /// * `Ok(Some(_))`: if there was another object in the list.
275    /// * `Ok(None)`: if there are no more objects in the list.
276    /// * `Err(_)`: if there was an error calling the Java method to
277    ///   get the next object.
278    ///
279    /// This is like [`std::iter::Iterator::next`], but requires a parameter of
280    /// type `&mut JNIEnv` in order to call into Java.
281    pub fn next<'other_local_2>(
282        &mut self,
283        env: &mut JNIEnv<'other_local_2>,
284    ) -> Result<Option<JObject<'other_local_2>>> {
285        if self.current == self.size {
286            return Ok(None);
287        }
288
289        let res = self.list.get(env, self.current);
290
291        self.current = match &res {
292            Ok(Some(_)) => self.current + 1,
293            Ok(None) => self.current,
294            Err(_) => self.size,
295        };
296
297        res
298    }
299}