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