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