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