001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018/////////////////////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.javadoc; 021 022import java.util.ArrayList; 023import java.util.List; 024import java.util.regex.Pattern; 025 026import com.puppycrawl.tools.checkstyle.FileStatefulCheck; 027import com.puppycrawl.tools.checkstyle.PropertyType; 028import com.puppycrawl.tools.checkstyle.XdocsPropertyType; 029import com.puppycrawl.tools.checkstyle.api.DetailNode; 030import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes; 031import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 032import com.puppycrawl.tools.checkstyle.utils.JavadocUtil; 033 034/** 035 * <div> 036 * Checks that Javadoc comments do not match a configured regular expression. 037 * </div> 038 * 039 * <p> 040 * The check can operate on raw Javadoc source or on the text visible to readers. 041 * Raw matching includes Javadoc tags, HTML tags, HTML attributes, and formatting 042 * characters. Visible-text matching ignores markup and matches only Javadoc text content. 043 * </p> 044 * 045 * @since 13.10.0 046 */ 047@FileStatefulCheck 048public class JavadocRegexpCheck extends AbstractJavadocCheck { 049 050 /** 051 * A key is pointing to the warning message text in "messages.properties" file. 052 */ 053 public static final String MSG_JAVADOC_REGEXP = "javadoc.regexp.match"; 054 055 /** 056 * Text fragments visible to readers of the current Javadoc comment. 057 */ 058 private final List<String> visibleText = new ArrayList<>(); 059 060 /** 061 * Specify the regular expression to match forbidden Javadoc content. 062 */ 063 @XdocsPropertyType(PropertyType.PATTERN) 064 private String format = "^$"; 065 066 /** 067 * Control whether to ignore case when matching. 068 */ 069 private boolean ignoreCase; 070 071 /** 072 * Control whether to ignore Javadoc and HTML markup when matching. 073 */ 074 private boolean ignoreMarkup; 075 076 /** 077 * Line number of the previous collected text node. 078 */ 079 private int previousTextLineNumber; 080 081 /** 082 * Creates a new {@code JavadocRegexpCheck} instance. 083 */ 084 public JavadocRegexpCheck() { 085 // no code by default 086 } 087 088 /** 089 * Setter to specify the regular expression to match forbidden Javadoc content. 090 * 091 * @param format regular expression to match forbidden Javadoc content. 092 * @since 13.10.0 093 */ 094 public void setFormat(String format) { 095 this.format = format; 096 } 097 098 /** 099 * 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}