1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2026 the original author or authors.
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ///////////////////////////////////////////////////////////////////////////////////////////////
19
20 package com.puppycrawl.tools.checkstyle.checks.javadoc;
21
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.regex.Pattern;
25
26 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
27 import com.puppycrawl.tools.checkstyle.PropertyType;
28 import com.puppycrawl.tools.checkstyle.XdocsPropertyType;
29 import com.puppycrawl.tools.checkstyle.api.DetailNode;
30 import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes;
31 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
32 import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
33
34 /**
35 * <div>
36 * Checks that Javadoc comments do not match a configured regular expression.
37 * </div>
38 *
39 * <p>
40 * The check can operate on raw Javadoc source or on the text visible to readers.
41 * Raw matching includes Javadoc tags, HTML tags, HTML attributes, and formatting
42 * characters. Visible-text matching ignores markup and matches only Javadoc text content.
43 * </p>
44 *
45 * @since 13.10.0
46 */
47 @FileStatefulCheck
48 public class JavadocRegexpCheck extends AbstractJavadocCheck {
49
50 /**
51 * A key is pointing to the warning message text in "messages.properties" file.
52 */
53 public static final String MSG_JAVADOC_REGEXP = "javadoc.regexp.match";
54
55 /**
56 * Text fragments visible to readers of the current Javadoc comment.
57 */
58 private final List<String> visibleText = new ArrayList<>();
59
60 /**
61 * Specify the regular expression to match forbidden Javadoc content.
62 */
63 @XdocsPropertyType(PropertyType.PATTERN)
64 private String format = "^$";
65
66 /**
67 * Control whether to ignore case when matching.
68 */
69 private boolean ignoreCase;
70
71 /**
72 * Control whether to ignore Javadoc and HTML markup when matching.
73 */
74 private boolean ignoreMarkup;
75
76 /**
77 * Line number of the previous collected text node.
78 */
79 private int previousTextLineNumber;
80
81 /**
82 * Creates a new {@code JavadocRegexpCheck} instance.
83 */
84 public JavadocRegexpCheck() {
85 // no code by default
86 }
87
88 /**
89 * Setter to specify the regular expression to match forbidden Javadoc content.
90 *
91 * @param format regular expression to match forbidden Javadoc content.
92 * @since 13.10.0
93 */
94 public void setFormat(String format) {
95 this.format = format;
96 }
97
98 /**
99 * Setter to control whether to ignore case when matching.
100 *
101 * @param ignoreCase whether to ignore case when matching.
102 * @since 13.10.0
103 */
104 public void setIgnoreCase(boolean ignoreCase) {
105 this.ignoreCase = ignoreCase;
106 }
107
108 /**
109 * Setter to control whether to ignore Javadoc and HTML markup when matching.
110 *
111 * @param ignoreMarkup whether to ignore Javadoc and HTML markup when matching.
112 * @since 13.10.0
113 */
114 public void setIgnoreMarkup(boolean ignoreMarkup) {
115 this.ignoreMarkup = ignoreMarkup;
116 }
117
118 @Override
119 public int[] getRequiredJavadocTokens() {
120 return new int[] {
121 JavadocCommentsTokenTypes.TEXT,
122 };
123 }
124
125 @Override
126 public int[] getDefaultJavadocTokens() {
127 return getRequiredJavadocTokens();
128 }
129
130 @Override
131 public void beginJavadocTree(DetailNode rootAst) {
132 visibleText.clear();
133 }
134
135 @Override
136 public void visitJavadocToken(DetailNode ast) {
137 if (!isIgnoredText(ast)) {
138 appendVisibleText(ast);
139 }
140 }
141
142 @Override
143 public void finishJavadocTree(DetailNode rootAst) {
144 final String content;
145 if (ignoreMarkup) {
146 content = String.join("", visibleText);
147 }
148 else {
149 content = JavadocUtil.getJavadocCommentContent(getBlockCommentAst());
150 }
151
152 final Pattern regexp = createRegexp();
153 if (regexp.matcher(content).find()) {
154 log(rootAst.getLineNumber(), MSG_JAVADOC_REGEXP, regexp.pattern());
155 }
156 }
157
158 /**
159 * Creates the regexp based on {@link #format} and {@link #ignoreCase}.
160 *
161 * @return pattern used to find forbidden content.
162 */
163 private Pattern createRegexp() {
164 final int compileFlags;
165 if (ignoreCase) {
166 compileFlags = Pattern.CASE_INSENSITIVE;
167 }
168 else {
169 compileFlags = 0;
170 }
171 return CommonUtil.createPattern(format, compileFlags);
172 }
173
174 /**
175 * Appends visible text from a Javadoc text node.
176 *
177 * @param textNode text node to append.
178 */
179 private void appendVisibleText(DetailNode textNode) {
180 if (!visibleText.isEmpty() && previousTextLineNumber < textNode.getLineNumber()) {
181 visibleText.add(" ");
182 }
183 visibleText.add(textNode.getText());
184 previousTextLineNumber = textNode.getLineNumber();
185 }
186
187 /**
188 * Checks whether a text node belongs to a non-rendered HTML comment.
189 *
190 * @param textNode text node to check.
191 * @return {@code true} if the text node should be ignored.
192 */
193 private static boolean isIgnoredText(DetailNode textNode) {
194 boolean result = false;
195 DetailNode current = textNode;
196
197 while (current != null) {
198 if (current.getType() == JavadocCommentsTokenTypes.HTML_COMMENT) {
199 result = true;
200 break;
201 }
202 current = current.getParent();
203 }
204
205 return result;
206 }
207
208 }