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 import java.util.Set;
11 import java.util.regex.Pattern;
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 public final class SingleUseConstantCheck extends AbstractCheck {
48
49
50
51
52 private static final Set<Integer> LITERALS = Set.of(
53 TokenTypes.EXPR,
54 TokenTypes.NUM_INT,
55 TokenTypes.NUM_LONG,
56 TokenTypes.NUM_FLOAT,
57 TokenTypes.NUM_DOUBLE,
58 TokenTypes.STRING_LITERAL,
59 TokenTypes.CHAR_LITERAL,
60 TokenTypes.TEXT_BLOCK_LITERAL_BEGIN,
61 TokenTypes.TEXT_BLOCK_CONTENT,
62 TokenTypes.TEXT_BLOCK_LITERAL_END,
63 TokenTypes.LITERAL_TRUE,
64 TokenTypes.LITERAL_FALSE,
65 TokenTypes.LITERAL_NULL,
66 TokenTypes.IDENT,
67 TokenTypes.DOT,
68 TokenTypes.LPAREN,
69 TokenTypes.RPAREN,
70 TokenTypes.UNARY_MINUS,
71 TokenTypes.UNARY_PLUS,
72 TokenTypes.PLUS,
73 TokenTypes.MINUS,
74 TokenTypes.STAR,
75 TokenTypes.DIV,
76 TokenTypes.MOD
77 );
78
79
80
81
82 public SingleUseConstantCheck() {
83
84 }
85
86 @Override
87 public int[] getDefaultTokens() {
88 return new int[]{TokenTypes.VARIABLE_DEF};
89 }
90
91 @Override
92 public int[] getAcceptableTokens() {
93 return this.getDefaultTokens();
94 }
95
96 @Override
97 public int[] getRequiredTokens() {
98 return this.getDefaultTokens();
99 }
100
101 @Override
102 public void visitToken(final DetailAST ast) {
103 final DetailAST name = ast.findFirstToken(TokenTypes.IDENT);
104 if (SingleUseConstantCheck.isInlineable(ast)
105 && !this.documented(name.getText())
106 && SingleUseConstantCheck.mentions(
107 SingleUseConstantCheck.root(ast), name.getText()
108 ) == 2) {
109 this.log(
110 name.getLineNo(),
111 String.format(
112 "Private constant \"%s\" is used only once, inline it",
113 name.getText()
114 )
115 );
116 }
117 }
118
119 private boolean documented(final String name) {
120 final Pattern ref = Pattern.compile(
121 String.format("#%s\\b", Pattern.quote(name))
122 );
123 boolean found = false;
124 for (final String line : this.getLines()) {
125 if (ref.matcher(line).find()) {
126 found = true;
127 break;
128 }
129 }
130 return found;
131 }
132
133 private static boolean isInlineable(final DetailAST node) {
134 final DetailAST assign = node.findFirstToken(TokenTypes.ASSIGN);
135 return SingleUseConstantCheck.isConstant(node)
136 && assign != null
137 && SingleUseConstantCheck.isLiteral(assign.getFirstChild());
138 }
139
140 private static boolean isConstant(final DetailAST node) {
141 return node.getParent().getType() == TokenTypes.OBJBLOCK
142 && !"serialVersionUID".equals(
143 node.findFirstToken(TokenTypes.IDENT).getText()
144 )
145 && SingleUseConstantCheck.isShared(node);
146 }
147
148 private static boolean isShared(final DetailAST node) {
149 final DetailAST mods = node.findFirstToken(TokenTypes.MODIFIERS);
150 return mods.findFirstToken(TokenTypes.LITERAL_PRIVATE) != null
151 && mods.findFirstToken(TokenTypes.LITERAL_STATIC) != null
152 && mods.findFirstToken(TokenTypes.FINAL) != null;
153 }
154
155 private static boolean isLiteral(final DetailAST node) {
156 boolean literal =
157 SingleUseConstantCheck.LITERALS.contains(node.getType());
158 DetailAST child = node.getFirstChild();
159 while (literal && child != null) {
160 literal = SingleUseConstantCheck.isLiteral(child);
161 child = child.getNextSibling();
162 }
163 return literal;
164 }
165
166 private static int mentions(final DetailAST node, final String name) {
167 int found = 0;
168 if (node.getType() == TokenTypes.IDENT
169 && node.getText().equals(name)) {
170 found = 1;
171 }
172 DetailAST child = node.getFirstChild();
173 while (child != null) {
174 found += SingleUseConstantCheck.mentions(child, name);
175 child = child.getNextSibling();
176 }
177 return found;
178 }
179
180 private static DetailAST root(final DetailAST node) {
181 DetailAST top = node;
182 while (top.getParent() != null) {
183 top = top.getParent();
184 }
185 return top;
186 }
187 }