99import com .puppycrawl .tools .checkstyle .api .TokenTypes ;
1010
1111/**
12- * Checks that there is no empty line between a javadoc and it's subject.
12+ * Checks that there is no empty line between a javadoc and it's subject,
13+ * and that no annotation is placed above the javadoc.
1314 *
1415 * <p>You can't have empty lines between javadoc block and
1516 * a class/method/variable. They should stay together, always.
1617 *
18+ * <p>Annotations must be placed after the javadoc, not before it,
19+ * so that the javadoc stays next to the subject it describes.
20+ *
1721 * @since 0.3
1822 */
1923public final class JavadocLocationCheck extends AbstractCheck {
@@ -45,6 +49,16 @@ public void visitToken(final DetailAST ast) {
4549 return ;
4650 }
4751 final String [] lines = this .getLines ();
52+ this .checkEmptyLines (ast , lines );
53+ this .checkAnnotationAboveJavadoc (ast , lines );
54+ }
55+
56+ /**
57+ * Check that there are no empty lines between the javadoc and the subject.
58+ * @param ast The AST node of the subject
59+ * @param lines The file lines
60+ */
61+ private void checkEmptyLines (final DetailAST ast , final String [] lines ) {
4862 int current = ast .getLineNo ();
4963 boolean found = false ;
5064 --current ;
@@ -63,23 +77,55 @@ public void visitToken(final DetailAST ast) {
6377 --current ;
6478 }
6579 if (found ) {
66- this .report (ast .getLineNo (), current );
80+ final int diff = ast .getLineNo () - current ;
81+ if (diff > 1 ) {
82+ for (int pos = 1 ; pos < diff ; pos += 1 ) {
83+ this .log (
84+ current + pos ,
85+ "Empty line between javadoc and subject"
86+ );
87+ }
88+ }
6789 }
6890 }
6991
7092 /**
71- * Report empty lines between current and end line .
72- * @param current Current line
73- * @param end Final line
93+ * Check that no annotation is placed above the javadoc of the subject .
94+ * @param ast The AST node of the subject
95+ * @param lines The file lines
7496 */
75- private void report (final int current , final int end ) {
76- final int diff = current - end ;
77- if (diff > 1 ) {
78- for (int pos = 1 ; pos < diff ; pos += 1 ) {
97+ private void checkAnnotationAboveJavadoc (
98+ final DetailAST ast , final String [] lines
99+ ) {
100+ final DetailAST modifiers = ast .findFirstToken (TokenTypes .MODIFIERS );
101+ if (modifiers == null ) {
102+ return ;
103+ }
104+ final DetailAST after = modifiers .getNextSibling ();
105+ if (after == null ) {
106+ return ;
107+ }
108+ final int signature = after .getLineNo ();
109+ int annotation = Integer .MAX_VALUE ;
110+ DetailAST child = modifiers .getFirstChild ();
111+ while (child != null ) {
112+ if (child .getType () == TokenTypes .ANNOTATION
113+ && child .getLineNo () < annotation ) {
114+ annotation = child .getLineNo ();
115+ }
116+ child = child .getNextSibling ();
117+ }
118+ if (annotation == Integer .MAX_VALUE ) {
119+ return ;
120+ }
121+ for (int pos = annotation + 1 ; pos < signature ; pos += 1 ) {
122+ final String line = lines [pos - 1 ].trim ();
123+ if (line .startsWith ("/**" ) || line .endsWith ("*/" )) {
79124 this .log (
80- end + pos ,
81- "Empty line between javadoc and subject "
125+ annotation ,
126+ "Annotation must be placed after Javadoc "
82127 );
128+ break ;
83129 }
84130 }
85131 }
0 commit comments