• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::{
2     errors::*,
3     objects::{AutoLocal, JClass, JMethodID, JObject, JValue},
4     signature::{Primitive, ReturnType},
5     sys::jint,
6     JNIEnv,
7 };
8 
9 use 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.
16 pub 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 
26 impl<'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 {
as_ref(&self) -> &JList<'local, 'other_local_1, 'obj_ref>29     fn as_ref(&self) -> &JList<'local, 'other_local_1, 'obj_ref> {
30         self
31     }
32 }
33 
34 impl<'local, 'other_local_1: 'obj_ref, 'obj_ref> AsRef<JObject<'other_local_1>>
35     for JList<'local, 'other_local_1, 'obj_ref>
36 {
as_ref(&self) -> &JObject<'other_local_1>37     fn as_ref(&self) -> &JObject<'other_local_1> {
38         self.internal
39     }
40 }
41 
42 impl<'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.
from_env( env: &mut JNIEnv<'local>, obj: &'obj_ref JObject<'other_local_1>, ) -> Result<JList<'local, 'other_local_1, 'obj_ref>>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.
get<'other_local_2>( &self, env: &mut JNIEnv<'other_local_2>, idx: jint, ) -> Result<Option<JObject<'other_local_2>>>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
add(&self, env: &mut JNIEnv, value: &JObject) -> Result<()>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
insert(&self, env: &mut JNIEnv, idx: jint, value: &JObject) -> Result<()>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
remove<'other_local_2>( &self, env: &mut JNIEnv<'other_local_2>, idx: jint, ) -> Result<Option<JObject<'other_local_2>>>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
size(&self, env: &mut JNIEnv) -> Result<jint>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.
pop<'other_local_2>( &self, env: &mut JNIEnv<'other_local_2>, ) -> Result<Option<JObject<'other_local_2>>>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.
iter<'list>( &'list self, env: &mut JNIEnv, ) -> Result<JListIter<'list, 'local, 'obj_ref, 'other_local_1>>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.
250 pub 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 
256 impl<'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.
next<'other_local_2>( &mut self, env: &mut JNIEnv<'other_local_2>, ) -> Result<Option<JObject<'other_local_2>>>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 }
300