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
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 public final class JavadocEmptyLineBeforeTagCheck extends AbstractCheck {
46
47 @Override
48 public int[] getDefaultTokens() {
49 return new int[] {
50 TokenTypes.PACKAGE_DEF,
51 TokenTypes.CLASS_DEF,
52 TokenTypes.INTERFACE_DEF,
53 TokenTypes.ANNOTATION_DEF,
54 TokenTypes.ANNOTATION_FIELD_DEF,
55 TokenTypes.ENUM_DEF,
56 TokenTypes.ENUM_CONSTANT_DEF,
57 TokenTypes.VARIABLE_DEF,
58 TokenTypes.CTOR_DEF,
59 TokenTypes.METHOD_DEF,
60 };
61 }
62
63 @Override
64 public int[] getAcceptableTokens() {
65 return this.getDefaultTokens();
66 }
67
68 @Override
69 public int[] getRequiredTokens() {
70 return this.getDefaultTokens();
71 }
72
73 @Override
74 public void visitToken(final DetailAST ast) {
75 final String[] lines = this.getLines();
76 final int current = ast.getLineNo();
77 final int start =
78 JavadocEmptyLineBeforeTagCheck.findCommentStart(lines, current) + 1;
79 final int end =
80 JavadocEmptyLineBeforeTagCheck.findCommentEnd(lines, current) - 1;
81 if (JavadocEmptyLineBeforeTagCheck.isNodeHavingJavadoc(ast, start)
82 && start < lines.length && end >= start) {
83 final int tag =
84 JavadocEmptyLineBeforeTagCheck.findFirstTag(lines, start, end);
85 if (tag > start) {
86 this.inspect(lines, start, tag);
87 }
88 }
89 }
90
91
92
93
94
95
96
97
98 private void inspect(final String[] lines, final int start, final int tag) {
99 int body = tag - 1;
100 while (body >= start
101 && JavadocEmptyLineBeforeTagCheck.isJavadocLineEmpty(lines[body])) {
102 body -= 1;
103 }
104 if (body >= start) {
105 boolean multi = false;
106 for (int pos = start; pos <= body; pos += 1) {
107 if (JavadocEmptyLineBeforeTagCheck.isJavadocLineEmpty(lines[pos])) {
108 multi = true;
109 break;
110 }
111 }
112 final boolean empty =
113 JavadocEmptyLineBeforeTagCheck.isJavadocLineEmpty(lines[tag - 1]);
114 if (multi && !empty) {
115 this.log(
116 tag + 1,
117 "Empty Javadoc line required before at-clauses, since the description has multiple paragraphs"
118 );
119 } else if (!multi && empty) {
120 this.log(
121 tag,
122 "Empty Javadoc line before at-clauses is not allowed, since the description is a single paragraph"
123 );
124 }
125 }
126 }
127
128
129
130
131
132
133 private static boolean isJavadocLineEmpty(final String line) {
134 return "*".equals(line.trim());
135 }
136
137
138
139
140
141
142
143 private static boolean isNodeHavingJavadoc(final DetailAST node,
144 final int start) {
145 int previous = 0;
146 final DetailAST prev = node.getPreviousSibling();
147 if (prev != null) {
148 previous = prev.getLineNo();
149 }
150 return start > previous;
151 }
152
153
154
155
156
157
158
159 private static int findCommentStart(final String[] lines, final int start) {
160 return JavadocEmptyLineBeforeTagCheck.findTrimmedTextUp(lines, start, "/**");
161 }
162
163
164
165
166
167
168
169 private static int findCommentEnd(final String[] lines, final int start) {
170 int found = -1;
171 for (int pos = start - 1; pos >= 0; pos -= 1) {
172 final String trimmed = lines[pos].trim();
173 if ("*/".equals(trimmed) || "**/".equals(trimmed)) {
174 found = pos;
175 break;
176 }
177 }
178 return found;
179 }
180
181
182
183
184
185
186
187
188 private static int findFirstTag(final String[] lines, final int start,
189 final int end) {
190 int found = -1;
191 for (int pos = start; pos <= end; pos += 1) {
192 final String trimmed = lines[pos].trim();
193 if (trimmed.startsWith("* @") || trimmed.startsWith("*@")) {
194 found = pos;
195 break;
196 }
197 }
198 return found;
199 }
200
201
202
203
204
205
206
207
208 private static int findTrimmedTextUp(final String[] lines,
209 final int start, final String text) {
210 int found = -1;
211 for (int pos = start - 1; pos >= 0; pos -= 1) {
212 if (lines[pos].trim().equals(text)) {
213 found = pos;
214 break;
215 }
216 }
217 return found;
218 }
219 }