Skip to content

Commit 8c8068c

Browse files
Vladimir.ShapkinVladimir.Shapkin
authored andcommitted
Fixed different behavior between @param and @SInCE
The @param regex allows varying numbers of spaces before and after the @param tag, while the @SInCE tag only allows one space before and one after it.
1 parent ad66e10 commit 8c8068c

File tree

6 files changed

+345
-64
lines changed

6 files changed

+345
-64
lines changed

qulice-checkstyle/src/main/java/com/qulice/checkstyle/JavadocTagsCheck.java

Lines changed: 9 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,7 @@
3636
import java.util.ArrayList;
3737
import java.util.Arrays;
3838
import java.util.Collection;
39-
import java.util.HashMap;
4039
import java.util.List;
41-
import java.util.Map;
4240
import java.util.regex.Pattern;
4341

4442
/**
@@ -69,7 +67,7 @@ public final class JavadocTagsCheck extends AbstractCheck {
6967
/**
7068
* Map of tag and its pattern.
7169
*/
72-
private final Map<String, Pattern> tags = new HashMap<>();
70+
private final List<RequiredJavaDocTag> required = new ArrayList<>(1);
7371

7472
/**
7573
* List of prohibited javadoc tags.
@@ -97,10 +95,13 @@ public int[] getRequiredTokens() {
9795

9896
@Override
9997
public void init() {
100-
this.tags.put(
101-
"since",
102-
Pattern.compile(
98+
this.required.add(
99+
new RequiredJavaDocTag(
100+
"since",
101+
Pattern.compile(
103102
"^\\d+(\\.\\d+){1,2}(\\.[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?$"
103+
),
104+
this::log
104105
)
105106
);
106107
}
@@ -115,25 +116,14 @@ public void visitToken(final DetailAST ast) {
115116
for (final String tag : this.prohibited) {
116117
this.findProhibited(lines, start, cstart, cend, tag);
117118
}
118-
for (final String tag : this.tags.keySet()) {
119-
this.matchTagFormat(lines, cstart, cend, tag);
119+
for (final RequiredJavaDocTag tag : this.required) {
120+
tag.matchTagFormat(lines, cstart, cend);
120121
}
121122
} else {
122123
this.log(0, "Problem finding class/interface comment");
123124
}
124125
}
125126

126-
/**
127-
* Get the text of the given tag.
128-
* @param line Line with the tag.
129-
* @return The text of the tag.
130-
*/
131-
private static String getTagText(final String line) {
132-
return line.substring(
133-
line.indexOf(' ', line.indexOf('@')) + 1
134-
);
135-
}
136-
137127
/**
138128
* Find a text in lines, by going up.
139129
* @param lines List of lines to check.
@@ -195,38 +185,6 @@ private void findProhibited(final String[] lines, final int start,
195185
}
196186
}
197187

198-
/**
199-
* Check if the tag text matches the format from pattern.
200-
* @param lines List of all lines.
201-
* @param start Line number where comment starts.
202-
* @param end Line number where comment ends.
203-
* @param tag Name of the tag.
204-
* @checkstyle ParameterNumber (3 lines)
205-
*/
206-
private void matchTagFormat(final String[] lines, final int start,
207-
final int end, final String tag) {
208-
final List<Integer> found = this.findTagLineNum(lines, start, end, tag);
209-
if (found.isEmpty()) {
210-
this.log(
211-
start + 1,
212-
"Missing ''@{0}'' tag in class/interface comment",
213-
tag
214-
);
215-
return;
216-
}
217-
for (final Integer item : found) {
218-
final String text = JavadocTagsCheck.getTagText(lines[item]);
219-
if (!this.tags.get(tag).matcher(text).matches()) {
220-
this.log(
221-
item + 1,
222-
"Tag text ''{0}'' does not match the pattern ''{1}''",
223-
text,
224-
this.tags.get(tag).toString()
225-
);
226-
}
227-
}
228-
}
229-
230188
/**
231189
* Find given tag in comment lines.
232190
* @param lines Lines to search for the tag.
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
* Copyright (c) 2011-2024 Qulice.com
3+
*
4+
* All rights reserved.
5+
*
6+
* Redistribution and use in source and binary forms, with or without
7+
* modification, are permitted provided that the following conditions
8+
* are met: 1) Redistributions of source code must retain the above
9+
* copyright notice, this list of conditions and the following
10+
* disclaimer. 2) Redistributions in binary form must reproduce the above
11+
* copyright notice, this list of conditions and the following
12+
* disclaimer in the documentation and/or other materials provided
13+
* with the distribution. 3) Neither the name of the Qulice.com nor
14+
* the names of its contributors may be used to endorse or promote
15+
* products derived from this software without specific prior written
16+
* permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
20+
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21+
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22+
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23+
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27+
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29+
* OF THE POSSIBILITY OF SUCH DAMAGE.
30+
*/
31+
package com.qulice.checkstyle;
32+
33+
import java.util.HashMap;
34+
import java.util.Map;
35+
import java.util.regex.Matcher;
36+
import java.util.regex.Pattern;
37+
38+
/**
39+
* Check the required JavaDoc tag in the lines.
40+
* <p>Correct format is the following (of a class javadoc):
41+
*
42+
* <pre>
43+
* &#47;**
44+
* * This is my new class.
45+
* *
46+
* * &#64;since 0.3
47+
* *&#47;
48+
* public final class Foo {
49+
* &#47;**
50+
* * This is my other class.
51+
* *
52+
* * &#64;since 0.3
53+
* *&#47;
54+
* public final class Boo {
55+
* // ...
56+
* </pre>
57+
*
58+
* <p>"&#36;Id&#36;" will be replaced by a full text automatically
59+
* by Subversion as explained in their documentation (see link below).
60+
*
61+
* @see <a href="http://svnbook.red-bean.com/en/1.4/svn.advanced.props.special.keywords.html">Keywords substitution in Subversion</a>
62+
63+
* @since 0.23.1
64+
*/
65+
final class RequiredJavaDocTag {
66+
/**
67+
* Tag name.
68+
*/
69+
private final String name;
70+
71+
/**
72+
* Pattern for searching a tag in a string.
73+
*/
74+
private final Pattern tag;
75+
76+
/**
77+
* Pattern for checking the contents of a tag in a string.
78+
*/
79+
private final Pattern content;
80+
81+
/**
82+
* Reference to a method for writing a message to the log.
83+
*/
84+
private final LogWriter writer;
85+
86+
/**
87+
* Ctor.
88+
* @param name Tag name.
89+
* @param patt Pattern for checking the contents of a tag in a string.
90+
* @param lwriter Reference to a method for writing a message to the log.
91+
*/
92+
RequiredJavaDocTag(final String name, final Pattern patt,
93+
final LogWriter lwriter) {
94+
this(
95+
name,
96+
Pattern.compile(
97+
String.format(
98+
"(?<name>^ +\\* +@%s)( +)(?<cont>.*)",
99+
name
100+
)
101+
),
102+
patt,
103+
lwriter
104+
);
105+
}
106+
107+
/**
108+
* Ctor.
109+
* @param cname Tag name.
110+
* @param ptag Pattern for searching a tag in a string.
111+
* @param patt Pattern for checking the contents of a tag in a string.
112+
* @param lwriter Reference to a method for writing a message to the log.
113+
* @checkstyle ParameterNumberCheck (3 lines)
114+
*/
115+
RequiredJavaDocTag(final String cname, final Pattern ptag,
116+
final Pattern patt, final LogWriter lwriter) {
117+
this.name = cname;
118+
this.tag = ptag;
119+
this.content = patt;
120+
this.writer = lwriter;
121+
}
122+
123+
/**
124+
* Check if the tag text matches the format from pattern.
125+
* @param lines List of all lines.
126+
* @param start Line number where comment starts.
127+
* @param end Line number where comment ends.
128+
*/
129+
public void matchTagFormat(final String[] lines, final int start,
130+
final int end) {
131+
final Map<Integer, String> found = new HashMap<>(1);
132+
for (int pos = start; pos <= end; pos += 1) {
133+
final String line = lines[pos];
134+
final Matcher matcher = this.tag.matcher(line);
135+
if (matcher.matches()
136+
&& !RequiredJavaDocTag.empty(matcher.group("name"))
137+
&& !RequiredJavaDocTag.empty(matcher.group("cont"))
138+
) {
139+
found.put(pos, matcher.group("cont"));
140+
break;
141+
}
142+
}
143+
if (found.isEmpty()) {
144+
this.writer.log(
145+
start + 1,
146+
"Missing ''@{0}'' tag in class/interface comment",
147+
this.name
148+
);
149+
} else {
150+
for (final Map.Entry<Integer, String> item : found.entrySet()) {
151+
if (!this.content.matcher(item.getValue()).matches()) {
152+
this.writer.log(
153+
item.getKey() + 1,
154+
"Tag text ''{0}'' does not match the pattern ''{1}''",
155+
item.getValue(),
156+
this.content.toString()
157+
);
158+
}
159+
}
160+
}
161+
}
162+
163+
/**
164+
* Checks for an empty string.
165+
* @param str Line to check.
166+
* @return True if str is empty.
167+
*/
168+
private static boolean empty(final String str) {
169+
return str == null || str.isBlank();
170+
}
171+
172+
/**
173+
* Logger.
174+
* @see com.puppycrawl.tools.checkstyle.api.AbstractCheck.log
175+
* @since 0.23.1
176+
*/
177+
interface LogWriter {
178+
/**
179+
* Log a message that has no column information.
180+
*
181+
* @param line The line number where the audit event was found.
182+
* @param msg The message that describes the audit event.
183+
* @param args The details of the message.
184+
* @see java.text.MessageFormat
185+
*/
186+
void log(int line, String msg, Object... args);
187+
}
188+
}

