1
2
3
4
5 package com.qulice.checkstyle;
6
7 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
8 import com.puppycrawl.tools.checkstyle.api.DetailAST;
9 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
10
11
12
13
14
15
16 public final class ConstantUsageCheck extends AbstractCheck {
17
18 @Override
19 public int[] getDefaultTokens() {
20 return new int[]{
21 TokenTypes.VARIABLE_DEF,
22 };
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 if (ConstantUsageCheck.isField(ast)
38 && ConstantUsageCheck.isFinal(ast)) {
39 final DetailAST namenode = ast.findFirstToken(TokenTypes.IDENT);
40 if (!"serialVersionUID".equals(this.getText(namenode))) {
41 this.checkField(ast, namenode);
42 }
43 }
44 }
45
46
47
48
49
50
51
52 private void checkField(final DetailAST ast, final DetailAST namenode) {
53 final String name = namenode.getText();
54 final DetailAST objblock = ast.getParent();
55 int counter = 0;
56 final DetailAST classdef = objblock.getParent();
57 if (classdef != null) {
58 final DetailAST mods =
59 classdef.findFirstToken(TokenTypes.MODIFIERS);
60 if (mods != null) {
61 counter += this.parseAnnotation(mods, name);
62 }
63 }
64 DetailAST variable = objblock.getFirstChild();
65 while (null != variable) {
66 if (!variable.equals(ast)) {
67 switch (variable.getType()) {
68 case TokenTypes.VARIABLE_DEF:
69 counter += this.parseVarDef(variable, name);
70 break;
71 case TokenTypes.CLASS_DEF:
72 counter += this.parseDef(
73 variable, name, TokenTypes.OBJBLOCK
74 );
75 break;
76 default:
77 counter += this.parseDef(variable, name, TokenTypes.SLIST);
78 break;
79 }
80 }
81 variable = variable.getNextSibling();
82 }
83 if (counter == 0 && ConstantUsageCheck.isPrivate(ast)) {
84 this.log(
85 namenode.getLineNo(),
86 String.format("Private constant \"%s\" is not used", name)
87 );
88 }
89 }
90
91
92
93
94
95
96
97
98 private int parseVarDef(final DetailAST variable, final String name) {
99 int counter = 0;
100 final DetailAST modifiers =
101 variable.findFirstToken(TokenTypes.MODIFIERS);
102 if (modifiers != null) {
103 counter += this.parseAnnotation(modifiers, name);
104 }
105 final DetailAST assign =
106 variable.findFirstToken(TokenTypes.ASSIGN);
107 if (assign != null) {
108 DetailAST expression =
109 assign.findFirstToken(TokenTypes.EXPR);
110 if (expression == null) {
111 expression = assign.findFirstToken(
112 TokenTypes.ARRAY_INIT
113 );
114 }
115 final String text = this.getText(expression);
116 if (text.contains(name)) {
117 ++counter;
118 }
119 }
120 return counter;
121 }
122
123
124
125
126
127
128
129 private String getText(final DetailAST node) {
130 final String ret;
131 if (node == null) {
132 ret = "";
133 } else if (0 == node.getChildCount()) {
134 ret = node.getText();
135 } else {
136 final StringBuilder result = new StringBuilder();
137 DetailAST child = node.getFirstChild();
138 while (null != child) {
139 final String text = this.getText(child);
140 result.append(text);
141 if (".".equals(node.getText())
142 && child.getNextSibling() != null) {
143 result.append(node.getText());
144 }
145 child = child.getNextSibling();
146 }
147 ret = result.toString();
148 }
149 return ret;
150 }
151
152
153
154
155
156
157
158
159 private static boolean isField(final DetailAST node) {
160 return TokenTypes.OBJBLOCK == node.getParent().getType();
161 }
162
163
164
165
166
167
168
169 private static boolean isFinal(final DetailAST node) {
170 return node.findFirstToken(TokenTypes.MODIFIERS)
171 .getChildCount(TokenTypes.FINAL) > 0;
172 }
173
174
175
176
177
178
179
180
181 private static boolean isPrivate(final DetailAST node) {
182 return node.findFirstToken(TokenTypes.MODIFIERS)
183 .getChildCount(TokenTypes.LITERAL_PRIVATE) > 0;
184 }
185
186
187
188
189
190
191
192
193
194 private int parseDef(final DetailAST definition, final String name,
195 final int type) {
196 int counter = 0;
197 final DetailAST modifiers =
198 definition.findFirstToken(TokenTypes.MODIFIERS);
199 if (modifiers != null) {
200 counter += this.parseAnnotation(modifiers, name);
201 }
202 final DetailAST opening = definition.findFirstToken(type);
203 if (null != opening) {
204 final DetailAST closing = opening.findFirstToken(TokenTypes.RCURLY);
205 final int start = opening.getLineNo();
206 final int end = closing.getLineNo() - 1;
207 final String[] lines = this.getLines();
208 for (int pos = start; pos < end; pos += 1) {
209 if (lines[pos].contains(name)) {
210 counter += 1;
211 }
212 }
213 }
214 return counter;
215 }
216
217
218
219
220
221
222
223
224 private int parseAnnotation(final DetailAST modifiers, final String name) {
225 int counter = 0;
226 final DetailAST variable =
227 modifiers.findFirstToken(TokenTypes.ANNOTATION);
228 if (variable != null) {
229 final String txt = this.getText(variable);
230 if (txt.contains(name)) {
231 ++counter;
232 }
233 }
234 return counter;
235 }
236 }