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