001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2024 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.design; 021 022import java.util.ArrayList; 023import java.util.Collection; 024import java.util.HashSet; 025import java.util.List; 026import java.util.Set; 027import java.util.regex.Pattern; 028import java.util.stream.Collectors; 029 030import com.puppycrawl.tools.checkstyle.FileStatefulCheck; 031import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 032import com.puppycrawl.tools.checkstyle.api.DetailAST; 033import com.puppycrawl.tools.checkstyle.api.FullIdent; 034import com.puppycrawl.tools.checkstyle.api.TokenTypes; 035import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil; 036import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 037import com.puppycrawl.tools.checkstyle.utils.ScopeUtil; 038 039/** 040 * <div> 041 * Checks visibility of class members. Only static final, immutable or annotated 042 * by specified annotation members may be public; 043 * other class members must be private unless the property {@code protectedAllowed} 044 * or {@code packageAllowed} is set. 045 * </div> 046 * 047 * <p> 048 * Public members are not flagged if the name matches the public 049 * member regular expression (contains {@code "^serialVersionUID$"} by 050 * default). 051 * </p> 052 * 053 * <p> 054 * Note that Checkstyle 2 used to include {@code "^f[A-Z][a-zA-Z0-9]*$"} in the default pattern 055 * to allow names used in container-managed persistence for Enterprise JavaBeans (EJB) 1.1 with 056 * the default settings. With EJB 2.0 it is no longer necessary to have public access for 057 * persistent fields, so the default has been changed. 058 * </p> 059 * 060 * <p> 061 * Rationale: Enforce encapsulation. 062 * </p> 063 * 064 * <p> 065 * Check also has options making it less strict: 066 * </p> 067 * 068 * <p> 069 * <b>ignoreAnnotationCanonicalNames</b>- the list of annotations which ignore 070 * variables in consideration. If user want to provide short annotation name that 071 * type will match to any named the same type without consideration of package. 072 * </p> 073 * 074 * <p> 075 * <b>allowPublicFinalFields</b>- which allows public final fields. 076 * </p> 077 * 078 * <p> 079 * <b>allowPublicImmutableFields</b>- which allows immutable fields to be 080 * declared as public if defined in final class. 081 * </p> 082 * 083 * <p> 084 * Field is known to be immutable if: 085 * </p> 086 * <ul> 087 * <li>It's declared as final</li> 088 * <li>Has either a primitive type or instance of class user defined to be immutable 089 * (such as String, ImmutableCollection from Guava, etc.)</li> 090 * </ul> 091 * 092 * <p> 093 * Classes known to be immutable are listed in <b>immutableClassCanonicalNames</b> 094 * by their canonical names. 095 * </p> 096 * 097 * <p> 098 * Property Rationale: Forcing all fields of class to have private modifier by default is 099 * good in most cases, but in some cases it drawbacks in too much boilerplate get/set code. 100 * One of such cases are immutable classes. 101 * </p> 102 * 103 * <p> 104 * Restriction: Check doesn't check if class is immutable, there's no checking 105 * if accessory methods are missing and all fields are immutable, we only check 106 * if current field is immutable or final. 107 * Under the flag <b>allowPublicImmutableFields</b>, the enclosing class must 108 * also be final, to encourage immutability. 109 * Under the flag <b>allowPublicFinalFields</b>, the final modifier 110 * on the enclosing class is optional. 111 * </p> 112 * 113 * <p> 114 * Star imports are out of scope of this Check. So if one of type imported via 115 * star import collides with user specified one by its short name - there 116 * won't be Check's violation. 117 * </p> 118 * <ul> 119 * <li> 120 * Property {@code allowPublicFinalFields} - Allow final fields to be declared as public. 121 * Type is {@code boolean}. 122 * Default value is {@code false}. 123 * </li> 124 * <li> 125 * Property {@code allowPublicImmutableFields} - Allow immutable fields to be 126 * declared as public if defined in final class. 127 * Type is {@code boolean}. 128 * Default value is {@code false}. 129 * </li> 130 * <li> 131 * Property {@code ignoreAnnotationCanonicalNames} - Specify annotations canonical 132 * names which ignore variables in consideration. 133 * Type is {@code java.lang.String[]}. 134 * Default value is {@code com.google.common.annotations.VisibleForTesting, 135 * org.junit.ClassRule, org.junit.Rule}. 136 * </li> 137 * <li> 138 * Property {@code immutableClassCanonicalNames} - Specify immutable classes canonical names. 139 * Type is {@code java.lang.String[]}. 140 * Default value is {@code java.io.File, java.lang.Boolean, java.lang.Byte, 141 * java.lang.Character, java.lang.Double, java.lang.Float, java.lang.Integer, 142 * java.lang.Long, java.lang.Short, java.lang.StackTraceElement, java.lang.String, 143 * java.math.BigDecimal, java.math.BigInteger, java.net.Inet4Address, java.net.Inet6Address, 144 * java.net.InetSocketAddress, java.net.URI, java.net.URL, java.util.Locale, java.util.UUID}. 145 * </li> 146 * <li> 147 * Property {@code packageAllowed} - Control whether package visible members are allowed. 148 * Type is {@code boolean}. 149 * Default value is {@code false}. 150 * </li> 151 * <li> 152 * Property {@code protectedAllowed} - Control whether protected members are allowed. 153 * Type is {@code boolean}. 154 * Default value is {@code false}. 155 * </li> 156 * <li> 157 * Property {@code publicMemberPattern} - Specify pattern for public members that should be ignored. 158 * Type is {@code java.util.regex.Pattern}. 159 * Default value is {@code "^serialVersionUID$"}. 160 * </li> 161 * </ul> 162 * 163 * <p> 164 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 165 * </p> 166 * 167 * <p> 168 * Violation Message Keys: 169 * </p> 170 * <ul> 171 * <li> 172 * {@code variable.notPrivate} 173 * </li> 174 * </ul> 175 * 176 * @since 3.0 177 */ 178@FileStatefulCheck 179public class VisibilityModifierCheck 180 extends AbstractCheck { 181 182 /** 183 * A key is pointing to the warning message text in "messages.properties" 184 * file. 185 */ 186 public static final String MSG_KEY = "variable.notPrivate"; 187 188 /** Default immutable types canonical names. */ 189 private static final Set<String> DEFAULT_IMMUTABLE_TYPES = Set.of( 190 "java.lang.String", 191 "java.lang.Integer", 192 "java.lang.Byte", 193 "java.lang.Character", 194 "java.lang.Short", 195 "java.lang.Boolean", 196 "java.lang.Long", 197 "java.lang.Double", 198 "java.lang.Float", 199 "java.lang.StackTraceElement", 200 "java.math.BigInteger", 201 "java.math.BigDecimal", 202 "java.io.File", 203 "java.util.Locale", 204 "java.util.UUID", 205 "java.net.URL", 206 "java.net.URI", 207 "java.net.Inet4Address", 208 "java.net.Inet6Address", 209 "java.net.InetSocketAddress" 210 ); 211 212 /** Default ignore annotations canonical names. */ 213 private static final Set<String> DEFAULT_IGNORE_ANNOTATIONS = Set.of( 214 "org.junit.Rule", 215 "org.junit.ClassRule", 216 "com.google.common.annotations.VisibleForTesting" 217 ); 218 219 /** Name for 'public' access modifier. */ 220 private static final String PUBLIC_ACCESS_MODIFIER = "public"; 221 222 /** Name for 'private' access modifier. */ 223 private static final String PRIVATE_ACCESS_MODIFIER = "private"; 224 225 /** Name for 'protected' access modifier. */ 226 private static final String PROTECTED_ACCESS_MODIFIER = "protected"; 227 228 /** Name for implicit 'package' access modifier. */ 229 private static final String PACKAGE_ACCESS_MODIFIER = "package"; 230 231 /** Name for 'static' keyword. */ 232 private static final String STATIC_KEYWORD = "static"; 233 234 /** Name for 'final' keyword. */ 235 private static final String FINAL_KEYWORD = "final"; 236 237 /** Contains explicit access modifiers. */ 238 private static final String[] EXPLICIT_MODS = { 239 PUBLIC_ACCESS_MODIFIER, 240 PRIVATE_ACCESS_MODIFIER, 241 PROTECTED_ACCESS_MODIFIER, 242 }; 243 244 /** 245 * Specify pattern for public members that should be ignored. 246 */ 247 private Pattern publicMemberPattern = Pattern.compile("^serialVersionUID$"); 248 249 /** Set of ignore annotations short names. */ 250 private Set<String> ignoreAnnotationShortNames; 251 252 /** Set of immutable classes short names. */ 253 private Set<String> immutableClassShortNames; 254 255 /** 256 * Specify annotations canonical names which ignore variables in 257 * consideration. 258 */ 259 private Set<String> ignoreAnnotationCanonicalNames = DEFAULT_IGNORE_ANNOTATIONS; 260 261 /** Control whether protected members are allowed. */ 262 private boolean protectedAllowed; 263 264 /** Control whether package visible members are allowed. */ 265 private boolean packageAllowed; 266 267 /** Allow immutable fields to be declared as public if defined in final class. */ 268 private boolean allowPublicImmutableFields; 269 270 /** Allow final fields to be declared as public. */ 271 private boolean allowPublicFinalFields; 272 273 /** Specify immutable classes canonical names. */ 274 private Set<String> immutableClassCanonicalNames = DEFAULT_IMMUTABLE_TYPES; 275 276 /** 277 * Setter to specify annotations canonical names which ignore variables 278 * in consideration. 279 * 280 * @param annotationNames array of ignore annotations canonical names. 281 * @since 6.5 282 */ 283 public void setIgnoreAnnotationCanonicalNames(String... annotationNames) { 284 ignoreAnnotationCanonicalNames = Set.of(annotationNames); 285 } 286 287 /** 288 * Setter to control whether protected members are allowed. 289 * 290 * @param protectedAllowed whether protected members are allowed 291 * @since 3.0 292 */ 293 public void setProtectedAllowed(boolean protectedAllowed) { 294 this.protectedAllowed = protectedAllowed; 295 } 296 297 /** 298 * Setter to control whether package visible members are allowed. 299 * 300 * @param packageAllowed whether package visible members are allowed 301 * @since 3.0 302 */ 303 public void setPackageAllowed(boolean packageAllowed) { 304 this.packageAllowed = packageAllowed; 305 } 306 307 /** 308 * Setter to specify pattern for public members that should be ignored. 309 * 310 * @param pattern 311 * pattern for public members to ignore. 312 * @since 3.0 313 */ 314 public void setPublicMemberPattern(Pattern pattern) { 315 publicMemberPattern = pattern; 316 } 317 318 /** 319 * Setter to allow immutable fields to be declared as public if defined in final class. 320 * 321 * @param allow user's value. 322 * @since 6.4 323 */ 324 public void setAllowPublicImmutableFields(boolean allow) { 325 allowPublicImmutableFields = allow; 326 } 327 328 /** 329 * Setter to allow final fields to be declared as public. 330 * 331 * @param allow user's value. 332 * @since 7.0 333 */ 334 public void setAllowPublicFinalFields(boolean allow) { 335 allowPublicFinalFields = allow; 336 } 337 338 /** 339 * Setter to specify immutable classes canonical names. 340 * 341 * @param classNames array of immutable types canonical names. 342 * @since 6.4.1 343 */ 344 public void setImmutableClassCanonicalNames(String... classNames) { 345 immutableClassCanonicalNames = Set.of(classNames); 346 } 347 348 @Override 349 public int[] getDefaultTokens() { 350 return getRequiredTokens(); 351 } 352 353 @Override 354 public int[] getAcceptableTokens() { 355 return getRequiredTokens(); 356 } 357 358 @Override 359 public int[] getRequiredTokens() { 360 return new int[] { 361 TokenTypes.VARIABLE_DEF, 362 TokenTypes.IMPORT, 363 }; 364 } 365 366 @Override 367 public void beginTree(DetailAST rootAst) { 368 immutableClassShortNames = getClassShortNames(immutableClassCanonicalNames); 369 ignoreAnnotationShortNames = getClassShortNames(ignoreAnnotationCanonicalNames); 370 } 371 372 @Override 373 public void visitToken(DetailAST ast) { 374 switch (ast.getType()) { 375 case TokenTypes.VARIABLE_DEF: 376 if (!isAnonymousClassVariable(ast)) { 377 visitVariableDef(ast); 378 } 379 break; 380 case TokenTypes.IMPORT: 381 visitImport(ast); 382 break; 383 default: 384 final String exceptionMsg = "Unexpected token type: " + ast.getText(); 385 throw new IllegalArgumentException(exceptionMsg); 386 } 387 } 388 389 /** 390 * Checks if current variable definition is definition of an anonymous class. 391 * 392 * @param variableDef {@link TokenTypes#VARIABLE_DEF VARIABLE_DEF} 393 * @return true if current variable definition is definition of an anonymous class. 394 */ 395 private static boolean isAnonymousClassVariable(DetailAST variableDef) { 396 return variableDef.getParent().getType() != TokenTypes.OBJBLOCK; 397 } 398 399 /** 400 * Checks access modifier of given variable. 401 * If it is not proper according to Check - puts violation on it. 402 * 403 * @param variableDef variable to check. 404 */ 405 private void visitVariableDef(DetailAST variableDef) { 406 final boolean inInterfaceOrAnnotationBlock = 407 ScopeUtil.isInInterfaceOrAnnotationBlock(variableDef); 408 409 if (!inInterfaceOrAnnotationBlock && !hasIgnoreAnnotation(variableDef)) { 410 final DetailAST varNameAST = variableDef.findFirstToken(TokenTypes.TYPE) 411 .getNextSibling(); 412 final String varName = varNameAST.getText(); 413 if (!hasProperAccessModifier(variableDef, varName)) { 414 log(varNameAST, MSG_KEY, varName); 415 } 416 } 417 } 418 419 /** 420 * Checks if variable def has ignore annotation. 421 * 422 * @param variableDef {@link TokenTypes#VARIABLE_DEF VARIABLE_DEF} 423 * @return true if variable def has ignore annotation. 424 */ 425 private boolean hasIgnoreAnnotation(DetailAST variableDef) { 426 final DetailAST firstIgnoreAnnotation = 427 findMatchingAnnotation(variableDef); 428 return firstIgnoreAnnotation != null; 429 } 430 431 /** 432 * Checks imported type. If type's canonical name was not specified in 433 * <b>immutableClassCanonicalNames</b>, but its short name collides with one from 434 * <b>immutableClassShortNames</b> - removes it from the last one. 435 * 436 * @param importAst {@link TokenTypes#IMPORT Import} 437 */ 438 private void visitImport(DetailAST importAst) { 439 if (!isStarImport(importAst)) { 440 final String canonicalName = getCanonicalName(importAst); 441 final String shortName = getClassShortName(canonicalName); 442 443 // If imported canonical class name is not specified as allowed immutable class, 444 // but its short name collides with one of specified class - removes the short name 445 // from list to avoid names collision 446 if (!immutableClassCanonicalNames.contains(canonicalName)) { 447 immutableClassShortNames.remove(shortName); 448 } 449 if (!ignoreAnnotationCanonicalNames.contains(canonicalName)) { 450 ignoreAnnotationShortNames.remove(shortName); 451 } 452 } 453 } 454 455 /** 456 * Checks if current import is star import. E.g.: 457 * 458 * <p> 459 * {@code 460 * import java.util.*; 461 * } 462 * </p> 463 * 464 * @param importAst {@link TokenTypes#IMPORT Import} 465 * @return true if it is star import 466 */ 467 private static boolean isStarImport(DetailAST importAst) { 468 boolean result = false; 469 DetailAST toVisit = importAst; 470 while (toVisit != null) { 471 toVisit = getNextSubTreeNode(toVisit, importAst); 472 if (toVisit != null && toVisit.getType() == TokenTypes.STAR) { 473 result = true; 474 break; 475 } 476 } 477 return result; 478 } 479 480 /** 481 * Checks if current variable has proper access modifier according to Check's options. 482 * 483 * @param variableDef Variable definition node. 484 * @param variableName Variable's name. 485 * @return true if variable has proper access modifier. 486 */ 487 private boolean hasProperAccessModifier(DetailAST variableDef, String variableName) { 488 boolean result = true; 489 490 final String variableScope = getVisibilityScope(variableDef); 491 492 if (!PRIVATE_ACCESS_MODIFIER.equals(variableScope)) { 493 result = 494 isStaticFinalVariable(variableDef) 495 || packageAllowed && PACKAGE_ACCESS_MODIFIER.equals(variableScope) 496 || protectedAllowed && PROTECTED_ACCESS_MODIFIER.equals(variableScope) 497 || isIgnoredPublicMember(variableName, variableScope) 498 || isAllowedPublicField(variableDef); 499 } 500 501 return result; 502 } 503 504 /** 505 * Checks whether variable has static final modifiers. 506 * 507 * @param variableDef Variable definition node. 508 * @return true of variable has static final modifiers. 509 */ 510 private static boolean isStaticFinalVariable(DetailAST variableDef) { 511 final Set<String> modifiers = getModifiers(variableDef); 512 return modifiers.contains(STATIC_KEYWORD) 513 && modifiers.contains(FINAL_KEYWORD); 514 } 515 516 /** 517 * Checks whether variable belongs to public members that should be ignored. 518 * 519 * @param variableName Variable's name. 520 * @param variableScope Variable's scope. 521 * @return true if variable belongs to public members that should be ignored. 522 */ 523 private boolean isIgnoredPublicMember(String variableName, String variableScope) { 524 return PUBLIC_ACCESS_MODIFIER.equals(variableScope) 525 && publicMemberPattern.matcher(variableName).find(); 526 } 527 528 /** 529 * Checks whether the variable satisfies the public field check. 530 * 531 * @param variableDef Variable definition node. 532 * @return true if allowed. 533 */ 534 private boolean isAllowedPublicField(DetailAST variableDef) { 535 return allowPublicFinalFields && isFinalField(variableDef) 536 || allowPublicImmutableFields && isImmutableFieldDefinedInFinalClass(variableDef); 537 } 538 539 /** 540 * Checks whether immutable field is defined in final class. 541 * 542 * @param variableDef Variable definition node. 543 * @return true if immutable field is defined in final class. 544 */ 545 private boolean isImmutableFieldDefinedInFinalClass(DetailAST variableDef) { 546 final DetailAST classDef = variableDef.getParent().getParent(); 547 final Set<String> classModifiers = getModifiers(classDef); 548 return (classModifiers.contains(FINAL_KEYWORD) || classDef.getType() == TokenTypes.ENUM_DEF) 549 && isImmutableField(variableDef); 550 } 551 552 /** 553 * Returns the set of modifier Strings for a VARIABLE_DEF or CLASS_DEF AST. 554 * 555 * @param defAST AST for a variable or class definition. 556 * @return the set of modifier Strings for defAST. 557 */ 558 private static Set<String> getModifiers(DetailAST defAST) { 559 final DetailAST modifiersAST = defAST.findFirstToken(TokenTypes.MODIFIERS); 560 final Set<String> modifiersSet = new HashSet<>(); 561 if (modifiersAST != null) { 562 DetailAST modifier = modifiersAST.getFirstChild(); 563 while (modifier != null) { 564 modifiersSet.add(modifier.getText()); 565 modifier = modifier.getNextSibling(); 566 } 567 } 568 return modifiersSet; 569 } 570 571 /** 572 * Returns the visibility scope for the variable. 573 * 574 * @param variableDef Variable definition node. 575 * @return one of "public", "private", "protected", "package" 576 */ 577 private static String getVisibilityScope(DetailAST variableDef) { 578 final Set<String> modifiers = getModifiers(variableDef); 579 String accessModifier = PACKAGE_ACCESS_MODIFIER; 580 for (final String modifier : EXPLICIT_MODS) { 581 if (modifiers.contains(modifier)) { 582 accessModifier = modifier; 583 break; 584 } 585 } 586 return accessModifier; 587 } 588 589 /** 590 * Checks if current field is immutable: 591 * has final modifier and either a primitive type or instance of class 592 * known to be immutable (such as String, ImmutableCollection from Guava, etc.). 593 * Classes known to be immutable are listed in 594 * {@link VisibilityModifierCheck#immutableClassCanonicalNames} 595 * 596 * @param variableDef Field in consideration. 597 * @return true if field is immutable. 598 */ 599 private boolean isImmutableField(DetailAST variableDef) { 600 boolean result = false; 601 if (isFinalField(variableDef)) { 602 final DetailAST type = variableDef.findFirstToken(TokenTypes.TYPE); 603 final boolean isCanonicalName = isCanonicalName(type); 604 final String typeName = getCanonicalName(type); 605 if (immutableClassShortNames.contains(typeName) 606 || isCanonicalName && immutableClassCanonicalNames.contains(typeName)) { 607 final DetailAST typeArgs = getGenericTypeArgs(type, isCanonicalName); 608 609 if (typeArgs == null) { 610 result = true; 611 } 612 else { 613 final List<String> argsClassNames = getTypeArgsClassNames(typeArgs); 614 result = areImmutableTypeArguments(argsClassNames); 615 } 616 } 617 else { 618 result = !isCanonicalName && isPrimitive(type); 619 } 620 } 621 return result; 622 } 623 624 /** 625 * Checks whether type definition is in canonical form. 626 * 627 * @param type type definition token. 628 * @return true if type definition is in canonical form. 629 */ 630 private static boolean isCanonicalName(DetailAST type) { 631 return type.getFirstChild().getType() == TokenTypes.DOT; 632 } 633 634 /** 635 * Returns generic type arguments token. 636 * 637 * @param type type token. 638 * @param isCanonicalName whether type name is in canonical form. 639 * @return generic type arguments token. 640 */ 641 private static DetailAST getGenericTypeArgs(DetailAST type, boolean isCanonicalName) { 642 final DetailAST typeArgs; 643 if (isCanonicalName) { 644 // if type class name is in canonical form, abstract tree has specific structure 645 typeArgs = type.getFirstChild().findFirstToken(TokenTypes.TYPE_ARGUMENTS); 646 } 647 else { 648 typeArgs = type.findFirstToken(TokenTypes.TYPE_ARGUMENTS); 649 } 650 return typeArgs; 651 } 652 653 /** 654 * Returns a list of type parameters class names. 655 * 656 * @param typeArgs type arguments token. 657 * @return a list of type parameters class names. 658 */ 659 private static List<String> getTypeArgsClassNames(DetailAST typeArgs) { 660 final List<String> typeClassNames = new ArrayList<>(); 661 DetailAST type = typeArgs.findFirstToken(TokenTypes.TYPE_ARGUMENT); 662 DetailAST sibling; 663 do { 664 final String typeName = getCanonicalName(type); 665 typeClassNames.add(typeName); 666 sibling = type.getNextSibling(); 667 type = sibling.getNextSibling(); 668 } while (sibling.getType() == TokenTypes.COMMA); 669 return typeClassNames; 670 } 671 672 /** 673 * Checks whether all generic type arguments are immutable. 674 * If at least one argument is mutable, we assume that the whole list of type arguments 675 * is mutable. 676 * 677 * @param typeArgsClassNames type arguments class names. 678 * @return true if all generic type arguments are immutable. 679 */ 680 private boolean areImmutableTypeArguments(Collection<String> typeArgsClassNames) { 681 return typeArgsClassNames.stream().noneMatch( 682 typeName -> { 683 return !immutableClassShortNames.contains(typeName) 684 && !immutableClassCanonicalNames.contains(typeName); 685 }); 686 } 687 688 /** 689 * Checks whether current field is final. 690 * 691 * @param variableDef field in consideration. 692 * @return true if current field is final. 693 */ 694 private static boolean isFinalField(DetailAST variableDef) { 695 final DetailAST modifiers = variableDef.findFirstToken(TokenTypes.MODIFIERS); 696 return modifiers.findFirstToken(TokenTypes.FINAL) != null; 697 } 698 699 /** 700 * Checks if current type is primitive type (int, short, float, boolean, double, etc.). 701 * As primitive types have special tokens for each one, such as: 702 * LITERAL_INT, LITERAL_BOOLEAN, etc. 703 * So, if type's identifier differs from {@link TokenTypes#IDENT IDENT} token - it's a 704 * primitive type. 705 * 706 * @param type Ast {@link TokenTypes#TYPE TYPE} node. 707 * @return true if current type is primitive type. 708 */ 709 private static boolean isPrimitive(DetailAST type) { 710 return type.getFirstChild().getType() != TokenTypes.IDENT; 711 } 712 713 /** 714 * Gets canonical type's name from given {@link TokenTypes#TYPE TYPE} node. 715 * 716 * @param type DetailAST {@link TokenTypes#TYPE TYPE} node. 717 * @return canonical type's name 718 */ 719 private static String getCanonicalName(DetailAST type) { 720 final StringBuilder canonicalNameBuilder = new StringBuilder(256); 721 DetailAST toVisit = type; 722 while (toVisit != null) { 723 toVisit = getNextSubTreeNode(toVisit, type); 724 if (toVisit != null && toVisit.getType() == TokenTypes.IDENT) { 725 if (canonicalNameBuilder.length() > 0) { 726 canonicalNameBuilder.append('.'); 727 } 728 canonicalNameBuilder.append(toVisit.getText()); 729 final DetailAST nextSubTreeNode = getNextSubTreeNode(toVisit, type); 730 if (nextSubTreeNode != null 731 && nextSubTreeNode.getType() == TokenTypes.TYPE_ARGUMENTS) { 732 break; 733 } 734 } 735 } 736 return canonicalNameBuilder.toString(); 737 } 738 739 /** 740 * Gets the next node of a syntactical tree (child of a current node or 741 * sibling of a current node, or sibling of a parent of a current node). 742 * 743 * @param currentNodeAst Current node in considering 744 * @param subTreeRootAst SubTree root 745 * @return Current node after bypassing, if current node reached the root of a subtree 746 * method returns null 747 */ 748 private static DetailAST 749 getNextSubTreeNode(DetailAST currentNodeAst, DetailAST subTreeRootAst) { 750 DetailAST currentNode = currentNodeAst; 751 DetailAST toVisitAst = currentNode.getFirstChild(); 752 while (toVisitAst == null) { 753 toVisitAst = currentNode.getNextSibling(); 754 if (currentNode.getParent().getColumnNo() == subTreeRootAst.getColumnNo()) { 755 break; 756 } 757 currentNode = currentNode.getParent(); 758 } 759 return toVisitAst; 760 } 761 762 /** 763 * Converts canonical class names to short names. 764 * 765 * @param canonicalClassNames the set of canonical class names. 766 * @return the set of short names of classes. 767 */ 768 private static Set<String> getClassShortNames(Set<String> canonicalClassNames) { 769 return canonicalClassNames.stream() 770 .map(CommonUtil::baseClassName) 771 .collect(Collectors.toCollection(HashSet::new)); 772 } 773 774 /** 775 * Gets the short class name from given canonical name. 776 * 777 * @param canonicalClassName canonical class name. 778 * @return short name of class. 779 */ 780 private static String getClassShortName(String canonicalClassName) { 781 return canonicalClassName 782 .substring(canonicalClassName.lastIndexOf('.') + 1); 783 } 784 785 /** 786 * Checks whether the AST is annotated with 787 * an annotation containing the passed in regular 788 * expression and return the AST representing that 789 * annotation. 790 * 791 * <p> 792 * This method will not look for imports or package 793 * statements to detect the passed in annotation. 794 * </p> 795 * 796 * <p> 797 * To check if an AST contains a passed in annotation 798 * taking into account fully-qualified names 799 * (ex: java.lang.Override, Override) 800 * this method will need to be called twice. Once for each 801 * name given. 802 * </p> 803 * 804 * @param variableDef {@link TokenTypes#VARIABLE_DEF variable def node}. 805 * @return the AST representing the first such annotation or null if 806 * no such annotation was found 807 */ 808 private DetailAST findMatchingAnnotation(DetailAST variableDef) { 809 DetailAST matchingAnnotation = null; 810 811 final DetailAST holder = AnnotationUtil.getAnnotationHolder(variableDef); 812 813 for (DetailAST child = holder.getFirstChild(); 814 child != null; child = child.getNextSibling()) { 815 if (child.getType() == TokenTypes.ANNOTATION) { 816 final DetailAST ast = child.getFirstChild(); 817 final String name = 818 FullIdent.createFullIdent(ast.getNextSibling()).getText(); 819 if (ignoreAnnotationCanonicalNames.contains(name) 820 || ignoreAnnotationShortNames.contains(name)) { 821 matchingAnnotation = child; 822 break; 823 } 824 } 825 } 826 827 return matchingAnnotation; 828 } 829 830}