1use serde::ser::{SerializeStruct, Serializer};
2use serde::Serialize;
3use std::fmt;
4
5use crate::checker::Checker;
6use crate::langs::*;
7use crate::node::Node;
8use crate::*;
9
10#[derive(Clone, Debug, Default)]
15pub struct Stats {
16 class_npa: usize,
17 interface_npa: usize,
18 class_na: usize,
19 interface_na: usize,
20 class_npa_sum: usize,
21 interface_npa_sum: usize,
22 class_na_sum: usize,
23 interface_na_sum: usize,
24 is_class_space: bool,
25}
26
27impl Serialize for Stats {
28 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
29 where
30 S: Serializer,
31 {
32 let mut st = serializer.serialize_struct("npa", 9)?;
33 st.serialize_field("classes", &self.class_npa_sum())?;
34 st.serialize_field("interfaces", &self.interface_npa_sum())?;
35 st.serialize_field("class_attributes", &self.class_na_sum())?;
36 st.serialize_field("interface_attributes", &self.interface_na_sum())?;
37 st.serialize_field("classes_average", &self.class_cda())?;
38 st.serialize_field("interfaces_average", &self.interface_cda())?;
39 st.serialize_field("total", &self.total_npa())?;
40 st.serialize_field("total_attributes", &self.total_na())?;
41 st.serialize_field("average", &self.total_cda())?;
42 st.end()
43 }
44}
45
46impl fmt::Display for Stats {
47 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
48 write!(
49 f,
50 "classes: {}, interfaces: {}, class_attributes: {}, interface_attributes: {}, classes_average: {}, interfaces_average: {}, total: {}, total_attributes: {}, average: {}",
51 self.class_npa_sum(),
52 self.interface_npa_sum(),
53 self.class_na_sum(),
54 self.interface_na_sum(),
55 self.class_cda(),
56 self.interface_cda(),
57 self.total_npa(),
58 self.total_na(),
59 self.total_cda()
60 )
61 }
62}
63
64impl Stats {
65 pub fn merge(&mut self, other: &Stats) {
67 self.class_npa_sum += other.class_npa_sum;
68 self.interface_npa_sum += other.interface_npa_sum;
69 self.class_na_sum += other.class_na_sum;
70 self.interface_na_sum += other.interface_na_sum;
71 }
72
73 #[inline(always)]
75 pub fn class_npa(&self) -> f64 {
76 self.class_npa as f64
77 }
78
79 #[inline(always)]
81 pub fn interface_npa(&self) -> f64 {
82 self.interface_npa as f64
83 }
84
85 #[inline(always)]
87 pub fn class_na(&self) -> f64 {
88 self.class_na as f64
89 }
90
91 #[inline(always)]
93 pub fn interface_na(&self) -> f64 {
94 self.interface_na as f64
95 }
96
97 #[inline(always)]
99 pub fn class_npa_sum(&self) -> f64 {
100 self.class_npa_sum as f64
101 }
102
103 #[inline(always)]
105 pub fn interface_npa_sum(&self) -> f64 {
106 self.interface_npa_sum as f64
107 }
108
109 #[inline(always)]
111 pub fn class_na_sum(&self) -> f64 {
112 self.class_na_sum as f64
113 }
114
115 #[inline(always)]
117 pub fn interface_na_sum(&self) -> f64 {
118 self.interface_na_sum as f64
119 }
120
121 #[inline(always)]
131 pub fn class_cda(&self) -> f64 {
132 self.class_npa_sum() / self.class_na_sum as f64
133 }
134
135 #[inline(always)]
145 pub fn interface_cda(&self) -> f64 {
146 if self.interface_npa_sum == self.interface_na_sum && self.interface_npa_sum != 0 {
149 1.0
150 } else {
151 self.interface_npa_sum() / self.interface_na_sum()
152 }
153 }
154
155 #[inline(always)]
165 pub fn total_cda(&self) -> f64 {
166 self.total_npa() / self.total_na()
167 }
168
169 #[inline(always)]
171 pub fn total_npa(&self) -> f64 {
172 self.class_npa_sum() + self.interface_npa_sum()
173 }
174
175 #[inline(always)]
177 pub fn total_na(&self) -> f64 {
178 self.class_na_sum() + self.interface_na_sum()
179 }
180
181 #[inline(always)]
184 pub(crate) fn compute_sum(&mut self) {
185 self.class_npa_sum += self.class_npa;
186 self.interface_npa_sum += self.interface_npa;
187 self.class_na_sum += self.class_na;
188 self.interface_na_sum += self.interface_na;
189 }
190
191 #[inline(always)]
193 pub(crate) fn is_disabled(&self) -> bool {
194 !self.is_class_space
195 }
196}
197
198#[doc(hidden)]
199pub trait Npa
200where
201 Self: Checker,
202{
203 fn compute(_node: &Node, _stats: &mut Stats) {}
204}
205
206impl Npa for PythonCode {}
207impl Npa for MozjsCode {}
208impl Npa for JavascriptCode {}
209impl Npa for TypescriptCode {}
210impl Npa for TsxCode {}
211impl Npa for RustCode {}
212impl Npa for CppCode {}
213impl Npa for PreprocCode {}
214impl Npa for CcommentCode {}
215
216impl Npa for JavaCode {
217 fn compute(node: &Node, stats: &mut Stats) {
218 use Java::*;
219
220 if Self::is_func_space(node) && stats.is_disabled() {
222 stats.is_class_space = true;
223 }
224
225 match node.object().kind_id().into() {
226 ClassBody => {
227 stats.class_na += node
228 .children()
229 .filter(|node| matches!(node.object().kind_id().into(), FieldDeclaration))
230 .map(|declaration| {
231 let attributes = declaration
232 .children()
233 .filter(|n| matches!(n.object().kind_id().into(), VariableDeclarator))
234 .count();
235 if declaration.object().child(0).map_or(false, |modifiers| {
239 matches!(modifiers.kind_id().into(), Modifiers)
241 && Node::new(modifiers)
242 .first_child(|id| id == Public)
243 .is_some()
244 }) {
245 stats.class_npa += attributes;
246 }
247 attributes
248 })
249 .sum::<usize>();
250 }
251 InterfaceBody => {
254 stats.interface_na += node
258 .children()
259 .filter(|node| matches!(node.object().kind_id().into(), ConstantDeclaration))
260 .flat_map(|node| node.children())
261 .filter(|node| matches!(node.object().kind_id().into(), VariableDeclarator))
262 .count();
263 stats.interface_npa = stats.interface_na;
264 }
265 _ => {}
266 }
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use std::path::PathBuf;
273
274 use super::*;
275
276 #[test]
277 fn java_single_attributes() {
278 check_metrics!(
279 "class X {
280 public byte a; // +1
281 public short b; // +1
282 public int c; // +1
283 public long d; // +1
284 public float e; // +1
285 public double f; // +1
286 public boolean g; // +1
287 public char h; // +1
288 byte i;
289 short j;
290 int k;
291 long l;
292 float m;
293 double n;
294 boolean o;
295 char p;
296 }",
297 "foo.java",
298 JavaParser,
299 npa,
300 [
301 (class_npa_sum, 8, usize),
302 (interface_npa_sum, 0, usize),
303 (class_na_sum, 16, usize),
304 (interface_na_sum, 0, usize),
305 (total_npa, 8, usize),
306 (total_na, 16, usize)
307 ],
308 [
309 (class_cda, 0.5),
310 (interface_cda, f64::NAN),
311 (total_cda, 0.5)
312 ]
313 );
314 }
315
316 #[test]
317 fn java_multiple_attributes() {
318 check_metrics!(
319 "class X {
320 public byte a1; // +1
321 public short b1, b2; // +2
322 public int c1, c2, c3; // +3
323 public long d1, d2, d3, d4; // +4
324 public float e1, e2, e3, e4; // +4
325 public double f1, f2, f3; // +3
326 public boolean g1, g2; // +2
327 public char h1; // +1
328 byte i1, i2, i3, i4;
329 short j1, j2, j3;
330 int k1, k2;
331 long l1;
332 float m1;
333 double n1, n2;
334 boolean o1, o2, o3;
335 char p1, p2, p3, p4;
336 }",
337 "foo.java",
338 JavaParser,
339 npa,
340 [
341 (class_npa_sum, 20, usize),
342 (interface_npa_sum, 0, usize),
343 (class_na_sum, 40, usize),
344 (interface_na_sum, 0, usize),
345 (total_npa, 20, usize),
346 (total_na, 40, usize)
347 ],
348 [
349 (class_cda, 0.5),
350 (interface_cda, f64::NAN),
351 (total_cda, 0.5)
352 ]
353 );
354 }
355
356 #[test]
357 fn java_initialized_attributes() {
358 check_metrics!(
359 "class X {
360 public byte a1 = 1; // +1
361 public short b1 = 2, b2; // +2
362 public int c1, c2 = 3, c3; // +3
363 public long d1 = 4, d2, d3, d4 = 5; // +4
364 public float e1, e2 = 6.0f, e3 = 7.0f, e4; // +4
365 public double f1 = 8.0, f2 = 9.0, f3 = 10.0; // +3
366 public boolean g1 = true, g2; // +2
367 public char h1 = 'a'; // +1
368 byte i1 = 1, i2 = 2, i3 = 3, i4 = 4;
369 short j1 = 5, j2, j3 = 6;
370 int k1, k2 = 7;
371 long l1 = 8;
372 float m1 = 9.0f;
373 double n1, n2 = 10.0;
374 boolean o1, o2 = false, o3;
375 char p1 = 'a', p2 = 'b', p3 = 'c', p4 = 'd';
376 }",
377 "foo.java",
378 JavaParser,
379 npa,
380 [
381 (class_npa_sum, 20, usize),
382 (interface_npa_sum, 0, usize),
383 (class_na_sum, 40, usize),
384 (interface_na_sum, 0, usize),
385 (total_npa, 20, usize),
386 (total_na, 40, usize)
387 ],
388 [
389 (class_cda, 0.5),
390 (interface_cda, f64::NAN),
391 (total_cda, 0.5)
392 ]
393 );
394 }
395
396 #[test]
397 fn java_array_attributes() {
398 check_metrics!(
399 "class X {
400 public byte[] a1, a2, a3, a4; // +4
401 public short b1[], b2[], b3[]; // +3
402 public int[] c1 = { 1 }, c2; // +2
403 public long d1[] = { 1 }; // +1
404 public float[] e1 = { 1.0f, 2.0f, 3.0f }; // +1
405 public double f1[] = { 1.0, 2.0, 3.0 }, f2[]; // +2
406 public boolean[] g1 = new boolean[5], g2, g3; // +3
407 public char[] h1 = new char[5], h2[], h3[], h4[]; // +4
408 byte[] i1;
409 short j1[], j2[];
410 int[] k1, k2, k3 = { 1 };
411 long l1[], l2[] = { 1 }, l3[] = { 2 }, l4[];
412 float[] m1, m2, m3, m4 = { 1.0f, 2.0f, 3.0f };
413 double n1[], n2[] = { 1.0, 2.0, 3.0 }, n3[];
414 boolean[] o1, o2 = new boolean[5];
415 char[] p1 = new char[5];
416 }",
417 "foo.java",
418 JavaParser,
419 npa,
420 [
421 (class_npa_sum, 20, usize),
422 (interface_npa_sum, 0, usize),
423 (class_na_sum, 40, usize),
424 (interface_na_sum, 0, usize),
425 (total_npa, 20, usize),
426 (total_na, 40, usize)
427 ],
428 [
429 (class_cda, 0.5),
430 (interface_cda, f64::NAN),
431 (total_cda, 0.5)
432 ]
433 );
434 }
435
436 #[test]
437 fn java_object_attributes() {
438 check_metrics!(
439 "class X {
440 public Integer[] a1 = { 1 }; // +1
441 public Integer b1, b2; // +2
442 public String[] c1 = { \"Hello\" }, c2, c3 = { \"World!\" }; // +3
443 public String d1[][] = { { \"Hello\" }, { \"World!\" } }; // +1
444 public Y[] e1, e2[]; // +2
445 public Y f1[], f2[][], f3[][][]; // +3
446 Integer[] g1 = { new Integer(1) };
447 Integer h1 = new Integer(1), h2 = new Integer(2);
448 String[] i1, i2 = { \"Hello World!\" }, i3;
449 String j1 = \"Hello World!\";
450 Y[] k1[], k2;
451 Y l1[][], l2[], l3 = new Y();
452 }",
453 "foo.java",
454 JavaParser,
455 npa,
456 [
457 (class_npa_sum, 12, usize),
458 (interface_npa_sum, 0, usize),
459 (class_na_sum, 24, usize),
460 (interface_na_sum, 0, usize),
461 (total_npa, 12, usize),
462 (total_na, 24, usize)
463 ],
464 [
465 (class_cda, 0.5),
466 (interface_cda, f64::NAN),
467 (total_cda, 0.5)
468 ]
469 );
470 }
471
472 #[test]
473 fn java_generic_attributes() {
474 check_metrics!(
475 "class X<T, S extends T> {
476 public T a1; // +1
477 public Entry<T, S> b1, b2[]; // +2
478 public ArrayList<T> c1, c2, c3; // +3
479 public HashMap<Long, Double> d1, d2; // +2
480 public TreeSet<String> e1; // +1
481 S f1;
482 Entry<S, T> g1[], g2;
483 ArrayList<S> h1, h2, h3;
484 HashMap<Long, Float> i1, i2;
485 TreeSet<Entry<S, T>> j1;
486 }",
487 "foo.java",
488 JavaParser,
489 npa,
490 [
491 (class_npa_sum, 9, usize),
492 (interface_npa_sum, 0, usize),
493 (class_na_sum, 18, usize),
494 (interface_na_sum, 0, usize),
495 (total_npa, 9, usize),
496 (total_na, 18, usize)
497 ],
498 [
499 (class_cda, 0.5),
500 (interface_cda, f64::NAN),
501 (total_cda, 0.5)
502 ]
503 );
504 }
505
506 #[test]
507 fn java_attribute_modifiers() {
508 check_metrics!(
509 "class X {
510 public transient volatile static int a; // +1
511 transient public volatile static int b; // +1
512 transient volatile public static int c; // +1
513 transient volatile static public int d; // +1
514 public transient static final int e = 1; // +1
515 transient public static final int f = 2; // +1
516 transient static public final int g = 3; // +1
517 transient static final public int h = 4; // +1
518 protected transient volatile static int i;
519 transient volatile static protected int j;
520 private transient volatile static int k;
521 transient volatile static private int l;
522 transient volatile static int m;
523 transient static final int n = 5;
524 static public final int o = 6; // +1
525 final public int p = 7; // +1
526 }",
527 "foo.java",
528 JavaParser,
529 npa,
530 [
531 (class_npa_sum, 10, usize),
532 (interface_npa_sum, 0, usize),
533 (class_na_sum, 16, usize),
534 (interface_na_sum, 0, usize),
535 (total_npa, 10, usize),
536 (total_na, 16, usize)
537 ],
538 [
539 (class_cda, 0.625),
540 (interface_cda, f64::NAN),
541 (total_cda, 0.625)
542 ]
543 );
544 }
545
546 #[test]
547 fn java_classes() {
548 check_metrics!(
549 "class X {
550 public int a; // +1
551 public boolean b; // +1
552 private char c;
553 }
554 class Y {
555 private double d;
556 private long e;
557 public float f; // +1
558 }",
559 "foo.java",
560 JavaParser,
561 npa,
562 [
563 (class_npa_sum, 3, usize),
564 (interface_npa_sum, 0, usize),
565 (class_na_sum, 6, usize),
566 (interface_na_sum, 0, usize),
567 (total_npa, 3, usize),
568 (total_na, 6, usize)
569 ],
570 [
571 (class_cda, 0.5),
572 (interface_cda, f64::NAN),
573 (total_cda, 0.5)
574 ]
575 );
576 }
577
578 #[test]
579 fn java_nested_inner_classes() {
580 check_metrics!(
581 "class X {
582 public int a; // +1
583 class Y {
584 public boolean b; // +1
585 class Z {
586 public char c; // +1
587 }
588 }
589 }",
590 "foo.java",
591 JavaParser,
592 npa,
593 [
594 (class_npa_sum, 3, usize),
595 (interface_npa_sum, 0, usize),
596 (class_na_sum, 3, usize),
597 (interface_na_sum, 0, usize),
598 (total_npa, 3, usize),
599 (total_na, 3, usize)
600 ],
601 [
602 (class_cda, 1.0),
603 (interface_cda, f64::NAN),
604 (total_cda, 1.0)
605 ]
606 );
607 }
608
609 #[test]
610 fn java_local_inner_classes() {
611 check_metrics!(
612 "class X {
613 public int a; // +1
614 void x() {
615 class Y {
616 public boolean b; // +1
617 void y() {
618 class Z {
619 public char c; // +1
620 void z() {}
621 }
622 }
623 }
624 }
625 }",
626 "foo.java",
627 JavaParser,
628 npa,
629 [
630 (class_npa_sum, 3, usize),
631 (interface_npa_sum, 0, usize),
632 (class_na_sum, 3, usize),
633 (interface_na_sum, 0, usize),
634 (total_npa, 3, usize),
635 (total_na, 3, usize)
636 ],
637 [
638 (class_cda, 1.0),
639 (interface_cda, f64::NAN),
640 (total_cda, 1.0)
641 ]
642 );
643 }
644
645 #[test]
646 fn java_anonymous_inner_classes() {
647 check_metrics!(
648 "abstract class X {
649 public int a; // +1
650 }
651 abstract class Y {
652 boolean b;
653 }
654 class Z {
655 public char c; // +1
656 public void z(){
657 X x1 = new X() {
658 public double d; // +1
659 };
660 Y y1 = new Y() {
661 long e;
662 };
663 }
664 }",
665 "foo.java",
666 JavaParser,
667 npa,
668 [
669 (class_npa_sum, 3, usize),
670 (interface_npa_sum, 0, usize),
671 (class_na_sum, 5, usize),
672 (interface_na_sum, 0, usize),
673 (total_npa, 3, usize),
674 (total_na, 5, usize)
675 ],
676 [
677 (class_cda, 0.6),
678 (interface_cda, f64::NAN),
679 (total_cda, 0.6)
680 ]
681 );
682 }
683
684 #[test]
685 fn java_interface() {
686 check_metrics!(
687 "interface X {
688 public int a = 0; // +1
689 static boolean b = false; // +1
690 final char c = ' '; // +1
691 }",
692 "foo.java",
693 JavaParser,
694 npa,
695 [
696 (class_npa_sum, 0, usize),
697 (interface_npa_sum, 3, usize),
698 (class_na_sum, 0, usize),
699 (interface_na_sum, 3, usize),
700 (total_npa, 3, usize),
701 (total_na, 3, usize)
702 ],
703 [
704 (class_cda, f64::NAN),
705 (interface_cda, 1.0),
706 (total_cda, 1.0)
707 ]
708 );
709 }
710}