1use super::*;
2use class_file_visitor::*;
3use gather_java::*;
4
5use std::collections::*;
6use std::error::Error;
7use std::fmt::Write;
8use std::io;
9
10struct KnownDocsUrl {
11 label: String,
12 url: String,
13}
14
15impl KnownDocsUrl {
16 pub fn from(java_class: &Class) -> Option<KnownDocsUrl> {
17 let java_name = java_class.this_class().name();
18
19 let prefix = "https://developer.android.com/reference/kotlin/";
28
29 for ch in java_name.chars() {
30 match ch {
31 'a'..='z' => {},
32 'A'..='Z' => {},
33 '0'..='9' => {},
34 '_' | '$' | '/' => {},
35 _ch => return None,
36 }
37 }
38
39 let last_slash = java_name.rfind(|ch| ch == '/');
40 let no_namespace = if let Some(last_slash) = last_slash {
41 &java_name[(last_slash+1)..]
42 } else {
43 &java_name[..]
44 };
45
46 Some(KnownDocsUrl{
47 label: no_namespace.to_owned().replace("$","."),
48 url: format!("{}{}.html", prefix, java_name.replace("$",".")),
49 })
50 }
51}
52
53const DEREF_FN : &'static str = "fn deref(&self) -> &Self::Target { unsafe { &*(self as *const Self as *const Self::Target) } }";
54
55#[derive(Debug, Default)]
56pub struct Struct {
57 pub rust_mod_prefix: String,
58 pub rust_struct_name: String,
59 pub java_class: Class,
60}
61
62impl Struct {
63 pub fn write(&self, context: &Context, indent: &str, out: &mut impl io::Write) -> io::Result<()> {
64 if let Some(url) = KnownDocsUrl::from(&self.java_class) {
65 writeln!(out, "{}/// [{}]({})", indent, url.label, url.url)?;
66 }
67 self.write_rust_struct(context, indent, out)?;
70 Ok(())
71 }
72
73 fn write_java_doc_comment(&self, _context: &Context, indent: &str, out: &mut impl io::Write) -> io::Result<()> {
74 writeln!(out, "{}/// ```java", indent)?;
76 write!(out, "{}/// ", indent)?;
77 if self.java_class.access_flags().contains(ClassAccessFlags::PUBLIC ) { write!(out, "public ")? }
79 if self.java_class.access_flags().contains(ClassAccessFlags::STATIC ) { write!(out, "static ")? }
80 if self.java_class.access_flags().contains(ClassAccessFlags::ABSTRACT) { write!(out, "abstract ")? }
81 if self.java_class.access_flags().contains(ClassAccessFlags::FINAL ) { write!(out, "final ")? }
82 let keyword = if self.java_class.access_flags().contains(ClassAccessFlags::INTERFACE) {
83 "interface"
84 } else if self.java_class.access_flags().contains(ClassAccessFlags::ENUM) {
85 "enum"
86 } else {
87 "class"
88 };
89 write!(out, "{} {}", keyword, self.java_class.this_class().name().replace("/", "."))?;
90 if let Some(super_class) = self.java_class.super_class() {
91 if super_class.name() != "java/lang/Object" {
92 write!(out, " extends {}", super_class.name().replace("/", "."))?;
93 }
94 }
95 let mut write_implements = false;
96 for interface in self.java_class.interfaces() {
97 if !write_implements {
98 write!(out, " implements ")?;
99 write_implements = true;
100 } else {
101 write!(out, ", ")?;
102 }
103 write!(out, "{}", interface.name().replace("/", "."))?;
104 }
105 writeln!(out, " {{ ... }}")?;
106 writeln!(out, "{}/// ```", indent)?;
107 Ok(())
108 }
109
110 fn write_rust_struct(&self, context: &Context, indent: &str, out: &mut impl io::Write) -> io::Result<()> {
111 let struct_access = if self.java_class.access_flags().contains(ClassAccessFlags::PUBLIC) {
112 "pub "
113 } else {
114 ""
115 };
116
117 let struct_body = if self.java_class.access_flags().contains(ClassAccessFlags::STATIC) {
118 ""
119 } else {
120 "(::jni_sys::jobject)"
121 };
122
123 writeln!(out, "{}#[repr(transparent)] {}struct {}{};", indent, struct_access, &self.rust_struct_name, struct_body)?;
124 if let Some(super_class) = self.java_class.super_class() {
125 let rust_super_class_name = context.jni_type_to_rust_type.get(super_class.name()).unwrap();
126 writeln!(out, "{}impl ::std::ops::Deref for {} {{ type Target = {}; {} }}", indent, &self.rust_struct_name, rust_super_class_name, DEREF_FN)?;
128 }
129 writeln!(out, "{}impl {} {{", indent, &self.rust_struct_name)?;
130 writeln!(out, "{} // ...", indent)?;
131 writeln!(out, "{}}}", indent)?;
132 Ok(())
133 }
134}
135
136
137
138#[derive(Debug, Default)]
139pub struct Module {
140 pub structs: BTreeMap<String, Struct>,
142 pub modules: BTreeMap<String, Module>,
143}
144
145impl Module {
146 pub fn write(&self, context: &Context, indent: &str, out: &mut impl io::Write) -> io::Result<()> {
147 let next_indent = format!("{} ", indent);
148
149 for (name, module) in self.modules.iter() {
150 if indent.is_empty() {
151 writeln!(out, "#[allow(non_camel_case_types)] // We map Java inner classes to Outer_Inner")?;
152 writeln!(out, "#[allow(dead_code)] // We generate structs for private Java types too, just in case.")?;
153 }
154 writeln!(out, "{}pub mod {} {{", indent, name)?;
155 module.write(context, &next_indent[..], out)?;
156 writeln!(out, "{}}}", indent)?;
157 writeln!(out, "")?;
158 }
159
160 for (name, structure) in self.structs.iter() {
161 if indent.is_empty() {
162 if name.contains("_") { writeln!(out, "#[allow(non_camel_case_types)] // We map Java inner classes to Outer_Inner")?; }
163 if !structure.java_class.access_flags().contains(ClassAccessFlags::PUBLIC) { writeln!(out, "#[allow(dead_code)] // We generate structs for private Java types too, just in case.")?; }
164 }
165 structure.write(context, indent, out)?;
166 writeln!(out, "")?;
167 }
168
169 Ok(())
170 }
171}
172
173
174
175#[derive(Debug, Default)]
176pub struct Context {
177 module: Module,
178 jni_type_to_rust_type: HashMap<String, String>,
179}
180
181impl Context {
182 pub fn new() -> Self { Default::default() }
183
184 pub fn add_struct(&mut self, java_class: Class) -> Result<(), Box<dyn Error>> {
185 let mut rust_mod : &mut Module = &mut self.module;
186 let mut rust_mod_prefix = String::from("crate::");
187 let mut rust_struct_name = String::new();
188
189 for component in JniPathIter::new(java_class.this_class().name()) {
190 match component {
191 JniIdentifier::Namespace(id) => {
192 let id = match RustIdentifier::from_str(id) {
193 RustIdentifier::Identifier(id) => id,
194 RustIdentifier::KeywordRawSafe(id) => id,
195 RustIdentifier::KeywordUnderscorePostfix(id) => id,
196 RustIdentifier::NonIdentifier(id) => Err(format!("Unable to add_struct(): parent java namespace name {:?} has no rust equivalent", id))?,
197 };
198
199 write!(&mut rust_mod_prefix, "{}::", id)?;
200 rust_mod = rust_mod.modules.entry(id.to_owned()).or_insert(Default::default());
201 },
202 JniIdentifier::ContainingClass(id) => {
203 let id = match RustIdentifier::from_str(id) {
204 RustIdentifier::Identifier(id) => id,
205 RustIdentifier::KeywordRawSafe(id) => id,
206 RustIdentifier::KeywordUnderscorePostfix(id) => id,
207 RustIdentifier::NonIdentifier(id) => Err(format!("Unable to add_struct(): parent java class name {:?} has no rust equivalent", id))?,
208 };
209
210 write!(&mut rust_struct_name, "{}_", id)?;
211 },
212 JniIdentifier::LeafClass(id) => {
213 let id = match RustIdentifier::from_str(id) {
214 RustIdentifier::Identifier(id) => id,
215 RustIdentifier::KeywordRawSafe(id) => id,
216 RustIdentifier::KeywordUnderscorePostfix(id) => id,
217 RustIdentifier::NonIdentifier(id) => Err(format!("Unable to add_struct(): java class name {:?} has no rust equivalent", id))?,
218 };
219
220 write!(&mut rust_struct_name, "{}", id)?;
221 let id = &rust_struct_name[..];
222
223 if rust_mod.structs.contains_key(id) {
224 Err(format!("Unable to add_struct(): java class name {:?} was already added", id))?
225 }
226
227 self.jni_type_to_rust_type.insert(java_class.this_class().name().clone(), format!("{}{}", rust_mod_prefix, &rust_struct_name));
228
229 rust_mod.structs.insert(rust_struct_name.clone(), Struct {
230 rust_mod_prefix,
231 rust_struct_name,
232 java_class,
233 });
234
235 return Ok(());
236 },
237 }
238 }
239
240 Err(format!("Failed to find LeafClass in {:?}", java_class.this_class().name()))?
241 }
242
243 pub fn write(&self, out: &mut impl io::Write) -> io::Result<()> {
244 self.module.write(self, "", out)
245 }
246}