1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.checks.javadoc;
21
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.LinkedHashMap;
25 import java.util.List;
26 import java.util.Map;
27
28 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
29 import com.puppycrawl.tools.checkstyle.api.DetailAST;
30 import com.puppycrawl.tools.checkstyle.api.DetailNode;
31 import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes;
32 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
33 import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
34 import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
35 import com.puppycrawl.tools.checkstyle.utils.NullUtil;
36 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 @FileStatefulCheck
59 public class JavadocParamOrderCheck extends AbstractJavadocCheck {
60
61
62
63
64
65 public static final String MSG_KEY = "javadoc.param.order";
66
67
68 private static final String ELEMENT_START = "<";
69
70
71 private static final String ELEMENT_END = ">";
72
73
74 private final Map<DetailNode, String> javadocTags = new LinkedHashMap<>();
75
76
77
78
79 public JavadocParamOrderCheck() {
80
81 }
82
83 @Override
84 public int[] getDefaultTokens() {
85 return getRequiredTokens();
86 }
87
88 @Override
89 public final int[] getRequiredTokens() {
90 return new int[] {
91 TokenTypes.METHOD_DEF,
92 TokenTypes.CTOR_DEF,
93 TokenTypes.CLASS_DEF,
94 TokenTypes.INTERFACE_DEF,
95 TokenTypes.COMPACT_CTOR_DEF,
96 TokenTypes.RECORD_DEF,
97 };
98 }
99
100 @Override
101 public int[] getDefaultJavadocTokens() {
102 return getRequiredJavadocTokens();
103 }
104
105 @Override
106 public int[] getRequiredJavadocTokens() {
107 return new int[] {
108 JavadocCommentsTokenTypes.PARAM_BLOCK_TAG,
109 };
110 }
111
112 @Override
113 public final void visitToken(final DetailAST ast) {
114 final DetailAST blockCommentNode = JavadocUtil.getAttachedJavadocComment(ast);
115 if (blockCommentNode != null) {
116 javadocTags.clear();
117 super.visitToken(blockCommentNode);
118 for (Map.Entry<DetailNode, String> javadocTag : getMisorderedParamTags(ast)) {
119 log(javadocTag.getKey(), MSG_KEY, javadocTag.getValue());
120 }
121 }
122 }
123
124 @Override
125 public void visitJavadocToken(final DetailNode ast) {
126 collectParam(ast);
127 }
128
129
130
131
132
133
134 private void collectParam(final DetailNode ast) {
135 final DetailNode parameterName = JavadocUtil.findFirstToken(
136 ast, JavadocCommentsTokenTypes.PARAMETER_NAME);
137 if (parameterName != null) {
138 javadocTags.put(ast, parameterName.getText());
139 }
140 }
141
142
143
144
145
146
147
148 private List<Map.Entry<DetailNode, String>> getMisorderedParamTags(final DetailAST ast) {
149 final List<String> expectedParamOrder = getExpectedParamOrder(ast);
150 final List<Map.Entry<DetailNode, String>> misorderedTags = new ArrayList<>();
151
152 int maxIndexOfPreviousParam = -1;
153 for (Map.Entry<DetailNode, String> javadocTag : javadocTags.entrySet()) {
154 final int currentIndex = expectedParamOrder.indexOf(javadocTag.getValue());
155
156 if (currentIndex >= 0) {
157 if (currentIndex < maxIndexOfPreviousParam) {
158 misorderedTags.add(javadocTag);
159 }
160 else {
161 maxIndexOfPreviousParam = currentIndex;
162 }
163 }
164 }
165 return misorderedTags;
166 }
167
168
169
170
171
172
173
174 private static List<String> getExpectedParamOrder(final DetailAST ast) {
175 final List<String> expectedParamOrder = new ArrayList<>();
176
177 addTypeParameterNames(expectedParamOrder, ast);
178
179 switch (ast.getType()) {
180 case TokenTypes.METHOD_DEF, TokenTypes.CTOR_DEF ->
181 addParameterNames(expectedParamOrder, ast);
182
183 case TokenTypes.RECORD_DEF ->
184 addRecordComponentNames(expectedParamOrder, ast);
185
186 case TokenTypes.COMPACT_CTOR_DEF ->
187 addRecordComponentNames(expectedParamOrder, getRecordDef(ast));
188
189 default -> {
190
191 }
192 }
193
194 return expectedParamOrder;
195 }
196
197
198
199
200
201
202
203 private static void addTypeParameterNames(final Collection<String> paramNames,
204 final DetailAST ast) {
205 for (String typeParamName : CheckUtil.getTypeParameterNames(ast)) {
206 paramNames.add(ELEMENT_START + typeParamName + ELEMENT_END);
207 }
208 }
209
210
211
212
213
214
215
216 private static void addParameterNames(final Collection<String> paramNames,
217 final DetailAST ast) {
218 final DetailAST parameters = NullUtil.notNull(ast.findFirstToken(TokenTypes.PARAMETERS));
219 TokenUtil.forEachChild(parameters, TokenTypes.PARAMETER_DEF,
220 paramDef -> addParameterName(paramNames, paramDef));
221 }
222
223
224
225
226
227
228
229 private static void addParameterName(final Collection<String> paramNames,
230 final DetailAST paramDef) {
231 if (!CheckUtil.isReceiverParameter(paramDef)) {
232 final DetailAST ident = NullUtil.notNull(paramDef.findFirstToken(TokenTypes.IDENT));
233 paramNames.add(ident.getText());
234 }
235 }
236
237
238
239
240
241
242
243 private static void addRecordComponentNames(final Collection<String> paramNames,
244 final DetailAST recordDef) {
245 for (DetailAST component : getRecordComponents(recordDef)) {
246 paramNames.add(component.getText());
247 }
248 }
249
250
251
252
253
254
255
256 private static DetailAST getRecordDef(final DetailAST ast) {
257 DetailAST current = ast;
258 while (current.getType() != TokenTypes.RECORD_DEF) {
259 current = current.getParent();
260 }
261 return current;
262 }
263
264
265
266
267
268
269
270 private static List<DetailAST> getRecordComponents(final DetailAST recordDef) {
271 final List<DetailAST> components = new ArrayList<>();
272 final DetailAST recordDecl = NullUtil.notNull(
273 recordDef.findFirstToken(TokenTypes.RECORD_COMPONENTS));
274
275 DetailAST child = recordDecl.getFirstChild();
276 while (child != null) {
277 if (child.getType() == TokenTypes.RECORD_COMPONENT_DEF) {
278 components.add(NullUtil.notNull(child.findFirstToken(TokenTypes.IDENT)));
279 }
280 child = child.getNextSibling();
281 }
282 return components;
283 }
284
285 }