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 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collection;
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.regex.Pattern;
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 public final class JavadocTagsCheck extends AbstractCheck {
68
69
70
71
72 private final Map<String, Pattern> tags = new HashMap<>();
73
74
75
76
77 private final Collection<String> prohibited =
78 Arrays.asList("author", "version");
79
80 @Override
81 public int[] getDefaultTokens() {
82 return new int[]{
83 TokenTypes.CLASS_DEF,
84 TokenTypes.INTERFACE_DEF,
85 };
86 }
87
88 @Override
89 public int[] getAcceptableTokens() {
90 return this.getDefaultTokens();
91 }
92
93 @Override
94 public int[] getRequiredTokens() {
95 return this.getDefaultTokens();
96 }
97
98 @Override
99 public void init() {
100 this.tags.put(
101 "since",
102 Pattern.compile(
103 "^\\d+(\\.\\d+){1,2}(\\.[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?$"
104 )
105 );
106 }
107
108 @Override
109 public void visitToken(final DetailAST ast) {
110 final String[] lines = this.getLines();
111 final int start = ast.getLineNo();
112 final int cstart = JavadocTagsCheck.findCommentStart(lines, start);
113 final int cend = JavadocTagsCheck.findCommentEnd(lines, start);
114 if (cend > cstart && cstart >= 0) {
115 for (final String tag : this.prohibited) {
116 this.findProhibited(lines, start, cstart, cend, tag);
117 }
118 for (final String tag : this.tags.keySet()) {
119 this.matchTagFormat(lines, cstart, cend, tag);
120 }
121 } else {
122 this.log(0, "Problem finding class/interface comment");
123 }
124 }
125
126
127
128
129
130
131 private static String getTagText(final String line) {
132 return line.substring(
133 line.indexOf(' ', line.indexOf('@')) + 1
134 );
135 }
136
137
138
139
140
141
142
143
144 private static int findTrimmedTextUp(final String[] lines,
145 final int start, final String text) {
146 int found = -1;
147 for (int pos = start - 1; pos >= 0; pos -= 1) {
148 if (lines[pos].trim().equals(text)) {
149 found = pos;
150 break;
151 }
152 }
153 return found;
154 }
155
156
157
158
159
160
161
162 private static int findCommentStart(final String[] lines, final int start) {
163 return JavadocTagsCheck.findTrimmedTextUp(lines, start, "/**");
164 }
165
166
167
168
169
170
171
172 private static int findCommentEnd(final String[] lines, final int start) {
173 return JavadocTagsCheck.findTrimmedTextUp(lines, start, "*/");
174 }
175
176
177
178
179
180
181
182
183
184
185 private void findProhibited(final String[] lines, final int start,
186 final int cstart, final int cend, final String tag) {
187 final List<Integer> found =
188 this.findTagLineNum(lines, cstart, cend, tag);
189 if (!found.isEmpty()) {
190 this.log(
191 start + 1,
192 "Prohibited ''@{0}'' tag in class/interface comment",
193 tag
194 );
195 }
196 }
197
198
199
200
201
202
203
204
205
206 private void matchTagFormat(final String[] lines, final int start,
207 final int end, final String tag) {
208 final List<Integer> found = this.findTagLineNum(lines, start, end, tag);
209 if (found.isEmpty()) {
210 this.log(
211 start + 1,
212 "Missing ''@{0}'' tag in class/interface comment",
213 tag
214 );
215 return;
216 }
217 for (final Integer item : found) {
218 final String text = JavadocTagsCheck.getTagText(lines[item]);
219 if (!this.tags.get(tag).matcher(text).matches()) {
220 this.log(
221 item + 1,
222 "Tag text ''{0}'' does not match the pattern ''{1}''",
223 text,
224 this.tags.get(tag).toString()
225 );
226 }
227 }
228 }
229
230
231
232
233
234
235
236
237
238
239 private List<Integer> findTagLineNum(final String[] lines, final int start,
240 final int end, final String tag) {
241 final String prefix = String.format(" * @%s ", tag);
242 final List<Integer> found = new ArrayList<>(1);
243 for (int pos = start; pos <= end; pos += 1) {
244 final String line = lines[pos];
245 if (line.contains(String.format("@%s ", tag))) {
246 if (!line.trim().startsWith(prefix.trim())) {
247 this.log(
248 start + pos + 1,
249 "Line with ''@{0}'' does not start with a ''{1}''",
250 tag,
251 prefix
252 );
253 break;
254 }
255 found.add(pos);
256 }
257 }
258 return found;
259 }
260 }
261