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.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 } 380 case TokenTypes.IMPORT -> visitImport(ast); 381 default -> { 382 final String exceptionMsg = "Unexpected token type: " + ast.getText(); 383 throw new IllegalArgumentException(exceptionMsg); 384 } 385 } 386 } 387 388 /** 389 * Checks if current variable definition is definition of an anonymous class. 390 * 391 * @param variableDef {@link TokenTypes#VARIABLE_DEF VARIABLE_DEF} 392 * @return true if current variable definition is definition of an anonymous class. 393 */ 394 private static boolean isAnonymousClassVariable(DetailAST variableDef) { 395 return variableDef.getParent().getType() != TokenTypes.OBJBLOCK; 396 } 397 398 /** 399 * Checks access modifier of given variable. 400 * If it is not proper according to Check - puts violation on it. 401 * 402 * @param variableDef variable to check. 403 */ 404 private void visitVariableDef(DetailAST variableDef) { 405 final boolean inInterfaceOrAnnotationBlock = 406 ScopeUtil.isInInterfaceOrAnnotationBlock(variableDef); 407 408 if (!inInterfaceOrAnnotationBlock && !hasIgnoreAnnotation(variableDef)) { 409 final DetailAST varNameAST = variableDef.findFirstToken(TokenTypes.TYPE) 410 .getNextSibling(); 411 final String varName = varNameAST.getText(); 412 if (!hasProperAccessModifier(variableDef, varName)) { 413 log(varNameAST, MSG_KEY, varName); 414 } 415 } 416 } 417 418 /** 419 * Checks if variable def has ignore annotation. 420 * 421 * @param variableDef {@link TokenTypes#VARIABLE_DEF VARIABLE_DEF} 422 * @return true if variable def has ignore annotation. 423 */ 424 private boolean hasIgnoreAnnotation(DetailAST variableDef) { 425 final DetailAST firstIgnoreAnnotation = 426 findMatchingAnnotation(variableDef); 427 return firstIgnoreAnnotation != null; 428 } 429 430 /** 431 * Checks imported type. If type's canonical name was not specified in 432 * <b>immutableClassCanonicalNames</b>, but its short name collides with one from 433 * <b>immutableClassShortNames</b> - removes it from the last one. 434 * 435 * @param importAst {@link TokenTypes#IMPORT Import} 436 */ 437 private void visitImport(DetailAST importAst) { 438 if (!isStarImport(importAst)) { 439 final String canonicalName = getCanonicalName(importAst); 440 final String shortName = getClassShortName(canonicalName); 441 442 // If imported canonical class name is not specified as allowed immutable class, 443 // but its short name collides with one of specified class - removes the short name 444 // from list to avoid names collision 445 if (!immutableClassCanonicalNames.contains(canonicalName)) { 446 immutableClassShortNames.remove(shortName); 447 } 448 if (!ignoreAnnotationCanonicalNames.contains(canonicalName)) { 449 ignoreAnnotationShortNames.remove(shortName); 450 } 451 } 452 } 453 454 /** 455 * Checks if current import is star import. E.g.: 456 * 457 * <p> 458 * {@code 459 * import java.util.*; 460 * } 461 * </p> 462 * 463 * @param importAst {@link TokenTypes#IMPORT Import} 464 * @return true if it is star import 465 */ 466 private static boolean isStarImport(DetailAST importAst) { 467 boolean result = false; 468 DetailAST toVisit = importAst; 469 while (toVisit != null) { 470 toVisit = getNextSubTreeNode(toVisit, importAst); 471 if (toVisit != null && toVisit.getType() == TokenTypes.STAR) { 472 result = true; 473 break; 474 } 475 } 476 return result; 477 } 478 479 /** 480 * Checks if current variable has proper access modifier according to Check's options. 481 * 482 * @param variableDef Variable definition node. 483 * @param variableName Variable's name. 484 * @return true if variable has proper access modifier. 485 */ 486 private boolean hasProperAccessModifier(DetailAST variableDef, String variableName) { 487 boolean result = true; 488 489 final String variableScope = getVisibilityScope(variableDef); 490 491 if (!PRIVATE_ACCESS_MODIFIER.equals(variableScope)) { 492 result = 493 isStaticFinalVariable(variableDef) 494 || packageAllowed && PACKAGE_ACCESS_MODIFIER.equals(variableScope) 495 || protectedAllowed && PROTECTED_ACCESS_MODIFIER.equals(variableScope) 496 || isIgnoredPublicMember(variableName, variableScope) 497 || isAllowedPublicField(variableDef); 498 } 499 500 return result; 501 } 502 503 /** 504 * Checks whether variable has static final modifiers. 505 * 506 * @param variableDef Variable definition node. 507 * @return true of variable has static final modifiers. 508 */ 509 private static boolean isStaticFinalVariable(DetailAST variableDef) { 510 final Set<String> modifiers = getModifiers(variableDef); 511 return modifiers.contains(STATIC_KEYWORD) 512 && modifiers.contains(FINAL_KEYWORD); 513 } 514 515 /** 516 * Checks whether variable belongs to public members that should be ignored. 517 * 518 * @param variableName Variable's name. 519 * @param variableScope Variable's scope. 520 * @return true if variable belongs to public members that should be ignored. 521 */ 522 private boolean isIgnoredPublicMember(String variableName, String variableScope) { 523 return PUBLIC_ACCESS_MODIFIER.equals(variableScope) 524 && publicMemberPattern.matcher(variableName).find(); 525 } 526 527 /** 528 * Checks whether the variable satisfies the public field check. 529 * 530 * @param variableDef Variable definition node. 531 * @return true if allowed. 532 */ 533 private boolean isAllowedPublicField(DetailAST variableDef) { 534 return allowPublicFinalFields && isFinalField(variableDef) 535 || allowPublicImmutableFields && isImmutableFieldDefinedInFinalClass(variableDef); 536 } 537 538 /** 539 * Checks whether immutable field is defined in final class. 540 * 541 * @param variableDef Variable definition node. 542 * @return true if immutable field is defined in final class. 543 */ 544 private boolean isImmutableFieldDefinedInFinalClass(DetailAST variableDef) { 545 final DetailAST classDef = variableDef.getParent().getParent(); 546 final Set<String> classModifiers = getModifiers(classDef); 547 return (classModifiers.contains(FINAL_KEYWORD) || classDef.getType() == TokenTypes.ENUM_DEF) 548 && isImmutableField(variableDef); 549 } 550 551 /** 552 * Returns the set of modifier Strings for a VARIABLE_DEF or CLASS_DEF AST. 553 * 554 * @param defAST AST for a variable or class definition. 555 * @return the set of modifier Strings for defAST. 556 */ 557 private static Set<String> getModifiers(DetailAST defAST) { 558 final DetailAST modifiersAST = defAST.findFirstToken(TokenTypes.MODIFIERS); 559 final Set<String> modifiersSet = new HashSet<>(); 560 if (modifiersAST != null) { 561 DetailAST modifier = modifiersAST.getFirstChild(); 562 while (modifier != null) { 563 modifiersSet.add(modifier.getText()); 564 modifier = modifier.getNextSibling(); 565 } 566 } 567 return modifiersSet; 568 } 569 570 /** 571 * Returns the visibility scope for the variable. 572 * 573 * @param variableDef Variable definition node. 574 * @return one of "public", "private", "protected", "package" 575 */ 576 private static String getVisibilityScope(DetailAST variableDef) { 577 final Set<String> modifiers = getModifiers(variableDef); 578 String accessModifier = PACKAGE_ACCESS_MODIFIER; 579 for (final String modifier : EXPLICIT_MODS) { 580 if (modifiers.contains(modifier)) { 581 accessModifier = modifier; 582 break; 583 } 584 } 585 return accessModifier; 586 } 587 588 /** 589 * Checks if current field is immutable: 590 * has final modifier and either a primitive type or instance of class 591 * known to be immutable (such as String, ImmutableCollection from Guava, etc.). 592 * Classes known to be immutable are listed in 593 * {@link VisibilityModifierCheck#immutableClassCanonicalNames} 594 * 595 * @param variableDef Field in consideration. 596 * @return true if field is immutable. 597 */ 598 private boolean isImmutableField(DetailAST variableDef) { 599 boolean result = false; 600 if (isFinalField(variableDef)) { 601 final DetailAST type = variableDef.findFirstToken(TokenTypes.TYPE); 602 final boolean isCanonicalName = isCanonicalName(type); 603 final String typeName = getCanonicalName(type); 604 if (immutableClassShortNames.contains(typeName) 605 || isCanonicalName && immutableClassCanonicalNames.contains(typeName)) { 606 final DetailAST typeArgs = getGenericTypeArgs(type, isCanonicalName); 607 608 if (typeArgs == null) { 609 result = true; 610 } 611 else { 612 final List<String> argsClassNames = getTypeArgsClassNames(typeArgs); 613 result = areImmutableTypeArguments(argsClassNames); 614 } 615 } 616 else { 617 result = !isCanonicalName && isPrimitive(type); 618 } 619 } 620 return result; 621 } 622 623 /** 624 * Checks whether type definition is in canonical form. 625 * 626 * @param type type definition token. 627 * @return true if type definition is in canonical form. 628 */ 629 private static boolean isCanonicalName(DetailAST type) { 630 return type.getFirstChild().getType() == TokenTypes.DOT; 631 } 632 633 /** 634 * Returns generic type arguments token. 635 * 636 * @param type type token. 637 * @param isCanonicalName whether type name is in canonical form. 638 * @return generic type arguments token. 639 */ 640 private static DetailAST getGenericTypeArgs(DetailAST type, boolean isCanonicalName) { 641 final DetailAST typeArgs; 642 if (isCanonicalName) { 643 // if type class name is in canonical form, abstract tree has specific structure 644 typeArgs = type.getFirstChild().findFirstToken(TokenTypes.TYPE_ARGUMENTS); 645 } 646 else { 647 typeArgs = type.findFirstToken(TokenTypes.TYPE_ARGUMENTS); 648 } 649 return typeArgs; 650 } 651 652 /** 653 * Returns a list of type parameters class names. 654 * 655 * @param typeArgs type arguments token. 656 * @return a list of type parameters class names. 657 */ 658 private static List<String> getTypeArgsClassNames(DetailAST typeArgs) { 659 final List<String> typeClassNames = new ArrayList<>(); 660 DetailAST type = typeArgs.findFirstToken(TokenTypes.TYPE_ARGUMENT); 661 DetailAST sibling; 662 do { 663 final String typeName = getCanonicalName(type); 664 typeClassNames.add(typeName); 665 sibling = type.getNextSibling(); 666 type = sibling.getNextSibling(); 667 } while (sibling.getType() == TokenTypes.COMMA); 668 return typeClassNames; 669 } 670 671 /** 672 * Checks whether all generic type arguments are immutable. 673 * If at least one argument is mutable, we assume that the whole list of type arguments 674 * is mutable. 675 * 676 * @param typeArgsClassNames type arguments class names. 677 * @return true if all generic type arguments are immutable. 678 */ 679 private boolean areImmutableTypeArguments(Collection<String> typeArgsClassNames) { 680 return typeArgsClassNames.stream().noneMatch( 681 typeName -> { 682 return !immutableClassShortNames.contains(typeName) 683 && !immutableClassCanonicalNames.contains(typeName); 684 }); 685 } 686 687 /** 688 * Checks whether current field is final. 689 * 690 * @param variableDef field in consideration. 691 * @return true if current field is final. 692 */ 693 private static boolean isFinalField(DetailAST variableDef) { 694 final DetailAST modifiers = variableDef.findFirstToken(TokenTypes.MODIFIERS); 695 return modifiers.findFirstToken(TokenTypes.FINAL) != null; 696 } 697 698 /** 699 * Checks if current type is primitive type (int, short, float, boolean, double, etc.). 700 * As primitive types have special tokens for each one, such as: 701 * LITERAL_INT, LITERAL_BOOLEAN, etc. 702 * So, if type's identifier differs from {@link TokenTypes#IDENT IDENT} token - it's a 703 * primitive type. 704 * 705 * @param type Ast {@link TokenTypes#TYPE TYPE} node. 706 * @return true if current type is primitive type. 707 */ 708 private static boolean isPrimitive(DetailAST type) { 709 return type.getFirstChild().getType() != TokenTypes.IDENT; 710 } 711 712 /** 713 * Gets canonical type's name from given {@link TokenTypes#TYPE TYPE} node. 714 * 715 * @param type DetailAST {@link TokenTypes#TYPE TYPE} node. 716 * @return canonical type's name 717 */ 718 private static String getCanonicalName(DetailAST type) { 719 final StringBuilder canonicalNameBuilder = new StringBuilder(256); 720 DetailAST toVisit = type; 721 while (toVisit != null) { 722 toVisit = getNextSubTreeNode(toVisit, type); 723 if (toVisit != null && toVisit.getType() == TokenTypes.IDENT) { 724 if (!canonicalNameBuilder.isEmpty()) { 725 canonicalNameBuilder.append('.'); 726 } 727 canonicalNameBuilder.append(toVisit.getText()); 728 final DetailAST nextSubTreeNode = getNextSubTreeNode(toVisit, type); 729 if (nextSubTreeNode != null 730 && nextSubTreeNode.getType() == TokenTypes.TYPE_ARGUMENTS) { 731 break; 732 } 733 } 734 } 735 return canonicalNameBuilder.toString(); 736 } 737 738 /** 739 * Gets the next node of a syntactical tree (child of a current node or 740 * sibling of a current node, or sibling of a parent of a current node). 741 * 742 * @param currentNodeAst Current node in considering 743 * @param subTreeRootAst SubTree root 744 * @return Current node after bypassing, if current node reached the root of a subtree 745 * method returns null 746 */ 747 private static DetailAST 748 getNextSubTreeNode(DetailAST currentNodeAst, DetailAST subTreeRootAst) { 749 DetailAST currentNode = currentNodeAst; 750 DetailAST toVisitAst = currentNode.getFirstChild(); 751 while (toVisitAst == null) { 752 toVisitAst = currentNode.getNextSibling(); 753 if (currentNode.getParent().getColumnNo() == subTreeRootAst.getColumnNo()) { 754 break; 755 } 756 currentNode = currentNode.getParent(); 757 } 758 return toVisitAst; 759 } 760 761 /** 762 * Converts canonical class names to short names. 763 * 764 * @param canonicalClassNames the set of canonical class names. 765 * @return the set of short names of classes. 766 */ 767 private static Set<String> getClassShortNames(Set<String> canonicalClassNames) { 768 return canonicalClassNames.stream() 769 .map(CommonUtil::baseClassName) 770 .collect(Collectors.toCollection(HashSet::new)); 771 } 772 773 /** 774 * Gets the short class name from given canonical name. 775 * 776 * @param canonicalClassName canonical class name. 777 * @return short name of class. 778 */ 779 private static String getClassShortName(String canonicalClassName) { 780 return canonicalClassName 781 .substring(canonicalClassName.lastIndexOf('.') + 1); 782 } 783 784 /** 785 * Checks whether the AST is annotated with 786 * an annotation containing the passed in regular 787 * expression and return the AST representing that 788 * annotation. 789 * 790 * <p> 791 * This method will not look for imports or package 792 * statements to detect the passed in annotation. 793 * </p> 794 * 795 * <p> 796 * To check if an AST contains a passed in annotation 797 * taking into account fully-qualified names 798 * (ex: java.lang.Override, Override) 799 * this method will need to be called twice. Once for each 800 * name given. 801 * </p> 802 * 803 * @param variableDef {@link TokenTypes#VARIABLE_DEF variable def node}. 804 * @return the AST representing the first such annotation or null if 805 * no such annotation was found 806 */ 807 private DetailAST findMatchingAnnotation(DetailAST variableDef) { 808 DetailAST matchingAnnotation = null; 809 810 final DetailAST holder = AnnotationUtil.getAnnotationHolder(variableDef); 811 812 for (DetailAST child = holder.getFirstChild(); 813 child != null; child = child.getNextSibling()) { 814 if (child.getType() == TokenTypes.ANNOTATION) { 815 final DetailAST ast = child.getFirstChild(); 816 final String name = 817 FullIdent.createFullIdent(ast.getNextSibling()).getText(); 818 if (ignoreAnnotationCanonicalNames.contains(name) 819 || ignoreAnnotationShortNames.contains(name)) { 820 matchingAnnotation = child; 821 break; 822 } 823 } 824 } 825 826 return matchingAnnotation; 827 } 828 829}