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
17 public final class ConstantUsageCheck extends AbstractCheck {
18
19
20
21
22 public ConstantUsageCheck() {
23
24 }
25
26 @Override
27 public int[] getDefaultTokens() {
28 return new int[]{
29 TokenTypes.VARIABLE_DEF,
30 };
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 if (ConstantUsageCheck.isField(ast)
46 && ConstantUsageCheck.isFinal(ast)) {
47 final DetailAST namenode = ast.findFirstToken(TokenTypes.IDENT);
48 if (!"serialVersionUID".equals(this.getText(namenode))) {
49 this.checkField(ast, namenode);
50 }
51 }
52 }
53
54 private void checkField(final DetailAST ast, final DetailAST namenode) {
55 final String name = namenode.getText();
56 final DetailAST objblock = ast.getParent();
57 int counter = 0;
58 final DetailAST classdef = objblock.getParent();
59 if (classdef != null) {
60 final DetailAST mods =
61 classdef.findFirstToken(TokenTypes.MODIFIERS);
62 if (mods != null) {
63 counter += this.parseAnnotation(mods, name);
64 }
65 }
66 DetailAST variable = objblock.getFirstChild();
67 while (null != variable) {
68 if (!variable.equals(ast)) {
69 counter += switch (variable.getType()) {
70 case TokenTypes.VARIABLE_DEF -> this.parseVarDef(variable, name);
71 case TokenTypes.CLASS_DEF -> this.parseDef(
72 variable, name, TokenTypes.OBJBLOCK
73 );
74 default -> this.parseDef(variable, name, TokenTypes.SLIST);
75 };
76 }
77 variable = variable.getNextSibling();
78 }
79 if (counter == 0 && ConstantUsageCheck.isPrivate(ast)) {
80 this.log(
81 namenode.getLineNo(),
82 String.format("Private constant \"%s\" is not used", name)
83 );
84 }
85 }
86
87 private int parseVarDef(final DetailAST variable, final String name) {
88 int counter = 0;
89 final DetailAST modifiers =
90 variable.findFirstToken(TokenTypes.MODIFIERS);
91 if (modifiers != null) {
92 counter += this.parseAnnotation(modifiers, name);
93 }
94 final DetailAST assign =
95 variable.findFirstToken(TokenTypes.ASSIGN);
96 if (assign != null) {
97 DetailAST expression =
98 assign.findFirstToken(TokenTypes.EXPR);
99 if (expression == null) {
100 expression = assign.findFirstToken(
101 TokenTypes.ARRAY_INIT
102 );
103 }
104 final String text = this.getText(expression);
105 if (text.contains(name)) {
106 ++counter;
107 }
108 }
109 return counter;
110 }
111
112 private String getText(final DetailAST node) {
113 final String ret;
114 if (node == null) {
115 ret = "";
116 } else if (0 == node.getChildCount()) {
117 ret = node.getText();
118 } else {
119 final StringBuilder result = new StringBuilder();
120 DetailAST child = node.getFirstChild();
121 while (null != child) {
122 final String text = this.getText(child);
123 result.append(text);
124 if (".".equals(node.getText())
125 && child.getNextSibling() != null) {
126 result.append(node.getText());
127 }
128 child = child.getNextSibling();
129 }
130 ret = result.toString();
131 }
132 return ret;
133 }
134
135 private static boolean isField(final DetailAST node) {
136 return TokenTypes.OBJBLOCK == node.getParent().getType();
137 }
138
139 private static boolean isFinal(final DetailAST node) {
140 return node.findFirstToken(TokenTypes.MODIFIERS)
141 .getChildCount(TokenTypes.FINAL) > 0;
142 }
143
144 private static boolean isPrivate(final DetailAST node) {
145 return node.findFirstToken(TokenTypes.MODIFIERS)
146 .getChildCount(TokenTypes.LITERAL_PRIVATE) > 0;
147 }
148
149 private int parseDef(final DetailAST definition, final String name,
150 final int type) {
151 int counter = 0;
152 final DetailAST modifiers =
153 definition.findFirstToken(TokenTypes.MODIFIERS);
154 if (modifiers != null) {
155 counter += this.parseAnnotation(modifiers, name);
156 }
157 final DetailAST opening = definition.findFirstToken(type);
158 if (null != opening) {
159 final DetailAST closing = opening.findFirstToken(TokenTypes.RCURLY);
160 final int start = opening.getLineNo();
161 final int end = closing.getLineNo() - 1;
162 final String[] lines = this.getLines();
163 for (int pos = start; pos < end; pos += 1) {
164 if (lines[pos].contains(name)) {
165 counter += 1;
166 }
167 }
168 }
169 return counter;
170 }
171
172 private int parseAnnotation(final DetailAST modifiers, final String name) {
173 int counter = 0;
174 final DetailAST variable =
175 modifiers.findFirstToken(TokenTypes.ANNOTATION);
176 if (variable != null) {
177 final String txt = this.getText(variable);
178 if (txt.contains(name)) {
179 ++counter;
180 }
181 }
182 return counter;
183 }
184 }