001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 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.Arrays; 024import java.util.Collections; 025import java.util.HashSet; 026import java.util.List; 027import java.util.Set; 028import java.util.regex.Pattern; 029import java.util.stream.Collectors; 030 031import com.puppycrawl.tools.checkstyle.FileStatefulCheck; 032import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 033import com.puppycrawl.tools.checkstyle.api.DetailAST; 034import com.puppycrawl.tools.checkstyle.api.FullIdent; 035import com.puppycrawl.tools.checkstyle.api.TokenTypes; 036import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil; 037import com.puppycrawl.tools.checkstyle.utils.ScopeUtil; 038 039/** 040 * <p> 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 * </p> 046 * <p> 047 * Public members are not flagged if the name matches the public 048 * member regular expression (contains {@code "^serialVersionUID$"} by 049 * default). 050 * </p> 051 * <p> 052 * Note that Checkstyle 2 used to include {@code "^f[A-Z][a-zA-Z0-9]*$"} in the default pattern 053 * to allow names used in container-managed persistence for Enterprise JavaBeans (EJB) 1.1 with 054 * the default settings. With EJB 2.0 it is no longer necessary to have public access for 055 * persistent fields, so the default has been changed. 056 * </p> 057 * <p> 058 * Rationale: Enforce encapsulation. 059 * </p> 060 * <p> 061 * Check also has options making it less strict: 062 * </p> 063 * <p> 064 * <b>ignoreAnnotationCanonicalNames</b>- the list of annotations which ignore 065 * variables in consideration. If user will provide short annotation name that 066 * type will match to any named the same type without consideration of package. 067 * </p> 068 * <p> 069 * <b>allowPublicFinalFields</b>- which allows public final fields. 070 * </p> 071 * <p> 072 * <b>allowPublicImmutableFields</b>- which allows immutable fields to be 073 * declared as public if defined in final class. 074 * </p> 075 * <p> 076 * Field is known to be immutable if: 077 * </p> 078 * <ul> 079 * <li>It's declared as final</li> 080 * <li>Has either a primitive type or instance of class user defined to be immutable 081 * (such as String, ImmutableCollection from Guava and etc)</li> 082 * </ul> 083 * <p> 084 * Classes known to be immutable are listed in <b>immutableClassCanonicalNames</b> 085 * by their canonical names. 086 * </p> 087 * <p> 088 * Property Rationale: Forcing all fields of class to have private modifier by default is 089 * good in most cases, but in some cases it drawbacks in too much boilerplate get/set code. 090 * One of such cases are immutable classes. 091 * </p> 092 * <p> 093 * Restriction: Check doesn't check if class is immutable, there's no checking 094 * if accessory methods are missing and all fields are immutable, we only check 095 * if current field is immutable or final. 096 * Under the flag <b>allowPublicImmutableFields</b>, the enclosing class must 097 * also be final, to encourage immutability. 098 * Under the flag <b>allowPublicFinalFields</b>, the final modifier 099 * on the enclosing class is optional. 100 * </p> 101 * <p> 102 * Star imports are out of scope of this Check. So if one of type imported via 103 * star import collides with user specified one by its short name - there 104 * won't be Check's violation. 105 * </p> 106 * <ul> 107 * <li> 108 * Property {@code packageAllowed} - Control whether package visible members are allowed. 109 * Default value is {@code false}. 110 * </li> 111 * <li> 112 * Property {@code protectedAllowed} - Control whether protected members are allowed. 113 * Default value is {@code false}. 114 * </li> 115 * <li> 116 * Property {@code publicMemberPattern} - Specify pattern for public members that should be ignored. 117 * Default value is {@code "^serialVersionUID$"}. 118 * </li> 119 * <li> 120 * Property {@code allowPublicFinalFields} - Allow final fields to be declared as public. 121 * Default value is {@code false}. 122 * </li> 123 * <li> 124 * Property {@code allowPublicImmutableFields} - Allow immutable fields to be 125 * declared as public if defined in final class. 126 * Default value is {@code false}. 127 * </li> 128 * <li> 129 * Property {@code immutableClassCanonicalNames} - Specify immutable classes canonical names. 130 * Default value is {@code java.io.File, java.lang.Boolean, java.lang.Byte, 131 * java.lang.Character, java.lang.Double, java.lang.Float, java.lang.Integer, 132 * java.lang.Long, java.lang.Short, java.lang.StackTraceElement, java.lang.String, 133 * java.math.BigDecimal, java.math.BigInteger, java.net.Inet4Address, java.net.Inet6Address, 134 * java.net.InetSocketAddress, java.net.URI, java.net.URL, java.util.Locale, java.util.UUID}. 135 * </li> 136 * <li> 137 * Property {@code ignoreAnnotationCanonicalNames} - Specify the list of annotations canonical 138 * names which ignore variables in consideration. 139 * Default value is {@code com.google.common.annotations.VisibleForTesting, 140 * org.junit.ClassRule, org.junit.Rule}. 141 * </li> 142 * </ul> 143 * <p> 144 * To configure the check: 145 * </p> 146 * <pre> 147 * <module name="VisibilityModifier"/> 148 * </pre> 149 * <p> 150 * To configure the check so that it allows package visible members: 151 * </p> 152 * <pre> 153 * <module name="VisibilityModifier"> 154 * <property name="packageAllowed" value="true"/> 155 * </module> 156 * </pre> 157 * <p> 158 * To configure the check so that it allows no public members: 159 * </p> 160 * <pre> 161 * <module name="VisibilityModifier"> 162 * <property name="publicMemberPattern" value="^$"/> 163 * </module> 164 * </pre> 165 * <p> 166 * To configure the Check so that it allows public immutable fields (mostly for immutable classes): 167 * </p> 168 * <pre> 169 * <module name="VisibilityModifier"> 170 * <property name="allowPublicImmutableFields" value="true"/> 171 * </module> 172 * </pre> 173 * <p> 174 * Example of allowed public immutable fields: 175 * </p> 176 * <pre> 177 * public class ImmutableClass 178 * { 179 * public final ImmutableSet<String> includes; // No warning 180 * public final ImmutableSet<String> excludes; // No warning 181 * public final java.lang.String notes; // No warning 182 * public final BigDecimal value; // No warning 183 * 184 * public ImmutableClass(Collection<String> includes, Collection<String> excludes, 185 * BigDecimal value, String notes) 186 * { 187 * this.includes = ImmutableSet.copyOf(includes); 188 * this.excludes = ImmutableSet.copyOf(excludes); 189 * this.value = value; 190 * this.notes = notes; 191 * } 192 * } 193 * </pre> 194 * <p> 195 * To configure the Check in order to allow user specified immutable class names: 196 * </p> 197 * <pre> 198 * <module name="VisibilityModifier"> 199 * <property name="allowPublicImmutableFields" value="true"/> 200 * <property name="immutableClassCanonicalNames" value=" 201 * com.google.common.collect.ImmutableSet"/> 202 * </module> 203 * </pre> 204 * <p> 205 * Example of allowed public immutable fields: 206 * </p> 207 * <pre> 208 * public class ImmutableClass 209 * { 210 * public final ImmutableSet<String> includes; // No warning 211 * public final ImmutableSet<String> excludes; // No warning 212 * public final java.lang.String notes; // Warning here because 213 * //'java.lang.String' wasn't specified as allowed class 214 * public final int someValue; // No warning 215 * 216 * public ImmutableClass(Collection<String> includes, Collection<String> excludes, 217 * String notes, int someValue) 218 * { 219 * this.includes = ImmutableSet.copyOf(includes); 220 * this.excludes = ImmutableSet.copyOf(excludes); 221 * this.value = value; 222 * this.notes = notes; 223 * this.someValue = someValue; 224 * } 225 * } 226 * </pre> 227 * <p> 228 * Note, if allowPublicImmutableFields is set to true, the check will also check 229 * whether generic type parameters are immutable. If at least one generic type 230 * parameter is mutable, there will be a violation. 231 * </p> 232 * <pre> 233 * <module name="VisibilityModifier"> 234 * <property name="allowPublicImmutableFields" value="true"/> 235 * <property name="immutableClassCanonicalNames" 236 * value="com.google.common.collect.ImmutableSet, com.google.common.collect.ImmutableMap, 237 * java.lang.String"/> 238 * </module> 239 * </pre> 240 * <p> 241 * Example of how the check works: 242 * </p> 243 * <pre> 244 * public final class Test { 245 * public final String s; 246 * public final ImmutableSet<String> names; 247 * public final ImmutableSet<Object> objects; // violation (Object class is mutable) 248 * public final ImmutableMap<String, Object> links; // violation (Object class is mutable) 249 * 250 * public Test() { 251 * s = "Hello!"; 252 * names = ImmutableSet.of(); 253 * objects = ImmutableSet.of(); 254 * links = ImmutableMap.of(); 255 * } 256 * } 257 * </pre> 258 * <p> 259 * To configure the Check passing fields annotated with @com.annotation.CustomAnnotation: 260 * </p> 261 * <pre> 262 * <module name="VisibilityModifier"> 263 * <property name="ignoreAnnotationCanonicalNames" value= 264 * "com.annotation.CustomAnnotation"/> 265 * </module> 266 * </pre> 267 * <p> 268 * Example of allowed field: 269 * </p> 270 * <pre> 271 * class SomeClass 272 * { 273 * @com.annotation.CustomAnnotation 274 * String annotatedString; // no warning 275 * @CustomAnnotation 276 * String shortCustomAnnotated; // no warning 277 * } 278 * </pre> 279 * <p> 280 * To configure the Check passing fields annotated with @org.junit.Rule, 281 * @org.junit.ClassRule and @com.google.common.annotations.VisibleForTesting annotations: 282 * </p> 283 * <pre> 284 * <module name="VisibilityModifier"/> 285 * </pre> 286 * <p> 287 * Example of allowed fields: 288 * </p> 289 * <pre> 290 * class SomeClass 291 * { 292 * @org.junit.Rule 293 * public TemporaryFolder publicJUnitRule = new TemporaryFolder(); // no warning 294 * @org.junit.ClassRule 295 * public static TemporaryFolder publicJUnitClassRule = new TemporaryFolder(); // no warning 296 * @com.google.common.annotations.VisibleForTesting 297 * public String testString = ""; // no warning 298 * } 299 * </pre> 300 * <p> 301 * To configure the Check passing fields annotated with short annotation name: 302 * </p> 303 * <pre> 304 * <module name="VisibilityModifier"> 305 * <property name="ignoreAnnotationCanonicalNames" 306 * value="CustomAnnotation"/> 307 * </module> 308 * </pre> 309 * <p> 310 * Example of allowed fields: 311 * </p> 312 * <pre> 313 * class SomeClass 314 * { 315 * @CustomAnnotation 316 * String customAnnotated; // no warning 317 * @com.annotation.CustomAnnotation 318 * String customAnnotated1; // no warning 319 * @mypackage.annotation.CustomAnnotation 320 * String customAnnotatedAnotherPackage; // another package but short name matches 321 * // so no violation 322 * } 323 * </pre> 324 * <p> 325 * To understand the difference between allowPublicImmutableFields and allowPublicFinalFields 326 * options, please, study the following examples. 327 * </p> 328 * <p> 329 * 1) To configure the check to use only 'allowPublicImmutableFields' option: 330 * </p> 331 * <pre> 332 * <module name="VisibilityModifier"> 333 * <property name="allowPublicImmutableFields" value="true"/> 334 * </module> 335 * </pre> 336 * <p> 337 * Code example: 338 * </p> 339 * <pre> 340 * public class InputPublicImmutable { 341 * public final int someIntValue; // violation 342 * public final ImmutableSet<String> includes; // violation 343 * public final java.lang.String notes; // violation 344 * public final BigDecimal value; // violation 345 * public final List list; // violation 346 * 347 * public InputPublicImmutable(Collection<String> includes, 348 * BigDecimal value, String notes, int someValue, List l) { 349 * this.includes = ImmutableSet.copyOf(includes); 350 * this.value = value; 351 * this.notes = notes; 352 * this.someIntValue = someValue; 353 * this.list = l; 354 * } 355 * } 356 * </pre> 357 * <p> 358 * 2) To configure the check to use only 'allowPublicFinalFields' option: 359 * </p> 360 * <pre> 361 * <module name="VisibilityModifier"> 362 * <property name="allowPublicFinalFields" value="true"/> 363 * </module> 364 * </pre> 365 * <p> 366 * Code example: 367 * </p> 368 * <pre> 369 * public class InputPublicImmutable { 370 * public final int someIntValue; 371 * public final ImmutableSet<String> includes; 372 * public final java.lang.String notes; 373 * public final BigDecimal value; 374 * public final List list; 375 * 376 * public InputPublicImmutable(Collection<String> includes, 377 * BigDecimal value, String notes, int someValue, List l) { 378 * this.includes = ImmutableSet.copyOf(includes); 379 * this.value = value; 380 * this.notes = notes; 381 * this.someIntValue = someValue; 382 * this.list = l; 383 * } 384 * } 385 * </pre> 386 * 387 * @since 3.0 388 */ 389@FileStatefulCheck 390public class VisibilityModifierCheck 391 extends AbstractCheck { 392 393 /** 394 * A key is pointing to the warning message text in "messages.properties" 395 * file. 396 */ 397 public static final String MSG_KEY = "variable.notPrivate"; 398 399 /** Default immutable types canonical names. */ 400 private static final List<String> DEFAULT_IMMUTABLE_TYPES = Collections.unmodifiableList( 401 Arrays.stream(new String[] { 402 "java.lang.String", 403 "java.lang.Integer", 404 "java.lang.Byte", 405 "java.lang.Character", 406 "java.lang.Short", 407 "java.lang.Boolean", 408 "java.lang.Long", 409 "java.lang.Double", 410 "java.lang.Float", 411 "java.lang.StackTraceElement", 412 "java.math.BigInteger", 413 "java.math.BigDecimal", 414 "java.io.File", 415 "java.util.Locale", 416 "java.util.UUID", 417 "java.net.URL", 418 "java.net.URI", 419 "java.net.Inet4Address", 420 "java.net.Inet6Address", 421 "java.net.InetSocketAddress", 422 }).collect(Collectors.toList())); 423 424 /** Default ignore annotations canonical names. */ 425 private static final List<String> DEFAULT_IGNORE_ANNOTATIONS = Collections.unmodifiableList( 426 Arrays.stream(new String[] { 427 "org.junit.Rule", 428 "org.junit.ClassRule", 429 "com.google.common.annotations.VisibleForTesting", 430 }).collect(Collectors.toList())); 431 432 /** Name for 'public' access modifier. */ 433 private static final String PUBLIC_ACCESS_MODIFIER = "public"; 434 435 /** Name for 'private' access modifier. */ 436 private static final String PRIVATE_ACCESS_MODIFIER = "private"; 437 438 /** Name for 'protected' access modifier. */ 439 private static final String PROTECTED_ACCESS_MODIFIER = "protected"; 440 441 /** Name for implicit 'package' access modifier. */ 442 private static final String PACKAGE_ACCESS_MODIFIER = "package"; 443 444 /** Name for 'static' keyword. */ 445 private static final String STATIC_KEYWORD = "static"; 446 447 /** Name for 'final' keyword. */ 448 private static final String FINAL_KEYWORD = "final"; 449 450 /** Contains explicit access modifiers. */ 451 private static final String[] EXPLICIT_MODS = { 452 PUBLIC_ACCESS_MODIFIER, 453 PRIVATE_ACCESS_MODIFIER, 454 PROTECTED_ACCESS_MODIFIER, 455 }; 456 457 /** 458 * Specify pattern for public members that should be ignored. 459 */ 460 private Pattern publicMemberPattern = Pattern.compile("^serialVersionUID$"); 461 462 /** List of ignore annotations short names. */ 463 private final List<String> ignoreAnnotationShortNames = 464 getClassShortNames(DEFAULT_IGNORE_ANNOTATIONS); 465 466 /** List of immutable classes short names. */ 467 private final List<String> immutableClassShortNames = 468 getClassShortNames(DEFAULT_IMMUTABLE_TYPES); 469 470 /** 471 * Specify the list of annotations canonical names which ignore variables in 472 * consideration. 473 */ 474 private List<String> ignoreAnnotationCanonicalNames = 475 new ArrayList<>(DEFAULT_IGNORE_ANNOTATIONS); 476 477 /** Control whether protected members are allowed. */ 478 private boolean protectedAllowed; 479 480 /** Control whether package visible members are allowed. */ 481 private boolean packageAllowed; 482 483 /** Allow immutable fields to be declared as public if defined in final class. */ 484 private boolean allowPublicImmutableFields; 485 486 /** Allow final fields to be declared as public. */ 487 private boolean allowPublicFinalFields; 488 489 /** Specify immutable classes canonical names. */ 490 private List<String> immutableClassCanonicalNames = new ArrayList<>(DEFAULT_IMMUTABLE_TYPES); 491 492 /** 493 * Setter to specify the list of annotations canonical names which ignore variables 494 * in consideration. 495 * 496 * @param annotationNames array of ignore annotations canonical names. 497 */ 498 public void setIgnoreAnnotationCanonicalNames(String... annotationNames) { 499 ignoreAnnotationCanonicalNames = Arrays.asList(annotationNames); 500 } 501 502 /** 503 * Setter to control whether protected members are allowed. 504 * 505 * @param protectedAllowed whether protected members are allowed 506 */ 507 public void setProtectedAllowed(boolean protectedAllowed) { 508 this.protectedAllowed = protectedAllowed; 509 } 510 511 /** 512 * Setter to control whether package visible members are allowed. 513 * 514 * @param packageAllowed whether package visible members are allowed 515 */ 516 public void setPackageAllowed(boolean packageAllowed) { 517 this.packageAllowed = packageAllowed; 518 } 519 520 /** 521 * Setter to specify pattern for public members that should be ignored. 522 * 523 * @param pattern 524 * pattern for public members to ignore. 525 */ 526 public void setPublicMemberPattern(Pattern pattern) { 527 publicMemberPattern = pattern; 528 } 529 530 /** 531 * Setter to allow immutable fields to be declared as public if defined in final class. 532 * 533 * @param allow user's value. 534 */ 535 public void setAllowPublicImmutableFields(boolean allow) { 536 allowPublicImmutableFields = allow; 537 } 538 539 /** 540 * Setter to allow final fields to be declared as public. 541 * 542 * @param allow user's value. 543 */ 544 public void setAllowPublicFinalFields(boolean allow) { 545 allowPublicFinalFields = allow; 546 } 547 548 /** 549 * Setter to specify immutable classes canonical names. 550 * 551 * @param classNames array of immutable types canonical names. 552 */ 553 public void setImmutableClassCanonicalNames(String... classNames) { 554 immutableClassCanonicalNames = Arrays.asList(classNames); 555 } 556 557 @Override 558 public int[] getDefaultTokens() { 559 return getRequiredTokens(); 560 } 561 562 @Override 563 public int[] getAcceptableTokens() { 564 return getRequiredTokens(); 565 } 566 567 @Override 568 public int[] getRequiredTokens() { 569 return new int[] { 570 TokenTypes.VARIABLE_DEF, 571 TokenTypes.IMPORT, 572 }; 573 } 574 575 @Override 576 public void beginTree(DetailAST rootAst) { 577 immutableClassShortNames.clear(); 578 final List<String> classShortNames = 579 getClassShortNames(immutableClassCanonicalNames); 580 immutableClassShortNames.addAll(classShortNames); 581 582 ignoreAnnotationShortNames.clear(); 583 final List<String> annotationShortNames = 584 getClassShortNames(ignoreAnnotationCanonicalNames); 585 ignoreAnnotationShortNames.addAll(annotationShortNames); 586 } 587 588 @Override 589 public void visitToken(DetailAST ast) { 590 switch (ast.getType()) { 591 case TokenTypes.VARIABLE_DEF: 592 if (!isAnonymousClassVariable(ast)) { 593 visitVariableDef(ast); 594 } 595 break; 596 case TokenTypes.IMPORT: 597 visitImport(ast); 598 break; 599 default: 600 final String exceptionMsg = "Unexpected token type: " + ast.getText(); 601 throw new IllegalArgumentException(exceptionMsg); 602 } 603 } 604 605 /** 606 * Checks if current variable definition is definition of an anonymous class. 607 * 608 * @param variableDef {@link TokenTypes#VARIABLE_DEF VARIABLE_DEF} 609 * @return true if current variable definition is definition of an anonymous class. 610 */ 611 private static boolean isAnonymousClassVariable(DetailAST variableDef) { 612 return variableDef.getParent().getType() != TokenTypes.OBJBLOCK; 613 } 614 615 /** 616 * Checks access modifier of given variable. 617 * If it is not proper according to Check - puts violation on it. 618 * 619 * @param variableDef variable to check. 620 */ 621 private void visitVariableDef(DetailAST variableDef) { 622 final boolean inInterfaceOrAnnotationBlock = 623 ScopeUtil.isInInterfaceOrAnnotationBlock(variableDef); 624 625 if (!inInterfaceOrAnnotationBlock && !hasIgnoreAnnotation(variableDef)) { 626 final DetailAST varNameAST = variableDef.findFirstToken(TokenTypes.TYPE) 627 .getNextSibling(); 628 final String varName = varNameAST.getText(); 629 if (!hasProperAccessModifier(variableDef, varName)) { 630 log(varNameAST, MSG_KEY, varName); 631 } 632 } 633 } 634 635 /** 636 * Checks if variable def has ignore annotation. 637 * 638 * @param variableDef {@link TokenTypes#VARIABLE_DEF VARIABLE_DEF} 639 * @return true if variable def has ignore annotation. 640 */ 641 private boolean hasIgnoreAnnotation(DetailAST variableDef) { 642 final DetailAST firstIgnoreAnnotation = 643 findMatchingAnnotation(variableDef); 644 return firstIgnoreAnnotation != null; 645 } 646 647 /** 648 * Checks imported type. If type's canonical name was not specified in 649 * <b>immutableClassCanonicalNames</b>, but it's short name collides with one from 650 * <b>immutableClassShortNames</b> - removes it from the last one. 651 * 652 * @param importAst {@link TokenTypes#IMPORT Import} 653 */ 654 private void visitImport(DetailAST importAst) { 655 if (!isStarImport(importAst)) { 656 final DetailAST type = importAst.getFirstChild(); 657 final String canonicalName = getCanonicalName(type); 658 final String shortName = getClassShortName(canonicalName); 659 660 // If imported canonical class name is not specified as allowed immutable class, 661 // but its short name collides with one of specified class - removes the short name 662 // from list to avoid names collision 663 if (!immutableClassCanonicalNames.contains(canonicalName)) { 664 immutableClassShortNames.remove(shortName); 665 } 666 if (!ignoreAnnotationCanonicalNames.contains(canonicalName)) { 667 ignoreAnnotationShortNames.remove(shortName); 668 } 669 } 670 } 671 672 /** 673 * Checks if current import is star import. E.g.: 674 * <p> 675 * {@code 676 * import java.util.*; 677 * } 678 * </p> 679 * 680 * @param importAst {@link TokenTypes#IMPORT Import} 681 * @return true if it is star import 682 */ 683 private static boolean isStarImport(DetailAST importAst) { 684 boolean result = false; 685 DetailAST toVisit = importAst; 686 while (toVisit != null) { 687 toVisit = getNextSubTreeNode(toVisit, importAst); 688 if (toVisit != null && toVisit.getType() == TokenTypes.STAR) { 689 result = true; 690 break; 691 } 692 } 693 return result; 694 } 695 696 /** 697 * Checks if current variable has proper access modifier according to Check's options. 698 * 699 * @param variableDef Variable definition node. 700 * @param variableName Variable's name. 701 * @return true if variable has proper access modifier. 702 */ 703 private boolean hasProperAccessModifier(DetailAST variableDef, String variableName) { 704 boolean result = true; 705 706 final String variableScope = getVisibilityScope(variableDef); 707 708 if (!PRIVATE_ACCESS_MODIFIER.equals(variableScope)) { 709 result = 710 isStaticFinalVariable(variableDef) 711 || packageAllowed && PACKAGE_ACCESS_MODIFIER.equals(variableScope) 712 || protectedAllowed && PROTECTED_ACCESS_MODIFIER.equals(variableScope) 713 || isIgnoredPublicMember(variableName, variableScope) 714 || isAllowedPublicField(variableDef); 715 } 716 717 return result; 718 } 719 720 /** 721 * Checks whether variable has static final modifiers. 722 * 723 * @param variableDef Variable definition node. 724 * @return true of variable has static final modifiers. 725 */ 726 private static boolean isStaticFinalVariable(DetailAST variableDef) { 727 final Set<String> modifiers = getModifiers(variableDef); 728 return modifiers.contains(STATIC_KEYWORD) 729 && modifiers.contains(FINAL_KEYWORD); 730 } 731 732 /** 733 * Checks whether variable belongs to public members that should be ignored. 734 * 735 * @param variableName Variable's name. 736 * @param variableScope Variable's scope. 737 * @return true if variable belongs to public members that should be ignored. 738 */ 739 private boolean isIgnoredPublicMember(String variableName, String variableScope) { 740 return PUBLIC_ACCESS_MODIFIER.equals(variableScope) 741 && publicMemberPattern.matcher(variableName).find(); 742 } 743 744 /** 745 * Checks whether the variable satisfies the public field check. 746 * 747 * @param variableDef Variable definition node. 748 * @return true if allowed. 749 */ 750 private boolean isAllowedPublicField(DetailAST variableDef) { 751 return allowPublicFinalFields && isFinalField(variableDef) 752 || allowPublicImmutableFields && isImmutableFieldDefinedInFinalClass(variableDef); 753 } 754 755 /** 756 * Checks whether immutable field is defined in final class. 757 * 758 * @param variableDef Variable definition node. 759 * @return true if immutable field is defined in final class. 760 */ 761 private boolean isImmutableFieldDefinedInFinalClass(DetailAST variableDef) { 762 final DetailAST classDef = variableDef.getParent().getParent(); 763 final Set<String> classModifiers = getModifiers(classDef); 764 return (classModifiers.contains(FINAL_KEYWORD) || classDef.getType() == TokenTypes.ENUM_DEF) 765 && isImmutableField(variableDef); 766 } 767 768 /** 769 * Returns the set of modifier Strings for a VARIABLE_DEF or CLASS_DEF AST. 770 * 771 * @param defAST AST for a variable or class definition. 772 * @return the set of modifier Strings for defAST. 773 */ 774 private static Set<String> getModifiers(DetailAST defAST) { 775 final DetailAST modifiersAST = defAST.findFirstToken(TokenTypes.MODIFIERS); 776 final Set<String> modifiersSet = new HashSet<>(); 777 if (modifiersAST != null) { 778 DetailAST modifier = modifiersAST.getFirstChild(); 779 while (modifier != null) { 780 modifiersSet.add(modifier.getText()); 781 modifier = modifier.getNextSibling(); 782 } 783 } 784 return modifiersSet; 785 } 786 787 /** 788 * Returns the visibility scope for the variable. 789 * 790 * @param variableDef Variable definition node. 791 * @return one of "public", "private", "protected", "package" 792 */ 793 private static String getVisibilityScope(DetailAST variableDef) { 794 final Set<String> modifiers = getModifiers(variableDef); 795 String accessModifier = PACKAGE_ACCESS_MODIFIER; 796 for (final String modifier : EXPLICIT_MODS) { 797 if (modifiers.contains(modifier)) { 798 accessModifier = modifier; 799 break; 800 } 801 } 802 return accessModifier; 803 } 804 805 /** 806 * Checks if current field is immutable: 807 * has final modifier and either a primitive type or instance of class 808 * known to be immutable (such as String, ImmutableCollection from Guava and etc). 809 * Classes known to be immutable are listed in 810 * {@link VisibilityModifierCheck#immutableClassCanonicalNames} 811 * 812 * @param variableDef Field in consideration. 813 * @return true if field is immutable. 814 */ 815 private boolean isImmutableField(DetailAST variableDef) { 816 boolean result = false; 817 if (isFinalField(variableDef)) { 818 final DetailAST type = variableDef.findFirstToken(TokenTypes.TYPE); 819 final boolean isCanonicalName = isCanonicalName(type); 820 final String typeName = getTypeName(type, isCanonicalName); 821 if (immutableClassShortNames.contains(typeName) 822 || isCanonicalName && immutableClassCanonicalNames.contains(typeName)) { 823 final DetailAST typeArgs = getGenericTypeArgs(type, isCanonicalName); 824 825 if (typeArgs == null) { 826 result = true; 827 } 828 else { 829 final List<String> argsClassNames = getTypeArgsClassNames(typeArgs); 830 result = areImmutableTypeArguments(argsClassNames); 831 } 832 } 833 else { 834 result = !isCanonicalName && isPrimitive(type); 835 } 836 } 837 return result; 838 } 839 840 /** 841 * Checks whether type definition is in canonical form. 842 * 843 * @param type type definition token. 844 * @return true if type definition is in canonical form. 845 */ 846 private static boolean isCanonicalName(DetailAST type) { 847 return type.getFirstChild().getType() == TokenTypes.DOT; 848 } 849 850 /** 851 * Returns generic type arguments token. 852 * 853 * @param type type token. 854 * @param isCanonicalName whether type name is in canonical form. 855 * @return generic type arguments token. 856 */ 857 private static DetailAST getGenericTypeArgs(DetailAST type, boolean isCanonicalName) { 858 final DetailAST typeArgs; 859 if (isCanonicalName) { 860 // if type class name is in canonical form, abstract tree has specific structure 861 typeArgs = type.getFirstChild().findFirstToken(TokenTypes.TYPE_ARGUMENTS); 862 } 863 else { 864 typeArgs = type.findFirstToken(TokenTypes.TYPE_ARGUMENTS); 865 } 866 return typeArgs; 867 } 868 869 /** 870 * Returns a list of type parameters class names. 871 * 872 * @param typeArgs type arguments token. 873 * @return a list of type parameters class names. 874 */ 875 private static List<String> getTypeArgsClassNames(DetailAST typeArgs) { 876 final List<String> typeClassNames = new ArrayList<>(); 877 DetailAST type = typeArgs.findFirstToken(TokenTypes.TYPE_ARGUMENT); 878 boolean isCanonicalName = isCanonicalName(type); 879 String typeName = getTypeName(type, isCanonicalName); 880 typeClassNames.add(typeName); 881 DetailAST sibling = type.getNextSibling(); 882 while (sibling.getType() == TokenTypes.COMMA) { 883 type = sibling.getNextSibling(); 884 isCanonicalName = isCanonicalName(type); 885 typeName = getTypeName(type, isCanonicalName); 886 typeClassNames.add(typeName); 887 sibling = type.getNextSibling(); 888 } 889 return typeClassNames; 890 } 891 892 /** 893 * Checks whether all of generic type arguments are immutable. 894 * If at least one argument is mutable, we assume that the whole list of type arguments 895 * is mutable. 896 * 897 * @param typeArgsClassNames type arguments class names. 898 * @return true if all of generic type arguments are immutable. 899 */ 900 private boolean areImmutableTypeArguments(List<String> typeArgsClassNames) { 901 return typeArgsClassNames.stream().noneMatch( 902 typeName -> { 903 return !immutableClassShortNames.contains(typeName) 904 && !immutableClassCanonicalNames.contains(typeName); 905 }); 906 } 907 908 /** 909 * Checks whether current field is final. 910 * 911 * @param variableDef field in consideration. 912 * @return true if current field is final. 913 */ 914 private static boolean isFinalField(DetailAST variableDef) { 915 final DetailAST modifiers = variableDef.findFirstToken(TokenTypes.MODIFIERS); 916 return modifiers.findFirstToken(TokenTypes.FINAL) != null; 917 } 918 919 /** 920 * Gets the name of type from given ast {@link TokenTypes#TYPE TYPE} node. 921 * If type is specified via its canonical name - canonical name will be returned, 922 * else - short type's name. 923 * 924 * @param type {@link TokenTypes#TYPE TYPE} node. 925 * @param isCanonicalName is given name canonical. 926 * @return String representation of given type's name. 927 */ 928 private static String getTypeName(DetailAST type, boolean isCanonicalName) { 929 final String typeName; 930 if (isCanonicalName) { 931 typeName = getCanonicalName(type); 932 } 933 else { 934 typeName = type.getFirstChild().getText(); 935 } 936 return typeName; 937 } 938 939 /** 940 * Checks if current type is primitive type (int, short, float, boolean, double, etc.). 941 * As primitive types have special tokens for each one, such as: 942 * LITERAL_INT, LITERAL_BOOLEAN, etc. 943 * So, if type's identifier differs from {@link TokenTypes#IDENT IDENT} token - it's a 944 * primitive type. 945 * 946 * @param type Ast {@link TokenTypes#TYPE TYPE} node. 947 * @return true if current type is primitive type. 948 */ 949 private static boolean isPrimitive(DetailAST type) { 950 return type.getFirstChild().getType() != TokenTypes.IDENT; 951 } 952 953 /** 954 * Gets canonical type's name from given {@link TokenTypes#TYPE TYPE} node. 955 * 956 * @param type DetailAST {@link TokenTypes#TYPE TYPE} node. 957 * @return canonical type's name 958 */ 959 private static String getCanonicalName(DetailAST type) { 960 final StringBuilder canonicalNameBuilder = new StringBuilder(256); 961 DetailAST toVisit = type.getFirstChild(); 962 while (toVisit != null) { 963 toVisit = getNextSubTreeNode(toVisit, type); 964 if (toVisit != null && toVisit.getType() == TokenTypes.IDENT) { 965 if (canonicalNameBuilder.length() > 0) { 966 canonicalNameBuilder.append('.'); 967 } 968 canonicalNameBuilder.append(toVisit.getText()); 969 final DetailAST nextSubTreeNode = getNextSubTreeNode(toVisit, type); 970 if (nextSubTreeNode != null 971 && nextSubTreeNode.getType() == TokenTypes.TYPE_ARGUMENTS) { 972 break; 973 } 974 } 975 } 976 return canonicalNameBuilder.toString(); 977 } 978 979 /** 980 * Gets the next node of a syntactical tree (child of a current node or 981 * sibling of a current node, or sibling of a parent of a current node). 982 * 983 * @param currentNodeAst Current node in considering 984 * @param subTreeRootAst SubTree root 985 * @return Current node after bypassing, if current node reached the root of a subtree 986 * method returns null 987 */ 988 private static DetailAST 989 getNextSubTreeNode(DetailAST currentNodeAst, DetailAST subTreeRootAst) { 990 DetailAST currentNode = currentNodeAst; 991 DetailAST toVisitAst = currentNode.getFirstChild(); 992 while (toVisitAst == null) { 993 toVisitAst = currentNode.getNextSibling(); 994 if (currentNode.getParent().getColumnNo() == subTreeRootAst.getColumnNo()) { 995 break; 996 } 997 currentNode = currentNode.getParent(); 998 } 999 return toVisitAst; 1000 } 1001 1002 /** 1003 * Gets the list with short names classes. 1004 * These names are taken from array of classes canonical names. 1005 * 1006 * @param canonicalClassNames canonical class names. 1007 * @return the list of short names of classes. 1008 */ 1009 private static List<String> getClassShortNames(List<String> canonicalClassNames) { 1010 final List<String> shortNames = new ArrayList<>(); 1011 for (String canonicalClassName : canonicalClassNames) { 1012 final String shortClassName = canonicalClassName 1013 .substring(canonicalClassName.lastIndexOf('.') + 1); 1014 shortNames.add(shortClassName); 1015 } 1016 return shortNames; 1017 } 1018 1019 /** 1020 * Gets the short class name from given canonical name. 1021 * 1022 * @param canonicalClassName canonical class name. 1023 * @return short name of class. 1024 */ 1025 private static String getClassShortName(String canonicalClassName) { 1026 return canonicalClassName 1027 .substring(canonicalClassName.lastIndexOf('.') + 1); 1028 } 1029 1030 /** 1031 * Checks whether the AST is annotated with 1032 * an annotation containing the passed in regular 1033 * expression and return the AST representing that 1034 * annotation. 1035 * 1036 * <p> 1037 * This method will not look for imports or package 1038 * statements to detect the passed in annotation. 1039 * </p> 1040 * 1041 * <p> 1042 * To check if an AST contains a passed in annotation 1043 * taking into account fully-qualified names 1044 * (ex: java.lang.Override, Override) 1045 * this method will need to be called twice. Once for each 1046 * name given. 1047 * </p> 1048 * 1049 * @param variableDef {@link TokenTypes#VARIABLE_DEF variable def node}. 1050 * @return the AST representing the first such annotation or null if 1051 * no such annotation was found 1052 */ 1053 private DetailAST findMatchingAnnotation(DetailAST variableDef) { 1054 DetailAST matchingAnnotation = null; 1055 1056 final DetailAST holder = AnnotationUtil.getAnnotationHolder(variableDef); 1057 1058 for (DetailAST child = holder.getFirstChild(); 1059 child != null; child = child.getNextSibling()) { 1060 if (child.getType() == TokenTypes.ANNOTATION) { 1061 final DetailAST ast = child.getFirstChild(); 1062 final String name = 1063 FullIdent.createFullIdent(ast.getNextSibling()).getText(); 1064 if (ignoreAnnotationCanonicalNames.contains(name) 1065 || ignoreAnnotationShortNames.contains(name)) { 1066 matchingAnnotation = child; 1067 break; 1068 } 1069 } 1070 } 1071 1072 return matchingAnnotation; 1073 } 1074 1075}