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 private static boolean isPrivateCtorUsedInMethods(
101 final DetailAST privatector, final DetailAST objblock) {
102 boolean result = false;
103 final DetailAST firstchld = objblock.getFirstChild();
104 for (DetailAST child = firstchld; child != null; child = child.getNextSibling()) {
105 if (child.getType() == TokenTypes.METHOD_DEF
106 &&
107 isCallingConstructor(child, privatector)) {
108 result = true;
109 break;
110 }
111 }
112 return result;
113 }
114
115
116
117
118
119
120
121 private static List<DetailAST> collectAllConstructors(final DetailAST objblock) {
122 final List<DetailAST> allctors = new LinkedList<>();
123 final DetailAST firstchld = objblock.getFirstChild();
124 for (DetailAST child = firstchld; child != null; child = child.getNextSibling()) {
125 if (child.getType() == TokenTypes.CTOR_DEF) {
126 allctors.add(child);
127 }
128 }
129 return allctors;
130 }
131
132
133
134
135
136
137
138
139
140 private static boolean isPrivate(final DetailAST node) {
141 final DetailAST modifiers = node.findFirstToken(TokenTypes.MODIFIERS);
142 return modifiers.getChildCount(TokenTypes.LITERAL_PRIVATE) > 0;
143 }
144
145 private static boolean isCallingConstructor(
146 final DetailAST methodorctor, final DetailAST targetctor) {
147 boolean result = false;
148 final DetailAST body = methodorctor.findFirstToken(TokenTypes.SLIST);
149 if (body != null) {
150 DetailAST stmt = body.getFirstChild();
151 while (stmt != null && !result) {
152 result = isMatchingConstructorCall(stmt, targetctor);
153 stmt = stmt.getNextSibling();
154 }
155 }
156 return result;
157 }
158
159 private static boolean isMatchingConstructorCall(
160 final DetailAST stmt, final DetailAST targetctor) {
161 return
162 stmt.getType() == TokenTypes.CTOR_CALL
163 &&
164 matchesConstructorSignature(stmt, targetctor);
165 }
166
167 private static boolean matchesConstructorSignature(
168 final DetailAST callexpr, final DetailAST ctor) {
169 final DetailAST callparams = callexpr.findFirstToken(TokenTypes.ELIST);
170 final DetailAST ctorparams = ctor.findFirstToken(TokenTypes.PARAMETERS);
171 return parametersCountMatch(callparams, ctorparams);
172 }
173
174 private static boolean parametersCountMatch(
175 final DetailAST callparams, final DetailAST ctorparams) {
176 final int ncallparams = callparams.getChildCount(TokenTypes.EXPR);
177 final int nctorparams = ctorparams.getChildCount(TokenTypes.PARAMETER_DEF);
178 return ncallparams == nctorparams;
179 }
180
181
182
183
184
185
186
187 private void checkConstructors(final DetailAST objblock) {
188 final List<DetailAST> prvctors = collectPrivateConstructors(objblock);
189 for (final DetailAST ctor : prvctors) {
190 if (!isPrivateConstructorUsed(ctor, objblock)) {
191 this.log(ctor.getLineNo(), "Unused private constructor.");
192 }
193 }
194 }
195
196 }