1
2
3
4
5
6 package com.qulice.checkstyle;
7
8 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
9 import com.puppycrawl.tools.checkstyle.api.DetailAST;
10 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
11 import java.util.LinkedList;
12 import java.util.List;
13
14
15
16
17
18
19 public final class ProhibitUnusedPrivateConstructorCheck extends AbstractCheck {
20
21 @Override
22 public int[] getDefaultTokens() {
23 return new int[] {TokenTypes.CLASS_DEF};
24 }
25
26 @Override
27 public int[] getAcceptableTokens() {
28 return this.getDefaultTokens();
29 }
30
31 @Override
32 public int[] getRequiredTokens() {
33 return this.getDefaultTokens();
34 }
35
36 @Override
37 public void visitToken(final DetailAST ast) {
38 final DetailAST objblock = ast.findFirstToken(TokenTypes.OBJBLOCK);
39 if (objblock != null) {
40 this.checkConstructors(objblock);
41 }
42 }
43
44
45
46
47
48
49
50 private static List<DetailAST> collectPrivateConstructors(final DetailAST objblock) {
51 final List<DetailAST> prvctors = new LinkedList<>();
52 final DetailAST firstchld = objblock.getFirstChild();
53 for (DetailAST child = firstchld; child != null; child = child.getNextSibling()) {
54 if (child.getType() == TokenTypes.CTOR_DEF && isPrivate(child)) {
55 prvctors.add(child);
56 }
57 }
58 return prvctors;
59 }
60
61
62
63
64
65
66
67
68 private static boolean isPrivateConstructorUsed(
69 final DetailAST privatector, final DetailAST objblock) {
70 return
71 isPrivateCtorUsedInOtherCtors(privatector, objblock)
72 ||
73 isPrivateCtorUsedInMethods(privatector, objblock);
74 }
75
76
77
78
79
80
81
82
83 private static boolean isPrivateCtorUsedInOtherCtors(
84 final DetailAST privatector, final DetailAST objblock) {
85 final List<DetailAST> allctors = collectAllConstructors(objblock);
86 return allctors.stream()
87 .anyMatch(
88 otherCtor -> otherCtor != privatector
89 &&
90 isCallingConstructor(otherCtor, privatector)
91 );
92 }
93
94
95
96
97
98
99
100
101 private static boolean isPrivateCtorUsedInMethods(
102 final DetailAST privatector, final DetailAST objblock) {
103 boolean result = false;
104 final DetailAST firstchld = objblock.getFirstChild();
105 for (DetailAST child = firstchld; child != null; child = child.getNextSibling()) {
106 if (child.getType() == TokenTypes.METHOD_DEF
107 &&
108 isCallingConstructor(child, privatector)) {
109 result = true;
110 break;
111 }
112 }
113 return result;
114 }
115
116
117
118
119
120
121
122 private static List<DetailAST> collectAllConstructors(final DetailAST objblock) {
123 final List<DetailAST> allctors = new LinkedList<>();
124 final DetailAST firstchld = objblock.getFirstChild();
125 for (DetailAST child = firstchld; child != null; child = child.getNextSibling()) {
126 if (child.getType() == TokenTypes.CTOR_DEF) {
127 allctors.add(child);
128 }
129 }
130 return allctors;
131 }
132
133
134
135
136
137
138
139
140
141 private static boolean isPrivate(final DetailAST node) {
142 final DetailAST modifiers = node.findFirstToken(TokenTypes.MODIFIERS);
143 return modifiers.getChildCount(TokenTypes.LITERAL_PRIVATE) > 0;
144 }
145
146 private static boolean isCallingConstructor(
147 final DetailAST methodorctor, final DetailAST targetctor) {
148 boolean result = false;
149 final DetailAST body = methodorctor.findFirstToken(TokenTypes.SLIST);
150 if (body != null) {
151 DetailAST stmt = body.getFirstChild();
152 while (stmt != null && !result) {
153 result = isMatchingConstructorCall(stmt, targetctor);
154 stmt = stmt.getNextSibling();
155 }
156 }
157 return result;
158 }
159
160 private static boolean isMatchingConstructorCall(
161 final DetailAST stmt, final DetailAST targetctor) {
162 return
163 stmt.getType() == TokenTypes.CTOR_CALL
164 &&
165 matchesConstructorSignature(stmt, targetctor);
166 }
167
168 private static boolean matchesConstructorSignature(
169 final DetailAST callexpr, final DetailAST ctor) {
170 final DetailAST callparams = callexpr.findFirstToken(TokenTypes.ELIST);
171 final DetailAST ctorparams = ctor.findFirstToken(TokenTypes.PARAMETERS);
172 return parametersCountMatch(callparams, ctorparams);
173 }
174
175 private static boolean parametersCountMatch(
176 final DetailAST callparams, final DetailAST ctorparams) {
177 final int ncallparams = callparams.getChildCount(TokenTypes.EXPR);
178 final int nctorparams = ctorparams.getChildCount(TokenTypes.PARAMETER_DEF);
179 return ncallparams == nctorparams;
180 }
181
182
183
184
185
186
187
188 private void checkConstructors(final DetailAST objblock) {
189 final List<DetailAST> prvctors = collectPrivateConstructors(objblock);
190 for (final DetailAST ctor : prvctors) {
191 if (!isPrivateConstructorUsed(ctor, objblock)) {
192 this.log(ctor.getLineNo(), "Unused private constructor.");
193 }
194 }
195 }
196
197 }