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