001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2016 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.HashMap; 023import java.util.Map; 024 025import com.google.common.primitives.Ints; 026import com.puppycrawl.tools.checkstyle.JavadocDetailNodeParser; 027import com.puppycrawl.tools.checkstyle.JavadocDetailNodeParser.ParseErrorMessage; 028import com.puppycrawl.tools.checkstyle.JavadocDetailNodeParser.ParseStatus; 029import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 030import com.puppycrawl.tools.checkstyle.api.DetailAST; 031import com.puppycrawl.tools.checkstyle.api.DetailNode; 032import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes; 033import com.puppycrawl.tools.checkstyle.api.TokenTypes; 034import com.puppycrawl.tools.checkstyle.utils.BlockCommentPosition; 035import com.puppycrawl.tools.checkstyle.utils.JavadocUtils; 036 037/** 038 * Base class for Checks that process Javadoc comments. 039 * @author Baratali Izmailov 040 */ 041public abstract class AbstractJavadocCheck extends AbstractCheck { 042 043 /** 044 * Message key of error message. Missed close HTML tag breaks structure 045 * of parse tree, so parser stops parsing and generates such error 046 * message. This case is special because parser prints error like 047 * {@code "no viable alternative at input 'b \n *\n'"} and it is not 048 * clear that error is about missed close HTML tag. 049 */ 050 public static final String MSG_JAVADOC_MISSED_HTML_CLOSE = 051 JavadocDetailNodeParser.MSG_JAVADOC_MISSED_HTML_CLOSE; 052 053 /** 054 * Message key of error message. 055 */ 056 public static final String MSG_JAVADOC_WRONG_SINGLETON_TAG = 057 JavadocDetailNodeParser.MSG_JAVADOC_WRONG_SINGLETON_TAG; 058 059 /** 060 * Parse error while rule recognition. 061 */ 062 public static final String MSG_JAVADOC_PARSE_RULE_ERROR = 063 JavadocDetailNodeParser.MSG_JAVADOC_PARSE_RULE_ERROR; 064 065 /** 066 * Error message key for common javadoc errors. 067 */ 068 public static final String MSG_KEY_PARSE_ERROR = 069 JavadocDetailNodeParser.MSG_KEY_PARSE_ERROR; 070 /** 071 * Unrecognized error from antlr parser. 072 */ 073 public static final String MSG_KEY_UNRECOGNIZED_ANTLR_ERROR = 074 JavadocDetailNodeParser.MSG_KEY_UNRECOGNIZED_ANTLR_ERROR; 075 076 /** 077 * Key is "line:column". Value is {@link DetailNode} tree. Map is stored in {@link ThreadLocal} 078 * to guarantee basic thread safety and avoid shared, mutable state when not necessary. 079 */ 080 private static final ThreadLocal<Map<String, ParseStatus>> TREE_CACHE = 081 new ThreadLocal<Map<String, ParseStatus>>() { 082 @Override 083 protected Map<String, ParseStatus> initialValue() { 084 return new HashMap<>(); 085 } 086 }; 087 088 /** 089 * Parses content of Javadoc comment as DetailNode tree. 090 */ 091 private final JavadocDetailNodeParser parser = new JavadocDetailNodeParser(); 092 093 /** 094 * DetailAST node of considered Javadoc comment that is just a block comment 095 * in Java language syntax tree. 096 */ 097 private DetailAST blockCommentAst; 098 099 /** 100 * Returns the default token types a check is interested in. 101 * @return the default token types 102 * @see JavadocTokenTypes 103 */ 104 public abstract int[] getDefaultJavadocTokens(); 105 106 /** 107 * Called to process a Javadoc token. 108 * @param ast 109 * the token to process 110 */ 111 public abstract void visitJavadocToken(DetailNode ast); 112 113 /** 114 * Called before the starting to process a tree. 115 * @param rootAst 116 * the root of the tree 117 */ 118 public void beginJavadocTree(DetailNode rootAst) { 119 // No code by default, should be overridden only by demand at subclasses 120 } 121 122 /** 123 * Called after finished processing a tree. 124 * @param rootAst 125 * the root of the tree 126 */ 127 public void finishJavadocTree(DetailNode rootAst) { 128 // No code by default, should be overridden only by demand at subclasses 129 } 130 131 /** 132 * Called after all the child nodes have been process. 133 * @param ast 134 * the token leaving 135 */ 136 public void leaveJavadocToken(DetailNode ast) { 137 // No code by default, should be overridden only by demand at subclasses 138 } 139 140 /** 141 * Defined final to not allow JavadocChecks to change default tokens. 142 * @return default tokens 143 */ 144 @Override 145 public final int[] getDefaultTokens() { 146 return new int[] {TokenTypes.BLOCK_COMMENT_BEGIN }; 147 } 148 149 /** 150 * Defined final because all JavadocChecks require comment nodes. 151 * @return true 152 */ 153 @Override 154 public final boolean isCommentNodesRequired() { 155 return true; 156 } 157 158 @Override 159 public final void beginTree(DetailAST rootAST) { 160 TREE_CACHE.get().clear(); 161 } 162 163 @Override 164 public final void finishTree(DetailAST rootAST) { 165 TREE_CACHE.get().clear(); 166 } 167 168 @Override 169 public final void visitToken(DetailAST blockCommentNode) { 170 if (JavadocUtils.isJavadocComment(blockCommentNode) 171 && isCorrectJavadocPosition(blockCommentNode)) { 172 // store as field, to share with child Checks 173 blockCommentAst = blockCommentNode; 174 175 final String treeCacheKey = blockCommentNode.getLineNo() + ":" 176 + blockCommentNode.getColumnNo(); 177 178 final ParseStatus result; 179 180 if (TREE_CACHE.get().containsKey(treeCacheKey)) { 181 result = TREE_CACHE.get().get(treeCacheKey); 182 } 183 else { 184 result = parser.parseJavadocAsDetailNode(blockCommentNode); 185 TREE_CACHE.get().put(treeCacheKey, result); 186 } 187 188 if (result.getParseErrorMessage() == null) { 189 processTree(result.getTree()); 190 } 191 else { 192 final ParseErrorMessage parseErrorMessage = result.getParseErrorMessage(); 193 log(parseErrorMessage.getLineNumber(), 194 parseErrorMessage.getMessageKey(), 195 parseErrorMessage.getMessageArguments()); 196 } 197 } 198 199 } 200 201 /** 202 * Getter for block comment in Java language syntax tree. 203 * @return A block comment in the syntax tree. 204 */ 205 protected DetailAST getBlockCommentAst() { 206 return blockCommentAst; 207 } 208 209 /** 210 * Checks Javadoc comment it's in right place. 211 * From Javadoc util documentation: 212 * "Placement of comments - Documentation comments are recognized only when placed 213 * immediately before class, interface, constructor, method, or field 214 * declarations -- see the class example, method example, and field example. 215 * Documentation comments placed in the body of a method are ignored. Only one 216 * documentation comment per declaration statement is recognized by the Javadoc tool." 217 * 218 * @param blockComment Block comment AST 219 * @return true if Javadoc is in right place 220 */ 221 private static boolean isCorrectJavadocPosition(DetailAST blockComment) { 222 return BlockCommentPosition.isOnClass(blockComment) 223 || BlockCommentPosition.isOnInterface(blockComment) 224 || BlockCommentPosition.isOnEnum(blockComment) 225 || BlockCommentPosition.isOnMethod(blockComment) 226 || BlockCommentPosition.isOnField(blockComment) 227 || BlockCommentPosition.isOnConstructor(blockComment) 228 || BlockCommentPosition.isOnEnumConstant(blockComment) 229 || BlockCommentPosition.isOnAnnotationDef(blockComment); 230 } 231 232 /** 233 * Processes JavadocAST tree notifying Check. 234 * @param root 235 * root of JavadocAST tree. 236 */ 237 private void processTree(DetailNode root) { 238 beginJavadocTree(root); 239 walk(root); 240 finishJavadocTree(root); 241 } 242 243 /** 244 * Processes a node calling Check at interested nodes. 245 * @param root 246 * the root of tree for process 247 */ 248 private void walk(DetailNode root) { 249 final int[] defaultTokenTypes = getDefaultJavadocTokens(); 250 251 DetailNode curNode = root; 252 while (curNode != null) { 253 final boolean waitsFor = Ints.contains(defaultTokenTypes, curNode.getType()); 254 255 if (waitsFor) { 256 visitJavadocToken(curNode); 257 } 258 DetailNode toVisit = JavadocUtils.getFirstChild(curNode); 259 while (curNode != null && toVisit == null) { 260 261 if (waitsFor) { 262 leaveJavadocToken(curNode); 263 } 264 265 toVisit = JavadocUtils.getNextSibling(curNode); 266 if (toVisit == null) { 267 curNode = curNode.getParent(); 268 } 269 } 270 curNode = toVisit; 271 } 272 } 273 274}