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