001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2022 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 com.puppycrawl.tools.checkstyle.StatelessCheck; 023import com.puppycrawl.tools.checkstyle.api.DetailNode; 024import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes; 025import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 026import com.puppycrawl.tools.checkstyle.utils.JavadocUtil; 027 028/** 029 * <p> 030 * Checks that the block tag is followed by description. 031 * </p> 032 * <ul> 033 * <li> 034 * Property {@code violateExecutionOnNonTightHtml} - Control when to print violations 035 * if the Javadoc being examined by this check violates the tight html rules defined at 036 * <a href="https://checkstyle.org/writingjavadocchecks.html#Tight-HTML_rules">Tight-HTML Rules</a>. 037 * Type is {@code boolean}. 038 * Default value is {@code false}. 039 * </li> 040 * <li> 041 * Property {@code javadocTokens} - javadoc tokens to check 042 * Type is {@code java.lang.String[]}. 043 * Validation type is {@code tokenSet}. 044 * Default value is 045 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/JavadocTokenTypes.html#PARAM_LITERAL"> 046 * PARAM_LITERAL</a>, 047 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/JavadocTokenTypes.html#RETURN_LITERAL"> 048 * RETURN_LITERAL</a>, 049 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/JavadocTokenTypes.html#THROWS_LITERAL"> 050 * THROWS_LITERAL</a>, 051 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/JavadocTokenTypes.html#EXCEPTION_LITERAL"> 052 * EXCEPTION_LITERAL</a>, 053 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/JavadocTokenTypes.html#DEPRECATED_LITERAL"> 054 * DEPRECATED_LITERAL</a>. 055 * </li> 056 * </ul> 057 * <p> 058 * To configure the default check that will check {@code @param}, {@code @return}, 059 * {@code @throws}, {@code @deprecated}: 060 * </p> 061 * <pre> 062 * <module name="NonEmptyAtclauseDescription"/> 063 * </pre> 064 * <p> 065 * Example: 066 * </p> 067 * <pre> 068 * class Test 069 * { 070 * /** 071 * * Violation for param "b" and at tags "deprecated", "throws". 072 * * @param a Some javadoc // OK 073 * * @param b 074 * * @deprecated 075 * * @throws Exception 076 * */ 077 * public int method(String a, int b) throws Exception 078 * { 079 * return 1; 080 * } 081 * } 082 * </pre> 083 * <p> 084 * To configure the check to validate only {@code @param} and {@code @return} tags: 085 * </p> 086 * <pre> 087 * <module name="NonEmptyAtclauseDescription"> 088 * <property name="javadocTokens" value="PARAM_LITERAL,RETURN_LITERAL"/> 089 * </module> 090 * </pre> 091 * <p> 092 * Example: 093 * </p> 094 * <pre> 095 * class Test 096 * { 097 * /** 098 * * Violation for param "b". Tags "deprecated", "throws" are ignored. 099 * * @param a Some javadoc // OK 100 * * @param b 101 * * @deprecated 102 * * @throws Exception 103 * */ 104 * public int method(String a, int b) throws Exception 105 * { 106 * return 1; 107 * } 108 * } 109 * </pre> 110 * <p> 111 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 112 * </p> 113 * <p> 114 * Violation Message Keys: 115 * </p> 116 * <ul> 117 * <li> 118 * {@code javadoc.missed.html.close} 119 * </li> 120 * <li> 121 * {@code javadoc.parse.rule.error} 122 * </li> 123 * <li> 124 * {@code javadoc.wrong.singleton.html.tag} 125 * </li> 126 * <li> 127 * {@code non.empty.atclause} 128 * </li> 129 * </ul> 130 * 131 * @since 6.0 132 */ 133@StatelessCheck 134public class NonEmptyAtclauseDescriptionCheck extends AbstractJavadocCheck { 135 136 /** 137 * A key is pointing to the warning message text in "messages.properties" 138 * file. 139 */ 140 public static final String MSG_KEY = "non.empty.atclause"; 141 142 @Override 143 public int[] getDefaultJavadocTokens() { 144 return new int[] { 145 JavadocTokenTypes.PARAM_LITERAL, 146 JavadocTokenTypes.RETURN_LITERAL, 147 JavadocTokenTypes.THROWS_LITERAL, 148 JavadocTokenTypes.EXCEPTION_LITERAL, 149 JavadocTokenTypes.DEPRECATED_LITERAL, 150 }; 151 } 152 153 @Override 154 public void visitJavadocToken(DetailNode ast) { 155 if (isEmptyTag(ast.getParent())) { 156 log(ast.getLineNumber(), MSG_KEY, ast.getText()); 157 } 158 } 159 160 /** 161 * Tests if block tag is empty. 162 * 163 * @param tagNode block tag. 164 * @return true, if block tag is empty. 165 */ 166 private static boolean isEmptyTag(DetailNode tagNode) { 167 final DetailNode tagDescription = 168 JavadocUtil.findFirstToken(tagNode, JavadocTokenTypes.DESCRIPTION); 169 return tagDescription == null 170 || hasOnlyEmptyText(tagDescription); 171 } 172 173 /** 174 * Tests if description node is empty (has only new lines and blank strings). 175 * 176 * @param description description node. 177 * @return true, if description node has only new lines and blank strings. 178 */ 179 private static boolean hasOnlyEmptyText(DetailNode description) { 180 boolean result = true; 181 for (DetailNode child : description.getChildren()) { 182 if (child.getType() != JavadocTokenTypes.TEXT 183 || !CommonUtil.isBlank(child.getText())) { 184 result = false; 185 break; 186 } 187 } 188 return result; 189 } 190 191}