001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2022 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 * <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 want to 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, 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 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 * &lt;module name=&quot;VisibilityModifier&quot;/&gt;
155 * </pre>
156 * <p>
157 * To configure the check so that it allows package visible members:
158 * </p>
159 * <pre>
160 * &lt;module name=&quot;VisibilityModifier&quot;&gt;
161 *   &lt;property name=&quot;packageAllowed&quot; value=&quot;true&quot;/&gt;
162 * &lt;/module&gt;
163 * </pre>
164 * <p>
165 * To configure the check so that it allows no public members:
166 * </p>
167 * <pre>
168 * &lt;module name=&quot;VisibilityModifier&quot;&gt;
169 *   &lt;property name=&quot;publicMemberPattern&quot; value=&quot;^$&quot;/&gt;
170 * &lt;/module&gt;
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 * &lt;module name=&quot;VisibilityModifier&quot;&gt;
177 *   &lt;property name=&quot;allowPublicImmutableFields&quot; value=&quot;true&quot;/&gt;
178 * &lt;/module&gt;
179 * </pre>
180 * <p>
181 * Example of allowed public immutable fields:
182 * </p>
183 * <pre>
184 * public class ImmutableClass
185 * {
186 *   public final ImmutableSet&lt;String&gt; includes; // No warning
187 *   public final ImmutableSet&lt;String&gt; excludes; // No warning
188 *   public final java.lang.String notes; // No warning
189 *   public final BigDecimal value; // No warning
190 *
191 *   public ImmutableClass(Collection&lt;String&gt; includes, Collection&lt;String&gt; 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 * &lt;module name=&quot;VisibilityModifier&quot;&gt;
206 *   &lt;property name=&quot;allowPublicImmutableFields&quot; value=&quot;true&quot;/&gt;
207 *   &lt;property name=&quot;immutableClassCanonicalNames&quot; value=&quot;
208 *   com.google.common.collect.ImmutableSet&quot;/&gt;
209 * &lt;/module&gt;
210 * </pre>
211 * <p>
212 * Example of allowed public immutable fields:
213 * </p>
214 * <pre>
215 * public class ImmutableClass
216 * {
217 *   public final ImmutableSet&lt;String&gt; includes; // No warning
218 *   public final ImmutableSet&lt;String&gt; 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&lt;String&gt; includes, Collection&lt;String&gt; 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 * &lt;module name=&quot;VisibilityModifier&quot;&gt;
241 *   &lt;property name=&quot;allowPublicImmutableFields&quot; value=&quot;true&quot;/&gt;
242 *   &lt;property name=&quot;immutableClassCanonicalNames&quot;
243 *     value=&quot;com.google.common.collect.ImmutableSet, com.google.common.collect.ImmutableMap,
244 *       java.lang.String&quot;/&gt;
245 * &lt;/module&gt;
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&lt;String&gt; names;
254 *   public final ImmutableSet&lt;Object&gt; objects; // violation (Object class is mutable)
255 *   public final ImmutableMap&lt;String, Object&gt; 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 * &lt;module name=&quot;VisibilityModifier&quot;&gt;
270 *   &lt;property name=&quot;ignoreAnnotationCanonicalNames&quot; value=
271 *   &quot;com.annotation.CustomAnnotation&quot;/&gt;
272 * &lt;/module&gt;
273 * </pre>
274 * <p>
275 * Example of allowed field:
276 * </p>
277 * <pre>
278 * class SomeClass
279 * {
280 *   &#64;com.annotation.CustomAnnotation
281 *   String annotatedString; // no warning
282 *   &#64;CustomAnnotation
283 *   String shortCustomAnnotated; // no warning
284 * }
285 * </pre>
286 * <p>
287 * To configure the Check passing fields annotated with &#64;org.junit.Rule,
288 * &#64;org.junit.ClassRule and &#64;com.google.common.annotations.VisibleForTesting annotations:
289 * </p>
290 * <pre>
291 * &lt;module name=&quot;VisibilityModifier&quot;/&gt;
292 * </pre>
293 * <p>
294 * Example of allowed fields:
295 * </p>
296 * <pre>
297 * class SomeClass
298 * {
299 *   &#64;org.junit.Rule
300 *   public TemporaryFolder publicJUnitRule = new TemporaryFolder(); // no warning
301 *   &#64;org.junit.ClassRule
302 *   public static TemporaryFolder publicJUnitClassRule = new TemporaryFolder(); // no warning
303 *   &#64;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 * &lt;module name=&quot;VisibilityModifier&quot;&gt;
312 *   &lt;property name=&quot;ignoreAnnotationCanonicalNames&quot;
313 *   value=&quot;CustomAnnotation&quot;/&gt;
314 * &lt;/module&gt;
315 * </pre>
316 * <p>
317 * Example of allowed fields:
318 * </p>
319 * <pre>
320 * class SomeClass
321 * {
322 *   &#64;CustomAnnotation
323 *   String customAnnotated; // no warning
324 *   &#64;com.annotation.CustomAnnotation
325 *   String customAnnotated1; // no warning
326 *   &#64;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 * &lt;module name=&quot;VisibilityModifier&quot;&gt;
340 *   &lt;property name=&quot;allowPublicImmutableFields&quot; value=&quot;true&quot;/&gt;
341 * &lt;/module&gt;
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&lt;String&gt; 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&lt;String&gt; 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 * &lt;module name=&quot;VisibilityModifier&quot;&gt;
369 *   &lt;property name=&quot;allowPublicFinalFields&quot; value=&quot;true&quot;/&gt;
370 * &lt;/module&gt;
371 * </pre>
372 * <p>
373 * Code example:
374 * </p>
375 * <pre>
376 * public class InputPublicImmutable {
377 *   public final int someIntValue;
378 *   public final ImmutableSet&lt;String&gt; includes;
379 *   public final java.lang.String notes;
380 *   public final BigDecimal value;
381 *   public final List list;
382 *
383 *   public InputPublicImmutable(Collection&lt;String&gt; 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 Set<String> DEFAULT_IMMUTABLE_TYPES = Set.of(
419        "java.lang.String",
420        "java.lang.Integer",
421        "java.lang.Byte",
422        "java.lang.Character",
423        "java.lang.Short",
424        "java.lang.Boolean",
425        "java.lang.Long",
426        "java.lang.Double",
427        "java.lang.Float",
428        "java.lang.StackTraceElement",
429        "java.math.BigInteger",
430        "java.math.BigDecimal",
431        "java.io.File",
432        "java.util.Locale",
433        "java.util.UUID",
434        "java.net.URL",
435        "java.net.URI",
436        "java.net.Inet4Address",
437        "java.net.Inet6Address",
438        "java.net.InetSocketAddress"
439    );
440
441    /** Default ignore annotations canonical names. */
442    private static final Set<String> DEFAULT_IGNORE_ANNOTATIONS = Set.of(
443        "org.junit.Rule",
444        "org.junit.ClassRule",
445        "com.google.common.annotations.VisibleForTesting"
446    );
447
448    /** Name for 'public' access modifier. */
449    private static final String PUBLIC_ACCESS_MODIFIER = "public";
450
451    /** Name for 'private' access modifier. */
452    private static final String PRIVATE_ACCESS_MODIFIER = "private";
453
454    /** Name for 'protected' access modifier. */
455    private static final String PROTECTED_ACCESS_MODIFIER = "protected";
456
457    /** Name for implicit 'package' access modifier. */
458    private static final String PACKAGE_ACCESS_MODIFIER = "package";
459
460    /** Name for 'static' keyword. */
461    private static final String STATIC_KEYWORD = "static";
462
463    /** Name for 'final' keyword. */
464    private static final String FINAL_KEYWORD = "final";
465
466    /** Contains explicit access modifiers. */
467    private static final String[] EXPLICIT_MODS = {
468        PUBLIC_ACCESS_MODIFIER,
469        PRIVATE_ACCESS_MODIFIER,
470        PROTECTED_ACCESS_MODIFIER,
471    };
472
473    /**
474     * Specify pattern for public members that should be ignored.
475     */
476    private Pattern publicMemberPattern = Pattern.compile("^serialVersionUID$");
477
478    /** Set of ignore annotations short names. */
479    private Set<String> ignoreAnnotationShortNames =
480            getClassShortNames(DEFAULT_IGNORE_ANNOTATIONS);
481
482    /** Set of immutable classes short names. */
483    private Set<String> immutableClassShortNames =
484        getClassShortNames(DEFAULT_IMMUTABLE_TYPES);
485
486    /**
487     * Specify annotations canonical names which ignore variables in
488     * consideration.
489     */
490    private Set<String> ignoreAnnotationCanonicalNames = DEFAULT_IGNORE_ANNOTATIONS;
491
492    /** Control whether protected members are allowed. */
493    private boolean protectedAllowed;
494
495    /** Control whether package visible members are allowed. */
496    private boolean packageAllowed;
497
498    /** Allow immutable fields to be declared as public if defined in final class. */
499    private boolean allowPublicImmutableFields;
500
501    /** Allow final fields to be declared as public. */
502    private boolean allowPublicFinalFields;
503
504    /** Specify immutable classes canonical names. */
505    private Set<String> immutableClassCanonicalNames = DEFAULT_IMMUTABLE_TYPES;
506
507    /**
508     * Setter to specify annotations canonical names which ignore variables
509     * in consideration.
510     *
511     * @param annotationNames array of ignore annotations canonical names.
512     */
513    public void setIgnoreAnnotationCanonicalNames(String... annotationNames) {
514        ignoreAnnotationCanonicalNames = Set.of(annotationNames);
515    }
516
517    /**
518     * Setter to control whether protected members are allowed.
519     *
520     * @param protectedAllowed whether protected members are allowed
521     */
522    public void setProtectedAllowed(boolean protectedAllowed) {
523        this.protectedAllowed = protectedAllowed;
524    }
525
526    /**
527     * Setter to control whether package visible members are allowed.
528     *
529     * @param packageAllowed whether package visible members are allowed
530     */
531    public void setPackageAllowed(boolean packageAllowed) {
532        this.packageAllowed = packageAllowed;
533    }
534
535    /**
536     * Setter to specify pattern for public members that should be ignored.
537     *
538     * @param pattern
539     *        pattern for public members to ignore.
540     */
541    public void setPublicMemberPattern(Pattern pattern) {
542        publicMemberPattern = pattern;
543    }
544
545    /**
546     * Setter to allow immutable fields to be declared as public if defined in final class.
547     *
548     * @param allow user's value.
549     */
550    public void setAllowPublicImmutableFields(boolean allow) {
551        allowPublicImmutableFields = allow;
552    }
553
554    /**
555     * Setter to allow final fields to be declared as public.
556     *
557     * @param allow user's value.
558     */
559    public void setAllowPublicFinalFields(boolean allow) {
560        allowPublicFinalFields = allow;
561    }
562
563    /**
564     * Setter to specify immutable classes canonical names.
565     *
566     * @param classNames array of immutable types canonical names.
567     */
568    public void setImmutableClassCanonicalNames(String... classNames) {
569        immutableClassCanonicalNames = Set.of(classNames);
570    }
571
572    @Override
573    public int[] getDefaultTokens() {
574        return getRequiredTokens();
575    }
576
577    @Override
578    public int[] getAcceptableTokens() {
579        return getRequiredTokens();
580    }
581
582    @Override
583    public int[] getRequiredTokens() {
584        return new int[] {
585            TokenTypes.VARIABLE_DEF,
586            TokenTypes.IMPORT,
587        };
588    }
589
590    @Override
591    public void beginTree(DetailAST rootAst) {
592        immutableClassShortNames = getClassShortNames(immutableClassCanonicalNames);
593        ignoreAnnotationShortNames = getClassShortNames(ignoreAnnotationCanonicalNames);
594    }
595
596    @Override
597    public void visitToken(DetailAST ast) {
598        switch (ast.getType()) {
599            case TokenTypes.VARIABLE_DEF:
600                if (!isAnonymousClassVariable(ast)) {
601                    visitVariableDef(ast);
602                }
603                break;
604            case TokenTypes.IMPORT:
605                visitImport(ast);
606                break;
607            default:
608                final String exceptionMsg = "Unexpected token type: " + ast.getText();
609                throw new IllegalArgumentException(exceptionMsg);
610        }
611    }
612
613    /**
614     * Checks if current variable definition is definition of an anonymous class.
615     *
616     * @param variableDef {@link TokenTypes#VARIABLE_DEF VARIABLE_DEF}
617     * @return true if current variable definition is definition of an anonymous class.
618     */
619    private static boolean isAnonymousClassVariable(DetailAST variableDef) {
620        return variableDef.getParent().getType() != TokenTypes.OBJBLOCK;
621    }
622
623    /**
624     * Checks access modifier of given variable.
625     * If it is not proper according to Check - puts violation on it.
626     *
627     * @param variableDef variable to check.
628     */
629    private void visitVariableDef(DetailAST variableDef) {
630        final boolean inInterfaceOrAnnotationBlock =
631                ScopeUtil.isInInterfaceOrAnnotationBlock(variableDef);
632
633        if (!inInterfaceOrAnnotationBlock && !hasIgnoreAnnotation(variableDef)) {
634            final DetailAST varNameAST = variableDef.findFirstToken(TokenTypes.TYPE)
635                .getNextSibling();
636            final String varName = varNameAST.getText();
637            if (!hasProperAccessModifier(variableDef, varName)) {
638                log(varNameAST, MSG_KEY, varName);
639            }
640        }
641    }
642
643    /**
644     * Checks if variable def has ignore annotation.
645     *
646     * @param variableDef {@link TokenTypes#VARIABLE_DEF VARIABLE_DEF}
647     * @return true if variable def has ignore annotation.
648     */
649    private boolean hasIgnoreAnnotation(DetailAST variableDef) {
650        final DetailAST firstIgnoreAnnotation =
651                 findMatchingAnnotation(variableDef);
652        return firstIgnoreAnnotation != null;
653    }
654
655    /**
656     * Checks imported type. If type's canonical name was not specified in
657     * <b>immutableClassCanonicalNames</b>, but its short name collides with one from
658     * <b>immutableClassShortNames</b> - removes it from the last one.
659     *
660     * @param importAst {@link TokenTypes#IMPORT Import}
661     */
662    private void visitImport(DetailAST importAst) {
663        if (!isStarImport(importAst)) {
664            final DetailAST type = importAst.getFirstChild();
665            final String canonicalName = getCanonicalName(type);
666            final String shortName = getClassShortName(canonicalName);
667
668            // If imported canonical class name is not specified as allowed immutable class,
669            // but its short name collides with one of specified class - removes the short name
670            // from list to avoid names collision
671            if (!immutableClassCanonicalNames.contains(canonicalName)) {
672                immutableClassShortNames.remove(shortName);
673            }
674            if (!ignoreAnnotationCanonicalNames.contains(canonicalName)) {
675                ignoreAnnotationShortNames.remove(shortName);
676            }
677        }
678    }
679
680    /**
681     * Checks if current import is star import. E.g.:
682     * <p>
683     * {@code
684     * import java.util.*;
685     * }
686     * </p>
687     *
688     * @param importAst {@link TokenTypes#IMPORT Import}
689     * @return true if it is star import
690     */
691    private static boolean isStarImport(DetailAST importAst) {
692        boolean result = false;
693        DetailAST toVisit = importAst;
694        while (toVisit != null) {
695            toVisit = getNextSubTreeNode(toVisit, importAst);
696            if (toVisit != null && toVisit.getType() == TokenTypes.STAR) {
697                result = true;
698                break;
699            }
700        }
701        return result;
702    }
703
704    /**
705     * Checks if current variable has proper access modifier according to Check's options.
706     *
707     * @param variableDef Variable definition node.
708     * @param variableName Variable's name.
709     * @return true if variable has proper access modifier.
710     */
711    private boolean hasProperAccessModifier(DetailAST variableDef, String variableName) {
712        boolean result = true;
713
714        final String variableScope = getVisibilityScope(variableDef);
715
716        if (!PRIVATE_ACCESS_MODIFIER.equals(variableScope)) {
717            result =
718                isStaticFinalVariable(variableDef)
719                || packageAllowed && PACKAGE_ACCESS_MODIFIER.equals(variableScope)
720                || protectedAllowed && PROTECTED_ACCESS_MODIFIER.equals(variableScope)
721                || isIgnoredPublicMember(variableName, variableScope)
722                || isAllowedPublicField(variableDef);
723        }
724
725        return result;
726    }
727
728    /**
729     * Checks whether variable has static final modifiers.
730     *
731     * @param variableDef Variable definition node.
732     * @return true of variable has static final modifiers.
733     */
734    private static boolean isStaticFinalVariable(DetailAST variableDef) {
735        final Set<String> modifiers = getModifiers(variableDef);
736        return modifiers.contains(STATIC_KEYWORD)
737                && modifiers.contains(FINAL_KEYWORD);
738    }
739
740    /**
741     * Checks whether variable belongs to public members that should be ignored.
742     *
743     * @param variableName Variable's name.
744     * @param variableScope Variable's scope.
745     * @return true if variable belongs to public members that should be ignored.
746     */
747    private boolean isIgnoredPublicMember(String variableName, String variableScope) {
748        return PUBLIC_ACCESS_MODIFIER.equals(variableScope)
749            && publicMemberPattern.matcher(variableName).find();
750    }
751
752    /**
753     * Checks whether the variable satisfies the public field check.
754     *
755     * @param variableDef Variable definition node.
756     * @return true if allowed.
757     */
758    private boolean isAllowedPublicField(DetailAST variableDef) {
759        return allowPublicFinalFields && isFinalField(variableDef)
760            || allowPublicImmutableFields && isImmutableFieldDefinedInFinalClass(variableDef);
761    }
762
763    /**
764     * Checks whether immutable field is defined in final class.
765     *
766     * @param variableDef Variable definition node.
767     * @return true if immutable field is defined in final class.
768     */
769    private boolean isImmutableFieldDefinedInFinalClass(DetailAST variableDef) {
770        final DetailAST classDef = variableDef.getParent().getParent();
771        final Set<String> classModifiers = getModifiers(classDef);
772        return (classModifiers.contains(FINAL_KEYWORD) || classDef.getType() == TokenTypes.ENUM_DEF)
773                && isImmutableField(variableDef);
774    }
775
776    /**
777     * Returns the set of modifier Strings for a VARIABLE_DEF or CLASS_DEF AST.
778     *
779     * @param defAST AST for a variable or class definition.
780     * @return the set of modifier Strings for defAST.
781     */
782    private static Set<String> getModifiers(DetailAST defAST) {
783        final DetailAST modifiersAST = defAST.findFirstToken(TokenTypes.MODIFIERS);
784        final Set<String> modifiersSet = new HashSet<>();
785        if (modifiersAST != null) {
786            DetailAST modifier = modifiersAST.getFirstChild();
787            while (modifier != null) {
788                modifiersSet.add(modifier.getText());
789                modifier = modifier.getNextSibling();
790            }
791        }
792        return modifiersSet;
793    }
794
795    /**
796     * Returns the visibility scope for the variable.
797     *
798     * @param variableDef Variable definition node.
799     * @return one of "public", "private", "protected", "package"
800     */
801    private static String getVisibilityScope(DetailAST variableDef) {
802        final Set<String> modifiers = getModifiers(variableDef);
803        String accessModifier = PACKAGE_ACCESS_MODIFIER;
804        for (final String modifier : EXPLICIT_MODS) {
805            if (modifiers.contains(modifier)) {
806                accessModifier = modifier;
807                break;
808            }
809        }
810        return accessModifier;
811    }
812
813    /**
814     * Checks if current field is immutable:
815     * has final modifier and either a primitive type or instance of class
816     * known to be immutable (such as String, ImmutableCollection from Guava, etc.).
817     * Classes known to be immutable are listed in
818     * {@link VisibilityModifierCheck#immutableClassCanonicalNames}
819     *
820     * @param variableDef Field in consideration.
821     * @return true if field is immutable.
822     */
823    private boolean isImmutableField(DetailAST variableDef) {
824        boolean result = false;
825        if (isFinalField(variableDef)) {
826            final DetailAST type = variableDef.findFirstToken(TokenTypes.TYPE);
827            final boolean isCanonicalName = isCanonicalName(type);
828            final String typeName = getTypeName(type, isCanonicalName);
829            if (immutableClassShortNames.contains(typeName)
830                    || isCanonicalName && immutableClassCanonicalNames.contains(typeName)) {
831                final DetailAST typeArgs = getGenericTypeArgs(type, isCanonicalName);
832
833                if (typeArgs == null) {
834                    result = true;
835                }
836                else {
837                    final List<String> argsClassNames = getTypeArgsClassNames(typeArgs);
838                    result = areImmutableTypeArguments(argsClassNames);
839                }
840            }
841            else {
842                result = !isCanonicalName && isPrimitive(type);
843            }
844        }
845        return result;
846    }
847
848    /**
849     * Checks whether type definition is in canonical form.
850     *
851     * @param type type definition token.
852     * @return true if type definition is in canonical form.
853     */
854    private static boolean isCanonicalName(DetailAST type) {
855        return type.getFirstChild().getType() == TokenTypes.DOT;
856    }
857
858    /**
859     * Returns generic type arguments token.
860     *
861     * @param type type token.
862     * @param isCanonicalName whether type name is in canonical form.
863     * @return generic type arguments token.
864     */
865    private static DetailAST getGenericTypeArgs(DetailAST type, boolean isCanonicalName) {
866        final DetailAST typeArgs;
867        if (isCanonicalName) {
868            // if type class name is in canonical form, abstract tree has specific structure
869            typeArgs = type.getFirstChild().findFirstToken(TokenTypes.TYPE_ARGUMENTS);
870        }
871        else {
872            typeArgs = type.findFirstToken(TokenTypes.TYPE_ARGUMENTS);
873        }
874        return typeArgs;
875    }
876
877    /**
878     * Returns a list of type parameters class names.
879     *
880     * @param typeArgs type arguments token.
881     * @return a list of type parameters class names.
882     */
883    private static List<String> getTypeArgsClassNames(DetailAST typeArgs) {
884        final List<String> typeClassNames = new ArrayList<>();
885        DetailAST type = typeArgs.findFirstToken(TokenTypes.TYPE_ARGUMENT);
886        DetailAST sibling;
887        do {
888            final boolean isCanonicalName = isCanonicalName(type);
889            final String typeName = getTypeName(type, isCanonicalName);
890            typeClassNames.add(typeName);
891            sibling = type.getNextSibling();
892            type = sibling.getNextSibling();
893        } while (sibling.getType() == TokenTypes.COMMA);
894        return typeClassNames;
895    }
896
897    /**
898     * Checks whether all generic type arguments are immutable.
899     * If at least one argument is mutable, we assume that the whole list of type arguments
900     * is mutable.
901     *
902     * @param typeArgsClassNames type arguments class names.
903     * @return true if all generic type arguments are immutable.
904     */
905    private boolean areImmutableTypeArguments(Collection<String> typeArgsClassNames) {
906        return typeArgsClassNames.stream().noneMatch(
907            typeName -> {
908                return !immutableClassShortNames.contains(typeName)
909                    && !immutableClassCanonicalNames.contains(typeName);
910            });
911    }
912
913    /**
914     * Checks whether current field is final.
915     *
916     * @param variableDef field in consideration.
917     * @return true if current field is final.
918     */
919    private static boolean isFinalField(DetailAST variableDef) {
920        final DetailAST modifiers = variableDef.findFirstToken(TokenTypes.MODIFIERS);
921        return modifiers.findFirstToken(TokenTypes.FINAL) != null;
922    }
923
924    /**
925     * Gets the name of type from given ast {@link TokenTypes#TYPE TYPE} node.
926     * If type is specified via its canonical name - canonical name will be returned,
927     * else - short type's name.
928     *
929     * @param type {@link TokenTypes#TYPE TYPE} node.
930     * @param isCanonicalName is given name canonical.
931     * @return String representation of given type's name.
932     */
933    private static String getTypeName(DetailAST type, boolean isCanonicalName) {
934        final String typeName;
935        if (isCanonicalName) {
936            typeName = getCanonicalName(type);
937        }
938        else {
939            typeName = type.getFirstChild().getText();
940        }
941        return typeName;
942    }
943
944    /**
945     * Checks if current type is primitive type (int, short, float, boolean, double, etc.).
946     * As primitive types have special tokens for each one, such as:
947     * LITERAL_INT, LITERAL_BOOLEAN, etc.
948     * So, if type's identifier differs from {@link TokenTypes#IDENT IDENT} token - it's a
949     * primitive type.
950     *
951     * @param type Ast {@link TokenTypes#TYPE TYPE} node.
952     * @return true if current type is primitive type.
953     */
954    private static boolean isPrimitive(DetailAST type) {
955        return type.getFirstChild().getType() != TokenTypes.IDENT;
956    }
957
958    /**
959     * Gets canonical type's name from given {@link TokenTypes#TYPE TYPE} node.
960     *
961     * @param type DetailAST {@link TokenTypes#TYPE TYPE} node.
962     * @return canonical type's name
963     */
964    private static String getCanonicalName(DetailAST type) {
965        final StringBuilder canonicalNameBuilder = new StringBuilder(256);
966        DetailAST toVisit = type.getFirstChild();
967        while (toVisit != null) {
968            toVisit = getNextSubTreeNode(toVisit, type);
969            if (toVisit != null && toVisit.getType() == TokenTypes.IDENT) {
970                if (canonicalNameBuilder.length() > 0) {
971                    canonicalNameBuilder.append('.');
972                }
973                canonicalNameBuilder.append(toVisit.getText());
974                final DetailAST nextSubTreeNode = getNextSubTreeNode(toVisit, type);
975                if (nextSubTreeNode != null
976                        && nextSubTreeNode.getType() == TokenTypes.TYPE_ARGUMENTS) {
977                    break;
978                }
979            }
980        }
981        return canonicalNameBuilder.toString();
982    }
983
984    /**
985     * Gets the next node of a syntactical tree (child of a current node or
986     * sibling of a current node, or sibling of a parent of a current node).
987     *
988     * @param currentNodeAst Current node in considering
989     * @param subTreeRootAst SubTree root
990     * @return Current node after bypassing, if current node reached the root of a subtree
991     *        method returns null
992     */
993    private static DetailAST
994        getNextSubTreeNode(DetailAST currentNodeAst, DetailAST subTreeRootAst) {
995        DetailAST currentNode = currentNodeAst;
996        DetailAST toVisitAst = currentNode.getFirstChild();
997        while (toVisitAst == null) {
998            toVisitAst = currentNode.getNextSibling();
999            if (currentNode.getParent().getColumnNo() == subTreeRootAst.getColumnNo()) {
1000                break;
1001            }
1002            currentNode = currentNode.getParent();
1003        }
1004        return toVisitAst;
1005    }
1006
1007    /**
1008     * Converts canonical class names to short names.
1009     *
1010     * @param canonicalClassNames the set of canonical class names.
1011     * @return the set of short names of classes.
1012     */
1013    private static Set<String> getClassShortNames(Set<String> canonicalClassNames) {
1014        return canonicalClassNames.stream()
1015            .map(CommonUtil::baseClassName)
1016            .collect(Collectors.toCollection(HashSet::new));
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}