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 public final class JavadocFirstLineCheck extends AbstractCheck {
29
30
31
32
33 public JavadocFirstLineCheck() {
34
35 }
36
37 @Override
38 public int[] getDefaultTokens() {
39 return new int[] {
40 TokenTypes.PACKAGE_DEF,
41 TokenTypes.CLASS_DEF,
42 TokenTypes.INTERFACE_DEF,
43 TokenTypes.ANNOTATION_DEF,
44 TokenTypes.ANNOTATION_FIELD_DEF,
45 TokenTypes.ENUM_DEF,
46 TokenTypes.ENUM_CONSTANT_DEF,
47 TokenTypes.VARIABLE_DEF,
48 TokenTypes.CTOR_DEF,
49 TokenTypes.METHOD_DEF,
50 };
51 }
52
53 @Override
54 public int[] getAcceptableTokens() {
55 return this.getDefaultTokens();
56 }
57
58 @Override
59 public int[] getRequiredTokens() {
60 return this.getDefaultTokens();
61 }
62
63 @Override
64 public void visitToken(final DetailAST ast) {
65 final String[] lines = this.getLines();
66 final int start = JavadocFirstLineCheck.findOpeningLine(
67 lines, ast.getLineNo() - 1
68 );
69 if (start >= 0 && JavadocFirstLineCheck.belongsToNode(ast, start)
70 && JavadocFirstLineCheck.hasTextAfterOpening(lines[start])
71 && !JavadocFirstLineCheck.hasClosingOnSameLine(lines[start])) {
72 this.log(start + 1, "No text allowed on the first line of Javadoc");
73 }
74 }
75
76 private static int findOpeningLine(final String[] lines, final int below) {
77 int found = -1;
78 for (int pos = below - 1; pos >= 0; pos -= 1) {
79 final String trimmed = lines[pos].trim();
80 if (trimmed.startsWith("/**")) {
81 found = pos;
82 break;
83 }
84 if (!trimmed.isEmpty() && !trimmed.startsWith("*")
85 && !trimmed.endsWith("*/")) {
86 break;
87 }
88 }
89 return found;
90 }
91
92 private static boolean belongsToNode(final DetailAST node, final int start) {
93 final DetailAST previous = node.getPreviousSibling();
94 boolean owns = true;
95 if (previous != null) {
96 owns = start + 1 > previous.getLineNo();
97 }
98 return owns;
99 }
100
101 private static boolean hasTextAfterOpening(final String line) {
102 final String trimmed = line.trim();
103 final String rest = trimmed.substring("/**".length()).trim();
104 return !rest.isEmpty() && !"/".equals(rest);
105 }
106
107 private static boolean hasClosingOnSameLine(final String line) {
108 return line.trim().endsWith("*/");
109 }
110 }