1mod builtins;
5mod traverse;
6
7use super::*;
8use crate::languages::ast_visitor::{DeclUseVisitor, RelevantName, ScopeVisitor};
9use traverse::traverse_ast;
10#[cfg(target_arch = "wasm32")]
11use wasm_bindgen::prelude::*;
12
13#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
14pub struct Java;
15
16impl TreeSitterLanguage for Java {
17 fn find_symbol_declarations_and_usages<'code>(
18 #[cfg(not(target_arch = "wasm32"))] cursor: &mut TreeCursor<'code>,
19 #[cfg(target_arch = "wasm32")] cursor: &mut TreeCursor,
20 code: &'code [u8],
21 ) -> ScopedDeclarationsAndUsages {
22 traverse_ast(cursor, code, DeclUseVisitor::new())
23 }
24
25 #[cfg(not(target_arch = "wasm32"))]
26 fn grammar() -> Grammar {
27 tree_sitter_java::language()
28 }
29}
30
31#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
32impl Java {
33 #[cfg(target_arch = "wasm32")]
34 #[wasm_bindgen(js_name = "findNames")]
35 pub async fn find_names(code: String) -> JsValue {
36 Parser::init().await;
37 let grammar =
38 crate::wasm::tree_sitter::load(include_bytes!(std::env!("JAVA_LANGUAGE"))).await;
39 let parser = Parser::new();
40 parser.set_language(grammar);
41 let ast = parser.parse(&code);
42 let names = <Self as Language>::find_names(&ast, &code);
43 serde_wasm_bindgen::to_value(&names).unwrap()
44 }
45
46 #[cfg(target_arch = "wasm32")]
47 #[wasm_bindgen(js_name = "findNamesWithInExtent")]
48 pub async fn find_names_within_extent(code: String, extent: Extent) -> JsValue {
49 Parser::init().await;
50 let grammar =
51 crate::wasm::tree_sitter::load(include_bytes!(std::env!("JAVA_LANGUAGE"))).await;
52 let parser = Parser::new();
53 parser.set_language(grammar);
54 let ast = parser.parse(&code);
55 let names = <Self as Language>::find_names(&ast, &code);
56 let extent_names = names.within_extent(&extent);
57 serde_wasm_bindgen::to_value(&extent_names).unwrap()
58 }
59
60 #[cfg(not(target_arch = "wasm32"))]
61 pub fn find_relevant_scope(
62 code: &str,
63 relevant_names: Vec<RelevantName>,
64 ) -> Result<RelevantScope> {
65 let code = code.as_bytes();
66 let ast = Java::parse(code)?;
67 let mut cursor = ast.walk();
68 let node = cursor.node();
69 let byte_range = node.byte_range();
70 let extent = node.into();
71 let (relevant_scope, snippet_extent, relevant_names_occurrences) = traverse_ast(
72 &mut cursor,
73 code,
74 ScopeVisitor::new(
75 Scope {
76 extent,
77 byte_range,
78 name: None,
79 },
80 relevant_names,
81 ),
82 );
83 Ok(RelevantScope {
84 snippet: String::from_utf8_lossy(&code[relevant_scope]).to_string(),
85 snippet_extent,
86 relevant_names_occurrences,
87 })
88 }
89}
90
91#[cfg(all(test, not(target_arch = "wasm32")))]
92mod test {
93 use super::*;
94 use crate::languages::test_macros::*;
95
96 #[test]
97 fn finds_names_in_import_declaration() {
98 let code = r#"import java.lang.String;
99 String a = "lala";"#;
100 let names = Java::find_names(code).expect("Cannot find names in test code");
101
102 assert_eq!(
103 names,
104 Names {
105 fully_qualified: DeclUse {
106 declared_symbols: vec![FullyQualifiedName {
107 source: sym!(java.lang),
108 symbol: sym!(String),
109 extent: extent!(0:7-0:23),
110 },],
111 used_symbols: vec![FullyQualifiedName {
112 source: sym!(java.lang),
113 symbol: sym!(String),
114 extent: extent!(1:8-1:14),
115 }]
116 },
117 simple: DeclUse {
118 declared_symbols: vec![
119 SimpleName {
120 symbol: "java".to_string(),
121 extent: optional_extent!(0:7-0:11),
122 },
123 SimpleName {
124 symbol: "lang".to_string(),
125 extent: optional_extent!(0:12-0:16),
126 },
127 SimpleName {
128 symbol: "String".to_string(),
129 extent: optional_extent!(0:17-0:23),
130 },
131 SimpleName {
132 symbol: "a".to_string(),
133 extent: optional_extent!(1:15-1:16),
134 },
135 ],
136 used_symbols: vec![
137 SimpleName {
138 symbol: "java".to_string(),
139 extent: None,
140 },
141 SimpleName {
142 symbol: "lang".to_string(),
143 extent: None,
144 },
145 SimpleName {
146 symbol: "String".to_string(),
147 extent: optional_extent!(1:8-1:14),
148 },
149 ]
150 },
151 external_simple: DeclUse {
152 declared_symbols: vec![
153 SimpleName {
154 symbol: "java".to_string(),
155 extent: optional_extent!(0:7-0:11),
156 },
157 SimpleName {
158 symbol: "lang".to_string(),
159 extent: optional_extent!(0:12-0:16),
160 },
161 SimpleName {
162 symbol: "String".to_string(),
163 extent: optional_extent!(0:17-0:23),
164 },
165 ],
166 used_symbols: vec![
167 SimpleName {
168 symbol: "java".to_string(),
169 extent: None,
170 },
171 SimpleName {
172 symbol: "lang".to_string(),
173 extent: None,
174 },
175 SimpleName {
176 symbol: "String".to_string(),
177 extent: optional_extent!(1:8-1:14),
178 },
179 ]
180 }
181 }
182 );
183 }
184
185 #[test]
186 fn finds_names_in_unscoped_import_declaration() {
187 let code = r#"import Example;"#;
188 let names = Java::find_names(code).expect("Cannot find names in test code");
189
190 assert_eq!(
191 names,
192 Names {
193 fully_qualified: DeclUse {
194 declared_symbols: vec![],
195 used_symbols: vec![]
196 },
197 simple: DeclUse {
198 declared_symbols: vec![SimpleName {
199 symbol: "Example".to_string(),
200 extent: optional_extent!(0:7-0:14),
201 },],
202 used_symbols: vec![]
203 },
204 external_simple: DeclUse {
205 declared_symbols: vec![SimpleName {
206 symbol: "Example".to_string(),
207 extent: optional_extent!(0:7-0:14),
208 },],
209 used_symbols: vec![]
210 }
211 }
212 );
213 }
214
215 #[test]
216 fn finds_names_with_asterisk_import_declaration() {
217 let code = r#"import java.lang.*;
218class Example {}"#;
219 let names = Java::find_names(code).expect("Cannot find names in test code");
220
221 assert_eq!(
222 names,
223 Names {
224 fully_qualified: DeclUse {
225 declared_symbols: vec![],
226 used_symbols: vec![]
227 },
228 simple: DeclUse {
229 declared_symbols: vec![SimpleName {
230 symbol: "Example".to_string(),
231 extent: optional_extent!(1:6-1:13)
232 }],
233 used_symbols: vec![]
234 },
235 external_simple: DeclUse {
236 declared_symbols: vec![],
237 used_symbols: vec![]
238 }
239 },
240 );
241 }
242
243 #[test]
244 fn finds_names_in_main() {
245 let code = r#"public class Main {
246 public static void main(String[] args) {
247 int i = 42;
248 }
249}"#;
250 let names = Java::find_names(code).expect("Cannot find names in test code");
251 assert_eq!(
252 names,
253 Names {
254 fully_qualified: DeclUse {
255 declared_symbols: vec![],
256 used_symbols: vec![]
257 },
258 simple: DeclUse {
259 declared_symbols: vec![
260 SimpleName {
261 symbol: "Main".to_string(),
262 extent: optional_extent!(0:13-0:17),
263 },
264 SimpleName {
265 symbol: "main".to_string(),
266 extent: optional_extent!(1:23-1:27),
267 },
268 SimpleName {
269 symbol: "args".to_string(),
270 extent: optional_extent!(1:37-1:41),
271 },
272 SimpleName {
273 symbol: "i".to_string(),
274 extent: optional_extent!(2:12-2:13),
275 }
276 ],
277 used_symbols: vec![
278 SimpleName {
279 symbol: "String".to_string(),
280 extent: optional_extent!(1:28-1:34),
281 },
282 SimpleName {
283 symbol: "int".to_string(),
284 extent: optional_extent!(2:8-2:11),
285 }
286 ]
287 },
288 external_simple: DeclUse {
289 declared_symbols: vec![],
290 used_symbols: vec![]
291 }
292 }
293 );
294 }
295
296 #[test]
297 fn finds_names_via_super() {
298 let code = r#"import restaurant.Food;
299public class Pretzel extends Food {
300 public taste() {
301 super.taste(Taste.SALTY);
302 }
303}"#;
304 let names = Java::find_names(code).expect("Cannot find names in test code");
305 assert_eq!(
306 names,
307 Names {
308 fully_qualified: DeclUse {
309 declared_symbols: vec![FullyQualifiedName {
310 source: vec!["restaurant".to_string()],
311 symbol: vec!["Food".to_string()],
312 extent: Extent {
313 start: Location {
314 line: 0,
315 character: 7
316 },
317 end: Location {
318 line: 0,
319 character: 22
320 }
321 }
322 }],
323 used_symbols: vec![
324 FullyQualifiedName {
325 source: vec!["restaurant".to_string()],
326 symbol: vec!["Food".to_string()],
327 extent: Extent {
328 start: Location {
329 line: 1,
330 character: 29
331 },
332 end: Location {
333 line: 1,
334 character: 33
335 }
336 }
337 },
338 FullyQualifiedName {
339 source: vec!["restaurant".to_string()],
340 symbol: vec!["Food".to_string(), "taste".to_string()],
341 extent: Extent {
342 start: Location {
343 line: 3,
344 character: 14
345 },
346 end: Location {
347 line: 3,
348 character: 19
349 }
350 }
351 }
352 ]
353 },
354 simple: DeclUse {
355 declared_symbols: vec![
356 SimpleName {
357 symbol: "restaurant".to_string(),
358 extent: optional_extent!(0:7-0:17),
359 },
360 SimpleName {
361 symbol: "Food".to_string(),
362 extent: optional_extent!(0:18-0:22),
363 },
364 SimpleName {
365 symbol: "Pretzel".to_string(),
366 extent: optional_extent!(1:13-1:20),
367 },
368 SimpleName {
369 symbol: "taste".to_string(),
370 extent: optional_extent!(2:11-2:16),
371 }
372 ],
373 used_symbols: vec![
374 SimpleName {
375 symbol: "restaurant".to_string(),
376 extent: None
377 },
378 SimpleName {
379 symbol: "Food".to_string(),
380 extent: optional_extent!(1:29-1:33),
381 },
382 SimpleName {
383 symbol: "restaurant".to_string(),
384 extent: None
385 },
386 SimpleName {
387 symbol: "Food".to_string(),
388 extent: None
389 },
390 SimpleName {
391 symbol: "taste".to_string(),
392 extent: optional_extent!(3:14-3:19),
393 },
394 SimpleName {
395 symbol: "Taste".to_string(),
396 extent: optional_extent!(3:20-3:25),
397 },
398 SimpleName {
399 symbol: "SALTY".to_string(),
400 extent: optional_extent!(3:26-3:31),
401 }
402 ]
403 },
404 external_simple: DeclUse {
405 declared_symbols: vec![
406 SimpleName {
407 symbol: "restaurant".to_string(),
408 extent: optional_extent!(0:7-0:17),
409 },
410 SimpleName {
411 symbol: "Food".to_string(),
412 extent: optional_extent!(0:18-0:22),
413 }
414 ],
415 used_symbols: vec![
416 SimpleName {
417 symbol: "restaurant".to_string(),
418 extent: None
419 },
420 SimpleName {
421 symbol: "Food".to_string(),
422 extent: optional_extent!(1:29-1:33),
423 },
424 SimpleName {
425 symbol: "restaurant".to_string(),
426 extent: None
427 },
428 SimpleName {
429 symbol: "Food".to_string(),
430 extent: None
431 },
432 SimpleName {
433 symbol: "taste".to_string(),
434 extent: optional_extent!(3:14-3:19),
435 },
436 SimpleName {
437 symbol: "Taste".to_string(),
438 extent: optional_extent!(3:20-3:25),
439 },
440 SimpleName {
441 symbol: "SALTY".to_string(),
442 extent: optional_extent!(3:26-3:31),
443 }
444 ]
445 }
446 }
447 );
448 }
449
450 #[test]
451 fn finds_names_in_assignment_expression() {
452 let code = r#"text += "and also...";"#;
453 let names = Java::find_names(code).expect("Cannot find names in test code");
454 assert_eq!(
455 names,
456 Names {
457 fully_qualified: DeclUse {
458 declared_symbols: vec![],
459 used_symbols: vec![]
460 },
461 simple: DeclUse {
462 declared_symbols: vec![],
463 used_symbols: vec![SimpleName {
464 symbol: "text".to_string(),
465 extent: optional_extent!(0:0-0:4),
466 }]
467 },
468 external_simple: DeclUse {
469 declared_symbols: vec![],
470 used_symbols: vec![SimpleName {
471 symbol: "text".to_string(),
472 extent: optional_extent!(0:0-0:4),
473 }]
474 }
475 },
476 );
477 }
478
479 #[test]
480 fn finds_names_in_basic_control_flow() {
481 let code = r#"int i = 0;
482while (i < 10) {
483 i += 3;
484 if (i % 2 == 0) break;
485}"#;
486 let names = Java::find_names(code).expect("Cannot find names in test code");
487 assert_eq!(
488 names,
489 Names {
490 fully_qualified: DeclUse {
491 declared_symbols: vec![],
492 used_symbols: vec![]
493 },
494 simple: DeclUse {
495 declared_symbols: vec![SimpleName {
496 symbol: "i".to_string(),
497 extent: optional_extent!(0:4-0:5),
498 }],
499 used_symbols: vec![
500 SimpleName {
501 symbol: "int".to_string(),
502 extent: optional_extent!(0:0-0:3),
503 },
504 SimpleName {
505 symbol: "i".to_string(),
506 extent: optional_extent!(1:7-1:8),
507 },
508 SimpleName {
509 symbol: "i".to_string(),
510 extent: optional_extent!(2:4-2:5),
511 },
512 SimpleName {
513 symbol: "i".to_string(),
514 extent: optional_extent!(3:8-3:9),
515 }
516 ]
517 },
518 external_simple: DeclUse {
519 declared_symbols: vec![],
520 used_symbols: vec![]
521 }
522 }
523 );
524 }
525
526 #[test]
527 fn finds_names_in_switch() {
528 let code = r#"switch (i > 5? 6 : i) {
529 case 1:
530 case 2:
531 i++;
532 break;
533 case 3:
534 --i;
535 break;
536 default:
537 throw new SwitchException();
538}
539"#;
540 let names = Java::find_names(code).expect("Cannot find names in test code");
541 assert_eq!(
542 names,
543 Names {
544 fully_qualified: DeclUse {
545 declared_symbols: vec![],
546 used_symbols: vec![]
547 },
548 simple: DeclUse {
549 declared_symbols: vec![],
550 used_symbols: vec![
551 SimpleName {
552 symbol: "i".to_string(),
553 extent: optional_extent!(0:8-0:9),
554 },
555 SimpleName {
556 symbol: "i".to_string(),
557 extent: optional_extent!(0:19-0:20),
558 },
559 SimpleName {
560 symbol: "i".to_string(),
561 extent: optional_extent!(3:8-3:9),
562 },
563 SimpleName {
564 symbol: "i".to_string(),
565 extent: optional_extent!(6:10-6:11),
566 },
567 SimpleName {
568 symbol: "SwitchException".to_string(),
569 extent: optional_extent!(9:18-9:33),
570 }
571 ]
572 },
573 external_simple: DeclUse {
574 declared_symbols: vec![],
575 used_symbols: vec![
576 SimpleName {
577 symbol: "i".to_string(),
578 extent: optional_extent!(0:8-0:9),
579 },
580 SimpleName {
581 symbol: "i".to_string(),
582 extent: optional_extent!(0:19-0:20),
583 },
584 SimpleName {
585 symbol: "i".to_string(),
586 extent: optional_extent!(3:8-3:9),
587 },
588 SimpleName {
589 symbol: "i".to_string(),
590 extent: optional_extent!(6:10-6:11),
591 },
592 SimpleName {
593 symbol: "SwitchException".to_string(),
594 extent: optional_extent!(9:18-9:33),
595 }
596 ]
597 }
598 }
599 );
600 }
601
602 #[test]
603 fn finds_names_in_lambda() {
604 let code = r#"(i) -> System.out.println(!(boolean)i);"#;
605 let names = Java::find_names(code).expect("Cannot find names in test code");
606 assert_eq!(
607 names,
608 Names {
609 fully_qualified: DeclUse {
610 declared_symbols: vec![],
611 used_symbols: vec![]
612 },
613 simple: DeclUse {
614 declared_symbols: vec![SimpleName {
615 symbol: "i".to_string(),
616 extent: optional_extent!(0:1-0:2),
617 }],
618 used_symbols: vec![
619 SimpleName {
620 symbol: "System".to_string(),
621 extent: optional_extent!(0:7-0:13),
622 },
623 SimpleName {
624 symbol: "System".to_string(),
625 extent: None
626 },
627 SimpleName {
628 symbol: "out".to_string(),
629 extent: optional_extent!(0:14-0:17),
630 },
631 SimpleName {
632 symbol: "println".to_string(),
633 extent: optional_extent!(0:18-0:25),
634 },
635 SimpleName {
636 symbol: "boolean".to_string(),
637 extent: optional_extent!(0:28-0:35),
638 },
639 SimpleName {
640 symbol: "i".to_string(),
641 extent: optional_extent!(0:36-0:37),
642 }
643 ]
644 },
645 external_simple: DeclUse {
646 declared_symbols: vec![],
647 used_symbols: vec![
648 SimpleName {
649 symbol: "println".to_string(),
650 extent: optional_extent!(0:18-0:25),
651 },
652 SimpleName {
653 symbol: "boolean".to_string(),
654 extent: optional_extent!(0:28-0:35),
655 }
656 ]
657 }
658 }
659 );
660 }
661
662 #[test]
663 fn finds_names_in_try() {
664 let code = r#"try (Reader reader = new Reader(), marker = nextMarker()) {
665 reader.readUpTo(marker);
666} catch (ReadException e) {
667 log(e.message);
668 return false;
669} finally {
670 return true;
671}"#;
672
673 let names = Java::find_names(code).expect("Cannot find names in test code");
674 assert_eq!(
675 names,
676 Names {
677 fully_qualified: DeclUse {
678 declared_symbols: vec![],
679 used_symbols: vec![]
680 },
681 simple: DeclUse {
682 declared_symbols: vec![
683 SimpleName {
684 symbol: "reader".to_string(),
685 extent: optional_extent!(0:12-0:18),
686 },
687 SimpleName {
688 symbol: "e".to_string(),
689 extent: optional_extent!(2:23-2:24),
690 }
691 ],
692 used_symbols: vec![
693 SimpleName {
694 symbol: "Reader".to_string(),
695 extent: optional_extent!(0:5-0:11),
696 },
697 SimpleName {
698 symbol: "marker".to_string(),
699 extent: optional_extent!(0:35-0:41),
700 },
701 SimpleName {
702 symbol: "reader".to_string(),
703 extent: optional_extent!(1:4-1:10),
704 },
705 SimpleName {
706 symbol: "readUpTo".to_string(),
707 extent: optional_extent!(1:11-1:19),
708 },
709 SimpleName {
710 symbol: "marker".to_string(),
711 extent: optional_extent!(1:20-1:26),
712 },
713 SimpleName {
714 symbol: "ReadException".to_string(),
715 extent: optional_extent!(2:9-2:22),
716 },
717 SimpleName {
718 symbol: "e".to_string(),
719 extent: optional_extent!(3:8-3:9),
720 },
721 SimpleName {
722 symbol: "message".to_string(),
723 extent: optional_extent!(3:10-3:17),
724 }
725 ]
726 },
727 external_simple: DeclUse {
728 declared_symbols: vec![],
729 used_symbols: vec![
730 SimpleName {
731 symbol: "Reader".to_string(),
732 extent: optional_extent!(0:5-0:11),
733 },
734 SimpleName {
735 symbol: "marker".to_string(),
736 extent: optional_extent!(0:35-0:41),
737 },
738 SimpleName {
739 symbol: "readUpTo".to_string(),
740 extent: optional_extent!(1:11-1:19),
741 },
742 SimpleName {
743 symbol: "marker".to_string(),
744 extent: optional_extent!(1:20-1:26),
745 },
746 SimpleName {
747 symbol: "ReadException".to_string(),
748 extent: optional_extent!(2:9-2:22),
749 },
750 SimpleName {
751 symbol: "message".to_string(),
752 extent: optional_extent!(3:10-3:17),
753 }
754 ]
755 }
756 }
757 );
758 }
759
760 #[test]
761 fn finds_name_in_annotation() {
762 let code = r#"class Dog extends Animal {
763 @Override
764 void eatSomething() {}
765 }"#;
766
767 let names = Java::find_names(code).expect("Cannot find names in test code");
768 assert_eq!(
769 names,
770 Names {
771 fully_qualified: DeclUse {
772 declared_symbols: vec![],
773 used_symbols: vec![]
774 },
775 simple: DeclUse {
776 declared_symbols: vec![
777 SimpleName {
778 symbol: "Dog".to_string(),
779 extent: optional_extent!(0:6-0:9),
780 },
781 SimpleName {
782 symbol: "eatSomething".to_string(),
783 extent: optional_extent!(2:17-2:29),
784 }
785 ],
786 used_symbols: vec![
787 SimpleName {
788 symbol: "Animal".to_string(),
789 extent: optional_extent!(0:18-0:24),
790 },
791 SimpleName {
792 symbol: "Override".to_string(),
793 extent: optional_extent!(1:13-1:21),
794 },
795 ]
796 },
797 external_simple: DeclUse {
798 declared_symbols: vec![],
799 used_symbols: vec![SimpleName {
800 symbol: "Animal".to_string(),
801 extent: optional_extent!(0:18-0:24),
802 }]
803 }
804 },
805 );
806 }
807
808 #[test]
809 fn finds_name_in_annotation_with_parameters() {
810 let code = r#"class TestAnnotation2 {
811 @SuppressWarnings("unchecked")
812 public static void main(String args[]) {
813 }"#;
814
815 let names = Java::find_names(code).expect("Cannot find names in test code");
816 assert_eq!(
817 names,
818 Names {
819 fully_qualified: DeclUse {
820 declared_symbols: vec![],
821 used_symbols: vec![]
822 },
823 simple: DeclUse {
824 declared_symbols: vec![
825 SimpleName {
826 symbol: "TestAnnotation2".to_string(),
827 extent: optional_extent!(0:6-0:21),
828 },
829 SimpleName {
830 symbol: "main".to_string(),
831 extent: optional_extent!(2:27-2:31),
832 },
833 SimpleName {
834 symbol: "args".to_string(),
835 extent: optional_extent!(2:39-2:43),
836 },
837 ],
838 used_symbols: vec![
839 SimpleName {
840 symbol: "SuppressWarnings".to_string(),
841 extent: optional_extent!(1:9-1:25),
842 },
843 SimpleName {
844 symbol: "String".to_string(),
845 extent: optional_extent!(2:32-2:38),
846 },
847 ]
848 },
849 external_simple: DeclUse {
850 declared_symbols: vec![],
851 used_symbols: vec![]
852 }
853 },
854 );
855 }
856
857 #[test]
858 fn finds_name_in_annotation_with_key_value_parameters() {
859 let code = r#"class TestAnnotation2 {
860 @AnnotationName(element1 = "value1", element2 = "value2")
861 public static void main(String args[]) {
862 }"#;
863
864 let names = Java::find_names(code).expect("Cannot find names in test code");
865 assert_eq!(
866 names,
867 Names {
868 fully_qualified: DeclUse {
869 declared_symbols: vec![],
870 used_symbols: vec![]
871 },
872 simple: DeclUse {
873 declared_symbols: vec![
874 SimpleName {
875 symbol: "TestAnnotation2".to_string(),
876 extent: optional_extent!(0:6-0:21),
877 },
878 SimpleName {
879 symbol: "main".to_string(),
880 extent: optional_extent!(2:27-2:31),
881 },
882 SimpleName {
883 symbol: "args".to_string(),
884 extent: optional_extent!(2:39-2:43),
885 },
886 ],
887 used_symbols: vec![
888 SimpleName {
889 symbol: "AnnotationName".to_string(),
890 extent: optional_extent!(1:9-1:23),
891 },
892 SimpleName {
893 symbol: "element1".to_string(),
894 extent: optional_extent!(1:24-1:32),
895 },
896 SimpleName {
897 symbol: "element2".to_string(),
898 extent: optional_extent!(1:45-1:53),
899 },
900 SimpleName {
901 symbol: "String".to_string(),
902 extent: optional_extent!(2:32-2:38),
903 },
904 ]
905 },
906 external_simple: DeclUse {
907 declared_symbols: vec![],
908 used_symbols: vec![
909 SimpleName {
910 symbol: "AnnotationName".to_string(),
911 extent: optional_extent!(1:9-1:23),
912 },
913 SimpleName {
914 symbol: "element1".to_string(),
915 extent: optional_extent!(1:24-1:32),
916 },
917 SimpleName {
918 symbol: "element2".to_string(),
919 extent: optional_extent!(1:45-1:53),
920 },
921 ]
922 }
923 },
924 );
925 }
926
927 #[test]
928 fn finds_names_in_enum_declaration() {
929 let code = r#"enum HotDrink implements Drink {
930 COFFEE(Ingredient.BEANS),
931 TEA(Ingredient.LEAVES);
932 private final Ingredient[] ingredients;
933 HotDrink(Ingredient... ingredients) {
934 for (var ingredient : ingredients)
935 assert ingredient instanceof AvailableIngredients;
936 this.ingredients = ingredients;
937 }
938}"#;
939
940 let names = Java::find_names(code).expect("Cannot find names in test code");
941 assert_eq!(
942 names,
943 Names {
944 fully_qualified: DeclUse {
945 declared_symbols: vec![],
946 used_symbols: vec![]
947 },
948 simple: DeclUse {
949 declared_symbols: vec![
950 SimpleName {
951 symbol: "HotDrink".to_string(),
952 extent: optional_extent!(0:5-0:13),
953 },
954 SimpleName {
955 symbol: "COFFEE".to_string(),
956 extent: optional_extent!(1:4-1:10),
957 },
958 SimpleName {
959 symbol: "TEA".to_string(),
960 extent: optional_extent!(2:4-2:7),
961 },
962 SimpleName {
963 symbol: "ingredients".to_string(),
964 extent: optional_extent!(3:31-3:42),
965 },
966 SimpleName {
967 symbol: "HotDrink".to_string(),
968 extent: optional_extent!(4:4-4:12),
969 },
970 SimpleName {
971 symbol: "ingredients".to_string(),
972 extent: optional_extent!(4:27-4:38),
973 },
974 SimpleName {
975 symbol: "ingredient".to_string(),
976 extent: optional_extent!(5:17-5:27),
977 }
978 ],
979 used_symbols: vec![
980 SimpleName {
981 symbol: "Drink".to_string(),
982 extent: optional_extent!(0:25-0:30),
983 },
984 SimpleName {
985 symbol: "Ingredient".to_string(),
986 extent: optional_extent!(1:11-1:21),
987 },
988 SimpleName {
989 symbol: "BEANS".to_string(),
990 extent: optional_extent!(1:22-1:27),
991 },
992 SimpleName {
993 symbol: "Ingredient".to_string(),
994 extent: optional_extent!(2:8-2:18),
995 },
996 SimpleName {
997 symbol: "LEAVES".to_string(),
998 extent: optional_extent!(2:19-2:25),
999 },
1000 SimpleName {
1001 symbol: "Ingredient".to_string(),
1002 extent: optional_extent!(3:18-3:28),
1003 },
1004 SimpleName {
1005 symbol: "Ingredient".to_string(),
1006 extent: optional_extent!(4:13-4:23),
1007 },
1008 SimpleName {
1009 symbol: "var".to_string(),
1010 extent: optional_extent!(5:13-5:16),
1011 },
1012 SimpleName {
1013 symbol: "ingredients".to_string(),
1014 extent: optional_extent!(5:30-5:41),
1015 },
1016 SimpleName {
1017 symbol: "ingredient".to_string(),
1018 extent: optional_extent!(6:19-6:29),
1019 },
1020 SimpleName {
1021 symbol: "AvailableIngredients".to_string(),
1022 extent: optional_extent!(6:41-6:61),
1023 },
1024 SimpleName {
1025 symbol: "HotDrink".to_string(),
1026 extent: None
1027 },
1028 SimpleName {
1029 symbol: "ingredients".to_string(),
1030 extent: optional_extent!(7:13-7:24),
1031 },
1032 SimpleName {
1033 symbol: "ingredients".to_string(),
1034 extent: optional_extent!(7:27-7:38),
1035 }
1036 ]
1037 },
1038 external_simple: DeclUse {
1039 declared_symbols: vec![],
1040 used_symbols: vec![
1041 SimpleName {
1042 symbol: "Drink".to_string(),
1043 extent: optional_extent!(0:25-0:30),
1044 },
1045 SimpleName {
1046 symbol: "Ingredient".to_string(),
1047 extent: optional_extent!(1:11-1:21),
1048 },
1049 SimpleName {
1050 symbol: "BEANS".to_string(),
1051 extent: optional_extent!(1:22-1:27),
1052 },
1053 SimpleName {
1054 symbol: "Ingredient".to_string(),
1055 extent: optional_extent!(2:8-2:18),
1056 },
1057 SimpleName {
1058 symbol: "LEAVES".to_string(),
1059 extent: optional_extent!(2:19-2:25),
1060 },
1061 SimpleName {
1062 symbol: "Ingredient".to_string(),
1063 extent: optional_extent!(3:18-3:28),
1064 },
1065 SimpleName {
1066 symbol: "Ingredient".to_string(),
1067 extent: optional_extent!(4:13-4:23),
1068 },
1069 SimpleName {
1070 symbol: "var".to_string(),
1071 extent: optional_extent!(5:13-5:16),
1072 },
1073 SimpleName {
1074 symbol: "AvailableIngredients".to_string(),
1075 extent: optional_extent!(6:41-6:61),
1076 }
1077 ]
1078 }
1079 }
1080 );
1081 }
1082
1083 #[test]
1084 fn finds_names_in_array_initializer() {
1085 let code = r#"String[] c = {"a", "b"};"#;
1086
1087 let names = Java::find_names(code).expect("Cannot find names in test code");
1088 assert_eq!(
1089 names,
1090 Names {
1091 fully_qualified: DeclUse {
1092 declared_symbols: vec![],
1093 used_symbols: vec![]
1094 },
1095 simple: DeclUse {
1096 declared_symbols: vec![SimpleName {
1097 symbol: "c".to_string(),
1098 extent: optional_extent!(0:9-0:10),
1099 },],
1100 used_symbols: vec![SimpleName {
1101 symbol: "String".to_string(),
1102 extent: optional_extent!(0:0-0:6),
1103 },]
1104 },
1105 external_simple: DeclUse {
1106 declared_symbols: vec![],
1107 used_symbols: vec![]
1108 }
1109 }
1110 );
1111 }
1112
1113 #[test]
1114 fn finds_names_in_double_array_initializer() {
1115 let code = r#"int[][] matrix = {{1,2}, {3,4}};"#;
1116
1117 let names = Java::find_names(code).expect("Cannot find names in test code");
1118 assert_eq!(
1119 names,
1120 Names {
1121 fully_qualified: DeclUse {
1122 declared_symbols: vec![],
1123 used_symbols: vec![]
1124 },
1125 simple: DeclUse {
1126 declared_symbols: vec![SimpleName {
1127 symbol: "matrix".to_string(),
1128 extent: optional_extent!(0:8-0:14),
1129 },],
1130 used_symbols: vec![SimpleName {
1131 symbol: "int".to_string(),
1132 extent: optional_extent!(0:0-0:3),
1133 },]
1134 },
1135 external_simple: DeclUse {
1136 declared_symbols: vec![],
1137 used_symbols: vec![]
1138 }
1139 }
1140 );
1141 }
1142
1143 #[test]
1144 fn finds_names_in_field_declaration() {
1145 let code = r#"public class Customer {
1146 String customerType = "OnlineCustomer";
1147 }"#;
1148
1149 let names = Java::find_names(code).expect("Cannot find names in test code");
1150 assert_eq!(
1151 names,
1152 Names {
1153 fully_qualified: DeclUse {
1154 declared_symbols: vec![],
1155 used_symbols: vec![]
1156 },
1157 simple: DeclUse {
1158 declared_symbols: vec![
1159 SimpleName {
1160 symbol: "Customer".to_string(),
1161 extent: optional_extent!(0:13-0:21),
1162 },
1163 SimpleName {
1164 symbol: "customerType".to_string(),
1165 extent: optional_extent!(1:19-1:31),
1166 },
1167 ],
1168 used_symbols: vec![SimpleName {
1169 symbol: "String".to_string(),
1170 extent: optional_extent!(1:12-1:18),
1171 },]
1172 },
1173 external_simple: DeclUse {
1174 declared_symbols: vec![],
1175 used_symbols: vec![]
1176 }
1177 }
1178 );
1179 }
1180
1181 #[test]
1182 fn finds_names_in_record_declaration() {
1183 let code = r#"record Rectangle(float length, float width) { }"#;
1184
1185 let names = Java::find_names(code).expect("Cannot find names in test code");
1186 assert_eq!(
1187 names,
1188 Names {
1189 fully_qualified: DeclUse {
1190 declared_symbols: vec![],
1191 used_symbols: vec![]
1192 },
1193 simple: DeclUse {
1194 declared_symbols: vec![
1195 SimpleName {
1196 symbol: "Rectangle".to_string(),
1197 extent: optional_extent!(0:7-0:16),
1198 },
1199 SimpleName {
1200 symbol: "length".to_string(),
1201 extent: optional_extent!(0:23-0:29),
1202 },
1203 SimpleName {
1204 symbol: "width".to_string(),
1205 extent: optional_extent!(0:37-0:42),
1206 },
1207 ],
1208 used_symbols: vec![
1209 SimpleName {
1210 symbol: "float".to_string(),
1211 extent: optional_extent!(0:17-0:22),
1212 },
1213 SimpleName {
1214 symbol: "float".to_string(),
1215 extent: optional_extent!(0:31-0:36),
1216 },
1217 ]
1218 },
1219 external_simple: DeclUse {
1220 declared_symbols: vec![],
1221 used_symbols: vec![],
1222 }
1223 }
1224 );
1225 }
1226
1227 #[test]
1228 fn finds_names_in_interface_declaration() {
1229 let code = r#"interface MyInterface
1230 {
1231 int constantVar = 5;
1232 public int method1();
1233 public void method2();
1234 }"#;
1235
1236 let names = Java::find_names(code).expect("Cannot find names in test code");
1237 assert_eq!(
1238 names,
1239 Names {
1240 fully_qualified: DeclUse {
1241 declared_symbols: vec![],
1242 used_symbols: vec![]
1243 },
1244 simple: DeclUse {
1245 declared_symbols: vec![
1246 SimpleName {
1247 symbol: "MyInterface".to_string(),
1248 extent: optional_extent!(0:10-0:21),
1249 },
1250 SimpleName {
1251 symbol: "constantVar".to_string(),
1252 extent: optional_extent!(2:15-2:26),
1253 },
1254 SimpleName {
1255 symbol: "method1".to_string(),
1256 extent: optional_extent!(3:22-3:29),
1257 },
1258 SimpleName {
1259 symbol: "method2".to_string(),
1260 extent: optional_extent!(4:23-4:30),
1261 },
1262 ],
1263 used_symbols: vec![
1264 SimpleName {
1265 symbol: "int".to_string(),
1266 extent: optional_extent!(2:11-2:14),
1267 },
1268 SimpleName {
1269 symbol: "int".to_string(),
1270 extent: optional_extent!(3:18-3:21),
1271 },
1272 ]
1273 },
1274 external_simple: DeclUse {
1275 declared_symbols: vec![],
1276 used_symbols: vec![]
1277 }
1278 }
1279 );
1280 }
1281
1282 #[test]
1283 fn finds_names_in_static_initalizer() {
1284 let code = r#"public class Demo
1285 {
1286 static double percentage;
1287 static
1288 {
1289 percentage = 44.6;
1290 }
1291 }"#;
1292
1293 let names = Java::find_names(code).expect("Cannot find names in test code");
1294 assert_eq!(
1295 names,
1296 Names {
1297 fully_qualified: DeclUse {
1298 declared_symbols: vec![],
1299 used_symbols: vec![]
1300 },
1301 simple: DeclUse {
1302 declared_symbols: vec![
1303 SimpleName {
1304 symbol: "Demo".to_string(),
1305 extent: optional_extent!(0:13-0:17),
1306 },
1307 SimpleName {
1308 symbol: "percentage".to_string(),
1309 extent: optional_extent!(2:26-2:36),
1310 },
1311 ],
1312 used_symbols: vec![
1313 SimpleName {
1314 symbol: "double".to_string(),
1315 extent: optional_extent!(2:19-2:25),
1316 },
1317 SimpleName {
1318 symbol: "percentage".to_string(),
1319 extent: optional_extent!(5:16-5:26),
1320 },
1321 ]
1322 },
1323 external_simple: DeclUse {
1324 declared_symbols: vec![],
1325 used_symbols: vec![]
1326 }
1327 }
1328 );
1329 }
1330
1331 #[test]
1332 fn finds_names_in_annotation_type_declaration() {
1333 let code = r#"public @interface RequestForEnhancement {
1334 int id();
1335 String synopsis();
1336 }"#;
1337
1338 let names = Java::find_names(code).expect("Cannot find names in test code");
1339 assert_eq!(
1340 names,
1341 Names {
1342 fully_qualified: DeclUse {
1343 declared_symbols: vec![],
1344 used_symbols: vec![]
1345 },
1346 simple: DeclUse {
1347 declared_symbols: vec![
1348 SimpleName {
1349 symbol: "RequestForEnhancement".to_string(),
1350 extent: optional_extent!(0:18-0:39),
1351 },
1352 SimpleName {
1353 symbol: "id".to_string(),
1354 extent: optional_extent!(1:19-1:21),
1355 },
1356 SimpleName {
1357 symbol: "synopsis".to_string(),
1358 extent: optional_extent!(2:19-2:27),
1359 },
1360 ],
1361 used_symbols: vec![
1362 SimpleName {
1363 symbol: "int".to_string(),
1364 extent: optional_extent!(1:12-1:15),
1365 },
1366 SimpleName {
1367 symbol: "String".to_string(),
1368 extent: optional_extent!(2:12-2:18),
1369 },
1370 ]
1371 },
1372 external_simple: DeclUse {
1373 declared_symbols: vec![],
1374 used_symbols: vec![]
1375 }
1376 }
1377 );
1378 }
1379
1380 #[test]
1381 fn finds_names_in_generic_type() {
1382 let code = r#"Box<Integer> integerBox = new Box<>();"#;
1383
1384 let names = Java::find_names(code).expect("Cannot find names in test code");
1385 assert_eq!(
1386 names,
1387 Names {
1388 fully_qualified: DeclUse {
1389 declared_symbols: vec![],
1390 used_symbols: vec![]
1391 },
1392 simple: DeclUse {
1393 declared_symbols: vec![SimpleName {
1394 symbol: "integerBox".to_string(),
1395 extent: optional_extent!(0:13-0:23),
1396 },],
1397 used_symbols: vec![
1398 SimpleName {
1399 symbol: "Box".to_string(),
1400 extent: optional_extent!(0:0-0:3),
1401 },
1402 SimpleName {
1403 symbol: "Integer".to_string(),
1404 extent: optional_extent!(0:4-0:11),
1405 },
1406 SimpleName {
1407 symbol: "Box".to_string(),
1408 extent: optional_extent!(0:30-0:33),
1409 },
1410 ]
1411 },
1412 external_simple: DeclUse {
1413 declared_symbols: vec![],
1414 used_symbols: vec![
1415 SimpleName {
1416 symbol: "Box".to_string(),
1417 extent: optional_extent!(0:0-0:3),
1418 },
1419 SimpleName {
1420 symbol: "Box".to_string(),
1421 extent: optional_extent!(0:30-0:33),
1422 },
1423 ]
1424 }
1425 }
1426 );
1427 }
1428
1429 #[test]
1430 fn finds_names_in_for_statement() {
1431 let code = r#"for(int i=1;i<=10;i++) {}"#;
1432
1433 let names = Java::find_names(code).expect("Cannot find names in test code");
1434 assert_eq!(
1435 names,
1436 Names {
1437 fully_qualified: DeclUse {
1438 declared_symbols: vec![],
1439 used_symbols: vec![]
1440 },
1441 simple: DeclUse {
1442 declared_symbols: vec![SimpleName {
1443 symbol: "i".to_string(),
1444 extent: optional_extent!(0:8-0:9),
1445 }],
1446 used_symbols: vec![
1447 SimpleName {
1448 symbol: "int".to_string(),
1449 extent: optional_extent!(0:4-0:7),
1450 },
1451 SimpleName {
1452 symbol: "i".to_string(),
1453 extent: optional_extent!(0:12-0:13),
1454 },
1455 SimpleName {
1456 symbol: "i".to_string(),
1457 extent: optional_extent!(0:18-0:19),
1458 },
1459 ]
1460 },
1461 external_simple: DeclUse {
1462 declared_symbols: vec![],
1463 used_symbols: vec![]
1464 }
1465 }
1466 );
1467 }
1468
1469 #[test]
1470 fn finds_names_in_synchronized_statement() {
1471 let code = r#"public class MyCounter {
1472 public void increment(int value) {
1473 synchronized(this) {
1474 this.count += value;
1475 }
1476 }
1477 }"#;
1478
1479 let names = Java::find_names(code).expect("Cannot find names in test code");
1480 assert_eq!(
1481 names,
1482 Names {
1483 fully_qualified: DeclUse {
1484 declared_symbols: vec![],
1485 used_symbols: vec![]
1486 },
1487 simple: DeclUse {
1488 declared_symbols: vec![
1489 SimpleName {
1490 symbol: "MyCounter".to_string(),
1491 extent: optional_extent!(0:13-0:22),
1492 },
1493 SimpleName {
1494 symbol: "increment".to_string(),
1495 extent: optional_extent!(1:24-1:33),
1496 },
1497 SimpleName {
1498 symbol: "value".to_string(),
1499 extent: optional_extent!(1:38-1:43),
1500 },
1501 ],
1502 used_symbols: vec![
1503 SimpleName {
1504 symbol: "int".to_string(),
1505 extent: optional_extent!(1:34-1:37),
1506 },
1507 SimpleName {
1508 symbol: "MyCounter".to_string(),
1509 extent: None,
1510 },
1511 SimpleName {
1512 symbol: "count".to_string(),
1513 extent: optional_extent!(3:21-3:26),
1514 },
1515 SimpleName {
1516 symbol: "value".to_string(),
1517 extent: optional_extent!(3:30-3:35),
1518 },
1519 ]
1520 },
1521 external_simple: DeclUse {
1522 declared_symbols: vec![],
1523 used_symbols: vec![]
1524 }
1525 }
1526 );
1527 }
1528
1529 #[test]
1530 fn finds_names_in_unqualified_object_creation() {
1531 let code = r#"public class MyCounter {
1532 public void increment(int value) {
1533 synchronized(this) {
1534 this.count += value;
1535 }
1536 }
1537 }"#;
1538
1539 let names = Java::find_names(code).expect("Cannot find names in test code");
1540 assert_eq!(
1541 names,
1542 Names {
1543 fully_qualified: DeclUse {
1544 declared_symbols: vec![],
1545 used_symbols: vec![]
1546 },
1547 simple: DeclUse {
1548 declared_symbols: vec![
1549 SimpleName {
1550 symbol: "MyCounter".to_string(),
1551 extent: optional_extent!(0:13-0:22),
1552 },
1553 SimpleName {
1554 symbol: "increment".to_string(),
1555 extent: optional_extent!(1:24-1:33),
1556 },
1557 SimpleName {
1558 symbol: "value".to_string(),
1559 extent: optional_extent!(1:38-1:43),
1560 },
1561 ],
1562 used_symbols: vec![
1563 SimpleName {
1564 symbol: "int".to_string(),
1565 extent: optional_extent!(1:34-1:37),
1566 },
1567 SimpleName {
1568 symbol: "MyCounter".to_string(),
1569 extent: None,
1570 },
1571 SimpleName {
1572 symbol: "count".to_string(),
1573 extent: optional_extent!(3:21-3:26),
1574 },
1575 SimpleName {
1576 symbol: "value".to_string(),
1577 extent: optional_extent!(3:30-3:35),
1578 },
1579 ]
1580 },
1581 external_simple: DeclUse {
1582 declared_symbols: vec![],
1583 used_symbols: vec![]
1584 }
1585 }
1586 );
1587 }
1588
1589 #[test]
1590 fn finds_names_in_labeled_statement() {
1591 let code = r#"int a = 1;
1592 block1: {
1593 if (a < 0) {
1594 break block1;
1595 }
1596 }"#;
1597
1598 let names = Java::find_names(code).expect("Cannot find names in test code");
1599 assert_eq!(
1600 names,
1601 Names {
1602 fully_qualified: DeclUse {
1603 declared_symbols: vec![],
1604 used_symbols: vec![]
1605 },
1606 simple: DeclUse {
1607 declared_symbols: vec![SimpleName {
1608 symbol: "a".to_string(),
1609 extent: optional_extent!(0:4-0:5),
1610 },],
1611 used_symbols: vec![
1612 SimpleName {
1613 symbol: "int".to_string(),
1614 extent: optional_extent!(0:0-0:3),
1615 },
1616 SimpleName {
1617 symbol: "block1".to_string(),
1618 extent: optional_extent!(1:28-1:34),
1619 },
1620 SimpleName {
1621 symbol: "a".to_string(),
1622 extent: optional_extent!(2:36-2:37),
1623 },
1624 ]
1625 },
1626 external_simple: DeclUse {
1627 declared_symbols: vec![],
1628 used_symbols: vec![SimpleName {
1629 symbol: "block1".to_string(),
1630 extent: optional_extent!(1:28-1:34),
1631 },]
1632 }
1633 }
1634 );
1635 }
1636
1637 #[test]
1639 fn finds_names_in_method_reference() {
1640 let code = r#"list.forEach(System.out::println);"#;
1641
1642 let names = Java::find_names(code).expect("Cannot find names in test code");
1643 assert_eq!(
1644 names,
1645 Names {
1646 fully_qualified: DeclUse {
1647 declared_symbols: vec![],
1648 used_symbols: vec![]
1649 },
1650 simple: DeclUse {
1651 declared_symbols: vec![],
1652 used_symbols: vec![
1653 SimpleName {
1654 symbol: "list".to_string(),
1655 extent: optional_extent!(0:0-0:4),
1656 },
1657 SimpleName {
1658 symbol: "forEach".to_string(),
1659 extent: optional_extent!(0:5-0:12),
1660 },
1661 SimpleName {
1662 symbol: "System".to_string(),
1663 extent: optional_extent!(0:13-0:19),
1664 },
1665 SimpleName {
1666 symbol: "System".to_string(),
1667 extent: None,
1668 },
1669 SimpleName {
1670 symbol: "out".to_string(),
1671 extent: optional_extent!(0:20-0:23),
1672 },
1673 SimpleName {
1674 symbol: "println".to_string(),
1675 extent: optional_extent!(0:25-0:32),
1676 },
1677 ]
1678 },
1679 external_simple: DeclUse {
1680 declared_symbols: vec![],
1681 used_symbols: vec![
1682 SimpleName {
1683 symbol: "list".to_string(),
1684 extent: optional_extent!(0:0-0:4),
1685 },
1686 SimpleName {
1687 symbol: "forEach".to_string(),
1688 extent: optional_extent!(0:5-0:12),
1689 },
1690 SimpleName {
1691 symbol: "println".to_string(),
1692 extent: optional_extent!(0:25-0:32),
1693 },
1694 ]
1695 }
1696 }
1697 );
1698 }
1699
1700 #[test]
1701 fn finds_names_in_method_reference_2() {
1702 let code = r#"ComparisonProvider comparator = new ComparisonProvider();
1703 Collections.sort(personList, comparator::compareByName);"#;
1704
1705 let names = Java::find_names(code).expect("Cannot find names in test code");
1706 assert_eq!(
1707 names,
1708 Names {
1709 fully_qualified: DeclUse {
1710 declared_symbols: vec![],
1711 used_symbols: vec![]
1712 },
1713 simple: DeclUse {
1714 declared_symbols: vec![SimpleName {
1715 symbol: "comparator".to_string(),
1716 extent: optional_extent!(0:19-0:29),
1717 },],
1718 used_symbols: vec![
1719 SimpleName {
1720 symbol: "ComparisonProvider".to_string(),
1721 extent: optional_extent!(0:0-0:18),
1722 },
1723 SimpleName {
1724 symbol: "ComparisonProvider".to_string(),
1725 extent: optional_extent!(0:36-0:54),
1726 },
1727 SimpleName {
1728 symbol: "Collections".to_string(),
1729 extent: optional_extent!(1:28-1:39),
1730 },
1731 SimpleName {
1732 symbol: "sort".to_string(),
1733 extent: optional_extent!(1:40-1:44),
1734 },
1735 SimpleName {
1736 symbol: "personList".to_string(),
1737 extent: optional_extent!(1:45-1:55),
1738 },
1739 SimpleName {
1740 symbol: "comparator".to_string(),
1741 extent: optional_extent!(1:57-1:67),
1742 },
1743 SimpleName {
1744 symbol: "compareByName".to_string(),
1745 extent: optional_extent!(1:69-1:82),
1746 },
1747 ]
1748 },
1749 external_simple: DeclUse {
1750 declared_symbols: vec![],
1751 used_symbols: vec![
1752 SimpleName {
1753 symbol: "ComparisonProvider".to_string(),
1754 extent: optional_extent!(0:0-0:18),
1755 },
1756 SimpleName {
1757 symbol: "ComparisonProvider".to_string(),
1758 extent: optional_extent!(0:36-0:54),
1759 },
1760 SimpleName {
1761 symbol: "Collections".to_string(),
1762 extent: optional_extent!(1:28-1:39),
1763 },
1764 SimpleName {
1765 symbol: "sort".to_string(),
1766 extent: optional_extent!(1:40-1:44),
1767 },
1768 SimpleName {
1769 symbol: "personList".to_string(),
1770 extent: optional_extent!(1:45-1:55),
1771 },
1772 SimpleName {
1773 symbol: "compareByName".to_string(),
1774 extent: optional_extent!(1:69-1:82),
1775 },
1776 ]
1777 }
1778 }
1779 );
1780 }
1781
1782 #[test]
1783 fn finds_names_in_array_creation_expression() {
1784 let code = r#"int[] myArray = new int[10];"#;
1785
1786 let names = Java::find_names(code).expect("Cannot find names in test code");
1787 assert_eq!(
1788 names,
1789 Names {
1790 fully_qualified: DeclUse {
1791 declared_symbols: vec![],
1792 used_symbols: vec![]
1793 },
1794 simple: DeclUse {
1795 declared_symbols: vec![SimpleName {
1796 symbol: "myArray".to_string(),
1797 extent: optional_extent!(0:6-0:13),
1798 },],
1799 used_symbols: vec![
1800 SimpleName {
1801 symbol: "int".to_string(),
1802 extent: optional_extent!(0:0-0:3),
1803 },
1804 SimpleName {
1805 symbol: "int".to_string(),
1806 extent: optional_extent!(0:20-0:23),
1807 },
1808 ]
1809 },
1810 external_simple: DeclUse {
1811 declared_symbols: vec![],
1812 used_symbols: vec![]
1813 }
1814 }
1815 );
1816 }
1817
1818 #[test]
1819 fn finds_names_in_module_declaration() {
1820 let code = r#"module demo.hello_modules {
1821 requires java.base;
1822 exports demo.modules;
1823 }"#;
1824
1825 let names = Java::find_names(code).expect("Cannot find names in test code");
1826 assert_eq!(
1827 names,
1828 Names {
1829 fully_qualified: DeclUse {
1830 declared_symbols: vec![FullyQualifiedName {
1831 source: sym!(java),
1832 symbol: sym!(base),
1833 extent: extent!(1:21-1:30),
1834 },],
1835 used_symbols: vec![FullyQualifiedName {
1836 source: sym!(demo),
1837 symbol: sym!(modules),
1838 extent: extent!(2:20-2:32),
1839 },]
1840 },
1841 simple: DeclUse {
1842 declared_symbols: vec![
1843 SimpleName {
1844 symbol: "demo".to_string(),
1845 extent: optional_extent!(0:7-0:11),
1846 },
1847 SimpleName {
1848 symbol: "hello_modules".to_string(),
1849 extent: optional_extent!(0:12-0:25),
1850 },
1851 SimpleName {
1852 symbol: "java".to_string(),
1853 extent: optional_extent!(1:21-1:25),
1854 },
1855 SimpleName {
1856 symbol: "base".to_string(),
1857 extent: optional_extent!(1:26-1:30),
1858 },
1859 ],
1860 used_symbols: vec![
1861 SimpleName {
1862 symbol: "demo".to_string(),
1863 extent: optional_extent!(2:20-2:24),
1864 },
1865 SimpleName {
1866 symbol: "modules".to_string(),
1867 extent: optional_extent!(2:25-2:32),
1868 },
1869 ]
1870 },
1871 external_simple: DeclUse {
1872 declared_symbols: vec![
1873 SimpleName {
1874 symbol: "java".to_string(),
1875 extent: optional_extent!(1:21-1:25),
1876 },
1877 SimpleName {
1878 symbol: "base".to_string(),
1879 extent: optional_extent!(1:26-1:30),
1880 },
1881 ],
1882 used_symbols: vec![
1883 SimpleName {
1884 symbol: "demo".to_string(),
1885 extent: optional_extent!(2:20-2:24),
1886 },
1887 SimpleName {
1888 symbol: "modules".to_string(),
1889 extent: optional_extent!(2:25-2:32),
1890 },
1891 ]
1892 }
1893 }
1894 );
1895 }
1896
1897 #[test]
1898 fn finds_names_in_package_declaration() {
1899 let code = r#"package graphics;"#;
1900
1901 let names = Java::find_names(code).expect("Cannot find names in test code");
1902 assert_eq!(
1903 names,
1904 Names {
1905 fully_qualified: DeclUse {
1906 declared_symbols: vec![],
1907 used_symbols: vec![]
1908 },
1909 simple: DeclUse {
1910 declared_symbols: vec![],
1911 used_symbols: vec![]
1912 },
1913 external_simple: DeclUse {
1914 declared_symbols: vec![],
1915 used_symbols: vec![]
1916 }
1917 }
1918 );
1919 }
1920
1921 #[test]
1922 fn find_names_in_for_variable_assignment() {
1923 let code = r#"for (i = x1Index; i < x2Index; ++i) {}"#;
1924
1925 let names = Java::find_names(code).expect("Cannot find names in test code");
1926 assert_eq!(
1927 names,
1928 Names {
1929 fully_qualified: DeclUse {
1930 declared_symbols: vec![],
1931 used_symbols: vec![]
1932 },
1933 simple: DeclUse {
1934 declared_symbols: vec![],
1935 used_symbols: vec![
1936 SimpleName {
1937 symbol: "i".to_string(),
1938 extent: optional_extent!(0:5-0:6),
1939 },
1940 SimpleName {
1941 symbol: "x1Index".to_string(),
1942 extent: optional_extent!(0:9-0:16),
1943 },
1944 SimpleName {
1945 symbol: "i".to_string(),
1946 extent: optional_extent!(0:18-0:19),
1947 },
1948 SimpleName {
1949 symbol: "x2Index".to_string(),
1950 extent: optional_extent!(0:22-0:29),
1951 },
1952 SimpleName {
1953 symbol: "i".to_string(),
1954 extent: optional_extent!(0:33-0:34),
1955 },
1956 ]
1957 },
1958 external_simple: DeclUse {
1959 declared_symbols: vec![],
1960 used_symbols: vec![
1961 SimpleName {
1962 symbol: "i".to_string(),
1963 extent: optional_extent!(0:5-0:6),
1964 },
1965 SimpleName {
1966 symbol: "x1Index".to_string(),
1967 extent: optional_extent!(0:9-0:16),
1968 },
1969 SimpleName {
1970 symbol: "i".to_string(),
1971 extent: optional_extent!(0:18-0:19),
1972 },
1973 SimpleName {
1974 symbol: "x2Index".to_string(),
1975 extent: optional_extent!(0:22-0:29),
1976 },
1977 SimpleName {
1978 symbol: "i".to_string(),
1979 extent: optional_extent!(0:33-0:34),
1980 },
1981 ]
1982 }
1983 }
1984 );
1985 }
1986
1987 #[test]
1988 fn finds_names_in_annotation_argument_list() {
1989 let code = r#"@RunWith(RobolectricTestRunner.class)
1990 public class FileReceiverActivityTest {
1991 }"#;
1992
1993 let names = Java::find_names(code).expect("Cannot find names in test code");
1994 assert_eq!(
1995 names,
1996 Names {
1997 fully_qualified: DeclUse {
1998 declared_symbols: vec![],
1999 used_symbols: vec![]
2000 },
2001 simple: DeclUse {
2002 declared_symbols: vec![SimpleName {
2003 symbol: "FileReceiverActivityTest".to_string(),
2004 extent: optional_extent!(1:21-1:45),
2005 },],
2006 used_symbols: vec![
2007 SimpleName {
2008 symbol: "RunWith".to_string(),
2009 extent: optional_extent!(0:1-0:8),
2010 },
2011 SimpleName {
2012 symbol: "RobolectricTestRunner".to_string(),
2013 extent: optional_extent!(0:9-0:30),
2014 },
2015 ]
2016 },
2017 external_simple: DeclUse {
2018 declared_symbols: vec![],
2019 used_symbols: vec![
2020 SimpleName {
2021 symbol: "RunWith".to_string(),
2022 extent: optional_extent!(0:1-0:8),
2023 },
2024 SimpleName {
2025 symbol: "RobolectricTestRunner".to_string(),
2026 extent: optional_extent!(0:9-0:30),
2027 },
2028 ]
2029 }
2030 }
2031 );
2032 }
2033
2034 #[test]
2035 fn finds_names_in_method_invocation_with_type_arguments() {
2036 let code =
2037 r#"Stream.Builder<RECIPE_ID> recipeIdsEnumBuilder = Stream.<RECIPE_ID>builder();"#;
2038
2039 let names = Java::find_names(code).expect("Cannot find names in test code");
2040 assert_eq!(
2041 names,
2042 Names {
2043 fully_qualified: DeclUse {
2044 declared_symbols: vec![],
2045 used_symbols: vec![]
2046 },
2047 simple: DeclUse {
2048 declared_symbols: vec![SimpleName {
2049 symbol: "recipeIdsEnumBuilder".to_string(),
2050 extent: optional_extent!(0:26-0:46),
2051 },],
2052 used_symbols: vec![
2053 SimpleName {
2054 symbol: "Stream".to_string(),
2055 extent: optional_extent!(0:0-0:6),
2056 },
2057 SimpleName {
2058 symbol: "Builder".to_string(),
2059 extent: optional_extent!(0:7-0:14),
2060 },
2061 SimpleName {
2062 symbol: "RECIPE_ID".to_string(),
2063 extent: optional_extent!(0:15-0:24),
2064 },
2065 SimpleName {
2066 symbol: "Stream".to_string(),
2067 extent: optional_extent!(0:49-0:55),
2068 },
2069 SimpleName {
2070 symbol: "RECIPE_ID".to_string(),
2071 extent: optional_extent!(0:57-0:66),
2072 },
2073 SimpleName {
2074 symbol: "builder".to_string(),
2075 extent: optional_extent!(0:67-0:74),
2076 },
2077 ]
2078 },
2079 external_simple: DeclUse {
2080 declared_symbols: vec![],
2081 used_symbols: vec![
2082 SimpleName {
2083 symbol: "Stream".to_string(),
2084 extent: optional_extent!(0:0-0:6),
2085 },
2086 SimpleName {
2087 symbol: "Builder".to_string(),
2088 extent: optional_extent!(0:7-0:14),
2089 },
2090 SimpleName {
2091 symbol: "RECIPE_ID".to_string(),
2092 extent: optional_extent!(0:15-0:24),
2093 },
2094 SimpleName {
2095 symbol: "Stream".to_string(),
2096 extent: optional_extent!(0:49-0:55),
2097 },
2098 SimpleName {
2099 symbol: "RECIPE_ID".to_string(),
2100 extent: optional_extent!(0:57-0:66),
2101 },
2102 SimpleName {
2103 symbol: "builder".to_string(),
2104 extent: optional_extent!(0:67-0:74),
2105 },
2106 ]
2107 }
2108 }
2109 );
2110 }
2111
2112 #[test]
2113 fn finds_names_in_for_with_expression() {
2114 let code = r#"
2115 private static void missedForBlock() {
2116 for (nop(); f(); nop()) {
2117 nop();
2118 }
2119 }"#;
2120
2121 let names = Java::find_names(code).expect("Cannot find names in test code");
2122 assert_eq!(
2123 names,
2124 Names {
2125 fully_qualified: DeclUse {
2126 declared_symbols: vec![],
2127 used_symbols: vec![]
2128 },
2129 simple: DeclUse {
2130 declared_symbols: vec![SimpleName {
2131 symbol: "missedForBlock".to_string(),
2132 extent: optional_extent!(1:32-1:46),
2133 },],
2134 used_symbols: vec![]
2135 },
2136 external_simple: DeclUse {
2137 declared_symbols: vec![],
2138 used_symbols: vec![]
2139 }
2140 }
2141 );
2142 }
2143
2144 #[test]
2145 fn finds_names_in_requires_with_modifiers() {
2146 let code = r#"
2147 module r2dbc.mariadb {
2148 requires transitive io.netty.buffer;
2149 }"#;
2150
2151 let names = Java::find_names(code).expect("Cannot find names in test code");
2152 assert_eq!(
2153 names,
2154 Names {
2155 fully_qualified: DeclUse {
2156 declared_symbols: vec![FullyQualifiedName {
2157 source: vec!["io".to_string(), "netty".to_string()],
2158 symbol: sym!(buffer),
2159 extent: extent!(2:36-2:51),
2160 },],
2161 used_symbols: vec![]
2162 },
2163 simple: DeclUse {
2164 declared_symbols: vec![
2165 SimpleName {
2166 symbol: "r2dbc".to_string(),
2167 extent: optional_extent!(1:19-1:24),
2168 },
2169 SimpleName {
2170 symbol: "mariadb".to_string(),
2171 extent: optional_extent!(1:25-1:32),
2172 },
2173 SimpleName {
2174 symbol: "io".to_string(),
2175 extent: optional_extent!(2:36-2:38),
2176 },
2177 SimpleName {
2178 symbol: "netty".to_string(),
2179 extent: optional_extent!(2:39-2:44),
2180 },
2181 SimpleName {
2182 symbol: "buffer".to_string(),
2183 extent: optional_extent!(2:45-2:51),
2184 },
2185 ],
2186 used_symbols: vec![]
2187 },
2188 external_simple: DeclUse {
2189 declared_symbols: vec![
2190 SimpleName {
2191 symbol: "io".to_string(),
2192 extent: optional_extent!(2:36-2:38),
2193 },
2194 SimpleName {
2195 symbol: "netty".to_string(),
2196 extent: optional_extent!(2:39-2:44),
2197 },
2198 SimpleName {
2199 symbol: "buffer".to_string(),
2200 extent: optional_extent!(2:45-2:51),
2201 },
2202 ],
2203 used_symbols: vec![]
2204 }
2205 }
2206 );
2207 }
2208
2209 #[test]
2210 fn finds_names_in_annotation_module() {
2211 let code = r#"
2212 @ModuleAnnotation("typeannotationtest")
2213 module org.jboss.jandex.typeannotationtest {
2214 }"#;
2215
2216 let names = Java::find_names(code).expect("Cannot find names in test code");
2217 assert_eq!(
2218 names,
2219 Names {
2220 fully_qualified: DeclUse {
2221 declared_symbols: vec![],
2222 used_symbols: vec![]
2223 },
2224 simple: DeclUse {
2225 declared_symbols: vec![
2226 SimpleName {
2227 symbol: "org".to_string(),
2228 extent: optional_extent!(2:15-2:18),
2229 },
2230 SimpleName {
2231 symbol: "jboss".to_string(),
2232 extent: optional_extent!(2:19-2:24),
2233 },
2234 SimpleName {
2235 symbol: "jandex".to_string(),
2236 extent: optional_extent!(2:25-2:31),
2237 },
2238 SimpleName {
2239 symbol: "typeannotationtest".to_string(),
2240 extent: optional_extent!(2:32-2:50),
2241 },
2242 ],
2243 used_symbols: vec![SimpleName {
2244 symbol: "ModuleAnnotation".to_string(),
2245 extent: optional_extent!(1:9-1:25),
2246 },]
2247 },
2248 external_simple: DeclUse {
2249 declared_symbols: vec![],
2250 used_symbols: vec![SimpleName {
2251 symbol: "ModuleAnnotation".to_string(),
2252 extent: optional_extent!(1:9-1:25),
2253 },]
2254 }
2255 }
2256 );
2257 }
2258
2259 #[test]
2260 fn finds_names_in_method_declaration_annotation() {
2261 let code =
2262 r#"static <T> @Nullable ArrayList<T> listToArrayList(@Nullable List<T> list) {}"#;
2263
2264 let names = Java::find_names(code).expect("Cannot find names in test code");
2265 assert_eq!(
2266 names,
2267 Names {
2268 fully_qualified: DeclUse {
2269 declared_symbols: vec![],
2270 used_symbols: vec![]
2271 },
2272 simple: DeclUse {
2273 declared_symbols: vec![
2274 SimpleName {
2275 symbol: "listToArrayList".to_string(),
2276 extent: optional_extent!(0:34-0:49),
2277 },
2278 SimpleName {
2279 symbol: "list".to_string(),
2280 extent: optional_extent!(0:68-0:72),
2281 }
2282 ],
2283 used_symbols: vec![
2284 SimpleName {
2285 symbol: "T".to_string(),
2286 extent: optional_extent!(0:8-0:9),
2287 },
2288 SimpleName {
2289 symbol: "Nullable".to_string(),
2290 extent: optional_extent!(0:12-0:20),
2291 },
2292 SimpleName {
2293 symbol: "ArrayList".to_string(),
2294 extent: optional_extent!(0:21-0:30),
2295 },
2296 SimpleName {
2297 symbol: "T".to_string(),
2298 extent: optional_extent!(0:31-0:32),
2299 },
2300 SimpleName {
2301 symbol: "Nullable".to_string(),
2302 extent: optional_extent!(0:51-0:59),
2303 },
2304 SimpleName {
2305 symbol: "List".to_string(),
2306 extent: optional_extent!(0:60-0:64),
2307 },
2308 SimpleName {
2309 symbol: "T".to_string(),
2310 extent: optional_extent!(0:65-0:66),
2311 },
2312 ]
2313 },
2314 external_simple: DeclUse {
2315 declared_symbols: vec![],
2316 used_symbols: vec![
2317 SimpleName {
2318 symbol: "T".to_string(),
2319 extent: optional_extent!(0:8-0:9),
2320 },
2321 SimpleName {
2322 symbol: "Nullable".to_string(),
2323 extent: optional_extent!(0:12-0:20),
2324 },
2325 SimpleName {
2326 symbol: "ArrayList".to_string(),
2327 extent: optional_extent!(0:21-0:30),
2328 },
2329 SimpleName {
2330 symbol: "T".to_string(),
2331 extent: optional_extent!(0:31-0:32),
2332 },
2333 SimpleName {
2334 symbol: "Nullable".to_string(),
2335 extent: optional_extent!(0:51-0:59),
2336 },
2337 SimpleName {
2338 symbol: "List".to_string(),
2339 extent: optional_extent!(0:60-0:64),
2340 },
2341 SimpleName {
2342 symbol: "T".to_string(),
2343 extent: optional_extent!(0:65-0:66),
2344 },
2345 ]
2346 }
2347 }
2348 );
2349 }
2350
2351 #[test]
2352 fn finds_relevant_snippet() {
2353 let code = r#"/*
2354 * SPDX-License-Identifier: Apache-2.0
2355 *
2356 * Copyright 2018-2022 Agorapulse.
2357 *
2358 * Licensed under the Apache License, Version 2.0 (the "License");
2359 * you may not use this file except in compliance with the License.
2360 * You may obtain a copy of the License at
2361 *
2362 * https://www.apache.org/licenses/LICENSE-2.0
2363 *
2364 * Unless required by applicable law or agreed to in writing, software
2365 * distributed under the License is distributed on an "AS IS" BASIS,
2366 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2367 * See the License for the specific language governing permissions and
2368 * limitations under the License.
2369 */
2370package com.agorapulse.micronaut.amazon.awssdk.ses;
2371
2372import io.micronaut.core.util.StringUtils;
2373import org.slf4j.Logger;
2374import org.slf4j.LoggerFactory;
2375import software.amazon.awssdk.awscore.exception.AwsServiceException;
2376import software.amazon.awssdk.core.SdkBytes;
2377import software.amazon.awssdk.services.ses.SesClient;
2378import software.amazon.awssdk.services.ses.model.SendEmailRequest;
2379import software.amazon.awssdk.services.ses.model.SendRawEmailRequest;
2380
2381import javax.activation.DataHandler;
2382import javax.activation.DataSource;
2383import javax.mail.BodyPart;
2384import javax.mail.MessagingException;
2385import javax.mail.Session;
2386import javax.mail.internet.InternetAddress;
2387import javax.mail.internet.MimeBodyPart;
2388import javax.mail.internet.MimeMessage;
2389import javax.mail.internet.MimeMultipart;
2390import javax.mail.util.ByteArrayDataSource;
2391import java.io.ByteArrayOutputStream;
2392import java.io.File;
2393import java.io.FileInputStream;
2394import java.io.IOException;
2395import java.nio.charset.StandardCharsets;
2396import java.util.Optional;
2397import java.util.Properties;
2398
2399import static javax.mail.Message.RecipientType.TO;
2400
2401/**
2402 * Default implementation of simple email service.
2403 */
2404public class DefaultSimpleEmailService implements SimpleEmailService {
2405
2406 private static final Logger LOGGER = LoggerFactory.getLogger(DefaultSimpleEmailService.class);
2407
2408 private final SesClient client;
2409 private final SimpleEmailServiceConfiguration configuration;
2410
2411 DefaultSimpleEmailService(SesClient client, SimpleEmailServiceConfiguration configuration) {
2412 this.client = client;
2413 this.configuration = configuration;
2414 }
2415
2416 public EmailDeliveryStatus send(TransactionalEmail email) {
2417 if (!email.getAttachments().isEmpty()) {
2418 try {
2419 return sendEmailWithAttachment(email);
2420 } catch (IOException | MessagingException exception) {
2421 LOGGER.error(
2422 String.format(
2423 "An exception was caught while sending email: destinationEmails=%s, from=%s, replyTo=%s, subject=%s",
2424 email.getRecipients(),
2425 email.getFrom(),
2426 email.getReplyTo(),
2427 email.getSubject()
2428 ),
2429 exception
2430 );
2431 return EmailDeliveryStatus.STATUS_NOT_DELIVERED;
2432 }
2433 }
2434
2435 return sendWithoutAttachments(email);
2436 }
2437
2438 private static EmailDeliveryStatus handleSend(TransactionalEmail email, Runnable c) {
2439 try {
2440 c.run();
2441 return EmailDeliveryStatus.STATUS_DELIVERED;
2442 } catch (AwsServiceException exception) {
2443 if (exception.getMessage().contains("blacklisted")) {
2444 LOGGER.warn(String.format("Address blacklisted destinationEmails=%s)", email.getRecipients()));
2445 return EmailDeliveryStatus.STATUS_BLACKLISTED;
2446 }
2447 LOGGER.error(
2448 String.format(
2449 "An amazon service exception was caught while sending email: destinationEmails=%s, from=%s, replyTo=%s, subject=%s",
2450 email.getRecipients(),
2451 email.getFrom(),
2452 email.getReplyTo(),
2453 email.getSubject()
2454 ),
2455 exception
2456 );
2457 return EmailDeliveryStatus.STATUS_NOT_DELIVERED;
2458 }
2459 }
2460
2461 private EmailDeliveryStatus sendEmailWithAttachment(TransactionalEmail email) throws MessagingException, IOException {
2462 Session session = Session.getInstance(new Properties());
2463 MimeMessage mimeMessage = new MimeMessage(session);
2464 mimeMessage.setSubject(configuration.getSubjectPrefix().isPresent()
2465 ? configuration.getSubjectPrefix().get() + " " + email.getSubject()
2466 : email.getSubject()
2467 );
2468
2469 if (!StringUtils.isEmpty(email.getFrom())) {
2470 mimeMessage.setFrom(new InternetAddress(email.getFrom()));
2471 } else if (configuration.getSourceEmail().isPresent()) {
2472 mimeMessage.setFrom(new InternetAddress(configuration.getSourceEmail().get()));
2473 }
2474
2475 if (!StringUtils.isEmpty(email.getReplyTo())) {
2476 mimeMessage.setReplyTo(new InternetAddress[] {new InternetAddress(email.getReplyTo())});
2477 }
2478
2479 for (String r : email.getRecipients()) {
2480 mimeMessage.addRecipients(TO, r);
2481 }
2482
2483 MimeMultipart mimeMultipart = new MimeMultipart();
2484
2485 BodyPart p = new MimeBodyPart();
2486 p.setContent(email.getHtmlBody(), "text/html");
2487 mimeMultipart.addBodyPart(p);
2488
2489 for (TransactionalEmailAttachment attachment : email.getAttachments()) {
2490 if (!MimeType.isMimeTypeSupported(attachment.getMimeType())) {
2491 throw new UnsupportedAttachmentTypeException("Attachment type not supported for " + attachment.getFilename());
2492 }
2493
2494 MimeBodyPart mimeBodyPart = new MimeBodyPart();
2495 mimeBodyPart.setFileName(attachment.getFilename());
2496 mimeBodyPart.setDescription(attachment.getDescription(), StandardCharsets.UTF_8.name());
2497 DataSource ds = new ByteArrayDataSource(new FileInputStream(new File(attachment.getFilepath())), attachment.getMimeType());
2498 mimeBodyPart.setDataHandler(new DataHandler(ds));
2499 mimeMultipart.addBodyPart(mimeBodyPart);
2500 }
2501
2502 mimeMessage.setContent(mimeMultipart);
2503
2504 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
2505 mimeMessage.writeTo(outputStream);
2506
2507 SendRawEmailRequest rawEmailRequest = SendRawEmailRequest.builder()
2508 .rawMessage(b -> b.data(SdkBytes.fromByteArray(outputStream.toByteArray())))
2509 .destinations(email.getRecipients())
2510 .source(Optional.ofNullable(email.getFrom()).orElseGet(() -> configuration.getSourceEmail().orElse(null))).build();
2511
2512 return handleSend(email, () -> client.sendRawEmail(rawEmailRequest));
2513 }
2514
2515 private EmailDeliveryStatus sendWithoutAttachments(TransactionalEmail email) {
2516 SendEmailRequest.Builder builder = SendEmailRequest.builder()
2517 .destination(b -> b.toAddresses(email.getRecipients()))
2518 .message(b -> {
2519 b.subject(c -> c.data(
2520 configuration.getSubjectPrefix().isPresent()
2521 ? configuration.getSubjectPrefix().get() + " " + email.getSubject()
2522 : email.getSubject()
2523 )
2524 );
2525 b.body(body -> body.html(c -> c.data(email.getHtmlBody())));
2526 })
2527 .source(Optional.ofNullable(email.getFrom()).orElseGet(() -> configuration.getSourceEmail().orElse(null)));
2528
2529 if (email.getReplyTo() != null && email.getReplyTo().length() > 0) {
2530 builder.replyToAddresses(email.getReplyTo());
2531 }
2532
2533 return handleSend(email, () -> client.sendEmail(builder.build()));
2534 }
2535
2536}"#;
2537
2538 let snippet = Java::find_relevant_scope(
2539 code,
2540 vec![RelevantName::FullyQualified {
2541 source: sym!(software.amazon.awssdk.services.ses.model),
2542 symbol: sym!(SendRawEmailRequest.builder),
2543 }],
2544 )
2545 .expect("Cannot find snippet in test code")
2546 .snippet;
2547
2548 assert_eq!(
2549 snippet,
2550 r#"private EmailDeliveryStatus sendEmailWithAttachment(TransactionalEmail email) throws MessagingException, IOException {
2551 Session session = Session.getInstance(new Properties());
2552 MimeMessage mimeMessage = new MimeMessage(session);
2553 mimeMessage.setSubject(configuration.getSubjectPrefix().isPresent()
2554 ? configuration.getSubjectPrefix().get() + " " + email.getSubject()
2555 : email.getSubject()
2556 );
2557
2558 if (!StringUtils.isEmpty(email.getFrom())) {
2559 mimeMessage.setFrom(new InternetAddress(email.getFrom()));
2560 } else if (configuration.getSourceEmail().isPresent()) {
2561 mimeMessage.setFrom(new InternetAddress(configuration.getSourceEmail().get()));
2562 }
2563
2564 if (!StringUtils.isEmpty(email.getReplyTo())) {
2565 mimeMessage.setReplyTo(new InternetAddress[] {new InternetAddress(email.getReplyTo())});
2566 }
2567
2568 for (String r : email.getRecipients()) {
2569 mimeMessage.addRecipients(TO, r);
2570 }
2571
2572 MimeMultipart mimeMultipart = new MimeMultipart();
2573
2574 BodyPart p = new MimeBodyPart();
2575 p.setContent(email.getHtmlBody(), "text/html");
2576 mimeMultipart.addBodyPart(p);
2577
2578 for (TransactionalEmailAttachment attachment : email.getAttachments()) {
2579 if (!MimeType.isMimeTypeSupported(attachment.getMimeType())) {
2580 throw new UnsupportedAttachmentTypeException("Attachment type not supported for " + attachment.getFilename());
2581 }
2582
2583 MimeBodyPart mimeBodyPart = new MimeBodyPart();
2584 mimeBodyPart.setFileName(attachment.getFilename());
2585 mimeBodyPart.setDescription(attachment.getDescription(), StandardCharsets.UTF_8.name());
2586 DataSource ds = new ByteArrayDataSource(new FileInputStream(new File(attachment.getFilepath())), attachment.getMimeType());
2587 mimeBodyPart.setDataHandler(new DataHandler(ds));
2588 mimeMultipart.addBodyPart(mimeBodyPart);
2589 }
2590
2591 mimeMessage.setContent(mimeMultipart);
2592
2593 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
2594 mimeMessage.writeTo(outputStream);
2595
2596 SendRawEmailRequest rawEmailRequest = SendRawEmailRequest.builder()
2597 .rawMessage(b -> b.data(SdkBytes.fromByteArray(outputStream.toByteArray())))
2598 .destinations(email.getRecipients())
2599 .source(Optional.ofNullable(email.getFrom()).orElseGet(() -> configuration.getSourceEmail().orElse(null))).build();
2600
2601 return handleSend(email, () -> client.sendRawEmail(rawEmailRequest));
2602 }"#
2603 );
2604 }
2605}