• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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