001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2023 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
481    /** Set of immutable classes short names. */
482    private Set<String> immutableClassShortNames;
483
484    /**
485     * Specify annotations canonical names which ignore variables in
486     * consideration.
487     */
488    private Set<String> ignoreAnnotationCanonicalNames = DEFAULT_IGNORE_ANNOTATIONS;
489
490    /** Control whether protected members are allowed. */
491    private boolean protectedAllowed;
492
493    /** Control whether package visible members are allowed. */
494    private boolean packageAllowed;
495
496    /** Allow immutable fields to be declared as public if defined in final class. */
497    private boolean allowPublicImmutableFields;
498
499    /** Allow final fields to be declared as public. */
500    private boolean allowPublicFinalFields;
501
502    /** Specify immutable classes canonical names. */
503    private Set<String> immutableClassCanonicalNames = DEFAULT_IMMUTABLE_TYPES;
504
505    /**
506     * Setter to specify annotations canonical names which ignore variables
507     * in consideration.
508     *
509     * @param annotationNames array of ignore annotations canonical names.
510     */
511    public void setIgnoreAnnotationCanonicalNames(String... annotationNames) {
512        ignoreAnnotationCanonicalNames = Set.of(annotationNames);
513    }
514
515    /**
516     * Setter to control whether protected members are allowed.
517     *
518     * @param protectedAllowed whether protected members are allowed
519     */
520    public void setProtectedAllowed(boolean protectedAllowed) {
521        this.protectedAllowed = protectedAllowed;
522    }
523
524    /**
525     * Setter to control whether package visible members are allowed.
526     *
527     * @param packageAllowed whether package visible members are allowed
528     */
529    public void setPackageAllowed(boolean packageAllowed) {
530        this.packageAllowed = packageAllowed;
531    }
532
533    /**
534     * Setter to specify pattern for public members that should be ignored.
535     *
536     * @param pattern
537     *        pattern for public members to ignore.
538     */
539    public void setPublicMemberPattern(Pattern pattern) {
540        publicMemberPattern = pattern;
541    }
542
543    /**
544     * Setter to allow immutable fields to be declared as public if defined in final class.
545     *
546     * @param allow user's value.
547     */
548    public void setAllowPublicImmutableFields(boolean allow) {
549        allowPublicImmutableFields = allow;
550    }
551
552    /**
553     * Setter to allow final fields to be declared as public.
554     *
555     * @param allow user's value.
556     */
557    public void setAllowPublicFinalFields(boolean allow) {
558        allowPublicFinalFields = allow;
559    }
560
561    /**
562     * Setter to specify immutable classes canonical names.
563     *
564     * @param classNames array of immutable types canonical names.
565     */
566    public void setImmutableClassCanonicalNames(String... classNames) {
567        immutableClassCanonicalNames = Set.of(classNames);
568    }
569
570    @Override
571    public int[] getDefaultTokens() {
572        return getRequiredTokens();
573    }
574
575    @Override
576    public int[] getAcceptableTokens() {
577        return getRequiredTokens();
578    }
579
580    @Override
581    public int[] getRequiredTokens() {
582        return new int[] {
583            TokenTypes.VARIABLE_DEF,
584            TokenTypes.IMPORT,
585        };
586    }
587
588    @Override
589    public void beginTree(DetailAST rootAst) {
590        immutableClassShortNames = getClassShortNames(immutableClassCanonicalNames);
591        ignoreAnnotationShortNames = getClassShortNames(ignoreAnnotationCanonicalNames);
592    }
593
594    @Override
595    public void visitToken(DetailAST ast) {
596        switch (ast.getType()) {
597            case TokenTypes.VARIABLE_DEF:
598                if (!isAnonymousClassVariable(ast)) {
599                    visitVariableDef(ast);
600                }
601                break;
602            case TokenTypes.IMPORT:
603                visitImport(ast);
604                break;
605            default:
606                final String exceptionMsg = "Unexpected token type: " + ast.getText();
607                throw new IllegalArgumentException(exceptionMsg);
608        }
609    }
610
611    /**
612     * Checks if current variable definition is definition of an anonymous class.
613     *
614     * @param variableDef {@link TokenTypes#VARIABLE_DEF VARIABLE_DEF}
615     * @return true if current variable definition is definition of an anonymous class.
616     */
617    private static boolean isAnonymousClassVariable(DetailAST variableDef) {
618        return variableDef.getParent().getType() != TokenTypes.OBJBLOCK;
619    }
620
621    /**
622     * Checks access modifier of given variable.
623     * If it is not proper according to Check - puts violation on it.
624     *
625     * @param variableDef variable to check.
626     */
627    private void visitVariableDef(DetailAST variableDef) {
628        final boolean inInterfaceOrAnnotationBlock =
629                ScopeUtil.isInInterfaceOrAnnotationBlock(variableDef);
630
631        if (!inInterfaceOrAnnotationBlock && !hasIgnoreAnnotation(variableDef)) {
632            final DetailAST varNameAST = variableDef.findFirstToken(TokenTypes.TYPE)
633                .getNextSibling();
634            final String varName = varNameAST.getText();
635            if (!hasProperAccessModifier(variableDef, varName)) {
636                log(varNameAST, MSG_KEY, varName);
637            }
638        }
639    }
640
641    /**
642     * Checks if variable def has ignore annotation.
643     *
644     * @param variableDef {@link TokenTypes#VARIABLE_DEF VARIABLE_DEF}
645     * @return true if variable def has ignore annotation.
646     */
647    private boolean hasIgnoreAnnotation(DetailAST variableDef) {
648        final DetailAST firstIgnoreAnnotation =
649                 findMatchingAnnotation(variableDef);
650        return firstIgnoreAnnotation != null;
651    }
652
653    /**
654     * Checks imported type. If type's canonical name was not specified in
655     * <b>immutableClassCanonicalNames</b>, but its short name collides with one from
656     * <b>immutableClassShortNames</b> - removes it from the last one.
657     *
658     * @param importAst {@link TokenTypes#IMPORT Import}
659     */
660    private void visitImport(DetailAST importAst) {
661        if (!isStarImport(importAst)) {
662            final String canonicalName = getCanonicalName(importAst);
663            final String shortName = getClassShortName(canonicalName);
664
665            // If imported canonical class name is not specified as allowed immutable class,
666            // but its short name collides with one of specified class - removes the short name
667            // from list to avoid names collision
668            if (!immutableClassCanonicalNames.contains(canonicalName)) {
669                immutableClassShortNames.remove(shortName);
670            }
671            if (!ignoreAnnotationCanonicalNames.contains(canonicalName)) {
672                ignoreAnnotationShortNames.remove(shortName);
673            }
674        }
675    }
676
677    /**
678     * Checks if current import is star import. E.g.:
679     * <p>
680     * {@code
681     * import java.util.*;
682     * }
683     * </p>
684     *
685     * @param importAst {@link TokenTypes#IMPORT Import}
686     * @return true if it is star import
687     */
688    private static boolean isStarImport(DetailAST importAst) {
689        boolean result = false;
690        DetailAST toVisit = importAst;
691        while (toVisit != null) {
692            toVisit = getNextSubTreeNode(toVisit, importAst);
693            if (toVisit != null && toVisit.getType() == TokenTypes.STAR) {
694                result = true;
695                break;
696            }
697        }
698        return result;
699    }
700
701    /**
702     * Checks if current variable has proper access modifier according to Check's options.
703     *
704     * @param variableDef Variable definition node.
705     * @param variableName Variable's name.
706     * @return true if variable has proper access modifier.
707     */
708    private boolean hasProperAccessModifier(DetailAST variableDef, String variableName) {
709        boolean result = true;
710
711        final String variableScope = getVisibilityScope(variableDef);
712
713        if (!PRIVATE_ACCESS_MODIFIER.equals(variableScope)) {
714            result =
715                isStaticFinalVariable(variableDef)
716                || packageAllowed && PACKAGE_ACCESS_MODIFIER.equals(variableScope)
717                || protectedAllowed && PROTECTED_ACCESS_MODIFIER.equals(variableScope)
718                || isIgnoredPublicMember(variableName, variableScope)
719                || isAllowedPublicField(variableDef);
720        }
721
722        return result;
723    }
724
725    /**
726     * Checks whether variable has static final modifiers.
727     *
728     * @param variableDef Variable definition node.
729     * @return true of variable has static final modifiers.
730     */
731    private static boolean isStaticFinalVariable(DetailAST variableDef) {
732        final Set<String> modifiers = getModifiers(variableDef);
733        return modifiers.contains(STATIC_KEYWORD)
734                && modifiers.contains(FINAL_KEYWORD);
735    }
736
737    /**
738     * Checks whether variable belongs to public members that should be ignored.
739     *
740     * @param variableName Variable's name.
741     * @param variableScope Variable's scope.
742     * @return true if variable belongs to public members that should be ignored.
743     */
744    private boolean isIgnoredPublicMember(String variableName, String variableScope) {
745        return PUBLIC_ACCESS_MODIFIER.equals(variableScope)
746            && publicMemberPattern.matcher(variableName).find();
747    }
748
749    /**
750     * Checks whether the variable satisfies the public field check.
751     *
752     * @param variableDef Variable definition node.
753     * @return true if allowed.
754     */
755    private boolean isAllowedPublicField(DetailAST variableDef) {
756        return allowPublicFinalFields && isFinalField(variableDef)
757            || allowPublicImmutableFields && isImmutableFieldDefinedInFinalClass(variableDef);
758    }
759
760    /**
761     * Checks whether immutable field is defined in final class.
762     *
763     * @param variableDef Variable definition node.
764     * @return true if immutable field is defined in final class.
765     */
766    private boolean isImmutableFieldDefinedInFinalClass(DetailAST variableDef) {
767        final DetailAST classDef = variableDef.getParent().getParent();
768        final Set<String> classModifiers = getModifiers(classDef);
769        return (classModifiers.contains(FINAL_KEYWORD) || classDef.getType() == TokenTypes.ENUM_DEF)
770                && isImmutableField(variableDef);
771    }
772
773    /**
774     * Returns the set of modifier Strings for a VARIABLE_DEF or CLASS_DEF AST.
775     *
776     * @param defAST AST for a variable or class definition.
777     * @return the set of modifier Strings for defAST.
778     */
779    private static Set<String> getModifiers(DetailAST defAST) {
780        final DetailAST modifiersAST = defAST.findFirstToken(TokenTypes.MODIFIERS);
781        final Set<String> modifiersSet = new HashSet<>();
782        if (modifiersAST != null) {
783            DetailAST modifier = modifiersAST.getFirstChild();
784            while (modifier != null) {
785                modifiersSet.add(modifier.getText());
786                modifier = modifier.getNextSibling();
787            }
788        }
789        return modifiersSet;
790    }
791
792    /**
793     * Returns the visibility scope for the variable.
794     *
795     * @param variableDef Variable definition node.
796     * @return one of "public", "private", "protected", "package"
797     */
798    private static String getVisibilityScope(DetailAST variableDef) {
799        final Set<String> modifiers = getModifiers(variableDef);
800        String accessModifier = PACKAGE_ACCESS_MODIFIER;
801        for (final String modifier : EXPLICIT_MODS) {
802            if (modifiers.contains(modifier)) {
803                accessModifier = modifier;
804                break;
805            }
806        }
807        return accessModifier;
808    }
809
810    /**
811     * Checks if current field is immutable:
812     * has final modifier and either a primitive type or instance of class
813     * known to be immutable (such as String, ImmutableCollection from Guava, etc.).
814     * Classes known to be immutable are listed in
815     * {@link VisibilityModifierCheck#immutableClassCanonicalNames}
816     *
817     * @param variableDef Field in consideration.
818     * @return true if field is immutable.
819     */
820    private boolean isImmutableField(DetailAST variableDef) {
821        boolean result = false;
822        if (isFinalField(variableDef)) {
823            final DetailAST type = variableDef.findFirstToken(TokenTypes.TYPE);
824            final boolean isCanonicalName = isCanonicalName(type);
825            final String typeName = getCanonicalName(type);
826            if (immutableClassShortNames.contains(typeName)
827                    || isCanonicalName && immutableClassCanonicalNames.contains(typeName)) {
828                final DetailAST typeArgs = getGenericTypeArgs(type, isCanonicalName);
829
830                if (typeArgs == null) {
831                    result = true;
832                }
833                else {
834                    final List<String> argsClassNames = getTypeArgsClassNames(typeArgs);
835                    result = areImmutableTypeArguments(argsClassNames);
836                }
837            }
838            else {
839                result = !isCanonicalName && isPrimitive(type);
840            }
841        }
842        return result;
843    }
844
845    /**
846     * Checks whether type definition is in canonical form.
847     *
848     * @param type type definition token.
849     * @return true if type definition is in canonical form.
850     */
851    private static boolean isCanonicalName(DetailAST type) {
852        return type.getFirstChild().getType() == TokenTypes.DOT;
853    }
854
855    /**
856     * Returns generic type arguments token.
857     *
858     * @param type type token.
859     * @param isCanonicalName whether type name is in canonical form.
860     * @return generic type arguments token.
861     */
862    private static DetailAST getGenericTypeArgs(DetailAST type, boolean isCanonicalName) {
863        final DetailAST typeArgs;
864        if (isCanonicalName) {
865            // if type class name is in canonical form, abstract tree has specific structure
866            typeArgs = type.getFirstChild().findFirstToken(TokenTypes.TYPE_ARGUMENTS);
867        }
868        else {
869            typeArgs = type.findFirstToken(TokenTypes.TYPE_ARGUMENTS);
870        }
871        return typeArgs;
872    }
873
874    /**
875     * Returns a list of type parameters class names.
876     *
877     * @param typeArgs type arguments token.
878     * @return a list of type parameters class names.
879     */
880    private static List<String> getTypeArgsClassNames(DetailAST typeArgs) {
881        final List<String> typeClassNames = new ArrayList<>();
882        DetailAST type = typeArgs.findFirstToken(TokenTypes.TYPE_ARGUMENT);
883        DetailAST sibling;
884        do {
885            final String typeName = getCanonicalName(type);
886            typeClassNames.add(typeName);
887            sibling = type.getNextSibling();
888            type = sibling.getNextSibling();
889        } while (sibling.getType() == TokenTypes.COMMA);
890        return typeClassNames;
891    }
892
893    /**
894     * Checks whether all generic type arguments are immutable.
895     * If at least one argument is mutable, we assume that the whole list of type arguments
896     * is mutable.
897     *
898     * @param typeArgsClassNames type arguments class names.
899     * @return true if all generic type arguments are immutable.
900     */
901    private boolean areImmutableTypeArguments(Collection<String> typeArgsClassNames) {
902        return typeArgsClassNames.stream().noneMatch(
903            typeName -> {
904                return !immutableClassShortNames.contains(typeName)
905                    && !immutableClassCanonicalNames.contains(typeName);
906            });
907    }
908
909    /**
910     * Checks whether current field is final.
911     *
912     * @param variableDef field in consideration.
913     * @return true if current field is final.
914     */
915    private static boolean isFinalField(DetailAST variableDef) {
916        final DetailAST modifiers = variableDef.findFirstToken(TokenTypes.MODIFIERS);
917        return modifiers.findFirstToken(TokenTypes.FINAL) != null;
918    }
919
920    /**
921     * Checks if current type is primitive type (int, short, float, boolean, double, etc.).
922     * As primitive types have special tokens for each one, such as:
923     * LITERAL_INT, LITERAL_BOOLEAN, etc.
924     * So, if type's identifier differs from {@link TokenTypes#IDENT IDENT} token - it's a
925     * primitive type.
926     *
927     * @param type Ast {@link TokenTypes#TYPE TYPE} node.
928     * @return true if current type is primitive type.
929     */
930    private static boolean isPrimitive(DetailAST type) {
931        return type.getFirstChild().getType() != TokenTypes.IDENT;
932    }
933
934    /**
935     * Gets canonical type's name from given {@link TokenTypes#TYPE TYPE} node.
936     *
937     * @param type DetailAST {@link TokenTypes#TYPE TYPE} node.
938     * @return canonical type's name
939     */
940    private static String getCanonicalName(DetailAST type) {
941        final StringBuilder canonicalNameBuilder = new StringBuilder(256);
942        DetailAST toVisit = type;
943        while (toVisit != null) {
944            toVisit = getNextSubTreeNode(toVisit, type);
945            if (toVisit != null && toVisit.getType() == TokenTypes.IDENT) {
946                if (canonicalNameBuilder.length() > 0) {
947                    canonicalNameBuilder.append('.');
948                }
949                canonicalNameBuilder.append(toVisit.getText());
950                final DetailAST nextSubTreeNode = getNextSubTreeNode(toVisit, type);
951                if (nextSubTreeNode != null
952                        && nextSubTreeNode.getType() == TokenTypes.TYPE_ARGUMENTS) {
953                    break;
954                }
955            }
956        }
957        return canonicalNameBuilder.toString();
958    }
959
960    /**
961     * Gets the next node of a syntactical tree (child of a current node or
962     * sibling of a current node, or sibling of a parent of a current node).
963     *
964     * @param currentNodeAst Current node in considering
965     * @param subTreeRootAst SubTree root
966     * @return Current node after bypassing, if current node reached the root of a subtree
967     *        method returns null
968     */
969    private static DetailAST
970        getNextSubTreeNode(DetailAST currentNodeAst, DetailAST subTreeRootAst) {
971        DetailAST currentNode = currentNodeAst;
972        DetailAST toVisitAst = currentNode.getFirstChild();
973        while (toVisitAst == null) {
974            toVisitAst = currentNode.getNextSibling();
975            if (currentNode.getParent().getColumnNo() == subTreeRootAst.getColumnNo()) {
976                break;
977            }
978            currentNode = currentNode.getParent();
979        }
980        return toVisitAst;
981    }
982
983    /**
984     * Converts canonical class names to short names.
985     *
986     * @param canonicalClassNames the set of canonical class names.
987     * @return the set of short names of classes.
988     */
989    private static Set<String> getClassShortNames(Set<String> canonicalClassNames) {
990        return canonicalClassNames.stream()
991            .map(CommonUtil::baseClassName)
992            .collect(Collectors.toCollection(HashSet::new));
993    }
994
995    /**
996     * Gets the short class name from given canonical name.
997     *
998     * @param canonicalClassName canonical class name.
999     * @return short name of class.
1000     */
1001    private static String getClassShortName(String canonicalClassName) {
1002        return canonicalClassName
1003                .substring(canonicalClassName.lastIndexOf('.') + 1);
1004    }
1005
1006    /**
1007     * Checks whether the AST is annotated with
1008     * an annotation containing the passed in regular
1009     * expression and return the AST representing that
1010     * annotation.
1011     *
1012     * <p>
1013     * This method will not look for imports or package
1014     * statements to detect the passed in annotation.
1015     * </p>
1016     *
1017     * <p>
1018     * To check if an AST contains a passed in annotation
1019     * taking into account fully-qualified names
1020     * (ex: java.lang.Override, Override)
1021     * this method will need to be called twice. Once for each
1022     * name given.
1023     * </p>
1024     *
1025     * @param variableDef {@link TokenTypes#VARIABLE_DEF variable def node}.
1026     * @return the AST representing the first such annotation or null if
1027     *         no such annotation was found
1028     */
1029    private DetailAST findMatchingAnnotation(DetailAST variableDef) {
1030        DetailAST matchingAnnotation = null;
1031
1032        final DetailAST holder = AnnotationUtil.getAnnotationHolder(variableDef);
1033
1034        for (DetailAST child = holder.getFirstChild();
1035            child != null; child = child.getNextSibling()) {
1036            if (child.getType() == TokenTypes.ANNOTATION) {
1037                final DetailAST ast = child.getFirstChild();
1038                final String name =
1039                    FullIdent.createFullIdent(ast.getNextSibling()).getText();
1040                if (ignoreAnnotationCanonicalNames.contains(name)
1041                         || ignoreAnnotationShortNames.contains(name)) {
1042                    matchingAnnotation = child;
1043                    break;
1044                }
1045            }
1046        }
1047
1048        return matchingAnnotation;
1049    }
1050
1051}