qulice-checkstyle/src/main/resources/com/qulice/checkstyle/checks.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,25 +104,25 @@ OF THE POSSIBILITY OF SUCH DAMAGE.
104104
JavaDoc regexp checks
105105
-->
106106
<module name="RegexpSingleline">
107-
<property name="format" value="\* +@return +[^A-Z]"/>
107+
<property name="format" value="\* +@return +[^A-Z ]"/>
108108
<property name="fileExtensions" value="java"/>
109109
<property name="id" value="ReturnShouldStartWithCapitalLetter"/>
110110
<property name="message" value="@return tag description should start with capital letter"/>
111111
</module>
112112
<module name="RegexpSingleline">
113-
<property name="format" value="\* +@param +\w+ +[^A-Z]"/>
113+
<property name="format" value="\* +@param +\w+ +[^A-Z ]"/>
114114
<property name="fileExtensions" value="java"/>
115115
<property name="id" value="ParamShouldStartWithCapitalLetter"/>
116116
<property name="message" value="@param tag description should start with capital letter"/>
117117
</module>
118118
<module name="RegexpSingleline">
119-
<property name="format" value="/\*\* +[^A-Z\{]"/>
119+
<property name="format" value="/\*\* +[^A-Z\{ ]"/>
120120
<property name="fileExtensions" value="java"/>
121121
<property name="id" value="FirstLetterInCommentShouldBeCapital"/>
122122
<property name="message" value="First sentence in a comment should start with a capital letter"/>
123123
</module>
124124
<module name="RegexpMultiline">
125-
<property name="format" value="/\*\*\W+\* +[^A-Z\{]"/>
125+
<property name="format" value="/\*\*\W+\* +[^A-Z\{ ]"/>
126126
<property name="fileExtensions" value="java"/>
127127
<property name="id" value="FirstLetterInCommentShouldBeCapital2"/>
128128
<property name="message" value="First sentence in a comment should start with a capital letter"/>

qulice-checkstyle/src/test/java/com/qulice/checkstyle/CheckstyleValidatorTest.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@
4848
import org.hamcrest.TypeSafeMatcher;
4949
import org.hamcrest.collection.IsIterableContainingInOrder;
5050
import org.junit.jupiter.api.BeforeEach;
51-
import org.junit.jupiter.api.Disabled;
5251
import org.junit.jupiter.api.Test;
5352

5453
/**
@@ -776,7 +775,6 @@ void allowsUppercaseAbbreviationExceptions() throws Exception {
776775
*
777776
* @throws Exception In case of error
778777
*/
779-
@Disabled
780778
@Test
781779
void checkLambdaAndGenericsAtEndOfLine() throws Exception {
782780
this.runValidation("ValidLambdaAndGenericsAtEndOfLine.java", true);

0 commit comments

Comments
 (0)