001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2021 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.design;
021
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.Collections;
025import java.util.HashSet;
026import java.util.List;
027import java.util.Set;
028import java.util.regex.Pattern;
029import java.util.stream.Collectors;
030
031import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
032import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
033import com.puppycrawl.tools.checkstyle.api.DetailAST;
034import com.puppycrawl.tools.checkstyle.api.FullIdent;
035import com.puppycrawl.tools.checkstyle.api.TokenTypes;
036import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
037import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
038
039/**
040 * <p>
041 * Checks visibility of class members. Only static final, immutable or annotated
042 * by specified annotation members may be public;
043 * other class members must be private unless the property {@code protectedAllowed}
044 * or {@code packageAllowed} is set.
045 * </p>
046 * <p>
047 * Public members are not flagged if the name matches the public
048 * member regular expression (contains {@code "^serialVersionUID$"} by
049 * default).
050 * </p>
051 * <p>
052 * Note that Checkstyle 2 used to include {@code "^f[A-Z][a-zA-Z0-9]*$"} in the default pattern
053 * to allow names used in container-managed persistence for Enterprise JavaBeans (EJB) 1.1 with
054 * the default settings. With EJB 2.0 it is no longer necessary to have public access for
055 * persistent fields, so the default has been changed.
056 * </p>
057 * <p>
058 * Rationale: Enforce encapsulation.
059 * </p>
060 * <p>
061 * Check also has options making it less strict:
062 * </p>
063 * <p>
064 * <b>ignoreAnnotationCanonicalNames</b>- the list of annotations which ignore
065 * variables in consideration. If user will provide short annotation name that
066 * type will match to any named the same type without consideration of package.
067 * </p>
068 * <p>
069 * <b>allowPublicFinalFields</b>- which allows public final fields.
070 * </p>
071 * <p>
072 * <b>allowPublicImmutableFields</b>- which allows immutable fields to be
073 * declared as public if defined in final class.
074 * </p>
075 * <p>
076 * Field is known to be immutable if:
077 * </p>
078 * <ul>
079 * <li>It's declared as final</li>
080 * <li>Has either a primitive type or instance of class user defined to be immutable
081 * (such as String, ImmutableCollection from Guava and etc)</li>
082 * </ul>
083 * <p>
084 * Classes known to be immutable are listed in <b>immutableClassCanonicalNames</b>
085 * by their canonical names.
086 * </p>
087 * <p>
088 * Property Rationale: Forcing all fields of class to have private modifier by default is
089 * good in most cases, but in some cases it drawbacks in too much boilerplate get/set code.
090 * One of such cases are immutable classes.
091 * </p>
092 * <p>
093 * Restriction: Check doesn't check if class is immutable, there's no checking
094 * if accessory methods are missing and all fields are immutable, we only check
095 * if current field is immutable or final.
096 * Under the flag <b>allowPublicImmutableFields</b>, the enclosing class must
097 * also be final, to encourage immutability.
098 * Under the flag <b>allowPublicFinalFields</b>, the final modifier
099 * on the enclosing class is optional.
100 * </p>
101 * <p>
102 * Star imports are out of scope of this Check. So if one of type imported via
103 * star import collides with user specified one by its short name - there
104 * won't be Check's violation.
105 * </p>
106 * <ul>
107 * <li>
108 * Property {@code packageAllowed} - Control whether package visible members are allowed.
109 * Type is {@code boolean}.
110 * Default value is {@code false}.
111 * </li>
112 * <li>
113 * Property {@code protectedAllowed} - Control whether protected members are allowed.
114 * Type is {@code boolean}.
115 * Default value is {@code false}.
116 * </li>
117 * <li>
118 * Property {@code publicMemberPattern} - Specify pattern for public members that should be ignored.
119 * Type is {@code java.util.regex.Pattern}.
120 * Default value is {@code "^serialVersionUID$"}.
121 * </li>
122 * <li>
123 * Property {@code allowPublicFinalFields} - Allow final fields to be declared as public.
124 * Type is {@code boolean}.
125 * Default value is {@code false}.
126 * </li>
127 * <li>
128 * Property {@code allowPublicImmutableFields} - Allow immutable fields to be
129 * declared as public if defined in final class.
130 * Type is {@code boolean}.
131 * Default value is {@code false}.
132 * </li>
133 * <li>
134 * Property {@code immutableClassCanonicalNames} - Specify immutable classes canonical names.
135 * Type is {@code java.lang.String[]}.
136 * Default value is {@code java.io.File, java.lang.Boolean, java.lang.Byte,
137 * java.lang.Character, java.lang.Double, java.lang.Float, java.lang.Integer,
138 * java.lang.Long, java.lang.Short, java.lang.StackTraceElement, java.lang.String,
139 * java.math.BigDecimal, java.math.BigInteger, java.net.Inet4Address, java.net.Inet6Address,
140 * java.net.InetSocketAddress, java.net.URI, java.net.URL, java.util.Locale, java.util.UUID}.
141 * </li>
142 * <li>
143 * Property {@code ignoreAnnotationCanonicalNames} - Specify the list of annotations canonical
144 * names which ignore variables in consideration.
145 * Type is {@code java.lang.String[]}.
146 * Default value is {@code com.google.common.annotations.VisibleForTesting,
147 * org.junit.ClassRule, org.junit.Rule}.
148 * </li>
149 * </ul>
150 * <p>
151 * To configure the check:
152 * </p>
153 * <pre>
154 * &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 List<String> DEFAULT_IMMUTABLE_TYPES = Collections.unmodifiableList(
419        Arrays.stream(new String[] {
420            "java.lang.String",
421            "java.lang.Integer",
422            "java.lang.Byte",
423            "java.lang.Character",
424            "java.lang.Short",
425            "java.lang.Boolean",
426            "java.lang.Long",
427            "java.lang.Double",
428            "java.lang.Float",
429            "java.lang.StackTraceElement",
430            "java.math.BigInteger",
431            "java.math.BigDecimal",
432            "java.io.File",
433            "java.util.Locale",
434            "java.util.UUID",
435            "java.net.URL",
436            "java.net.URI",
437            "java.net.Inet4Address",
438            "java.net.Inet6Address",
439            "java.net.InetSocketAddress",
440        }).collect(Collectors.toList()));
441
442    /** Default ignore annotations canonical names. */
443    private static final List<String> DEFAULT_IGNORE_ANNOTATIONS = Collections.unmodifiableList(
444        Arrays.stream(new String[] {
445            "org.junit.Rule",
446            "org.junit.ClassRule",
447            "com.google.common.annotations.VisibleForTesting",
448        }).collect(Collectors.toList()));
449
450    /** Name for 'public' access modifier. */
451    private static final String PUBLIC_ACCESS_MODIFIER = "public";
452
453    /** Name for 'private' access modifier. */
454    private static final String PRIVATE_ACCESS_MODIFIER = "private";
455
456    /** Name for 'protected' access modifier. */
457    private static final String PROTECTED_ACCESS_MODIFIER = "protected";
458
459    /** Name for implicit 'package' access modifier. */
460    private static final String PACKAGE_ACCESS_MODIFIER = "package";
461
462    /** Name for 'static' keyword. */
463    private static final String STATIC_KEYWORD = "static";
464
465    /** Name for 'final' keyword. */
466    private static final String FINAL_KEYWORD = "final";
467
468    /** Contains explicit access modifiers. */
469    private static final String[] EXPLICIT_MODS = {
470        PUBLIC_ACCESS_MODIFIER,
471        PRIVATE_ACCESS_MODIFIER,
472        PROTECTED_ACCESS_MODIFIER,
473    };
474
475    /**
476     * Specify pattern for public members that should be ignored.
477     */
478    private Pattern publicMemberPattern = Pattern.compile("^serialVersionUID$");
479
480    /** List of ignore annotations short names. */
481    private final List<String> ignoreAnnotationShortNames =
482            getClassShortNames(DEFAULT_IGNORE_ANNOTATIONS);
483
484    /** List of immutable classes short names. */
485    private final List<String> immutableClassShortNames =
486        getClassShortNames(DEFAULT_IMMUTABLE_TYPES);
487
488    /**
489     * Specify the list of annotations canonical names which ignore variables in
490     * consideration.
491     */
492    private List<String> ignoreAnnotationCanonicalNames =
493        new ArrayList<>(DEFAULT_IGNORE_ANNOTATIONS);
494
495    /** Control whether protected members are allowed. */
496    private boolean protectedAllowed;
497
498    /** Control whether package visible members are allowed. */
499    private boolean packageAllowed;
500
501    /** Allow immutable fields to be declared as public if defined in final class. */
502    private boolean allowPublicImmutableFields;
503
504    /** Allow final fields to be declared as public. */
505    private boolean allowPublicFinalFields;
506
507    /** Specify immutable classes canonical names. */
508    private List<String> immutableClassCanonicalNames = new ArrayList<>(DEFAULT_IMMUTABLE_TYPES);
509
510    /**
511     * Setter to specify the list of annotations canonical names which ignore variables
512     * in consideration.
513     *
514     * @param annotationNames array of ignore annotations canonical names.
515     */
516    public void setIgnoreAnnotationCanonicalNames(String... annotationNames) {
517        ignoreAnnotationCanonicalNames = Arrays.asList(annotationNames);
518    }
519
520    /**
521     * Setter to control whether protected members are allowed.
522     *
523     * @param protectedAllowed whether protected members are allowed
524     */
525    public void setProtectedAllowed(boolean protectedAllowed) {
526        this.protectedAllowed = protectedAllowed;
527    }
528
529    /**
530     * Setter to control whether package visible members are allowed.
531     *
532     * @param packageAllowed whether package visible members are allowed
533     */
534    public void setPackageAllowed(boolean packageAllowed) {
535        this.packageAllowed = packageAllowed;
536    }
537
538    /**
539     * Setter to specify pattern for public members that should be ignored.
540     *
541     * @param pattern
542     *        pattern for public members to ignore.
543     */
544    public void setPublicMemberPattern(Pattern pattern) {
545        publicMemberPattern = pattern;
546    }
547
548    /**
549     * Setter to allow immutable fields to be declared as public if defined in final class.
550     *
551     * @param allow user's value.
552     */
553    public void setAllowPublicImmutableFields(boolean allow) {
554        allowPublicImmutableFields = allow;
555    }
556
557    /**
558     * Setter to allow final fields to be declared as public.
559     *
560     * @param allow user's value.
561     */
562    public void setAllowPublicFinalFields(boolean allow) {
563        allowPublicFinalFields = allow;
564    }
565
566    /**
567     * Setter to specify immutable classes canonical names.
568     *
569     * @param classNames array of immutable types canonical names.
570     */
571    public void setImmutableClassCanonicalNames(String... classNames) {
572        immutableClassCanonicalNames = Arrays.asList(classNames);
573    }
574
575    @Override
576    public int[] getDefaultTokens() {
577        return getRequiredTokens();
578    }
579
580    @Override
581    public int[] getAcceptableTokens() {
582        return getRequiredTokens();
583    }
584
585    @Override
586    public int[] getRequiredTokens() {
587        return new int[] {
588            TokenTypes.VARIABLE_DEF,
589            TokenTypes.IMPORT,
590        };
591    }
592
593    @Override
594    public void beginTree(DetailAST rootAst) {
595        immutableClassShortNames.clear();
596        final List<String> classShortNames =
597                getClassShortNames(immutableClassCanonicalNames);
598        immutableClassShortNames.addAll(classShortNames);
599
600        ignoreAnnotationShortNames.clear();
601        final List<String> annotationShortNames =
602                getClassShortNames(ignoreAnnotationCanonicalNames);
603        ignoreAnnotationShortNames.addAll(annotationShortNames);
604    }
605
606    @Override
607    public void visitToken(DetailAST ast) {
608        switch (ast.getType()) {
609            case TokenTypes.VARIABLE_DEF:
610                if (!isAnonymousClassVariable(ast)) {
611                    visitVariableDef(ast);
612                }
613                break;
614            case TokenTypes.IMPORT:
615                visitImport(ast);
616                break;
617            default:
618                final String exceptionMsg = "Unexpected token type: " + ast.getText();
619                throw new IllegalArgumentException(exceptionMsg);
620        }
621    }
622
623    /**
624     * Checks if current variable definition is definition of an anonymous class.
625     *
626     * @param variableDef {@link TokenTypes#VARIABLE_DEF VARIABLE_DEF}
627     * @return true if current variable definition is definition of an anonymous class.
628     */
629    private static boolean isAnonymousClassVariable(DetailAST variableDef) {
630        return variableDef.getParent().getType() != TokenTypes.OBJBLOCK;
631    }
632
633    /**
634     * Checks access modifier of given variable.
635     * If it is not proper according to Check - puts violation on it.
636     *
637     * @param variableDef variable to check.
638     */
639    private void visitVariableDef(DetailAST variableDef) {
640        final boolean inInterfaceOrAnnotationBlock =
641                ScopeUtil.isInInterfaceOrAnnotationBlock(variableDef);
642
643        if (!inInterfaceOrAnnotationBlock && !hasIgnoreAnnotation(variableDef)) {
644            final DetailAST varNameAST = variableDef.findFirstToken(TokenTypes.TYPE)
645                .getNextSibling();
646            final String varName = varNameAST.getText();
647            if (!hasProperAccessModifier(variableDef, varName)) {
648                log(varNameAST, MSG_KEY, varName);
649            }
650        }
651    }
652
653    /**
654     * Checks if variable def has ignore annotation.
655     *
656     * @param variableDef {@link TokenTypes#VARIABLE_DEF VARIABLE_DEF}
657     * @return true if variable def has ignore annotation.
658     */
659    private boolean hasIgnoreAnnotation(DetailAST variableDef) {
660        final DetailAST firstIgnoreAnnotation =
661                 findMatchingAnnotation(variableDef);
662        return firstIgnoreAnnotation != null;
663    }
664
665    /**
666     * Checks imported type. If type's canonical name was not specified in
667     * <b>immutableClassCanonicalNames</b>, but it's short name collides with one from
668     * <b>immutableClassShortNames</b> - removes it from the last one.
669     *
670     * @param importAst {@link TokenTypes#IMPORT Import}
671     */
672    private void visitImport(DetailAST importAst) {
673        if (!isStarImport(importAst)) {
674            final DetailAST type = importAst.getFirstChild();
675            final String canonicalName = getCanonicalName(type);
676            final String shortName = getClassShortName(canonicalName);
677
678            // If imported canonical class name is not specified as allowed immutable class,
679            // but its short name collides with one of specified class - removes the short name
680            // from list to avoid names collision
681            if (!immutableClassCanonicalNames.contains(canonicalName)) {
682                immutableClassShortNames.remove(shortName);
683            }
684            if (!ignoreAnnotationCanonicalNames.contains(canonicalName)) {
685                ignoreAnnotationShortNames.remove(shortName);
686            }
687        }
688    }
689
690    /**
691     * Checks if current import is star import. E.g.:
692     * <p>
693     * {@code
694     * import java.util.*;
695     * }
696     * </p>
697     *
698     * @param importAst {@link TokenTypes#IMPORT Import}
699     * @return true if it is star import
700     */
701    private static boolean isStarImport(DetailAST importAst) {
702        boolean result = false;
703        DetailAST toVisit = importAst;
704        while (toVisit != null) {
705            toVisit = getNextSubTreeNode(toVisit, importAst);
706            if (toVisit != null && toVisit.getType() == TokenTypes.STAR) {
707                result = true;
708                break;
709            }
710        }
711        return result;
712    }
713
714    /**
715     * Checks if current variable has proper access modifier according to Check's options.
716     *
717     * @param variableDef Variable definition node.
718     * @param variableName Variable's name.
719     * @return true if variable has proper access modifier.
720     */
721    private boolean hasProperAccessModifier(DetailAST variableDef, String variableName) {
722        boolean result = true;
723
724        final String variableScope = getVisibilityScope(variableDef);
725
726        if (!PRIVATE_ACCESS_MODIFIER.equals(variableScope)) {
727            result =
728                isStaticFinalVariable(variableDef)
729                || packageAllowed && PACKAGE_ACCESS_MODIFIER.equals(variableScope)
730                || protectedAllowed && PROTECTED_ACCESS_MODIFIER.equals(variableScope)
731                || isIgnoredPublicMember(variableName, variableScope)
732                || isAllowedPublicField(variableDef);
733        }
734
735        return result;
736    }
737
738    /**
739     * Checks whether variable has static final modifiers.
740     *
741     * @param variableDef Variable definition node.
742     * @return true of variable has static final modifiers.
743     */
744    private static boolean isStaticFinalVariable(DetailAST variableDef) {
745        final Set<String> modifiers = getModifiers(variableDef);
746        return modifiers.contains(STATIC_KEYWORD)
747                && modifiers.contains(FINAL_KEYWORD);
748    }
749
750    /**
751     * Checks whether variable belongs to public members that should be ignored.
752     *
753     * @param variableName Variable's name.
754     * @param variableScope Variable's scope.
755     * @return true if variable belongs to public members that should be ignored.
756     */
757    private boolean isIgnoredPublicMember(String variableName, String variableScope) {
758        return PUBLIC_ACCESS_MODIFIER.equals(variableScope)
759            && publicMemberPattern.matcher(variableName).find();
760    }
761
762    /**
763     * Checks whether the variable satisfies the public field check.
764     *
765     * @param variableDef Variable definition node.
766     * @return true if allowed.
767     */
768    private boolean isAllowedPublicField(DetailAST variableDef) {
769        return allowPublicFinalFields && isFinalField(variableDef)
770            || allowPublicImmutableFields && isImmutableFieldDefinedInFinalClass(variableDef);
771    }
772
773    /**
774     * Checks whether immutable field is defined in final class.
775     *
776     * @param variableDef Variable definition node.
777     * @return true if immutable field is defined in final class.
778     */
779    private boolean isImmutableFieldDefinedInFinalClass(DetailAST variableDef) {
780        final DetailAST classDef = variableDef.getParent().getParent();
781        final Set<String> classModifiers = getModifiers(classDef);
782        return (classModifiers.contains(FINAL_KEYWORD) || classDef.getType() == TokenTypes.ENUM_DEF)
783                && isImmutableField(variableDef);
784    }
785
786    /**
787     * Returns the set of modifier Strings for a VARIABLE_DEF or CLASS_DEF AST.
788     *
789     * @param defAST AST for a variable or class definition.
790     * @return the set of modifier Strings for defAST.
791     */
792    private static Set<String> getModifiers(DetailAST defAST) {
793        final DetailAST modifiersAST = defAST.findFirstToken(TokenTypes.MODIFIERS);
794        final Set<String> modifiersSet = new HashSet<>();
795        if (modifiersAST != null) {
796            DetailAST modifier = modifiersAST.getFirstChild();
797            while (modifier != null) {
798                modifiersSet.add(modifier.getText());
799                modifier = modifier.getNextSibling();
800            }
801        }
802        return modifiersSet;
803    }
804
805    /**
806     * Returns the visibility scope for the variable.
807     *
808     * @param variableDef Variable definition node.
809     * @return one of "public", "private", "protected", "package"
810     */
811    private static String getVisibilityScope(DetailAST variableDef) {
812        final Set<String> modifiers = getModifiers(variableDef);
813        String accessModifier = PACKAGE_ACCESS_MODIFIER;
814        for (final String modifier : EXPLICIT_MODS) {
815            if (modifiers.contains(modifier)) {
816                accessModifier = modifier;
817                break;
818            }
819        }
820        return accessModifier;
821    }
822
823    /**
824     * Checks if current field is immutable:
825     * has final modifier and either a primitive type or instance of class
826     * known to be immutable (such as String, ImmutableCollection from Guava and etc).
827     * Classes known to be immutable are listed in
828     * {@link VisibilityModifierCheck#immutableClassCanonicalNames}
829     *
830     * @param variableDef Field in consideration.
831     * @return true if field is immutable.
832     */
833    private boolean isImmutableField(DetailAST variableDef) {
834        boolean result = false;
835        if (isFinalField(variableDef)) {
836            final DetailAST type = variableDef.findFirstToken(TokenTypes.TYPE);
837            final boolean isCanonicalName = isCanonicalName(type);
838            final String typeName = getTypeName(type, isCanonicalName);
839            if (immutableClassShortNames.contains(typeName)
840                    || isCanonicalName && immutableClassCanonicalNames.contains(typeName)) {
841                final DetailAST typeArgs = getGenericTypeArgs(type, isCanonicalName);
842
843                if (typeArgs == null) {
844                    result = true;
845                }
846                else {
847                    final List<String> argsClassNames = getTypeArgsClassNames(typeArgs);
848                    result = areImmutableTypeArguments(argsClassNames);
849                }
850            }
851            else {
852                result = !isCanonicalName && isPrimitive(type);
853            }
854        }
855        return result;
856    }
857
858    /**
859     * Checks whether type definition is in canonical form.
860     *
861     * @param type type definition token.
862     * @return true if type definition is in canonical form.
863     */
864    private static boolean isCanonicalName(DetailAST type) {
865        return type.getFirstChild().getType() == TokenTypes.DOT;
866    }
867
868    /**
869     * Returns generic type arguments token.
870     *
871     * @param type type token.
872     * @param isCanonicalName whether type name is in canonical form.
873     * @return generic type arguments token.
874     */
875    private static DetailAST getGenericTypeArgs(DetailAST type, boolean isCanonicalName) {
876        final DetailAST typeArgs;
877        if (isCanonicalName) {
878            // if type class name is in canonical form, abstract tree has specific structure
879            typeArgs = type.getFirstChild().findFirstToken(TokenTypes.TYPE_ARGUMENTS);
880        }
881        else {
882            typeArgs = type.findFirstToken(TokenTypes.TYPE_ARGUMENTS);
883        }
884        return typeArgs;
885    }
886
887    /**
888     * Returns a list of type parameters class names.
889     *
890     * @param typeArgs type arguments token.
891     * @return a list of type parameters class names.
892     */
893    private static List<String> getTypeArgsClassNames(DetailAST typeArgs) {
894        final List<String> typeClassNames = new ArrayList<>();
895        DetailAST type = typeArgs.findFirstToken(TokenTypes.TYPE_ARGUMENT);
896        boolean isCanonicalName = isCanonicalName(type);
897        String typeName = getTypeName(type, isCanonicalName);
898        typeClassNames.add(typeName);
899        DetailAST sibling = type.getNextSibling();
900        while (sibling.getType() == TokenTypes.COMMA) {
901            type = sibling.getNextSibling();
902            isCanonicalName = isCanonicalName(type);
903            typeName = getTypeName(type, isCanonicalName);
904            typeClassNames.add(typeName);
905            sibling = type.getNextSibling();
906        }
907        return typeClassNames;
908    }
909
910    /**
911     * Checks whether all of generic type arguments are immutable.
912     * If at least one argument is mutable, we assume that the whole list of type arguments
913     * is mutable.
914     *
915     * @param typeArgsClassNames type arguments class names.
916     * @return true if all of generic type arguments are immutable.
917     */
918    private boolean areImmutableTypeArguments(List<String> typeArgsClassNames) {
919        return typeArgsClassNames.stream().noneMatch(
920            typeName -> {
921                return !immutableClassShortNames.contains(typeName)
922                    && !immutableClassCanonicalNames.contains(typeName);
923            });
924    }
925
926    /**
927     * Checks whether current field is final.
928     *
929     * @param variableDef field in consideration.
930     * @return true if current field is final.
931     */
932    private static boolean isFinalField(DetailAST variableDef) {
933        final DetailAST modifiers = variableDef.findFirstToken(TokenTypes.MODIFIERS);
934        return modifiers.findFirstToken(TokenTypes.FINAL) != null;
935    }
936
937    /**
938     * Gets the name of type from given ast {@link TokenTypes#TYPE TYPE} node.
939     * If type is specified via its canonical name - canonical name will be returned,
940     * else - short type's name.
941     *
942     * @param type {@link TokenTypes#TYPE TYPE} node.
943     * @param isCanonicalName is given name canonical.
944     * @return String representation of given type's name.
945     */
946    private static String getTypeName(DetailAST type, boolean isCanonicalName) {
947        final String typeName;
948        if (isCanonicalName) {
949            typeName = getCanonicalName(type);
950        }
951        else {
952            typeName = type.getFirstChild().getText();
953        }
954        return typeName;
955    }
956
957    /**
958     * Checks if current type is primitive type (int, short, float, boolean, double, etc.).
959     * As primitive types have special tokens for each one, such as:
960     * LITERAL_INT, LITERAL_BOOLEAN, etc.
961     * So, if type's identifier differs from {@link TokenTypes#IDENT IDENT} token - it's a
962     * primitive type.
963     *
964     * @param type Ast {@link TokenTypes#TYPE TYPE} node.
965     * @return true if current type is primitive type.
966     */
967    private static boolean isPrimitive(DetailAST type) {
968        return type.getFirstChild().getType() != TokenTypes.IDENT;
969    }
970
971    /**
972     * Gets canonical type's name from given {@link TokenTypes#TYPE TYPE} node.
973     *
974     * @param type DetailAST {@link TokenTypes#TYPE TYPE} node.
975     * @return canonical type's name
976     */
977    private static String getCanonicalName(DetailAST type) {
978        final StringBuilder canonicalNameBuilder = new StringBuilder(256);
979        DetailAST toVisit = type.getFirstChild();
980        while (toVisit != null) {
981            toVisit = getNextSubTreeNode(toVisit, type);
982            if (toVisit != null && toVisit.getType() == TokenTypes.IDENT) {
983                if (canonicalNameBuilder.length() > 0) {
984                    canonicalNameBuilder.append('.');
985                }
986                canonicalNameBuilder.append(toVisit.getText());
987                final DetailAST nextSubTreeNode = getNextSubTreeNode(toVisit, type);
988                if (nextSubTreeNode != null
989                        && nextSubTreeNode.getType() == TokenTypes.TYPE_ARGUMENTS) {
990                    break;
991                }
992            }
993        }
994        return canonicalNameBuilder.toString();
995    }
996
997    /**
998     * Gets the next node of a syntactical tree (child of a current node or
999     * sibling of a current node, or sibling of a parent of a current node).
1000     *
1001     * @param currentNodeAst Current node in considering
1002     * @param subTreeRootAst SubTree root
1003     * @return Current node after bypassing, if current node reached the root of a subtree
1004     *        method returns null
1005     */
1006    private static DetailAST
1007        getNextSubTreeNode(DetailAST currentNodeAst, DetailAST subTreeRootAst) {
1008        DetailAST currentNode = currentNodeAst;
1009        DetailAST toVisitAst = currentNode.getFirstChild();
1010        while (toVisitAst == null) {
1011            toVisitAst = currentNode.getNextSibling();
1012            if (currentNode.getParent().getColumnNo() == subTreeRootAst.getColumnNo()) {
1013                break;
1014            }
1015            currentNode = currentNode.getParent();
1016        }
1017        return toVisitAst;
1018    }
1019
1020    /**
1021     * Gets the list with short names classes.
1022     * These names are taken from array of classes canonical names.
1023     *
1024     * @param canonicalClassNames canonical class names.
1025     * @return the list of short names of classes.
1026     */
1027    private static List<String> getClassShortNames(List<String> canonicalClassNames) {
1028        final List<String> shortNames = new ArrayList<>();
1029        for (String canonicalClassName : canonicalClassNames) {
1030            final String shortClassName = canonicalClassName
1031                    .substring(canonicalClassName.lastIndexOf('.') + 1);
1032            shortNames.add(shortClassName);
1033        }
1034        return shortNames;
1035    }
1036
1037    /**
1038     * Gets the short class name from given canonical name.
1039     *
1040     * @param canonicalClassName canonical class name.
1041     * @return short name of class.
1042     */
1043    private static String getClassShortName(String canonicalClassName) {
1044        return canonicalClassName
1045                .substring(canonicalClassName.lastIndexOf('.') + 1);
1046    }
1047
1048    /**
1049     * Checks whether the AST is annotated with
1050     * an annotation containing the passed in regular
1051     * expression and return the AST representing that
1052     * annotation.
1053     *
1054     * <p>
1055     * This method will not look for imports or package
1056     * statements to detect the passed in annotation.
1057     * </p>
1058     *
1059     * <p>
1060     * To check if an AST contains a passed in annotation
1061     * taking into account fully-qualified names
1062     * (ex: java.lang.Override, Override)
1063     * this method will need to be called twice. Once for each
1064     * name given.
1065     * </p>
1066     *
1067     * @param variableDef {@link TokenTypes#VARIABLE_DEF variable def node}.
1068     * @return the AST representing the first such annotation or null if
1069     *         no such annotation was found
1070     */
1071    private DetailAST findMatchingAnnotation(DetailAST variableDef) {
1072        DetailAST matchingAnnotation = null;
1073
1074        final DetailAST holder = AnnotationUtil.getAnnotationHolder(variableDef);
1075
1076        for (DetailAST child = holder.getFirstChild();
1077            child != null; child = child.getNextSibling()) {
1078            if (child.getType() == TokenTypes.ANNOTATION) {
1079                final DetailAST ast = child.getFirstChild();
1080                final String name =
1081                    FullIdent.createFullIdent(ast.getNextSibling()).getText();
1082                if (ignoreAnnotationCanonicalNames.contains(name)
1083                         || ignoreAnnotationShortNames.contains(name)) {
1084                    matchingAnnotation = child;
1085                    break;
1086                }
1087            }
1088        }
1089
1090        return matchingAnnotation;
1091    }
1092
1093}