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