1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.checks.javadoc;
21
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.List;
25
26 import com.puppycrawl.tools.checkstyle.StatelessCheck;
27 import com.puppycrawl.tools.checkstyle.api.DetailNode;
28 import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes;
29 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
30 import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
31
32
33
34
35
36
37
38
39 @StatelessCheck
40 @SuppressWarnings("InvalidInlineTag")
41 public class RequireEmptyLineBeforeBlockTagGroupCheck extends AbstractJavadocCheck {
42
43
44
45
46
47 public static final String MSG_JAVADOC_TAG_LINE_BEFORE = "javadoc.tag.line.before";
48
49
50
51
52
53
54
55
56 private static final List<Integer> ONLY_TAG_VARIATION_1 = Arrays.asList(
57 JavadocCommentsTokenTypes.TEXT,
58 JavadocCommentsTokenTypes.LEADING_ASTERISK,
59 JavadocCommentsTokenTypes.NEWLINE);
60
61
62
63
64
65
66
67
68 private static final List<Integer> ONLY_TAG_VARIATION_2 = Arrays.asList(
69 JavadocCommentsTokenTypes.LEADING_ASTERISK,
70 JavadocCommentsTokenTypes.NEWLINE);
71
72
73
74
75
76
77
78
79 private static final List<Integer> ONLY_TAG_VARIATION_3 = Arrays.asList(
80 JavadocCommentsTokenTypes.TEXT,
81 JavadocCommentsTokenTypes.LEADING_ASTERISKS,
82 JavadocCommentsTokenTypes.NEWLINE);
83
84
85
86
87
88
89
90
91 private static final List<Integer> ONLY_TAG_VARIATION_4 = Arrays.asList(
92 JavadocCommentsTokenTypes.LEADING_ASTERISKS,
93 JavadocCommentsTokenTypes.NEWLINE);
94
95
96
97
98
99
100
101
102 private static final List<Integer> ONLY_TAG_VARIATION_5 = Arrays.asList(
103 JavadocCommentsTokenTypes.TEXT,
104 JavadocCommentsTokenTypes.LEADING_ASTERISK,
105 JavadocCommentsTokenTypes.NEWLINE,
106 JavadocCommentsTokenTypes.LEADING_ASTERISK);
107
108
109
110
111
112
113
114
115 private static final List<Integer> ONLY_TAG_VARIATION_6 = Arrays.asList(
116 JavadocCommentsTokenTypes.TEXT,
117 JavadocCommentsTokenTypes.LEADING_ASTERISKS,
118 JavadocCommentsTokenTypes.NEWLINE,
119 JavadocCommentsTokenTypes.LEADING_ASTERISKS);
120
121
122
123
124 public RequireEmptyLineBeforeBlockTagGroupCheck() {
125
126 }
127
128
129
130
131
132
133 @Override
134 public int[] getDefaultJavadocTokens() {
135 return new int[] {
136 JavadocCommentsTokenTypes.JAVADOC_BLOCK_TAG,
137 };
138 }
139
140 @Override
141 public int[] getRequiredJavadocTokens() {
142 return getAcceptableJavadocTokens();
143 }
144
145
146
147
148
149
150 @Override
151 public void visitJavadocToken(DetailNode tagNode) {
152
153
154 if (!isAnotherTagBefore(tagNode)
155 && !isOnlyTagInWholeJavadoc(tagNode)
156 && hasInsufficientConsecutiveNewlines(tagNode)) {
157 final String tagName = JavadocUtil.getTagName(tagNode);
158 log(tagNode.getLineNumber(),
159 MSG_JAVADOC_TAG_LINE_BEFORE,
160 "@" + tagName);
161 }
162 }
163
164
165
166
167
168
169
170 private static boolean isAnotherTagBefore(DetailNode tagNode) {
171 boolean found = false;
172 DetailNode currentNode = tagNode.getPreviousSibling();
173 while (currentNode != null) {
174 if (currentNode.getType() == JavadocCommentsTokenTypes.JAVADOC_BLOCK_TAG) {
175 found = true;
176 break;
177 }
178 currentNode = currentNode.getPreviousSibling();
179 }
180 return found;
181 }
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216 @SuppressWarnings("InvalidInlineTag")
217 private static boolean isOnlyTagInWholeJavadoc(DetailNode tagNode) {
218 final List<Integer> previousNodeTypes = new ArrayList<>();
219 DetailNode currentNode = tagNode.getPreviousSibling();
220
221 while (currentNode != null) {
222 previousNodeTypes.add(currentNode.getType());
223 currentNode = currentNode.getPreviousSibling();
224 }
225 return ONLY_TAG_VARIATION_1.equals(previousNodeTypes)
226 || ONLY_TAG_VARIATION_2.equals(previousNodeTypes)
227 || ONLY_TAG_VARIATION_3.equals(previousNodeTypes)
228 || ONLY_TAG_VARIATION_4.equals(previousNodeTypes)
229 || ONLY_TAG_VARIATION_5.equals(previousNodeTypes)
230 || ONLY_TAG_VARIATION_6.equals(previousNodeTypes);
231 }
232
233
234
235
236
237
238
239
240
241
242
243 private static boolean hasInsufficientConsecutiveNewlines(DetailNode tagNode) {
244 int count = 0;
245 DetailNode currentNode = tagNode.getPreviousSibling();
246 while (currentNode != null
247 && (CommonUtil.isBlank(currentNode.getText())
248 || isLeadingAsterisk(currentNode))) {
249 if (currentNode.getType() == JavadocCommentsTokenTypes.NEWLINE) {
250 count++;
251 }
252 currentNode = currentNode.getPreviousSibling();
253 }
254
255 return count <= 1;
256 }
257
258
259
260
261
262
263
264 private static boolean isLeadingAsterisk(DetailNode detailNode) {
265 return detailNode.getType() == JavadocCommentsTokenTypes.LEADING_ASTERISK
266 || detailNode.getType() == JavadocCommentsTokenTypes.LEADING_ASTERISKS;
267 }
268
269 }