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