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