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
28 @SuppressWarnings({"PMD.AvoidInstantiatingObjectsInLoops", "PMD.LongVariable"})
29 public final class JavadocParameterOrderCheck extends AbstractCheck {
30
31
32
33
34 private static final Pattern MATCH_JAVADOC_ARG = Pattern.compile(
35 "^\\s*(?>\\*|\\/\\*\\*)?\\s*@(param)\\s+(\\S+)\\s+\\S*"
36 );
37
38
39
40
41 private static final Pattern MATCH_JAVADOC_ARG_MULTILINE_START =
42 Pattern.compile(
43 "^\\s*(?>\\*|\\/\\*\\*)?\\s*@(param)\\s+(\\S+)\\s*$"
44 );
45
46
47
48
49 private static final Pattern MATCH_JAVADOC_MULTILINE_CONT =
50 Pattern.compile("(\\*/|@|[^\\s\\*])");
51
52
53
54
55 private static final String END_JAVADOC = "*/";
56
57
58
59
60 private static final String NEXT_TAG = "@";
61
62 @Override
63 public int[] getDefaultTokens() {
64 return new int[] {
65 TokenTypes.INTERFACE_DEF,
66 TokenTypes.CLASS_DEF,
67 TokenTypes.CTOR_DEF,
68 TokenTypes.METHOD_DEF,
69 };
70 }
71
72 @Override
73 public int[] getAcceptableTokens() {
74 return this.getDefaultTokens();
75 }
76
77 @Override
78 public int[] getRequiredTokens() {
79 return this.getDefaultTokens();
80 }
81
82 @Override
83 @SuppressWarnings("deprecation")
84 public void visitToken(final DetailAST ast) {
85 final FileContents contents = this.getFileContents();
86 final TextBlock doc = contents.getJavadocBefore(ast.getLineNo());
87 if (doc != null) {
88 this.checkParameters(ast, doc);
89 }
90 }
91
92
93
94
95
96
97
98 private static List<JavadocTag> getMethodTags(final TextBlock comment) {
99 final String[] lines = comment.getText();
100 final List<JavadocTag> tags = new LinkedList<>();
101 int current = comment.getStartLineNo() - 1;
102 final int start = comment.getStartColNo();
103 for (int line = 0; line < lines.length; line = line + 1) {
104 current = current + 1;
105 final Matcher docmatcher =
106 MATCH_JAVADOC_ARG.matcher(lines[line]);
107 final Matcher multiline =
108 MATCH_JAVADOC_ARG_MULTILINE_START.matcher(lines[line]);
109 if (docmatcher.find()) {
110 final int col = calculateTagColumn(
111 docmatcher, line, start
112 );
113 tags.add(
114 new JavadocTag(
115 current,
116 col,
117 docmatcher.group(1),
118 docmatcher.group(2)
119 )
120 );
121 } else if (multiline.find()) {
122 final int col =
123 calculateTagColumn(
124 multiline,
125 line,
126 start
127 );
128 tags.addAll(
129 getMultilineArgTags(
130 multiline,
131 col,
132 lines,
133 line,
134 current
135 )
136 );
137 }
138 }
139 return tags;
140 }
141
142
143
144
145
146
147
148
149 private static int calculateTagColumn(
150 final Matcher matcher, final int line, final int start
151 ) {
152 int col = matcher.start(1) - 1;
153 if (line == 0) {
154 col += start;
155 }
156 return col;
157 }
158
159
160
161
162
163
164
165
166
167
168
169 private static List<JavadocTag> getMultilineArgTags(
170 final Matcher matcher, final int column, final String[] lines,
171 final int index, final int line) {
172 final List<JavadocTag> tags = new ArrayList<>(0);
173 final String paramone = matcher.group(1);
174 final String paramtwo = matcher.group(2);
175 int remindex = index + 1;
176 while (remindex < lines.length) {
177 final Matcher multiline =
178 MATCH_JAVADOC_MULTILINE_CONT.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 }