1 use crate::{ 2 errors::*, 3 objects::{JMethodID, JObject, JValue}, 4 signature::{Primitive, ReturnType}, 5 sys::jint, 6 JNIEnv, 7 }; 8 9 /// Wrapper for JObjects that implement `java/util/List`. Provides methods to get, 10 /// add, and remove elements. 11 /// 12 /// Looks up the class and method ids on creation rather than for every method 13 /// call. 14 pub struct JList<'a: 'b, 'b> { 15 internal: JObject<'a>, 16 get: JMethodID, 17 add: JMethodID, 18 add_idx: JMethodID, 19 remove: JMethodID, 20 size: JMethodID, 21 env: &'b JNIEnv<'a>, 22 } 23 24 impl<'a: 'b, 'b> ::std::ops::Deref for JList<'a, 'b> { 25 type Target = JObject<'a>; 26 deref(&self) -> &Self::Target27 fn deref(&self) -> &Self::Target { 28 &self.internal 29 } 30 } 31 32 impl<'a: 'b, 'b> From<JList<'a, 'b>> for JObject<'a> { from(other: JList<'a, 'b>) -> JObject<'a>33 fn from(other: JList<'a, 'b>) -> JObject<'a> { 34 other.internal 35 } 36 } 37 38 impl<'a: 'b, 'b> JList<'a, 'b> { 39 /// Create a map from the environment and an object. This looks up the 40 /// necessary class and method ids to call all of the methods on it so that 41 /// exra work doesn't need to be done on every method call. from_env(env: &'b JNIEnv<'a>, obj: JObject<'a>) -> Result<JList<'a, 'b>>42 pub fn from_env(env: &'b JNIEnv<'a>, obj: JObject<'a>) -> Result<JList<'a, 'b>> { 43 let class = env.auto_local(env.find_class("java/util/List")?); 44 45 let get = env.get_method_id(&class, "get", "(I)Ljava/lang/Object;")?; 46 let add = env.get_method_id(&class, "add", "(Ljava/lang/Object;)Z")?; 47 let add_idx = env.get_method_id(&class, "add", "(ILjava/lang/Object;)V")?; 48 let remove = env.get_method_id(&class, "remove", "(I)Ljava/lang/Object;")?; 49 let size = env.get_method_id(&class, "size", "()I")?; 50 51 Ok(JList { 52 internal: obj, 53 get, 54 add, 55 add_idx, 56 remove, 57 size, 58 env, 59 }) 60 } 61 62 /// Look up the value for a key. Returns `Some` if it's found and `None` if 63 /// a null pointer would be returned. get(&self, idx: jint) -> Result<Option<JObject<'a>>>64 pub fn get(&self, idx: jint) -> Result<Option<JObject<'a>>> { 65 let result = self.env.call_method_unchecked( 66 self.internal, 67 self.get, 68 ReturnType::Object, 69 &[JValue::from(idx).to_jni()], 70 ); 71 72 match result { 73 Ok(val) => Ok(Some(val.l()?)), 74 Err(e) => match e { 75 Error::NullPtr(_) => Ok(None), 76 _ => Err(e), 77 }, 78 } 79 } 80 81 /// Append an element to the list add(&self, value: JObject<'a>) -> Result<()>82 pub fn add(&self, value: JObject<'a>) -> Result<()> { 83 let result = self.env.call_method_unchecked( 84 self.internal, 85 self.add, 86 ReturnType::Primitive(Primitive::Boolean), 87 &[JValue::from(value).to_jni()], 88 ); 89 90 let _ = result?; 91 Ok(()) 92 } 93 94 /// Insert an element at a specific index insert(&self, idx: jint, value: JObject<'a>) -> Result<()>95 pub fn insert(&self, idx: jint, value: JObject<'a>) -> Result<()> { 96 let result = self.env.call_method_unchecked( 97 self.internal, 98 self.add_idx, 99 ReturnType::Primitive(Primitive::Void), 100 &[JValue::from(idx).to_jni(), JValue::from(value).to_jni()], 101 ); 102 103 let _ = result?; 104 Ok(()) 105 } 106 107 /// Remove an element from the list by index remove(&self, idx: jint) -> Result<Option<JObject<'a>>>108 pub fn remove(&self, idx: jint) -> Result<Option<JObject<'a>>> { 109 let result = self.env.call_method_unchecked( 110 self.internal, 111 self.remove, 112 ReturnType::Object, 113 &[JValue::from(idx).to_jni()], 114 ); 115 116 match result { 117 Ok(val) => Ok(Some(val.l()?)), 118 Err(e) => match e { 119 Error::NullPtr(_) => Ok(None), 120 _ => Err(e), 121 }, 122 } 123 } 124 125 /// Get the size of the list size(&self) -> Result<jint>126 pub fn size(&self) -> Result<jint> { 127 let result = self.env.call_method_unchecked( 128 self.internal, 129 self.size, 130 ReturnType::Primitive(Primitive::Int), 131 &[], 132 ); 133 134 result.and_then(|v| v.i()) 135 } 136 137 /// Pop the last element from the list 138 /// 139 /// Note that this calls `size()` to determine the last index. pop(&self) -> Result<Option<JObject<'a>>>140 pub fn pop(&self) -> Result<Option<JObject<'a>>> { 141 let size = self.size()?; 142 if size == 0 { 143 return Ok(None); 144 } 145 146 let result = self.env.call_method_unchecked( 147 self.internal, 148 self.remove, 149 ReturnType::Object, 150 &[JValue::from(size - 1).to_jni()], 151 ); 152 153 match result { 154 Ok(val) => Ok(Some(val.l()?)), 155 Err(e) => match e { 156 Error::NullPtr(_) => Ok(None), 157 _ => Err(e), 158 }, 159 } 160 } 161 162 /// Get key/value iterator for the map. This is done by getting the 163 /// `EntrySet` from java and iterating over it. iter(&self) -> Result<JListIter<'a, 'b, '_>>164 pub fn iter(&self) -> Result<JListIter<'a, 'b, '_>> { 165 Ok(JListIter { 166 list: self, 167 current: 0, 168 size: self.size()?, 169 }) 170 } 171 } 172 173 /// An iterator over the keys and values in a map. 174 /// 175 /// TODO: make the iterator implementation for java iterators its own thing 176 /// and generic enough to use elsewhere. 177 pub struct JListIter<'a: 'b, 'b: 'c, 'c> { 178 list: &'c JList<'a, 'b>, 179 current: jint, 180 size: jint, 181 } 182 183 impl<'a: 'b, 'b: 'c, 'c> Iterator for JListIter<'a, 'b, 'c> { 184 type Item = JObject<'a>; 185 next(&mut self) -> Option<Self::Item>186 fn next(&mut self) -> Option<Self::Item> { 187 if self.current == self.size { 188 return None; 189 } 190 let res = self.list.get(self.current); 191 match res { 192 Ok(elem) => { 193 self.current += 1; 194 elem 195 } 196 Err(_) => { 197 self.current = self.size; 198 None 199 } 200 } 201 } 202 } 203