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