001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2014 Oliver Burn 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//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle.api; 020 021import com.google.common.collect.ImmutableMap; 022import java.util.Map; 023 024/** 025 * This enum defines the various Javadoc tags and there properties. 026 * 027 * <p> 028 * This class was modeled after documentation located at 029 * <a href="http://java.sun.com/j2se/1.5.0/docs/tooldocs/windows/javadoc.html"> 030 * javadoc</a> 031 * 032 * and 033 * 034 * <a href="http://java.sun.com/j2se/javadoc/writingdoccomments/index.html"> 035 * how to write</a>. 036 * </p> 037 * 038 * <p> 039 * Some of this documentation was a little incomplete (ex: valid placement of 040 * code, value, and literal tags). 041 * </p> 042 * 043 * <p> 044 * Whenever an inconsistency was found the author's judgment was used. 045 * </p> 046 * 047 * <p> 048 * For now, the number of required/optional tag arguments are not included 049 * because some Javadoc tags have very complex rules for determining this 050 * (ex: {@code {@value}} tag). 051 * </p> 052 * 053 * <p> 054 * Also, the {@link #isValidOn(DetailAST) isValidOn} method does not consider 055 * classes defined in a local code block (method, init block, etc.). 056 * </p> 057 * 058 * @author Travis Schneeberger 059 */ 060public enum JavadocTagInfo 061{ 062 /** 063 * {@code @author}. 064 */ 065 AUTHOR("@author", "author", Type.BLOCK, true, true) 066 { 067 /** {@inheritDoc} */ 068 @Override 069 public boolean isValidOn(final DetailAST ast) 070 { 071 final int type = ast.getType(); 072 return type == TokenTypes.PACKAGE_DEF 073 || type == TokenTypes.CLASS_DEF 074 || type == TokenTypes.INTERFACE_DEF 075 || type == TokenTypes.ENUM_DEF 076 || type == TokenTypes.ANNOTATION_DEF; 077 } 078 }, 079 080 /** 081 * {@code {@code}}. 082 */ 083 CODE("{@code}", "code", Type.INLINE, true, true) 084 { 085 /** {@inheritDoc} */ 086 @Override 087 public boolean isValidOn(final DetailAST ast) 088 { 089 final int type = ast.getType(); 090 return type == TokenTypes.PACKAGE_DEF 091 || type == TokenTypes.CLASS_DEF 092 || type == TokenTypes.INTERFACE_DEF 093 || type == TokenTypes.ENUM_DEF 094 || type == TokenTypes.ANNOTATION_DEF 095 || type == TokenTypes.METHOD_DEF 096 || type == TokenTypes.CTOR_DEF 097 || (type == TokenTypes.VARIABLE_DEF 098 && !ScopeUtils.isLocalVariableDef(ast)); 099 } 100 }, 101 102 /** 103 * {@code {@docRoot}}. 104 */ 105 DOC_ROOT("{@docRoot}", "docRoot", Type.INLINE, true, true) 106 { 107 /** {@inheritDoc} */ 108 @Override 109 public boolean isValidOn(final DetailAST ast) 110 { 111 final int type = ast.getType(); 112 return type == TokenTypes.PACKAGE_DEF 113 || type == TokenTypes.CLASS_DEF 114 || type == TokenTypes.INTERFACE_DEF 115 || type == TokenTypes.ENUM_DEF 116 || type == TokenTypes.ANNOTATION_DEF 117 || type == TokenTypes.METHOD_DEF || type == TokenTypes.CTOR_DEF 118 || (type == TokenTypes.VARIABLE_DEF 119 && !ScopeUtils.isLocalVariableDef(ast)); 120 } 121 }, 122 123 /** 124 * {@code @deprecated}. 125 */ 126 DEPRECATED("@deprecated", "deprecated", Type.BLOCK, false, false) 127 { 128 /** {@inheritDoc} */ 129 @Override 130 public boolean isValidOn(final DetailAST ast) 131 { 132 final int type = ast.getType(); 133 return type == TokenTypes.CLASS_DEF 134 || type == TokenTypes.INTERFACE_DEF 135 || type == TokenTypes.ENUM_DEF 136 || type == TokenTypes.ANNOTATION_DEF 137 || type == TokenTypes.METHOD_DEF 138 || type == TokenTypes.CTOR_DEF 139 || type == TokenTypes.ENUM_CONSTANT_DEF 140 || type == TokenTypes.ANNOTATION_FIELD_DEF 141 || (type == TokenTypes.VARIABLE_DEF 142 && !ScopeUtils.isLocalVariableDef(ast)); 143 } 144 }, 145 146 /** 147 * {@code @exception}. 148 */ 149 EXCEPTION("@exception", "exception", Type.BLOCK, false, false) 150 { 151 /** {@inheritDoc} */ 152 @Override 153 public boolean isValidOn(final DetailAST ast) 154 { 155 final int type = ast.getType(); 156 return type == TokenTypes.METHOD_DEF || type == TokenTypes.CTOR_DEF; 157 } 158 }, 159 160 /** 161 * {@code {@inheritDoc}}. 162 */ 163 INHERIT_DOC("{@inheritDoc}", "inheritDoc", Type.INLINE, false, false) 164 { 165 /** {@inheritDoc} */ 166 @Override 167 public boolean isValidOn(final DetailAST ast) 168 { 169 final int type = ast.getType(); 170 171 return type == TokenTypes.METHOD_DEF 172 && !ast.branchContains(TokenTypes.LITERAL_STATIC) 173 && ScopeUtils.getScopeFromMods(ast 174 .findFirstToken(TokenTypes.MODIFIERS)) != Scope.PRIVATE; 175 } 176 }, 177 178 /** 179 * {@code {@link}}. 180 */ 181 LINK("{@link}", "link", Type.INLINE, true, true) 182 { 183 /** {@inheritDoc} */ 184 @Override 185 public boolean isValidOn(final DetailAST ast) 186 { 187 final int type = ast.getType(); 188 return type == TokenTypes.PACKAGE_DEF 189 || type == TokenTypes.CLASS_DEF 190 || type == TokenTypes.INTERFACE_DEF 191 || type == TokenTypes.ENUM_DEF 192 || type == TokenTypes.ANNOTATION_DEF 193 || type == TokenTypes.METHOD_DEF || type == TokenTypes.CTOR_DEF 194 || (type == TokenTypes.VARIABLE_DEF 195 && !ScopeUtils.isLocalVariableDef(ast)); 196 } 197 }, 198 199 /** 200 * {@code {@linkplain}}. 201 */ 202 LINKPLAIN("{@linkplain}", "linkplain", Type.INLINE, true, true) 203 { 204 /** {@inheritDoc} */ 205 @Override 206 public boolean isValidOn(final DetailAST ast) 207 { 208 final int type = ast.getType(); 209 return type == TokenTypes.PACKAGE_DEF 210 || type == TokenTypes.CLASS_DEF 211 || type == TokenTypes.INTERFACE_DEF 212 || type == TokenTypes.ENUM_DEF 213 || type == TokenTypes.ANNOTATION_DEF 214 || type == TokenTypes.METHOD_DEF || type == TokenTypes.CTOR_DEF 215 || (type == TokenTypes.VARIABLE_DEF 216 && !ScopeUtils.isLocalVariableDef(ast)); 217 } 218 }, 219 220 /** 221 * {@code {@literal}}. 222 */ 223 LITERAL("{@literal}", "literal", Type.INLINE, true, true) 224 { 225 /** {@inheritDoc} */ 226 @Override 227 public boolean isValidOn(final DetailAST ast) 228 { 229 final int type = ast.getType(); 230 return type == TokenTypes.PACKAGE_DEF 231 || type == TokenTypes.CLASS_DEF 232 || type == TokenTypes.INTERFACE_DEF 233 || type == TokenTypes.ENUM_DEF 234 || type == TokenTypes.ANNOTATION_DEF 235 || type == TokenTypes.METHOD_DEF || type == TokenTypes.CTOR_DEF 236 || (type == TokenTypes.VARIABLE_DEF 237 && !ScopeUtils.isLocalVariableDef(ast)); 238 } 239 }, 240 241 /** 242 * {@code @param}. 243 */ 244 PARAM("@param", "param", Type.BLOCK, false, false) 245 { 246 /** {@inheritDoc} */ 247 @Override 248 public boolean isValidOn(final DetailAST ast) 249 { 250 final int type = ast.getType(); 251 return type == TokenTypes.CLASS_DEF 252 || type == TokenTypes.INTERFACE_DEF 253 || type == TokenTypes.METHOD_DEF 254 || type == TokenTypes.CTOR_DEF; 255 } 256 }, 257 258 /** 259 * {@code @return}. 260 */ 261 RETURN("@return", "return", Type.BLOCK, false, false) 262 { 263 /** {@inheritDoc} */ 264 @Override 265 public boolean isValidOn(final DetailAST ast) 266 { 267 final int type = ast.getType(); 268 final DetailAST returnType = ast.findFirstToken(TokenTypes.TYPE); 269 270 return type == TokenTypes.METHOD_DEF 271 && returnType.getFirstChild().getType() 272 != TokenTypes.LITERAL_VOID; 273 274 } 275 }, 276 277 /** 278 * {@code @see}. 279 */ 280 SEE("@see", "see", Type.BLOCK, true, true) 281 { 282 /** {@inheritDoc} */ 283 @Override 284 public boolean isValidOn(final DetailAST ast) 285 { 286 final int type = ast.getType(); 287 return type == TokenTypes.PACKAGE_DEF 288 || type == TokenTypes.CLASS_DEF 289 || type == TokenTypes.INTERFACE_DEF 290 || type == TokenTypes.ENUM_DEF 291 || type == TokenTypes.ANNOTATION_DEF 292 || type == TokenTypes.METHOD_DEF 293 || type == TokenTypes.CTOR_DEF 294 || (type == TokenTypes.VARIABLE_DEF 295 && !ScopeUtils.isLocalVariableDef(ast)); 296 } 297 }, 298 299 /** 300 * {@code @serial}. 301 */ 302 SERIAL("@serial", "serial", Type.BLOCK, true, false) 303 { 304 /** {@inheritDoc} */ 305 @Override 306 public boolean isValidOn(final DetailAST ast) 307 { 308 final int type = ast.getType(); 309 310 return type == TokenTypes.VARIABLE_DEF 311 && !ScopeUtils.isLocalVariableDef(ast); 312 } 313 }, 314 315 /** 316 * {@code @serialData}. 317 */ 318 SERIAL_DATA("@serialData", "serialData", Type.BLOCK, false, false) 319 { 320 /** {@inheritDoc} */ 321 @Override 322 public boolean isValidOn(final DetailAST ast) 323 { 324 final int type = ast.getType(); 325 final DetailAST methodNameAst = ast 326 .findFirstToken(TokenTypes.IDENT); 327 final String methodName = methodNameAst.getText(); 328 329 return type == TokenTypes.METHOD_DEF 330 && ("writeObject".equals(methodName) 331 || "readObject".equals(methodName) 332 || "writeExternal".equals(methodName) 333 || "readExternal".equals(methodName) 334 || "writeReplace".equals(methodName) 335 || "readResolve" 336 .equals(methodName)); 337 } 338 }, 339 340 /** 341 * {@code @serialField}. 342 */ 343 SERIAL_FIELD("@serialField", "serialField", Type.BLOCK, false, false) 344 { 345 /** {@inheritDoc} */ 346 @Override 347 public boolean isValidOn(final DetailAST ast) 348 { 349 final int type = ast.getType(); 350 final DetailAST varType = ast.findFirstToken(TokenTypes.TYPE); 351 352 return type == TokenTypes.VARIABLE_DEF 353 && varType.getType() == TokenTypes.ARRAY_DECLARATOR 354 && "ObjectStreafield" 355 .equals(varType.getFirstChild().getText()); 356 } 357 }, 358 359 /** 360 * {@code @since}. 361 */ 362 SINCE("@since", "since", Type.BLOCK, true, true) 363 { 364 /** {@inheritDoc} */ 365 @Override 366 public boolean isValidOn(final DetailAST ast) 367 { 368 final int type = ast.getType(); 369 return type == TokenTypes.PACKAGE_DEF 370 || type == TokenTypes.CLASS_DEF 371 || type == TokenTypes.INTERFACE_DEF 372 || type == TokenTypes.ENUM_DEF 373 || type == TokenTypes.ANNOTATION_DEF 374 || type == TokenTypes.METHOD_DEF 375 || type == TokenTypes.CTOR_DEF 376 || (type == TokenTypes.VARIABLE_DEF 377 && !ScopeUtils.isLocalVariableDef(ast)); 378 } 379 }, 380 381 /** 382 * {@code @throws}. 383 */ 384 THROWS("@throws", "throws", Type.BLOCK, false, false) 385 { 386 /** {@inheritDoc} */ 387 @Override 388 public boolean isValidOn(final DetailAST ast) 389 { 390 final int type = ast.getType(); 391 return type == TokenTypes.METHOD_DEF 392 || type == TokenTypes.CTOR_DEF; 393 } 394 }, 395 396 /** 397 * {@code {@value}}. 398 */ 399 VALUE("{@value}", "value", Type.INLINE, true, true) 400 { 401 /** {@inheritDoc} */ 402 @Override 403 public boolean isValidOn(final DetailAST ast) 404 { 405 final int type = ast.getType(); 406 return type == TokenTypes.PACKAGE_DEF 407 || type == TokenTypes.CLASS_DEF 408 || type == TokenTypes.INTERFACE_DEF 409 || type == TokenTypes.ENUM_DEF 410 || type == TokenTypes.ANNOTATION_DEF 411 || type == TokenTypes.METHOD_DEF 412 || type == TokenTypes.CTOR_DEF 413 || (type == TokenTypes.VARIABLE_DEF 414 && !ScopeUtils.isLocalVariableDef(ast)); 415 } 416 }, 417 418 /** 419 * {@code @version}. 420 */ 421 VERSION("@version", "version", Type.BLOCK, true, true) 422 { 423 /** {@inheritDoc} */ 424 @Override 425 public boolean isValidOn(final DetailAST ast) 426 { 427 final int type = ast.getType(); 428 return type == TokenTypes.PACKAGE_DEF 429 || type == TokenTypes.CLASS_DEF 430 || type == TokenTypes.INTERFACE_DEF 431 || type == TokenTypes.ENUM_DEF 432 || type == TokenTypes.ANNOTATION_DEF; 433 } 434 }; 435 436 /** holds tag text to tag enum mappings **/ 437 private static final Map<String, JavadocTagInfo> TEXT_TO_TAG; 438 /** holds tag name to tag enum mappings **/ 439 private static final Map<String, JavadocTagInfo> NAME_TO_TAG; 440 441 static 442 { 443 final ImmutableMap.Builder<String, JavadocTagInfo> textToTagBuilder = 444 new ImmutableMap.Builder<String, JavadocTagInfo>(); 445 446 final ImmutableMap.Builder<String, JavadocTagInfo> nameToTagBuilder = 447 new ImmutableMap.Builder<String, JavadocTagInfo>(); 448 449 for (final JavadocTagInfo tag : JavadocTagInfo.values()) { 450 textToTagBuilder.put(tag.getText(), tag); 451 nameToTagBuilder.put(tag.getName(), tag); 452 } 453 454 TEXT_TO_TAG = textToTagBuilder.build(); 455 NAME_TO_TAG = nameToTagBuilder.build(); 456 } 457 458 /** the tag text **/ 459 private final String text; 460 /** the tag name **/ 461 private final String name; 462 /** the tag type **/ 463 private final Type type; 464 /** if tag is valid in package.html **/ 465 private final boolean validInPackageHtml; 466 /** if tag is valid in overview.html **/ 467 private final boolean validInOverviewHtml; 468 469 /** 470 * Sets the various properties of a Javadoc tag. 471 * 472 * @param text the tag text 473 * @param name the tag name 474 * @param type the type of tag 475 * @param validInPackageHtml whether the tag is valid 476 * in package.html file 477 * @param validInOverviewHtml whether the tag is valid 478 * in overview.html file 479 */ 480 private JavadocTagInfo(final String text, final String name, 481 final Type type, final boolean validInPackageHtml, 482 final boolean validInOverviewHtml) 483 { 484 this.text = text; 485 this.name = name; 486 this.type = type; 487 this.validInPackageHtml = validInPackageHtml; 488 this.validInOverviewHtml = validInOverviewHtml; 489 } 490 491 /** 492 * Checks if a particular Javadoc tag is valid within a Javadoc block of a 493 * given AST. 494 * 495 * <p> 496 * For example: Given a call to 497 * <code>JavadocTag.RETURN{@link #isValidOn(DetailAST)}</code>. 498 * </p> 499 * 500 * <p> 501 * If passing in a DetailAST representing a non-void METHOD_DEF 502 * <code> true </code> would be returned. If passing in a DetailAST 503 * representing a CLASS_DEF <code> false </code> would be returned because 504 * CLASS_DEF's cannot return a value. 505 * </p> 506 * 507 * @param ast the AST representing a type that can be Javadoc'd 508 * @return true if tag is valid. 509 */ 510 public abstract boolean isValidOn(DetailAST ast); 511 512 /** 513 * Checks if tag is valid in a package.html Javadoc file. 514 * 515 * @return true if tag is valid. 516 */ 517 public boolean isValidInPackageHtml() 518 { 519 return this.validInPackageHtml; 520 } 521 522 /** 523 * Checks if tag is valid in a overview.html Javadoc file. 524 * 525 * @return true if tag is valid. 526 */ 527 public boolean isValidInOverviewHtml() 528 { 529 return this.validInOverviewHtml; 530 } 531 532 /** 533 * Gets the tag text. 534 * @return the tag text 535 */ 536 public String getText() 537 { 538 return this.text; 539 } 540 541 /** 542 * Gets the tag name. 543 * @return the tag name 544 */ 545 public String getName() 546 { 547 return this.name; 548 } 549 550 /** 551 * Gets the Tag type defined by {@link JavadocTagInfo.Type Type}. 552 * @return the Tag type 553 */ 554 public Type getType() 555 { 556 return this.type; 557 } 558 559 /** 560 * returns a JavadocTag from the tag text. 561 * @param text String representing the tag text 562 * @return Returns a JavadocTag type from a String representing the tag 563 * @throws NullPointerException if the text is null 564 * @throws IllegalArgumentException if the text is not a valid tag 565 */ 566 public static JavadocTagInfo fromText(final String text) 567 { 568 if (text == null) { 569 throw new NullPointerException("the text is null"); 570 } 571 572 final JavadocTagInfo tag = TEXT_TO_TAG.get(text); 573 574 if (tag == null) { 575 throw new IllegalArgumentException("the text [" + text 576 + "] is not a valid Javadoc tag text"); 577 } 578 579 return tag; 580 } 581 582 /** 583 * returns a JavadocTag from the tag name. 584 * @param name String name of the tag 585 * @return Returns a JavadocTag type from a String representing the tag 586 * @throws NullPointerException if the text is null 587 * @throws IllegalArgumentException if the text is not a valid tag. The name 588 * can be checked using {@link JavadocTagInfo#isValidName(String)} 589 */ 590 public static JavadocTagInfo fromName(final String name) 591 { 592 if (name == null) { 593 throw new NullPointerException("the name is null"); 594 } 595 596 final JavadocTagInfo tag = NAME_TO_TAG.get(name); 597 598 if (tag == null) { 599 throw new IllegalArgumentException("the name [" + name 600 + "] is not a valid Javadoc tag name"); 601 } 602 603 return tag; 604 } 605 606 /** 607 * Returns whether the provided name is for a valid tag. 608 * @param name the tag name to check. 609 * @return whether the provided name is for a valid tag. 610 */ 611 public static boolean isValidName(final String name) 612 { 613 return NAME_TO_TAG.containsKey(name); 614 } 615 616 /** 617 * {@inheritDoc} 618 */ 619 @Override 620 public String toString() 621 { 622 return "text [" + this.text + "] name [" + this.name 623 + "] type [" + this.type 624 + "] validInPackageHtml [" + this.validInPackageHtml 625 + "] validInOverviewHtml [" + this.validInOverviewHtml + "]"; 626 } 627 628 /** 629 * The Javadoc Type. 630 * 631 * For example a {@code @param} tag is a block tag while a 632 * {@code {@link}} tag is a inline tag. 633 * 634 * @author Travis Schneeberger 635 */ 636 public enum Type 637 { 638 /** block type. **/ 639 BLOCK, 640 641 /** inline type. **/ 642 INLINE; 643 } 644}