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.FileContents;
10 import com.puppycrawl.tools.checkstyle.api.TextBlock;
11 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
12 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTag;
13 import com.qulice.checkstyle.parameters.Arguments;
14 import com.qulice.checkstyle.parameters.TypeParameters;
15 import java.util.ArrayList;
16 import java.util.LinkedList;
17 import java.util.List;
18 import java.util.function.Consumer;
19 import java.util.regex.Matcher;
20 import java.util.regex.Pattern;
21
22
23
24
25
26
27 @SuppressWarnings("PMD.LongVariable")
28 public final class JavadocParameterOrderCheck extends AbstractCheck {
29
30
31
32
33 private static final Pattern MATCH_JAVADOC_ARG = Pattern.compile(
34 "^\\s*(?>\\*|\\/\\*\\*)?\\s*@(param)\\s+(\\S+)\\s+\\S*"
35 );
36
37
38
39
40 private static final Pattern MATCH_JAVADOC_ARG_MULTILINE_START =
41 Pattern.compile(
42 "^\\s*(?>\\*|\\/\\*\\*)?\\s*@(param)\\s+(\\S+)\\s*$"
43 );
44
45
46
47
48 private static final Pattern MATCH_JAVADOC_MULTILINE_CONT =
49 Pattern.compile("(\\*/|@|[^\\s\\*])");
50
51
52
53
54 private static final String END_JAVADOC = "*/";
55
56
57
58
59 private static final String NEXT_TAG = "@";
60
61 @Override
62 public int[] getDefaultTokens() {
63 return new int[] {
64 TokenTypes.INTERFACE_DEF,
65 TokenTypes.CLASS_DEF,
66 TokenTypes.CTOR_DEF,
67 TokenTypes.METHOD_DEF,
68 };
69 }
70
71 @Override
72 public int[] getAcceptableTokens() {
73 return this.getDefaultTokens();
74 }
75
76 @Override
77 public int[] getRequiredTokens() {
78 return this.getDefaultTokens();
79 }
80
81 @Override
82 @SuppressWarnings("deprecation")
83 public void visitToken(final DetailAST ast) {
84 final FileContents contents = this.getFileContents();
85 final TextBlock doc = contents.getJavadocBefore(ast.getLineNo());
86 if (doc != null) {
87 this.checkParameters(ast, doc);
88 }
89 }
90
91
92
93
94
95
96 private static List<JavadocTag> getMethodTags(final TextBlock comment) {
97 final String[] lines = comment.getText();
98 final List<JavadocTag> tags = new LinkedList<>();
99 int current = comment.getStartLineNo() - 1;
100 final int start = comment.getStartColNo();
101 for (int line = 0; line < lines.length; line = line + 1) {
102 current = current + 1;
103 final Matcher docmatcher =
104 JavadocParameterOrderCheck.MATCH_JAVADOC_ARG.matcher(lines[line]);
105 final Matcher multiline =
106 JavadocParameterOrderCheck.MATCH_JAVADOC_ARG_MULTILINE_START
107 .matcher(lines[line]);
108 if (docmatcher.find()) {
109 final int col = calculateTagColumn(
110 docmatcher, line, start
111 );
112 tags.add(
113 new JavadocTag(
114 current,
115 col,
116 docmatcher.group(1),
117 docmatcher.group(2)
118 )
119 );
120 } else if (multiline.find()) {
121 final int col =
122 calculateTagColumn(
123 multiline,
124 line,
125 start
126 );
127 tags.addAll(
128 getMultilineArgTags(
129 multiline,
130 col,
131 lines,
132 line,
133 current
134 )
135 );
136 }
137 }
138 return tags;
139 }
140
141
142
143
144
145
146
147
148 private static int calculateTagColumn(
149 final Matcher matcher, final int line, final int start
150 ) {
151 int col = matcher.start(1) - 1;
152 if (line == 0) {
153 col += start;
154 }
155 return col;
156 }
157
158
159
160
161
162
163
164
165
166
167
168 private static List<JavadocTag> getMultilineArgTags(
169 final Matcher matcher, final int column, final String[] lines,
170 final int index, final int line) {
171 final List<JavadocTag> tags = new ArrayList<>(0);
172 final String paramone = matcher.group(1);
173 final String paramtwo = matcher.group(2);
174 int remindex = index + 1;
175 while (remindex < lines.length) {
176 final Matcher multiline =
177 JavadocParameterOrderCheck.MATCH_JAVADOC_MULTILINE_CONT
178 .matcher(lines[remindex]);
179 if (multiline.find()) {
180 remindex = lines.length;
181 final String lfin = multiline.group(1);
182 if (!JavadocParameterOrderCheck.NEXT_TAG.equals(lfin)
183 && !JavadocParameterOrderCheck.END_JAVADOC.equals(lfin)) {
184 tags.add(new JavadocTag(line, column, paramone, paramtwo));
185 }
186 }
187 remindex = remindex + 1;
188 }
189 return tags;
190 }
191
192
193
194
195
196
197
198 private void checkParameters(final DetailAST ast, final TextBlock doc) {
199 final List<JavadocTag> tags = getMethodTags(doc);
200 final Arguments args = new Arguments(ast);
201 final TypeParameters types = new TypeParameters(ast);
202 final int count = args.count() + types.count();
203 if (tags.size() == count) {
204 final Consumer<JavadocTag> logger = tag -> this.log(
205 tag.getLineNo(),
206 "Javadoc parameter order different than method signature"
207 );
208 args.checkOrder(tags, logger);
209 types.checkOrder(tags, logger);
210 } else {
211 this.log(
212 ast.getLineNo(),
213 "Number of javadoc parameters different than method signature"
214 );
215 }
216 }
217 }