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