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