001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2026 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018///////////////////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.design;
021
022import java.util.ArrayList;
023import java.util.Collection;
024import java.util.HashSet;
025import java.util.List;
026import java.util.Set;
027import java.util.regex.Pattern;
028import java.util.stream.Collectors;
029
030import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
031import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
032import com.puppycrawl.tools.checkstyle.api.DetailAST;
033import com.puppycrawl.tools.checkstyle.api.FullIdent;
034import com.puppycrawl.tools.checkstyle.api.TokenTypes;
035import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
036import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
037import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
038
039/**
040 * <div>
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 * </div>
046 *
047 * <p>
048 * Rationale: Enforce encapsulation.
049 * </p>
050 *
051 * <p>
052 * Check also has options making it less strict:
053 * </p>
054 *
055 * <p>
056 * <b>ignoreAnnotationCanonicalNames</b> - the list of annotations which ignore
057 * variables in consideration. If user want to provide short annotation name that
058 * type will match to any named the same type without consideration of package.
059 * </p>
060 *
061 * <p>
062 * <b>allowPublicFinalFields</b> - which allows public final fields.
063 * </p>
064 *
065 * <p>
066 * <b>allowPublicImmutableFields</b> - which allows immutable fields to be
067 * declared as public if defined in final class.
068 * </p>
069 *
070 * <p>
071 * Field is known to be immutable if:
072 * </p>
073 * <ul>
074 * <li>It's declared as final</li>
075 * <li>Has either a primitive type or instance of class user defined to be immutable
076 * (such as String, ImmutableCollection from Guava, etc.)</li>
077 * </ul>
078 *
079 * <p>
080 * Classes known to be immutable are listed in <b>immutableClassCanonicalNames</b>
081 * by their canonical names.
082 * </p>
083 *
084 * <p>
085 * Property Rationale: Forcing all fields of class to have private modifier by default is
086 * good in most cases, but in some cases it drawbacks in too much boilerplate get/set code.
087 * One of such cases are immutable classes.
088 * </p>
089 *
090 * <p>
091 * Restriction: Check doesn't check if class is immutable, there's no checking
092 * if accessory methods are missing and all fields are immutable, we only check
093 * if current field is immutable or final.
094 * Under the flag <b>allowPublicImmutableFields</b>, the enclosing class must
095 * also be final, to encourage immutability.
096 * Under the flag <b>allowPublicFinalFields</b>, the final modifier
097 * on the enclosing class is optional.
098 * </p>
099 *
100 * <p>
101 * Star imports are out of scope of this Check. So if one of type imported via
102 * star import collides with user specified one by its short name - there
103 * won't be Check's violation.
104 * </p>
105 *
106 * <p>
107 * Notes:
108 * Top-level fields of compact source files
109 * (<a href="https://openjdk.org/jeps/512">JEP 512</a>)
110 * are skipped by design. Their access modifiers do not provide the encapsulation
111 * that this check enforces for ordinary classes.
112 * Public members are not flagged if the name matches the public
113 * member regular expression (contains {@code "^serialVersionUID$"} by
114 * default).
115 * </p>
116 *
117 * @since 3.0
118 */
119@FileStatefulCheck
120public class VisibilityModifierCheck
121    extends AbstractCheck {
122
123    /**
124     * A key is pointing to the warning message text in "messages.properties"
125     * file.
126     */
127    public static final String MSG_KEY = "variable.notPrivate";
128
129    /** Default immutable types canonical names. */
130    private static final Set<String> DEFAULT_IMMUTABLE_TYPES = Set.of(
131        "java.lang.String",
132        "java.lang.Integer",
133        "java.lang.Byte",
134        "java.lang.Character",
135        "java.lang.Short",
136        "java.lang.Boolean",
137        "java.lang.Long",
138        "java.lang.Double",
139        "java.lang.Float",
140        "java.lang.StackTraceElement",
141        "java.math.BigInteger",
142        "java.math.BigDecimal",
143        "java.io.File",
144        "java.util.Locale",
145        "java.util.UUID",
146        "java.net.URL",
147        "java.net.URI",
148        "java.net.Inet4Address",
149        "java.net.Inet6Address",
150        "java.net.InetSocketAddress"
151    );
152
153    /** Default ignore annotations canonical names. */
154    private static final Set<String> DEFAULT_IGNORE_ANNOTATIONS = Set.of(
155        "org.junit.Rule",
156        "org.junit.ClassRule",
157        "com.google.common.annotations.VisibleForTesting"
158    );
159
160    /** Name for 'public' access modifier. */
161    private static final String PUBLIC_ACCESS_MODIFIER = "public";
162
163    /** Name for 'private' access modifier. */
164    private static final String PRIVATE_ACCESS_MODIFIER = "private";
165
166    /** Name for 'protected' access modifier. */
167    private static final String PROTECTED_ACCESS_MODIFIER = "protected";
168
169    /** Name for implicit 'package' access modifier. */
170    private static final String PACKAGE_ACCESS_MODIFIER = "package";
171
172    /** Name for 'static' keyword. */
173    private static final String STATIC_KEYWORD = "static";
174
175    /** Name for 'final' keyword. */
176    private static final String FINAL_KEYWORD = "final";
177
178    /** Contains explicit access modifiers. */
179    private static final String[] EXPLICIT_MODS = {
180        PUBLIC_ACCESS_MODIFIER,
181        PRIVATE_ACCESS_MODIFIER,
182        PROTECTED_ACCESS_MODIFIER,
183    };
184
185    /**
186     * Specify pattern for public members that should be ignored.
187     */
188    private Pattern publicMemberPattern = Pattern.compile("^serialVersionUID$");
189
190    /** Set of ignore annotations short names. */
191    private Set<String> ignoreAnnotationShortNames;
192
193    /** Set of immutable classes short names. */
194    private Set<String> immutableClassShortNames;
195
196    /**
197     * Specify annotations canonical names which ignore variables in
198     * consideration.
199     */
200    private Set<String> ignoreAnnotationCanonicalNames = DEFAULT_IGNORE_ANNOTATIONS;
201
202    /** Control whether protected members are allowed. */
203    private boolean protectedAllowed;
204
205    /** Control whether package visible members are allowed. */
206    private boolean packageAllowed;
207
208    /** Allow immutable fields to be declared as public if defined in final class. */
209    private boolean allowPublicImmutableFields;
210
211    /** Allow final fields to be declared as public. */
212    private boolean allowPublicFinalFields;
213
214    /** Specify immutable classes canonical names. */
215    private Set<String> immutableClassCanonicalNames = DEFAULT_IMMUTABLE_TYPES;
216
217    /**
218     * Creates a new {@code VisibilityModifierCheck} instance.
219     */
220    public VisibilityModifierCheck() {
221        // no code by default
222    }
223
224    /**
225     * Setter to specify annotations canonical names which ignore variables
226     * in consideration.
227     *
228     * @param annotationNames array of ignore annotations canonical names.
229     * @since 6.5
230     */
231    public void setIgnoreAnnotationCanonicalNames(String... annotationNames) {
232        ignoreAnnotationCanonicalNames = Set.of(annotationNames);
233    }
234
235    /**
236     * Setter to control whether protected members are allowed.
237     *
238     * @param protectedAllowed whether protected members are allowed
239     * @since 3.0
240     */
241    public void setProtectedAllowed(boolean protectedAllowed) {
242        this.protectedAllowed = protectedAllowed;
243    }
244
245    /**
246     * Setter to control whether package visible members are allowed.
247     *
248     * @param packageAllowed whether package visible members are allowed
249     * @since 3.0
250     */
251    public void setPackageAllowed(boolean packageAllowed) {
252        this.packageAllowed = packageAllowed;
253    }
254
255    /**
256     * Setter to specify pattern for public members that should be ignored.
257     *
258     * @param pattern
259     *        pattern for public members to ignore.
260     * @since 3.0
261     */
262    public void setPublicMemberPattern(Pattern pattern) {
263        publicMemberPattern = pattern;
264    }
265
266    /**
267     * Setter to allow immutable fields to be declared as public if defined in final class.
268     *
269     * @param allow user's value.
270     * @since 6.4
271     */
272    public void setAllowPublicImmutableFields(boolean allow) {
273        allowPublicImmutableFields = allow;
274    }
275
276    /**
277     * Setter to allow final fields to be declared as public.
278     *
279     * @param allow user's value.
280     * @since 7.0
281     */
282    public void setAllowPublicFinalFields(boolean allow) {
283        allowPublicFinalFields = allow;
284    }
285
286    /**
287     * Setter to specify immutable classes canonical names.
288     *
289     * @param classNames array of immutable types canonical names.
290     * @since 6.4.1
291     */
292    public void setImmutableClassCanonicalNames(String... classNames) {
293        immutableClassCanonicalNames = Set.of(classNames);
294    }
295
296    @Override
297    public int[] getDefaultTokens() {
298        return getRequiredTokens();
299    }
300
301    @Override
302    public int[] getAcceptableTokens() {
303        return getRequiredTokens();
304    }
305
306    @Override
307    public int[] getRequiredTokens() {
308        return new int[] {
309            TokenTypes.VARIABLE_DEF,
310            TokenTypes.IMPORT,
311        };
312    }
313
314    @Override
315    public void beginTree(DetailAST rootAst) {
316        immutableClassShortNames = getClassShortNames(immutableClassCanonicalNames);
317        ignoreAnnotationShortNames = getClassShortNames(ignoreAnnotationCanonicalNames);
318    }
319
320    @Override
321    public void visitToken(DetailAST ast) {
322        switch (ast.getType()) {
323            case TokenTypes.VARIABLE_DEF -> {
324                if (!isAnonymousClassVariable(ast)) {
325                    visitVariableDef(ast);
326                }
327            }
328            case TokenTypes.IMPORT -> visitImport(ast);
329            default -> {
330                final String exceptionMsg = "Unexpected token type: " + ast.getText();
331                throw new IllegalArgumentException(exceptionMsg);
332            }
333        }
334    }
335
336    /**
337     * Checks if current variable definition is definition of an anonymous class.
338     *
339     * @param variableDef {@link TokenTypes#VARIABLE_DEF VARIABLE_DEF}
340     * @return true if current variable definition is definition of an anonymous class.
341     */
342    private static boolean isAnonymousClassVariable(DetailAST variableDef) {
343        return variableDef.getParent().getType() != TokenTypes.OBJBLOCK;
344    }
345
346    /**
347     * Checks access modifier of given variable.
348     * If it is not proper according to Check - puts violation on it.
349     *
350     * @param variableDef variable to check.
351     */
352    private void visitVariableDef(DetailAST variableDef) {
353        final boolean inInterfaceOrAnnotationBlock =
354                ScopeUtil.isInInterfaceOrAnnotationBlock(variableDef);
355
356        if (!inInterfaceOrAnnotationBlock && !hasIgnoreAnnotation(variableDef)) {
357            final DetailAST varNameAST = variableDef.findFirstToken(TokenTypes.TYPE)
358                .getNextSibling();
359            final String varName = varNameAST.getText();
360            if (!hasProperAccessModifier(variableDef, varName)) {
361                log(varNameAST, MSG_KEY, varName);
362            }
363        }
364    }
365
366    /**
367     * Checks if variable def has ignore annotation.
368     *
369     * @param variableDef {@link TokenTypes#VARIABLE_DEF VARIABLE_DEF}
370     * @return true if variable def has ignore annotation.
371     */
372    private boolean hasIgnoreAnnotation(DetailAST variableDef) {
373        final DetailAST firstIgnoreAnnotation =
374                 findMatchingAnnotation(variableDef);
375        return firstIgnoreAnnotation != null;
376    }
377
378    /**
379     * Checks imported type. If type's canonical name was not specified in
380     * <b>immutableClassCanonicalNames</b>, but its short name collides with one from
381     * <b>immutableClassShortNames</b> - removes it from the last one.
382     *
383     * @param importAst {@link TokenTypes#IMPORT Import}
384     */
385    private void visitImport(DetailAST importAst) {
386        if (!isStarImport(importAst)) {
387            final String canonicalName = getCanonicalName(importAst);
388            final String shortName = getClassShortName(canonicalName);
389
390            // If imported canonical class name is not specified as allowed immutable class,
391            // but its short name collides with one of specified class - removes the short name
392            // from list to avoid names collision
393            if (!immutableClassCanonicalNames.contains(canonicalName)) {
394                immutableClassShortNames.remove(shortName);
395            }
396            if (!ignoreAnnotationCanonicalNames.contains(canonicalName)) {
397                ignoreAnnotationShortNames.remove(shortName);
398            }
399        }
400    }
401
402    /**
403     * Checks if current import is star import. E.g.:
404     *
405     * <p>
406     * {@code
407     * import java.util.*;
408     * }
409     * </p>
410     *
411     * @param importAst {@link TokenTypes#IMPORT Import}
412     * @return true if it is star import
413     */
414    private static boolean isStarImport(DetailAST importAst) {
415        boolean result = false;
416        DetailAST toVisit = importAst;
417        while (toVisit != null) {
418            toVisit = getNextSubTreeNode(toVisit, importAst);
419            if (toVisit != null && toVisit.getType() == TokenTypes.STAR) {
420                result = true;
421                break;
422            }
423        }
424        return result;
425    }
426
427    /**
428     * Checks if current variable has proper access modifier according to Check's options.
429     *
430     * @param variableDef Variable definition node.
431     * @param variableName Variable's name.
432     * @return true if variable has proper access modifier.
433     */
434    private boolean hasProperAccessModifier(DetailAST variableDef, String variableName) {
435        boolean result = true;
436
437        final String variableScope = getVisibilityScope(variableDef);
438
439        if (!PRIVATE_ACCESS_MODIFIER.equals(variableScope)) {
440            result =
441                isStaticFinalVariable(variableDef)
442                || packageAllowed && PACKAGE_ACCESS_MODIFIER.equals(variableScope)
443                || protectedAllowed && PROTECTED_ACCESS_MODIFIER.equals(variableScope)
444                || isIgnoredPublicMember(variableName, variableScope)
445                || isAllowedPublicField(variableDef);
446        }
447
448        return result;
449    }
450
451    /**
452     * Checks whether variable has static final modifiers.
453     *
454     * @param variableDef Variable definition node.
455     * @return true of variable has static final modifiers.
456     */
457    private static boolean isStaticFinalVariable(DetailAST variableDef) {
458        final Set<String> modifiers = getModifiers(variableDef);
459        return modifiers.contains(STATIC_KEYWORD)
460                && modifiers.contains(FINAL_KEYWORD);
461    }
462
463    /**
464     * Checks whether variable belongs to public members that should be ignored.
465     *
466     * @param variableName Variable's name.
467     * @param variableScope Variable's scope.
468     * @return true if variable belongs to public members that should be ignored.
469     */
470    private boolean isIgnoredPublicMember(String variableName, String variableScope) {
471        return PUBLIC_ACCESS_MODIFIER.equals(variableScope)
472            && publicMemberPattern.matcher(variableName).find();
473    }
474
475    /**
476     * Checks whether the variable satisfies the public field check.
477     *
478     * @param variableDef Variable definition node.
479     * @return true if allowed.
480     */
481    private boolean isAllowedPublicField(DetailAST variableDef) {
482        return allowPublicFinalFields && isFinalField(variableDef)
483            || allowPublicImmutableFields && isImmutableFieldDefinedInFinalClass(variableDef);
484    }
485
486    /**
487     * Checks whether immutable field is defined in final class.
488     *
489     * @param variableDef Variable definition node.
490     * @return true if immutable field is defined in final class.
491     */
492    private boolean isImmutableFieldDefinedInFinalClass(DetailAST variableDef) {
493        final DetailAST classDef = variableDef.getParent().getParent();
494        final Set<String> classModifiers = getModifiers(classDef);
495        return (classModifiers.contains(FINAL_KEYWORD) || classDef.getType() == TokenTypes.ENUM_DEF)
496                && isImmutableField(variableDef);
497    }
498
499    /**
500     * Returns the set of modifier Strings for a VARIABLE_DEF or CLASS_DEF AST.
501     *
502     * @param defAST AST for a variable or class definition.
503     * @return the set of modifier Strings for defAST.
504     */
505    private static Set<String> getModifiers(DetailAST defAST) {
506        final DetailAST modifiersAST = defAST.findFirstToken(TokenTypes.MODIFIERS);
507        final Set<String> modifiersSet = new HashSet<>();
508        if (modifiersAST != null) {
509            DetailAST modifier = modifiersAST.getFirstChild();
510            while (modifier != null) {
511                modifiersSet.add(modifier.getText());
512                modifier = modifier.getNextSibling();
513            }
514        }
515        return modifiersSet;
516    }
517
518    /**
519     * Returns the visibility scope for the variable.
520     *
521     * @param variableDef Variable definition node.
522     * @return one of "public", "private", "protected", "package"
523     */
524    private static String getVisibilityScope(DetailAST variableDef) {
525        final Set<String> modifiers = getModifiers(variableDef);
526        String accessModifier = PACKAGE_ACCESS_MODIFIER;
527        for (final String modifier : EXPLICIT_MODS) {
528            if (modifiers.contains(modifier)) {
529                accessModifier = modifier;
530                break;
531            }
532        }
533        return accessModifier;
534    }
535
536    /**
537     * Checks if current field is immutable:
538     * has final modifier and either a primitive type or instance of class
539     * known to be immutable (such as String, ImmutableCollection from Guava, etc.).
540     * Classes known to be immutable are listed in
541     * {@link VisibilityModifierCheck#immutableClassCanonicalNames}
542     *
543     * @param variableDef Field in consideration.
544     * @return true if field is immutable.
545     */
546    private boolean isImmutableField(DetailAST variableDef) {
547        boolean result = false;
548        if (isFinalField(variableDef)) {
549            final DetailAST type = variableDef.findFirstToken(TokenTypes.TYPE);
550            final boolean isCanonicalName = isCanonicalName(type);
551            final String typeName = getCanonicalName(type);
552            if (immutableClassShortNames.contains(typeName)
553                    || isCanonicalName && immutableClassCanonicalNames.contains(typeName)) {
554                final DetailAST typeArgs = getGenericTypeArgs(type, isCanonicalName);
555
556                if (typeArgs == null) {
557                    result = true;
558                }
559                else {
560                    final List<String> argsClassNames = getTypeArgsClassNames(typeArgs);
561                    result = areImmutableTypeArguments(argsClassNames);
562                }
563            }
564            else {
565                result = !isCanonicalName && isPrimitive(type);
566            }
567        }
568        return result;
569    }
570
571    /**
572     * Checks whether type definition is in canonical form.
573     *
574     * @param type type definition token.
575     * @return true if type definition is in canonical form.
576     */
577    private static boolean isCanonicalName(DetailAST type) {
578        return type.getFirstChild().getType() == TokenTypes.DOT;
579    }
580
581    /**
582     * Returns generic type arguments token.
583     *
584     * @param type type token.
585     * @param isCanonicalName whether type name is in canonical form.
586     * @return generic type arguments token.
587     */
588    private static DetailAST getGenericTypeArgs(DetailAST type, boolean isCanonicalName) {
589        final DetailAST typeArgs;
590        if (isCanonicalName) {
591            // if type class name is in canonical form, abstract tree has specific structure
592            typeArgs = type.getFirstChild().findFirstToken(TokenTypes.TYPE_ARGUMENTS);
593        }
594        else {
595            typeArgs = type.findFirstToken(TokenTypes.TYPE_ARGUMENTS);
596        }
597        return typeArgs;
598    }
599
600    /**
601     * Returns a list of type parameters class names.
602     *
603     * @param typeArgs type arguments token.
604     * @return a list of type parameters class names.
605     */
606    private static List<String> getTypeArgsClassNames(DetailAST typeArgs) {
607        final List<String> typeClassNames = new ArrayList<>();
608        DetailAST type = typeArgs.findFirstToken(TokenTypes.TYPE_ARGUMENT);
609        DetailAST sibling;
610        do {
611            final String typeName = getCanonicalName(type);
612            typeClassNames.add(typeName);
613            sibling = type.getNextSibling();
614            type = sibling.getNextSibling();
615        } while (sibling.getType() == TokenTypes.COMMA);
616        return typeClassNames;
617    }
618
619    /**
620     * Checks whether all generic type arguments are immutable.
621     * If at least one argument is mutable, we assume that the whole list of type arguments
622     * is mutable.
623     *
624     * @param typeArgsClassNames type arguments class names.
625     * @return true if all generic type arguments are immutable.
626     */
627    private boolean areImmutableTypeArguments(Collection<String> typeArgsClassNames) {
628        return typeArgsClassNames.stream().noneMatch(
629            typeName -> {
630                return !immutableClassShortNames.contains(typeName)
631                    && !immutableClassCanonicalNames.contains(typeName);
632            });
633    }
634
635    /**
636     * Checks whether current field is final.
637     *
638     * @param variableDef field in consideration.
639     * @return true if current field is final.
640     */
641    private static boolean isFinalField(DetailAST variableDef) {
642        final DetailAST modifiers = variableDef.findFirstToken(TokenTypes.MODIFIERS);
643        return modifiers.findFirstToken(TokenTypes.FINAL) != null;
644    }
645
646    /**
647     * Checks if current type is primitive type (int, short, float, boolean, double, etc.).
648     * As primitive types have special tokens for each one, such as:
649     * LITERAL_INT, LITERAL_BOOLEAN, etc.
650     * So, if type's identifier differs from {@link TokenTypes#IDENT IDENT} token - it's a
651     * primitive type.
652     *
653     * @param type Ast {@link TokenTypes#TYPE TYPE} node.
654     * @return true if current type is primitive type.
655     */
656    private static boolean isPrimitive(DetailAST type) {
657        return type.getFirstChild().getType() != TokenTypes.IDENT;
658    }
659
660    /**
661     * Gets canonical type's name from given {@link TokenTypes#TYPE TYPE} node.
662     *
663     * @param type DetailAST {@link TokenTypes#TYPE TYPE} node.
664     * @return canonical type's name
665     */
666    private static String getCanonicalName(DetailAST type) {
667        final StringBuilder canonicalNameBuilder = new StringBuilder(256);
668        DetailAST toVisit = type;
669        while (toVisit != null) {
670            toVisit = getNextSubTreeNode(toVisit, type);
671            if (toVisit != null && toVisit.getType() == TokenTypes.IDENT) {
672                if (!canonicalNameBuilder.isEmpty()) {
673                    canonicalNameBuilder.append('.');
674                }
675                canonicalNameBuilder.append(toVisit.getText());
676                final DetailAST nextSubTreeNode = getNextSubTreeNode(toVisit, type);
677                if (nextSubTreeNode != null
678                        && nextSubTreeNode.getType() == TokenTypes.TYPE_ARGUMENTS) {
679                    break;
680                }
681            }
682        }
683        return canonicalNameBuilder.toString();
684    }
685
686    /**
687     * Gets the next node of a syntactical tree (child of a current node or
688     * sibling of a current node, or sibling of a parent of a current node).
689     *
690     * @param currentNodeAst Current node in considering
691     * @param subTreeRootAst SubTree root
692     * @return Current node after bypassing, if current node reached the root of a subtree
693     *        method returns null
694     */
695    private static DetailAST
696        getNextSubTreeNode(DetailAST currentNodeAst, DetailAST subTreeRootAst) {
697        DetailAST currentNode = currentNodeAst;
698        DetailAST toVisitAst = currentNode.getFirstChild();
699        while (toVisitAst == null) {
700            toVisitAst = currentNode.getNextSibling();
701            if (currentNode.getParent().getColumnNo() == subTreeRootAst.getColumnNo()) {
702                break;
703            }
704            currentNode = currentNode.getParent();
705        }
706        return toVisitAst;
707    }
708
709    /**
710     * Converts canonical class names to short names.
711     *
712     * @param canonicalClassNames the set of canonical class names.
713     * @return the set of short names of classes.
714     */
715    private static Set<String> getClassShortNames(Set<String> canonicalClassNames) {
716        return canonicalClassNames.stream()
717            .map(CommonUtil::baseClassName)
718            .collect(Collectors.toCollection(HashSet::new));
719    }
720
721    /**
722     * Gets the short class name from given canonical name.
723     *
724     * @param canonicalClassName canonical class name.
725     * @return short name of class.
726     */
727    private static String getClassShortName(String canonicalClassName) {
728        return canonicalClassName
729                .substring(canonicalClassName.lastIndexOf('.') + 1);
730    }
731
732    /**
733     * Checks whether the AST is annotated with
734     * an annotation containing the passed in regular
735     * expression and return the AST representing that
736     * annotation.
737     *
738     * <p>
739     * This method will not look for imports or package
740     * statements to detect the passed in annotation.
741     * </p>
742     *
743     * <p>
744     * To check if an AST contains a passed in annotation
745     * taking into account fully-qualified names
746     * (ex: java.lang.Override, Override)
747     * this method will need to be called twice. Once for each
748     * name given.
749     * </p>
750     *
751     * @param variableDef {@link TokenTypes#VARIABLE_DEF variable def node}.
752     * @return the AST representing the first such annotation or null if
753     *         no such annotation was found
754     */
755    private DetailAST findMatchingAnnotation(DetailAST variableDef) {
756        DetailAST matchingAnnotation = null;
757
758        final DetailAST holder = AnnotationUtil.getAnnotationHolder(variableDef);
759
760        for (DetailAST child = holder.getFirstChild();
761            child != null; child = child.getNextSibling()) {
762            if (child.getType() == TokenTypes.ANNOTATION) {
763                final DetailAST ast = child.getFirstChild();
764                final String name =
765                    FullIdent.createFullIdent(ast.getNextSibling()).getText();
766                if (ignoreAnnotationCanonicalNames.contains(name)
767                         || ignoreAnnotationShortNames.contains(name)) {
768                    matchingAnnotation = child;
769                    break;
770                }
771            }
772        }
773
774        return matchingAnnotation;
775    }
776
777}