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}