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.List;
17 import java.util.function.Consumer;
18 import java.util.regex.Matcher;
19 import java.util.regex.Pattern;
20
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 public JavadocParameterOrderCheck() {
55
56 }
57
58 @Override
59 public int[] getDefaultTokens() {
60 return new int[] {
61 TokenTypes.INTERFACE_DEF,
62 TokenTypes.CLASS_DEF,
63 TokenTypes.CTOR_DEF,
64 TokenTypes.METHOD_DEF,
65 };
66 }
67
68 @Override
69 public int[] getAcceptableTokens() {
70 return this.getDefaultTokens();
71 }
72
73 @Override
74 public int[] getRequiredTokens() {
75 return this.getDefaultTokens();
76 }
77
78 @Override
79 @SuppressWarnings("deprecation")
80 public void visitToken(final DetailAST ast) {
81 final FileContents contents = this.getFileContents();
82 final TextBlock doc = contents.getJavadocBefore(ast.getLineNo());
83 if (doc != null) {
84 this.checkParameters(ast, doc);
85 }
86 }
87
88 private static List<JavadocTag> getMethodTags(final TextBlock comment) {
89 final String[] lines = comment.getText();
90 final List<JavadocTag> tags = new ArrayList<>(0);
91 int current = comment.getStartLineNo() - 1;
92 final int start = comment.getStartColNo();
93 for (int line = 0; line < lines.length; line = line + 1) {
94 current = current + 1;
95 final Matcher docmatcher =
96 JavadocParameterOrderCheck.MATCH_JAVADOC_ARG.matcher(lines[line]);
97 final Matcher multiline =
98 JavadocParameterOrderCheck.MATCH_JAVADOC_ARG_MULTILINE_START
99 .matcher(lines[line]);
100 if (docmatcher.find()) {
101 final int col = calculateTagColumn(
102 docmatcher, line, start
103 );
104 tags.add(
105 new JavadocTag(
106 current,
107 col,
108 docmatcher.group(1),
109 docmatcher.group(2)
110 )
111 );
112 } else if (multiline.find()) {
113 final int col =
114 calculateTagColumn(
115 multiline,
116 line,
117 start
118 );
119 tags.addAll(
120 getMultilineArgTags(
121 multiline,
122 col,
123 lines,
124 line,
125 current
126 )
127 );
128 }
129 }
130 return tags;
131 }
132
133 private static int calculateTagColumn(
134 final Matcher matcher, final int line, final int start
135 ) {
136 int col = matcher.start(1) - 1;
137 if (line == 0) {
138 col += start;
139 }
140 return col;
141 }
142
143 private static List<JavadocTag> getMultilineArgTags(
144 final Matcher matcher, final int column, final String[] lines,
145 final int index, final int line) {
146 final List<JavadocTag> tags = new ArrayList<>(0);
147 final String paramone = matcher.group(1);
148 final String paramtwo = matcher.group(2);
149 int remindex = index + 1;
150 while (remindex < lines.length) {
151 final Matcher multiline =
152 JavadocParameterOrderCheck.MATCH_JAVADOC_MULTILINE_CONT
153 .matcher(lines[remindex]);
154 if (multiline.find()) {
155 remindex = lines.length;
156 final String lfin = multiline.group(1);
157 if (!"@".equals(lfin) && !"*/".equals(lfin)) {
158 tags.add(new JavadocTag(line, column, paramone, paramtwo));
159 }
160 }
161 remindex = remindex + 1;
162 }
163 return tags;
164 }
165
166 private void checkParameters(final DetailAST ast, final TextBlock doc) {
167 final List<JavadocTag> tags = getMethodTags(doc);
168 final Arguments args = new Arguments(ast);
169 final TypeParameters types = new TypeParameters(ast);
170 final int count = args.count() + types.count();
171 if (tags.size() == count) {
172 final Consumer<JavadocTag> logger = tag -> this.log(
173 tag.getLineNo(),
174 "Javadoc parameter order different than method signature"
175 );
176 args.checkOrder(tags, logger);
177 types.checkOrder(tags, logger);
178 } else {
179 this.log(
180 ast.getLineNo(),
181 "Number of javadoc parameters different than method signature"
182 );
183 }
184 }
185 }