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