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