001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2017 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.imports;
021
022import com.puppycrawl.tools.checkstyle.StatelessCheck;
023import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
024import com.puppycrawl.tools.checkstyle.api.DetailAST;
025import com.puppycrawl.tools.checkstyle.api.FullIdent;
026import com.puppycrawl.tools.checkstyle.api.TokenTypes;
027import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
028
029/**
030 * <p>
031 * Check that finds static imports.
032 * </p>
033 * <p>
034 * Rationale: Importing static members can lead to naming conflicts
035 * between class' members. It may lead to poor code readability since it
036 * may no longer be clear what class a member resides (without looking
037 * at the import statement).
038 * </p>
039 * <p>
040 * An example of how to configure the check is:
041 * </p>
042 * <pre>
043 * &lt;module name="AvoidStaticImport"&gt;
044 *   &lt;property name="excludes"
045 *       value="java.lang.System.out,java.lang.Math.*"/&gt;
046 * &lt;/module&gt;
047 * </pre>
048 * The optional "excludes" property allows for certain classes via a star
049 * notation to be excluded such as java.lang.Math.* or specific
050 * static members to be excluded like java.lang.System.out for a variable
051 * or java.lang.Math.random for a method.
052 *
053 * <p>
054 * If you exclude a starred import on a class this automatically
055 * excludes each member individually.
056 * </p>
057 *
058 * <p>
059 * For example:
060 * Excluding java.lang.Math.* will allow the import of
061 * each static member in the Math class individually like
062 * java.lang.Math.PI
063 * </p>
064 * @author Travis Schneeberger
065 */
066@StatelessCheck
067public class AvoidStaticImportCheck
068    extends AbstractCheck {
069
070    /**
071     * A key is pointing to the warning message text in "messages.properties"
072     * file.
073     */
074    public static final String MSG_KEY = "import.avoidStatic";
075
076    /** The classes/static members to exempt from this check. */
077    private String[] excludes = CommonUtils.EMPTY_STRING_ARRAY;
078
079    @Override
080    public int[] getDefaultTokens() {
081        return getRequiredTokens();
082    }
083
084    @Override
085    public int[] getAcceptableTokens() {
086        return getRequiredTokens();
087    }
088
089    @Override
090    public int[] getRequiredTokens() {
091        return new int[] {TokenTypes.STATIC_IMPORT};
092    }
093
094    /**
095     * Sets the list of classes or static members to be exempt from the check.
096     * @param excludes a list of fully-qualified class names/specific
097     *     static members where static imports are ok
098     */
099    public void setExcludes(String... excludes) {
100        this.excludes = excludes.clone();
101    }
102
103    @Override
104    public void visitToken(final DetailAST ast) {
105        final DetailAST startingDot =
106            ast.getFirstChild().getNextSibling();
107        final FullIdent name = FullIdent.createFullIdent(startingDot);
108
109        if (!isExempt(name.getText())) {
110            log(startingDot.getLineNo(), MSG_KEY, name.getText());
111        }
112    }
113
114    /**
115     * Checks if a class or static member is exempt from known excludes.
116     *
117     * @param classOrStaticMember
118     *                the class or static member
119     * @return true if except false if not
120     */
121    private boolean isExempt(String classOrStaticMember) {
122        boolean exempt = false;
123
124        for (String exclude : excludes) {
125            if (classOrStaticMember.equals(exclude)
126                    || isStarImportOfPackage(classOrStaticMember, exclude)) {
127                exempt = true;
128                break;
129            }
130        }
131        return exempt;
132    }
133
134    /**
135     * Returns true if classOrStaticMember is a starred name of package,
136     *  not just member name.
137     * @param classOrStaticMember - full name of member
138     * @param exclude - current exclusion
139     * @return true if member in exclusion list
140     */
141    private static boolean isStarImportOfPackage(String classOrStaticMember, String exclude) {
142        boolean result = false;
143        if (exclude.endsWith(".*")) {
144            //this section allows explicit imports
145            //to be exempt when configured using
146            //a starred import
147            final String excludeMinusDotStar =
148                exclude.substring(0, exclude.length() - 2);
149            if (classOrStaticMember.startsWith(excludeMinusDotStar)
150                    && !classOrStaticMember.equals(excludeMinusDotStar)) {
151                final String member = classOrStaticMember.substring(
152                        excludeMinusDotStar.length() + 1);
153                //if it contains a dot then it is not a member but a package
154                if (member.indexOf('.') == -1) {
155                    result = true;
156                }
157            }
158        }
159        return result;
160    }
161}