1
2
3
4
5 package com.qulice.pmd.rules;
6
7 import java.util.Collection;
8 import java.util.List;
9 import java.util.Set;
10 import net.sourceforge.pmd.lang.java.ast.ASTAnnotation;
11 import net.sourceforge.pmd.lang.java.ast.ASTClassDeclaration;
12 import net.sourceforge.pmd.lang.java.ast.ASTClassType;
13 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
14 import net.sourceforge.pmd.lang.java.ast.ASTTypeDeclaration;
15 import net.sourceforge.pmd.lang.java.ast.ModifierOwner;
16 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 public final class TooManyMethodsRule extends AbstractJavaRulechainRule {
42
43
44
45
46 private static final Collection<NamedSupertype> LIBRARIES = List.of(
47 new NamedSupertype("com.sun.jna.Library"),
48 new NamedSupertype("com.sun.jna.win32.StdCallLibrary")
49 );
50
51
52
53
54 private static final Set<String> SUFFIXES = Set.of(
55 "Test",
56 "Tests",
57 "IT",
58 "TestCase",
59 "ITCase"
60 );
61
62
63
64
65
66
67
68 private static final Set<String> ANNOTATIONS = Set.of(
69 "TestFactory",
70 "TestTemplate",
71 "Theory",
72 "Nested"
73 );
74
75
76
77
78 public TooManyMethodsRule() {
79 super(ASTClassDeclaration.class);
80 }
81
82 @Override
83 public Object visit(final ASTClassDeclaration type, final Object data) {
84 if (!TooManyMethodsRule.tested(type)
85 && !TooManyMethodsRule.binding(type)
86 && TooManyMethodsRule.exposed(type) > 10) {
87 this.asCtx(data).addViolation(type);
88 }
89 return data;
90 }
91
92 private static int exposed(final ASTClassDeclaration type) {
93 return type.getDeclarations(ASTMethodDeclaration.class)
94 .filter(TooManyMethodsRule::visible)
95 .count();
96 }
97
98 private static boolean visible(final ASTMethodDeclaration method) {
99 return method.getVisibility()
100 .isAtLeast(ModifierOwner.Visibility.V_PROTECTED)
101 && !method.isAnnotationPresent(Override.class);
102 }
103
104 private static boolean binding(final ASTClassDeclaration type) {
105 return type.getSuperInterfaceTypeNodes().any(TooManyMethodsRule::jna);
106 }
107
108 private static boolean jna(final ASTClassType parent) {
109 return TooManyMethodsRule.LIBRARIES.stream().anyMatch(
110 known -> known.matches(parent)
111 );
112 }
113
114 private static boolean tested(final ASTClassDeclaration type) {
115 return type.ancestorsOrSelf()
116 .filterIs(ASTTypeDeclaration.class)
117 .any(TooManyMethodsRule::marked);
118 }
119
120 private static boolean marked(final ASTTypeDeclaration type) {
121 return TooManyMethodsRule.named(type.getSimpleName())
122 || type.descendants(ASTAnnotation.class).any(
123 TooManyMethodsRule::testing
124 );
125 }
126
127 private static boolean named(final String simple) {
128 return TooManyMethodsRule.SUFFIXES.stream().anyMatch(simple::endsWith);
129 }
130
131 private static boolean testing(final ASTAnnotation annotation) {
132 return annotation.getSimpleName().endsWith("Test")
133 || TooManyMethodsRule.ANNOTATIONS.contains(
134 annotation.getSimpleName()
135 );
136 }
137 }