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.site; 021 022import java.beans.Introspector; 023import java.util.regex.Pattern; 024 025import com.puppycrawl.tools.checkstyle.FileStatefulCheck; 026import com.puppycrawl.tools.checkstyle.api.DetailAST; 027import com.puppycrawl.tools.checkstyle.api.DetailNode; 028import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes; 029import com.puppycrawl.tools.checkstyle.api.TokenTypes; 030import com.puppycrawl.tools.checkstyle.checks.javadoc.AbstractJavadocCheck; 031import com.puppycrawl.tools.checkstyle.utils.BlockCommentPosition; 032import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 033 034/** 035 * Class for scraping class javadoc and all property setter javadocs from the 036 * given checkstyle module. 037 */ 038@FileStatefulCheck 039public class ClassAndPropertiesSettersJavadocScraper extends AbstractJavadocCheck { 040 041 /** Name of the module being scraped. */ 042 private static String moduleName = ""; 043 044 /** 045 * Initialize the scraper. Clears static context and sets the module name. 046 * 047 * @param newModuleName the module name. 048 */ 049 public static void initialize(String newModuleName) { 050 JavadocScraperResultUtil.clearData(); 051 moduleName = newModuleName; 052 } 053 054 @Override 055 public int[] getDefaultJavadocTokens() { 056 return new int[] { 057 JavadocCommentsTokenTypes.JAVADOC_CONTENT, 058 }; 059 } 060 061 @Override 062 public void visitJavadocToken(DetailNode ast) { 063 final DetailAST blockCommentAst = getBlockCommentAst(); 064 if (BlockCommentPosition.isOnMethod(blockCommentAst)) { 065 final DetailAST methodDef = getParentAst(blockCommentAst, TokenTypes.METHOD_DEF); 066 if (methodDef != null 067 && isSetterMethod(methodDef) 068 && isMethodOfScrapedModule(methodDef)) { 069 final String methodName = TokenUtil.getIdent(methodDef).getText(); 070 final String propertyName = getPropertyName(methodName); 071 JavadocScraperResultUtil.putPropertyJavadocNode(propertyName, ast); 072 } 073 074 } 075 else if (BlockCommentPosition.isOnClass(blockCommentAst)) { 076 final DetailAST classDef = getParentAst(blockCommentAst, TokenTypes.CLASS_DEF); 077 if (classDef != null) { 078 final String className = TokenUtil.getIdent(classDef).getText(); 079 if (className.equals(moduleName)) { 080 JavadocScraperResultUtil.setModuleJavadocNode(ast); 081 } 082 } 083 } 084 } 085 086 /** 087 * Checks if the given method is a method of the module being scraped. Traverses 088 * parent nodes until it finds the class definition and checks if the class name 089 * is the same as the module name. We want to avoid scraping javadocs from 090 * inner classes. 091 * 092 * @param methodDef the method definition. 093 * @return true if the method is a method of the given module, false otherwise. 094 */ 095 private static boolean isMethodOfScrapedModule(DetailAST methodDef) { 096 final DetailAST classDef = getParentAst(methodDef, TokenTypes.CLASS_DEF); 097 098 boolean isMethodOfModule = false; 099 if (classDef != null) { 100 final String className = TokenUtil.getIdent(classDef).getText(); 101 isMethodOfModule = className.equals(moduleName); 102 } 103 104 return isMethodOfModule; 105 } 106 107 /** 108 * Get the parent node of the given type. Traverses up the tree until it finds 109 * the given type. 110 * 111 * @param ast the node to start traversing from. 112 * @param type the type of the parent node to find. 113 * @return the parent node of the given type, or null if not found. 114 */ 115 private static DetailAST getParentAst(DetailAST ast, int type) { 116 DetailAST node = ast.getParent(); 117 118 while (node != null && node.getType() != type) { 119 node = node.getParent(); 120 } 121 122 return node; 123 } 124 125 /** 126 * Get the property name from the setter method name. For example, getPropertyName("setFoo") 127 * returns "foo". This method removes the "set" prefix and decapitalizes the first letter 128 * of the property name. 129 * 130 * @param setterName the setter method name. 131 * @return the property name. 132 */ 133 private static String getPropertyName(String setterName) { 134 return Introspector.decapitalize(setterName.substring("set".length())); 135 } 136 137 /** 138 * Returns whether an AST represents a setter method. 139 * 140 * @param ast the AST to check with 141 * @return whether the AST represents a setter method 142 */ 143 private static boolean isSetterMethod(DetailAST ast) { 144 boolean setterMethod = false; 145 146 if (ast.getType() == TokenTypes.METHOD_DEF) { 147 final DetailAST type = ast.findFirstToken(TokenTypes.TYPE); 148 final String name = type.getNextSibling().getText(); 149 final Pattern setterPattern = Pattern.compile("^set[A-Z].*"); 150 151 setterMethod = setterPattern.matcher(name).matches(); 152 } 153 return setterMethod; 154 } 155}