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