001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2023 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.Optional; 023 024import com.puppycrawl.tools.checkstyle.StatelessCheck; 025import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 026import com.puppycrawl.tools.checkstyle.api.DetailAST; 027import com.puppycrawl.tools.checkstyle.api.TokenTypes; 028import com.puppycrawl.tools.checkstyle.utils.CheckUtil; 029import com.puppycrawl.tools.checkstyle.utils.JavadocUtil; 030 031/** 032 * <p> 033 * Checks for missing package definition Javadoc comments in package-info.java files. 034 * </p> 035 * <p> 036 * Rationale: description and other related documentation for a package can be written up 037 * in the package-info.java file and it gets used in the production of the Javadocs. 038 * See <a 039 * href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#packagecomment" 040 * >link</a> for more info. 041 * </p> 042 * <p> 043 * This check specifically only validates package definitions. It will not validate any methods or 044 * interfaces declared in the package-info.java file. 045 * </p> 046 * <p> 047 * To configure the check: 048 * </p> 049 * <pre> 050 * <module name="MissingJavadocPackage"/> 051 * </pre> 052 * <p>Example:</p> 053 * <pre> 054 * /** 055 * * Provides API classes 056 * */ 057 * package com.checkstyle.api; // no violation 058 * /* 059 * * Block comment is not a javadoc 060 * */ 061 * package com.checkstyle.api; // violation 062 * </pre> 063 * <p> 064 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 065 * </p> 066 * <p> 067 * Violation Message Keys: 068 * </p> 069 * <ul> 070 * <li> 071 * {@code package.javadoc.missing} 072 * </li> 073 * </ul> 074 * 075 * @since 8.22 076 */ 077@StatelessCheck 078public class MissingJavadocPackageCheck extends AbstractCheck { 079 080 /** 081 * A key is pointing to the warning message text in "messages.properties" 082 * file. 083 */ 084 public static final String MSG_PKG_JAVADOC_MISSING = "package.javadoc.missing"; 085 086 @Override 087 public int[] getDefaultTokens() { 088 return getRequiredTokens(); 089 } 090 091 @Override 092 public int[] getAcceptableTokens() { 093 return getRequiredTokens(); 094 } 095 096 @Override 097 public int[] getRequiredTokens() { 098 return new int[] {TokenTypes.PACKAGE_DEF}; 099 } 100 101 @Override 102 public boolean isCommentNodesRequired() { 103 return true; 104 } 105 106 @Override 107 public void visitToken(DetailAST ast) { 108 if (CheckUtil.isPackageInfo(getFilePath()) && !hasJavadoc(ast)) { 109 log(ast, MSG_PKG_JAVADOC_MISSING); 110 } 111 } 112 113 /** 114 * Checks that there is javadoc before ast. 115 * Because of <a href="https://github.com/checkstyle/checkstyle/issues/4392">parser bug</a> 116 * parser can place javadoc comment either as previous sibling of package definition 117 * or (if there is annotation between package def and javadoc) inside package definition tree. 118 * So we should look for javadoc in both places. 119 * 120 * @param ast {@link TokenTypes#PACKAGE_DEF} token to check 121 * @return true if there is javadoc, false otherwise 122 */ 123 private static boolean hasJavadoc(DetailAST ast) { 124 final boolean hasJavadocBefore = ast.getPreviousSibling() != null 125 && isJavadoc(ast.getPreviousSibling()); 126 return hasJavadocBefore || hasJavadocAboveAnnotation(ast); 127 } 128 129 /** 130 * Checks javadoc existence in annotations list. 131 * 132 * @param ast package def 133 * @return true if there is a javadoc, false otherwise 134 */ 135 private static boolean hasJavadocAboveAnnotation(DetailAST ast) { 136 final Optional<DetailAST> firstAnnotationChild = Optional.of(ast.getFirstChild()) 137 .map(DetailAST::getFirstChild) 138 .map(DetailAST::getFirstChild); 139 boolean result = false; 140 if (firstAnnotationChild.isPresent()) { 141 for (DetailAST child = firstAnnotationChild.get(); child != null; 142 child = child.getNextSibling()) { 143 if (isJavadoc(child)) { 144 result = true; 145 break; 146 } 147 } 148 } 149 return result; 150 } 151 152 /** 153 * Checks that ast is a javadoc comment. 154 * 155 * @param ast token to check 156 * @return true if ast is a javadoc comment, false otherwise 157 */ 158 private static boolean isJavadoc(DetailAST ast) { 159 return ast.getType() == TokenTypes.BLOCK_COMMENT_BEGIN && JavadocUtil.isJavadocComment(ast); 160 } 161}