1 use crate::{ 2 errors::*, 3 objects::{AutoLocal, JMethodID, JObject}, 4 signature::{JavaType, Primitive}, 5 JNIEnv, 6 }; 7 8 /// Wrapper for JObjects that implement `java/util/Map`. Provides methods to get 9 /// and set entries and a way to iterate over key/value pairs. 10 /// 11 /// Looks up the class and method ids on creation rather than for every method 12 /// call. 13 pub struct JMap<'a: 'b, 'b> { 14 internal: JObject<'a>, 15 class: AutoLocal<'a, 'b>, 16 get: JMethodID<'a>, 17 put: JMethodID<'a>, 18 remove: JMethodID<'a>, 19 env: &'b JNIEnv<'a>, 20 } 21 22 impl<'a: 'b, 'b> ::std::ops::Deref for JMap<'a, 'b> { 23 type Target = JObject<'a>; 24 deref(&self) -> &Self::Target25 fn deref(&self) -> &Self::Target { 26 &self.internal 27 } 28 } 29 30 impl<'a: 'b, 'b> From<JMap<'a, 'b>> for JObject<'a> { from(other: JMap<'a, 'b>) -> JObject<'a>31 fn from(other: JMap<'a, 'b>) -> JObject<'a> { 32 other.internal 33 } 34 } 35 36 impl<'a: 'b, 'b> JMap<'a, 'b> { 37 /// Create a map from the environment and an object. This looks up the 38 /// necessary class and method ids to call all of the methods on it so that 39 /// exra work doesn't need to be done on every method call. from_env(env: &'b JNIEnv<'a>, obj: JObject<'a>) -> Result<JMap<'a, 'b>>40 pub fn from_env(env: &'b JNIEnv<'a>, obj: JObject<'a>) -> Result<JMap<'a, 'b>> { 41 let class = env.auto_local(env.find_class("java/util/Map")?); 42 43 let get = env.get_method_id(&class, "get", "(Ljava/lang/Object;)Ljava/lang/Object;")?; 44 let put = env.get_method_id( 45 &class, 46 "put", 47 "(Ljava/lang/Object;Ljava/lang/Object;\ 48 )Ljava/lang/Object;", 49 )?; 50 51 let remove = 52 env.get_method_id(&class, "remove", "(Ljava/lang/Object;)Ljava/lang/Object;")?; 53 54 Ok(JMap { 55 internal: obj, 56 class, 57 get, 58 put, 59 remove, 60 env, 61 }) 62 } 63 64 /// Look up the value for a key. Returns `Some` if it's found and `None` if 65 /// a null pointer would be returned. get(&self, key: JObject<'a>) -> Result<Option<JObject<'a>>>66 pub fn get(&self, key: JObject<'a>) -> Result<Option<JObject<'a>>> { 67 let result = self.env.call_method_unchecked( 68 self.internal, 69 self.get, 70 JavaType::Object("java/lang/Object".into()), 71 &[key.into()], 72 ); 73 74 match result { 75 Ok(val) => Ok(Some(val.l()?)), 76 Err(e) => match e { 77 Error::NullPtr(_) => Ok(None), 78 _ => Err(e), 79 }, 80 } 81 } 82 83 /// Look up the value for a key. Returns `Some` with the old value if the 84 /// key already existed and `None` if it's a new key. put(&self, key: JObject<'a>, value: JObject<'a>) -> Result<Option<JObject<'a>>>85 pub fn put(&self, key: JObject<'a>, value: JObject<'a>) -> Result<Option<JObject<'a>>> { 86 let result = self.env.call_method_unchecked( 87 self.internal, 88 self.put, 89 JavaType::Object("java/lang/Object".into()), 90 &[key.into(), value.into()], 91 ); 92 93 match result { 94 Ok(val) => Ok(Some(val.l()?)), 95 Err(e) => match e { 96 Error::NullPtr(_) => Ok(None), 97 _ => Err(e), 98 }, 99 } 100 } 101 102 /// Remove a value from the map. Returns `Some` with the removed value and 103 /// `None` if there was no value for the key. remove(&self, key: JObject<'a>) -> Result<Option<JObject<'a>>>104 pub fn remove(&self, key: JObject<'a>) -> Result<Option<JObject<'a>>> { 105 let result = self.env.call_method_unchecked( 106 self.internal, 107 self.remove, 108 JavaType::Object("java/lang/Object".into()), 109 &[key.into()], 110 ); 111 112 match result { 113 Ok(val) => Ok(Some(val.l()?)), 114 Err(e) => match e { 115 Error::NullPtr(_) => Ok(None), 116 _ => Err(e), 117 }, 118 } 119 } 120 121 /// Get key/value iterator for the map. This is done by getting the 122 /// `EntrySet` from java and iterating over it. iter(&self) -> Result<JMapIter<'a, 'b, '_>>123 pub fn iter(&self) -> Result<JMapIter<'a, 'b, '_>> { 124 let iter_class = self 125 .env 126 .auto_local(self.env.find_class("java/util/Iterator")?); 127 128 let has_next = self.env.get_method_id(&iter_class, "hasNext", "()Z")?; 129 130 let next = self 131 .env 132 .get_method_id(&iter_class, "next", "()Ljava/lang/Object;")?; 133 134 let entry_class = self 135 .env 136 .auto_local(self.env.find_class("java/util/Map$Entry")?); 137 138 let get_key = self 139 .env 140 .get_method_id(&entry_class, "getKey", "()Ljava/lang/Object;")?; 141 142 let get_value = self 143 .env 144 .get_method_id(&entry_class, "getValue", "()Ljava/lang/Object;")?; 145 146 // Get the iterator over Map entries. 147 // Use the local frame till #109 is resolved, so that implicitly looked-up 148 // classes are freed promptly. 149 let iter = self.env.with_local_frame(16, || { 150 let entry_set = self 151 .env 152 .call_method_unchecked( 153 self.internal, 154 (&self.class, "entrySet", "()Ljava/util/Set;"), 155 JavaType::Object("java/util/Set".into()), 156 &[], 157 )? 158 .l()?; 159 160 let iter = self 161 .env 162 .call_method_unchecked( 163 entry_set, 164 ("java/util/Set", "iterator", "()Ljava/util/Iterator;"), 165 JavaType::Object("java/util/Iterator".into()), 166 &[], 167 )? 168 .l()?; 169 170 Ok(iter) 171 })?; 172 let iter = self.env.auto_local(iter); 173 174 Ok(JMapIter { 175 map: &self, 176 has_next, 177 next, 178 get_key, 179 get_value, 180 iter, 181 }) 182 } 183 } 184 185 /// An iterator over the keys and values in a map. 186 /// 187 /// TODO: make the iterator implementation for java iterators its own thing 188 /// and generic enough to use elsewhere. 189 pub struct JMapIter<'a, 'b, 'c> { 190 map: &'c JMap<'a, 'b>, 191 has_next: JMethodID<'a>, 192 next: JMethodID<'a>, 193 get_key: JMethodID<'a>, 194 get_value: JMethodID<'a>, 195 iter: AutoLocal<'a, 'b>, 196 } 197 198 impl<'a: 'b, 'b: 'c, 'c> JMapIter<'a, 'b, 'c> { get_next(&self) -> Result<Option<(JObject<'a>, JObject<'a>)>>199 fn get_next(&self) -> Result<Option<(JObject<'a>, JObject<'a>)>> { 200 let iter = self.iter.as_obj(); 201 let has_next = self 202 .map 203 .env 204 .call_method_unchecked( 205 iter, 206 self.has_next, 207 JavaType::Primitive(Primitive::Boolean), 208 &[], 209 )? 210 .z()?; 211 212 if !has_next { 213 return Ok(None); 214 } 215 let next = self 216 .map 217 .env 218 .call_method_unchecked( 219 iter, 220 self.next, 221 JavaType::Object("java/util/Map$Entry".into()), 222 &[], 223 )? 224 .l()?; 225 226 let key = self 227 .map 228 .env 229 .call_method_unchecked( 230 next, 231 self.get_key, 232 JavaType::Object("java/lang/Object".into()), 233 &[], 234 )? 235 .l()?; 236 237 let value = self 238 .map 239 .env 240 .call_method_unchecked( 241 next, 242 self.get_value, 243 JavaType::Object("java/lang/Object".into()), 244 &[], 245 )? 246 .l()?; 247 248 Ok(Some((key, value))) 249 } 250 } 251 252 impl<'a: 'b, 'b: 'c, 'c> Iterator for JMapIter<'a, 'b, 'c> { 253 type Item = (JObject<'a>, JObject<'a>); 254 next(&mut self) -> Option<Self::Item>255 fn next(&mut self) -> Option<Self::Item> { 256 match self.get_next() { 257 Ok(Some(n)) => Some(n), 258 _ => None, 259 } 260 } 261 } 262