1 use crate::{ 2 errors::*, 3 objects::{AutoLocal, JMethodID, JObject, JValue}, 4 signature::{Primitive, ReturnType}, 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, 17 put: JMethodID, 18 remove: JMethodID, 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 ReturnType::Object, 71 &[JValue::from(key).to_jni()], 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 ReturnType::Object, 90 &[JValue::from(key).to_jni(), JValue::from(value).to_jni()], 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 ReturnType::Object, 109 &[JValue::from(key).to_jni()], 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 ReturnType::Object, 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 ReturnType::Object, 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, 192 next: JMethodID, 193 get_key: JMethodID, 194 get_value: JMethodID, 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 ReturnType::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(iter, self.next, ReturnType::Object, &[])? 219 .l()?; 220 221 let key = self 222 .map 223 .env 224 .call_method_unchecked(next, self.get_key, ReturnType::Object, &[])? 225 .l()?; 226 227 let value = self 228 .map 229 .env 230 .call_method_unchecked(next, self.get_value, ReturnType::Object, &[])? 231 .l()?; 232 233 Ok(Some((key, value))) 234 } 235 } 236 237 impl<'a: 'b, 'b: 'c, 'c> Iterator for JMapIter<'a, 'b, 'c> { 238 type Item = (JObject<'a>, JObject<'a>); 239 next(&mut self) -> Option<Self::Item>240 fn next(&mut self) -> Option<Self::Item> { 241 match self.get_next() { 242 Ok(Some(n)) => Some(n), 243 _ => None, 244 } 245 } 246 } 247