1 use crate::{
2     errors::*,
3     objects::{JMethodID, JObject},
4     signature::{JavaType, Primitive},
5     sys::jint,
6     JNIEnv,
7 };
8 
9 /// Wrapper for JObjects that implement `java/util/Map`. Provides methods to get
10 /// and set entries and a way to iterate over key/value pairs.
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<'a>,
17     add: JMethodID<'a>,
18     add_idx: JMethodID<'a>,
19     remove: JMethodID<'a>,
20     size: JMethodID<'a>,
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             JavaType::Object("java/lang/Object".into()),
69             &[idx.into()],
70         );
71 
72         match result {
73             Ok(val) => Ok(Some(val.l()?)),
74             Err(e) => match *e.kind() {
75                 ErrorKind::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             JavaType::Primitive(Primitive::Boolean),
87             &[value.into()],
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             JavaType::Primitive(Primitive::Void),
100             &[idx.into(), value.into()],
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             JavaType::Object("java/lang/Object".into()),
113             &[idx.into()],
114         );
115 
116         match result {
117             Ok(val) => Ok(Some(val.l()?)),
118             Err(e) => match *e.kind() {
119                 ErrorKind::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             JavaType::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             JavaType::Object("java/lang/Object".into()),
150             &[(size - 1).into()],
151         );
152 
153         match result {
154             Ok(val) => Ok(Some(val.l()?)),
155             Err(e) => match *e.kind() {
156                 ErrorKind::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