1use tree_sitter::Node;
4
5use super::{field_text, line, named_children, spring, text, LangSpec, RawContract, Target};
6use crate::facts::{Import, ImportItem, RefKind, TypeSource, ValueExpr};
7use crate::model::SymbolKind;
8
9pub struct Java;
10
11impl LangSpec for Java {
12 fn receiver(&self) -> &'static str {
13 "this"
14 }
15
16 fn package(&self, root: Node, src: &[u8]) -> Option<String> {
17 let decl = named_children(root)
18 .into_iter()
19 .find(|n| n.kind() == "package_declaration")?;
20 named_children(decl)
21 .into_iter()
22 .find(|n| matches!(n.kind(), "scoped_identifier" | "identifier"))
23 .map(|n| text(n, src).to_string())
24 }
25
26 fn def(&self, node: Node, src: &[u8], _in_class: bool) -> Option<(SymbolKind, String)> {
27 let kind = match node.kind() {
28 "class_declaration" | "record_declaration" => SymbolKind::Class,
29 "interface_declaration" | "annotation_type_declaration" => SymbolKind::Interface,
30 "enum_declaration" => SymbolKind::Enum,
31 "method_declaration" => SymbolKind::Method,
32 "enum_constant" => SymbolKind::Constant,
33 _ => return None,
34 };
35 Some((kind, field_text(node, "name", src)?.to_string()))
36 }
37
38 fn variable(&self, _node: Node, _src: &[u8]) -> Option<String> {
39 None
40 }
41
42 fn contract(&self, node: Node, src: &[u8]) -> Option<RawContract> {
43 if let Some(r) = spring::route(node, src) {
44 return Some(r);
45 }
46 if node.kind() != "method_invocation" {
47 return None;
48 }
49 let method = field_text(node, "name", src)?;
50 if method == "uri" {
51 let inner = node
52 .child_by_field_name("object")
53 .filter(|o| o.kind() == "method_invocation")?;
54 let verb = spring::webclient_verb(field_text(inner, "name", src)?)?;
55 return Some(RawContract::Op {
56 receiver: vec![spring::WEBCLIENT.into()],
57 method: verb,
58 args: spring::args(node.child_by_field_name("arguments")?, src),
59 });
60 }
61 if !spring::REST_TEMPLATE.iter().any(|(m, _)| *m == method) {
62 return None;
63 }
64 Some(RawContract::Op {
65 receiver: node
66 .child_by_field_name("object")
67 .and_then(|o| path(o, src))
68 .unwrap_or_default(),
69 method: method.to_string(),
70 args: spring::args(node.child_by_field_name("arguments")?, src),
71 })
72 }
73
74 fn value(&self, node: Node, src: &[u8]) -> Option<(String, ValueExpr)> {
75 if !matches!(
76 node.kind(),
77 "field_declaration" | "local_variable_declaration" | "constant_declaration"
78 ) {
79 return None;
80 }
81 let d = node.child_by_field_name("declarator")?;
82 Some((
83 field_text(d, "name", src)?.to_string(),
84 spring::value(d.child_by_field_name("value")?, src)?,
85 ))
86 }
87
88 fn import(&self, node: Node, src: &[u8]) -> Option<Vec<Import>> {
89 if node.kind() != "import_declaration" {
90 return None;
91 }
92 let kids = named_children(node);
93 let full = kids
94 .iter()
95 .find(|n| matches!(n.kind(), "scoped_identifier" | "identifier"))
96 .map(|n| text(*n, src).to_string())?;
97 let all = kids.iter().any(|n| n.kind() == "asterisk");
98 let (module, items) = if all {
99 (full, vec![ImportItem::All])
100 } else {
101 let (m, name) = full.rsplit_once('.')?;
102 (
103 m.to_string(),
104 vec![ImportItem::Name {
105 name: name.to_string(),
106 alias: None,
107 }],
108 )
109 };
110 Some(vec![Import {
111 module,
112 level: 0,
113 items,
114 line: line(node),
115 file: None,
116 }])
117 }
118
119 fn reference(&self, node: Node, src: &[u8]) -> Option<(RefKind, Vec<String>)> {
120 match node.kind() {
121 "method_invocation" => {
122 let name = field_text(node, "name", src)?.to_string();
123 let mut p = match node.child_by_field_name("object") {
124 Some(o) => path(o, src)?,
125 None => Vec::new(),
126 };
127 p.push(name);
128 Some((RefKind::Call, p))
129 }
130 "object_creation_expression" => Some((
131 RefKind::New,
132 type_path(node.child_by_field_name("type")?, src)?,
133 )),
134 _ => None,
135 }
136 }
137
138 fn assignment(&self, node: Node, src: &[u8]) -> Option<(Target, TypeSource)> {
139 let local = match node.kind() {
140 "field_declaration" => false,
141 "local_variable_declaration" => true,
142 _ => return None,
143 };
144 let decl = node.child_by_field_name("declarator")?;
145 let name = field_text(decl, "name", src)?.to_string();
146 let target = if local {
147 Target::Var(name)
148 } else {
149 Target::Attr(name)
150 };
151 let ty = node.child_by_field_name("type")?;
152 if text(ty, src) != "var" {
153 return Some((target, TypeSource::Annotation(type_path(ty, src)?)));
154 }
155 let v = decl.child_by_field_name("value")?;
156 let source = match v.kind() {
157 "object_creation_expression" => {
158 TypeSource::Construct(type_path(v.child_by_field_name("type")?, src)?)
159 }
160 "method_invocation" => TypeSource::Construct(self.reference(v, src)?.1),
161 _ => return None,
162 };
163 Some((target, source))
164 }
165
166 fn bases(&self, def: Node, src: &[u8]) -> Vec<(RefKind, Vec<String>)> {
167 let mut out = Vec::new();
168 let types = |n: Node| -> Vec<Vec<String>> {
169 let list = named_children(n)
170 .into_iter()
171 .find(|c| c.kind() == "type_list")
172 .map(named_children)
173 .unwrap_or_else(|| named_children(n));
174 list.into_iter().filter_map(|t| type_path(t, src)).collect()
175 };
176 for c in named_children(def) {
177 let kind = match c.kind() {
178 "superclass" | "extends_interfaces" => RefKind::Inherits,
179 "super_interfaces" => RefKind::Implements,
180 _ => continue,
181 };
182 out.extend(types(c).into_iter().map(|p| (kind, p)));
183 }
184 out
185 }
186
187 fn returns(&self, def: Node, src: &[u8]) -> Option<Vec<String>> {
188 type_path(def.child_by_field_name("type")?, src)
189 }
190
191 fn params(&self, def: Node, src: &[u8]) -> Vec<(String, Vec<String>, bool)> {
192 let Some(ps) = def.child_by_field_name("parameters") else {
193 return Vec::new();
194 };
195 named_children(ps)
196 .into_iter()
197 .filter(|p| p.kind() == "formal_parameter")
198 .filter_map(|p| {
199 Some((
200 field_text(p, "name", src)?.to_string(),
201 type_path(p.child_by_field_name("type")?, src)?,
202 false,
203 ))
204 })
205 .collect()
206 }
207}
208
209fn path(node: Node, src: &[u8]) -> Option<Vec<String>> {
210 match node.kind() {
211 "identifier" | "type_identifier" => Some(vec![text(node, src).to_string()]),
212 "this" => Some(vec!["this".into()]),
213 "field_access" => {
214 let mut p = path(node.child_by_field_name("object")?, src)?;
215 p.push(field_text(node, "field", src)?.to_string());
216 Some(p)
217 }
218 "scoped_identifier" => {
219 let mut p = path(node.child_by_field_name("scope")?, src)?;
220 p.push(field_text(node, "name", src)?.to_string());
221 Some(p)
222 }
223 _ => None,
224 }
225}
226
227fn type_path(node: Node, src: &[u8]) -> Option<Vec<String>> {
228 match node.kind() {
229 "type_identifier" | "identifier" => Some(vec![text(node, src).to_string()]),
230 "scoped_type_identifier" => {
231 let mut out = Vec::new();
232 for c in named_children(node) {
233 out.extend(type_path(c, src)?);
234 }
235 (!out.is_empty()).then_some(out)
236 }
237 "generic_type" => type_path(named_children(node).into_iter().next()?, src),
238 _ => None,
239 }
240}