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
46 public final class JavadocEmptyLineBeforeTagCheck extends AbstractCheck {
47
48
49
50
51 public JavadocEmptyLineBeforeTagCheck() {
52
53 }
54
55 @Override
56 public int[] getDefaultTokens() {
57 return new int[] {
58 TokenTypes.PACKAGE_DEF,
59 TokenTypes.CLASS_DEF,
60 TokenTypes.INTERFACE_DEF,
61 TokenTypes.ANNOTATION_DEF,
62 TokenTypes.ANNOTATION_FIELD_DEF,
63 TokenTypes.ENUM_DEF,
64 TokenTypes.ENUM_CONSTANT_DEF,
65 TokenTypes.RECORD_DEF,
66 TokenTypes.VARIABLE_DEF,
67 TokenTypes.CTOR_DEF,
68 TokenTypes.COMPACT_CTOR_DEF,
69 TokenTypes.METHOD_DEF,
70 };
71 }
72
73 @Override
74 public int[] getAcceptableTokens() {
75 return this.getDefaultTokens();
76 }
77
78 @Override
79 public int[] getRequiredTokens() {
80 return this.getDefaultTokens();
81 }
82
83 @Override
84 public void visitToken(final DetailAST ast) {
85 final String[] lines = this.getLines();
86 final int current = ast.getLineNo();
87 final int start =
88 JavadocEmptyLineBeforeTagCheck.findCommentStart(lines, current) + 1;
89 final int end =
90 JavadocEmptyLineBeforeTagCheck.findCommentEnd(lines, current) - 1;
91 if (JavadocEmptyLineBeforeTagCheck.isNodeHavingJavadoc(ast, start)
92 && start < lines.length && end >= start) {
93 final int tag =
94 JavadocEmptyLineBeforeTagCheck.findFirstTag(lines, start, end);
95 if (tag > start
96 && !JavadocEmptyLineBeforeTagCheck.isJavadocLineEmpty(lines[tag - 1])) {
97 this.log(
98 tag + 1,
99 "Empty Javadoc line required before the block of at-clauses"
100 );
101 }
102 }
103 }
104
105 private static boolean isJavadocLineEmpty(final String line) {
106 return "*".equals(line.trim());
107 }
108
109 private static boolean isNodeHavingJavadoc(final DetailAST node,
110 final int start) {
111 int previous = 0;
112 final DetailAST prev = node.getPreviousSibling();
113 if (prev != null) {
114 previous = prev.getLineNo();
115 }
116 return start > previous;
117 }
118
119 private static int findCommentStart(final String[] lines, final int start) {
120 return JavadocEmptyLineBeforeTagCheck.findTrimmedTextUp(lines, start, "/**");
121 }
122
123 private static int findCommentEnd(final String[] lines, final int start) {
124 int found = -1;
125 for (int pos = start - 1; pos >= 0; pos -= 1) {
126 final String trimmed = lines[pos].trim();
127 if ("*/".equals(trimmed) || "**/".equals(trimmed)) {
128 found = pos;
129 break;
130 }
131 }
132 return found;
133 }
134
135 private static int findFirstTag(final String[] lines, final int start,
136 final int end) {
137 int found = -1;
138 for (int pos = start; pos <= end; pos += 1) {
139 final String trimmed = lines[pos].trim();
140 if (trimmed.startsWith("* @") || trimmed.startsWith("*@")) {
141 found = pos;
142 break;
143 }
144 }
145 return found;
146 }
147
148 private static int findTrimmedTextUp(final String[] lines,
149 final int start, final String text) {
150 int found = -1;
151 for (int pos = start - 1; pos >= 0; pos -= 1) {
152 if (lines[pos].trim().equals(text)) {
153 found = pos;
154 break;
155 }
156 }
157 return found;
158 }
159 }