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.Arrays; 023import java.util.Map; 024 025import com.google.common.collect.ImmutableMap; 026import com.puppycrawl.tools.checkstyle.api.DetailAST; 027import com.puppycrawl.tools.checkstyle.api.Scope; 028import com.puppycrawl.tools.checkstyle.api.TokenTypes; 029import com.puppycrawl.tools.checkstyle.utils.ScopeUtils; 030 031/** 032 * This enum defines the various Javadoc tags and there properties. 033 * 034 * <p> 035 * This class was modeled after documentation located at 036 * <a href="http://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html"> 037 * javadoc</a> 038 * 039 * and 040 * 041 * <a href="http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html"> 042 * how to write</a>. 043 * </p> 044 * 045 * <p> 046 * Some of this documentation was a little incomplete (ex: valid placement of 047 * code, value, and literal tags). 048 * </p> 049 * 050 * <p> 051 * Whenever an inconsistency was found the author's judgment was used. 052 * </p> 053 * 054 * <p> 055 * For now, the number of required/optional tag arguments are not included 056 * because some Javadoc tags have very complex rules for determining this 057 * (ex: {@code {@value}} tag). 058 * </p> 059 * 060 * <p> 061 * Also, the {@link #isValidOn(DetailAST) isValidOn} method does not consider 062 * classes defined in a local code block (method, init block, etc.). 063 * </p> 064 * 065 * @author Travis Schneeberger 066 */ 067public enum JavadocTagInfo { 068 069 /** 070 * {@code @author}. 071 */ 072 AUTHOR("@author", "author", Type.BLOCK) { 073 @Override 074 public boolean isValidOn(final DetailAST ast) { 075 final int astType = ast.getType(); 076 return astType == TokenTypes.PACKAGE_DEF 077 || astType == TokenTypes.CLASS_DEF 078 || astType == TokenTypes.INTERFACE_DEF 079 || astType == TokenTypes.ENUM_DEF 080 || astType == TokenTypes.ANNOTATION_DEF; 081 } 082 }, 083 084 /** 085 * {@code {@code}}. 086 */ 087 CODE("{@code}", "code", Type.INLINE) { 088 @Override 089 public boolean isValidOn(final DetailAST ast) { 090 final int astType = ast.getType(); 091 return Arrays.binarySearch(DEF_TOKEN_TYPES, astType) >= 0 092 && !ScopeUtils.isLocalVariableDef(ast); 093 } 094 }, 095 096 /** 097 * {@code {@docRoot}}. 098 */ 099 DOC_ROOT("{@docRoot}", "docRoot", Type.INLINE) { 100 @Override 101 public boolean isValidOn(final DetailAST ast) { 102 final int astType = ast.getType(); 103 return Arrays.binarySearch(DEF_TOKEN_TYPES, astType) >= 0 104 && !ScopeUtils.isLocalVariableDef(ast); 105 } 106 }, 107 108 /** 109 * {@code @deprecated}. 110 */ 111 DEPRECATED("@deprecated", "deprecated", Type.BLOCK) { 112 @Override 113 public boolean isValidOn(final DetailAST ast) { 114 final int astType = ast.getType(); 115 return Arrays.binarySearch(DEF_TOKEN_TYPES_DEPRECATED, astType) >= 0 116 && !ScopeUtils.isLocalVariableDef(ast); 117 } 118 }, 119 120 /** 121 * {@code @exception}. 122 */ 123 EXCEPTION("@exception", "exception", Type.BLOCK) { 124 @Override 125 public boolean isValidOn(final DetailAST ast) { 126 final int astType = ast.getType(); 127 return astType == TokenTypes.METHOD_DEF || astType == TokenTypes.CTOR_DEF; 128 } 129 }, 130 131 /** 132 * {@code {@inheritDoc}}. 133 */ 134 INHERIT_DOC("{@inheritDoc}", "inheritDoc", Type.INLINE) { 135 @Override 136 public boolean isValidOn(final DetailAST ast) { 137 final int astType = ast.getType(); 138 139 return astType == TokenTypes.METHOD_DEF 140 && !ast.branchContains(TokenTypes.LITERAL_STATIC) 141 && ScopeUtils.getScopeFromMods(ast 142 .findFirstToken(TokenTypes.MODIFIERS)) != Scope.PRIVATE; 143 } 144 }, 145 146 /** 147 * {@code {@link}}. 148 */ 149 LINK("{@link}", "link", Type.INLINE) { 150 @Override 151 public boolean isValidOn(final DetailAST ast) { 152 final int astType = ast.getType(); 153 return Arrays.binarySearch(DEF_TOKEN_TYPES, astType) >= 0 154 && !ScopeUtils.isLocalVariableDef(ast); 155 } 156 }, 157 158 /** 159 * {@code {@linkplain}}. 160 */ 161 LINKPLAIN("{@linkplain}", "linkplain", Type.INLINE) { 162 @Override 163 public boolean isValidOn(final DetailAST ast) { 164 final int astType = ast.getType(); 165 return Arrays.binarySearch(DEF_TOKEN_TYPES, astType) >= 0 166 && !ScopeUtils.isLocalVariableDef(ast); 167 } 168 }, 169 170 /** 171 * {@code {@literal}}. 172 */ 173 LITERAL("{@literal}", "literal", Type.INLINE) { 174 @Override 175 public boolean isValidOn(final DetailAST ast) { 176 final int astType = ast.getType(); 177 return Arrays.binarySearch(DEF_TOKEN_TYPES, astType) >= 0 178 && !ScopeUtils.isLocalVariableDef(ast); 179 } 180 }, 181 182 /** 183 * {@code @param}. 184 */ 185 PARAM("@param", "param", Type.BLOCK) { 186 @Override 187 public boolean isValidOn(final DetailAST ast) { 188 final int astType = ast.getType(); 189 return astType == TokenTypes.CLASS_DEF 190 || astType == TokenTypes.INTERFACE_DEF 191 || astType == TokenTypes.METHOD_DEF 192 || astType == TokenTypes.CTOR_DEF; 193 } 194 }, 195 196 /** 197 * {@code @return}. 198 */ 199 RETURN("@return", "return", Type.BLOCK) { 200 @Override 201 public boolean isValidOn(final DetailAST ast) { 202 final int astType = ast.getType(); 203 final DetailAST returnType = ast.findFirstToken(TokenTypes.TYPE); 204 205 return astType == TokenTypes.METHOD_DEF 206 && returnType.getFirstChild().getType() != TokenTypes.LITERAL_VOID; 207 208 } 209 }, 210 211 /** 212 * {@code @see}. 213 */ 214 SEE("@see", "see", Type.BLOCK) { 215 @Override 216 public boolean isValidOn(final DetailAST ast) { 217 final int astType = ast.getType(); 218 return Arrays.binarySearch(DEF_TOKEN_TYPES, astType) >= 0 219 && !ScopeUtils.isLocalVariableDef(ast); 220 } 221 }, 222 223 /** 224 * {@code @serial}. 225 */ 226 SERIAL("@serial", "serial", Type.BLOCK) { 227 @Override 228 public boolean isValidOn(final DetailAST ast) { 229 final int astType = ast.getType(); 230 231 return astType == TokenTypes.VARIABLE_DEF 232 && !ScopeUtils.isLocalVariableDef(ast); 233 } 234 }, 235 236 /** 237 * {@code @serialData}. 238 */ 239 SERIAL_DATA("@serialData", "serialData", Type.BLOCK) { 240 @Override 241 public boolean isValidOn(final DetailAST ast) { 242 final int astType = ast.getType(); 243 final DetailAST methodNameAst = ast.findFirstToken(TokenTypes.IDENT); 244 final String methodName = methodNameAst.getText(); 245 246 return astType == TokenTypes.METHOD_DEF 247 && ("writeObject".equals(methodName) 248 || "readObject".equals(methodName) 249 || "writeExternal".equals(methodName) 250 || "readExternal".equals(methodName) 251 || "writeReplace".equals(methodName) 252 || "readResolve".equals(methodName)); 253 } 254 }, 255 256 /** 257 * {@code @serialField}. 258 */ 259 SERIAL_FIELD("@serialField", "serialField", Type.BLOCK) { 260 @Override 261 public boolean isValidOn(final DetailAST ast) { 262 final int astType = ast.getType(); 263 final DetailAST varType = ast.findFirstToken(TokenTypes.TYPE); 264 265 return astType == TokenTypes.VARIABLE_DEF 266 && varType.getFirstChild().getType() == TokenTypes.ARRAY_DECLARATOR 267 && "ObjectStreafield".equals(varType.getFirstChild().getText()); 268 } 269 }, 270 271 /** 272 * {@code @since}. 273 */ 274 SINCE("@since", "since", Type.BLOCK) { 275 @Override 276 public boolean isValidOn(final DetailAST ast) { 277 final int astType = ast.getType(); 278 return Arrays.binarySearch(DEF_TOKEN_TYPES, astType) >= 0 279 && !ScopeUtils.isLocalVariableDef(ast); 280 } 281 }, 282 283 /** 284 * {@code @throws}. 285 */ 286 THROWS("@throws", "throws", Type.BLOCK) { 287 @Override 288 public boolean isValidOn(final DetailAST ast) { 289 final int astType = ast.getType(); 290 return astType == TokenTypes.METHOD_DEF 291 || astType == TokenTypes.CTOR_DEF; 292 } 293 }, 294 295 /** 296 * {@code {@value}}. 297 */ 298 VALUE("{@value}", "value", Type.INLINE) { 299 @Override 300 public boolean isValidOn(final DetailAST ast) { 301 final int astType = ast.getType(); 302 return Arrays.binarySearch(DEF_TOKEN_TYPES, astType) >= 0 303 && !ScopeUtils.isLocalVariableDef(ast); 304 } 305 }, 306 307 /** 308 * {@code @version}. 309 */ 310 VERSION("@version", "version", Type.BLOCK) { 311 @Override 312 public boolean isValidOn(final DetailAST ast) { 313 final int astType = ast.getType(); 314 return astType == TokenTypes.PACKAGE_DEF 315 || astType == TokenTypes.CLASS_DEF 316 || astType == TokenTypes.INTERFACE_DEF 317 || astType == TokenTypes.ENUM_DEF 318 || astType == TokenTypes.ANNOTATION_DEF; 319 } 320 }; 321 322 /** Default token types for DEPRECATED Javadoc tag.*/ 323 private static final int[] DEF_TOKEN_TYPES_DEPRECATED = { 324 TokenTypes.CTOR_DEF, 325 TokenTypes.METHOD_DEF, 326 TokenTypes.VARIABLE_DEF, 327 TokenTypes.CLASS_DEF, 328 TokenTypes.INTERFACE_DEF, 329 TokenTypes.ENUM_DEF, 330 TokenTypes.ENUM_CONSTANT_DEF, 331 TokenTypes.ANNOTATION_DEF, 332 TokenTypes.ANNOTATION_FIELD_DEF, 333 }; 334 335 /** Default token types.*/ 336 private static final int[] DEF_TOKEN_TYPES = { 337 TokenTypes.CTOR_DEF, 338 TokenTypes.METHOD_DEF, 339 TokenTypes.VARIABLE_DEF, 340 TokenTypes.CLASS_DEF, 341 TokenTypes.INTERFACE_DEF, 342 TokenTypes.PACKAGE_DEF, 343 TokenTypes.ENUM_DEF, 344 TokenTypes.ANNOTATION_DEF, 345 }; 346 347 /** Holds tag text to tag enum mappings. **/ 348 private static final Map<String, JavadocTagInfo> TEXT_TO_TAG; 349 /** Holds tag name to tag enum mappings. **/ 350 private static final Map<String, JavadocTagInfo> NAME_TO_TAG; 351 352 static { 353 final ImmutableMap.Builder<String, JavadocTagInfo> textToTagBuilder = 354 new ImmutableMap.Builder<>(); 355 356 final ImmutableMap.Builder<String, JavadocTagInfo> nameToTagBuilder = 357 new ImmutableMap.Builder<>(); 358 359 for (final JavadocTagInfo tag : JavadocTagInfo.values()) { 360 textToTagBuilder.put(tag.text, tag); 361 nameToTagBuilder.put(tag.name, tag); 362 } 363 364 TEXT_TO_TAG = textToTagBuilder.build(); 365 NAME_TO_TAG = nameToTagBuilder.build(); 366 367 //Arrays sorting for binary search 368 Arrays.sort(DEF_TOKEN_TYPES); 369 Arrays.sort(DEF_TOKEN_TYPES_DEPRECATED); 370 } 371 372 /** The tag text. **/ 373 private final String text; 374 /** The tag name. **/ 375 private final String name; 376 /** The tag type. **/ 377 private final Type type; 378 379 /** 380 * Sets the various properties of a Javadoc tag. 381 * 382 * @param text the tag text 383 * @param name the tag name 384 * @param type the type of tag 385 */ 386 JavadocTagInfo(final String text, final String name, 387 final Type type) { 388 this.text = text; 389 this.name = name; 390 this.type = type; 391 } 392 393 /** 394 * Checks if a particular Javadoc tag is valid within a Javadoc block of a 395 * given AST. 396 * 397 * <p> 398 * If passing in a DetailAST representing a non-void METHOD_DEF 399 * {@code true } would be returned. If passing in a DetailAST 400 * representing a CLASS_DEF {@code false } would be returned because 401 * CLASS_DEF's cannot return a value. 402 * </p> 403 * 404 * @param ast the AST representing a type that can be Javadoc'd 405 * @return true if tag is valid. 406 */ 407 public abstract boolean isValidOn(DetailAST ast); 408 409 /** 410 * Gets the tag text. 411 * @return the tag text 412 */ 413 public String getText() { 414 return text; 415 } 416 417 /** 418 * Gets the tag name. 419 * @return the tag name 420 */ 421 public String getName() { 422 return name; 423 } 424 425 /** 426 * Gets the Tag type defined by {@link Type Type}. 427 * @return the Tag type 428 */ 429 public Type getType() { 430 return type; 431 } 432 433 /** 434 * Returns a JavadocTag from the tag text. 435 * @param text String representing the tag text 436 * @return Returns a JavadocTag type from a String representing the tag 437 * @throws NullPointerException if the text is null 438 * @throws IllegalArgumentException if the text is not a valid tag 439 */ 440 public static JavadocTagInfo fromText(final String text) { 441 if (text == null) { 442 throw new IllegalArgumentException("the text is null"); 443 } 444 445 final JavadocTagInfo tag = TEXT_TO_TAG.get(text); 446 447 if (tag == null) { 448 throw new IllegalArgumentException("the text [" + text 449 + "] is not a valid Javadoc tag text"); 450 } 451 452 return tag; 453 } 454 455 /** 456 * Returns a JavadocTag from the tag name. 457 * @param name String name of the tag 458 * @return Returns a JavadocTag type from a String representing the tag 459 * @throws NullPointerException if the text is null 460 * @throws IllegalArgumentException if the text is not a valid tag. The name 461 * can be checked using {@link JavadocTagInfo#isValidName(String)} 462 */ 463 public static JavadocTagInfo fromName(final String name) { 464 if (name == null) { 465 throw new IllegalArgumentException("the name is null"); 466 } 467 468 final JavadocTagInfo tag = NAME_TO_TAG.get(name); 469 470 if (tag == null) { 471 throw new IllegalArgumentException("the name [" + name 472 + "] is not a valid Javadoc tag name"); 473 } 474 475 return tag; 476 } 477 478 /** 479 * Returns whether the provided name is for a valid tag. 480 * @param name the tag name to check. 481 * @return whether the provided name is for a valid tag. 482 */ 483 public static boolean isValidName(final String name) { 484 return NAME_TO_TAG.containsKey(name); 485 } 486 487 @Override 488 public String toString() { 489 return "text [" + text + "] name [" + name 490 + "] type [" + type + "]"; 491 } 492 493 /** 494 * The Javadoc Type. 495 * 496 * <p>For example a {@code @param} tag is a block tag while a 497 * {@code {@link}} tag is a inline tag. 498 * 499 * @author Travis Schneeberger 500 */ 501 public enum Type { 502 /** Block type. **/ 503 BLOCK, 504 505 /** Inline type. **/ 506 INLINE 507 } 508}