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