NoMainMethodInAbstractClassCheck.java
- ////////////////////////////////////////////////////////////////////////////////
- // checkstyle: Checks Java source code for adherence to a set of rules.
- // Copyright (C) 2001-2019 the original author or authors.
- //
- // This library is free software; you can redistribute it and/or
- // modify it under the terms of the GNU Lesser General Public
- // License as published by the Free Software Foundation; either
- // version 2.1 of the License, or (at your option) any later version.
- //
- // This library is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- // Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public
- // License along with this library; if not, write to the Free Software
- // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- ////////////////////////////////////////////////////////////////////////////////
- package com.github.sevntu.checkstyle.checks.design;
- import java.util.Deque;
- import java.util.LinkedList;
- import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
- import com.puppycrawl.tools.checkstyle.api.DetailAST;
- import com.puppycrawl.tools.checkstyle.api.TokenTypes;
- /**
- * Forbids main methods in abstract classes. Existence of 'main' method can
- * mislead a developer to consider this class as a ready-to-use implementation.
- * @author Baratali Izmailov <a href="mailto:barataliba@gmail.com">email</a>
- * @since 1.9.0
- */
- public class NoMainMethodInAbstractClassCheck extends AbstractCheck {
- /**
- * Key for error message.
- */
- public static final String MSG_KEY = "avoid.main.method.in.abstract.class";
- /** String representation of string class. */
- private static final String STRING_CLASS = "String";
- /**
- * Keep OBJBLOCKs of classes that are under validation.
- */
- private final Deque<DetailAST> objBlockTokensStack =
- new LinkedList<>();
- @Override
- public final int[] getDefaultTokens() {
- return new int[] {
- TokenTypes.CLASS_DEF,
- TokenTypes.METHOD_DEF,
- };
- }
- @Override
- public int[] getAcceptableTokens() {
- return getDefaultTokens();
- }
- @Override
- public int[] getRequiredTokens() {
- return getDefaultTokens();
- }
- @Override
- public final void visitToken(final DetailAST ast) {
- if (ast.getType() == TokenTypes.CLASS_DEF) {
- if (isNotInnerClass(ast)) {
- // remove all tokens from stack
- objBlockTokensStack.clear();
- }
- if (hasAbstractModifier(ast)) {
- objBlockTokensStack.push(
- ast.findFirstToken(TokenTypes.OBJBLOCK));
- }
- }
- // type of token is METHOD_DEF
- else if (isChildOfCurrentObjBlockToken(ast) && isMainMethod(ast)) {
- log(ast, MSG_KEY);
- // remove current objblock
- objBlockTokensStack.pop();
- }
- }
- /**
- * Verify that class is not inner.
- * @param classDefAST
- * DetailAST of class definition.
- * @return true if class is not inner, false otherwise.
- */
- private boolean isNotInnerClass(final DetailAST classDefAST) {
- boolean result = true;
- final DetailAST objBlockAST = classDefAST.getParent();
- for (DetailAST currentObjBlock : objBlockTokensStack) {
- if (objBlockAST == currentObjBlock) {
- result = false;
- break;
- }
- }
- return result;
- }
- /**
- * Verify that aMethodDefAST is child token of considered objblock.
- * @param methodDefAST DetailAST of method definition.
- * @return true if aMethodDefAST is child of of considered objblock.
- */
- private boolean isChildOfCurrentObjBlockToken(final DetailAST methodDefAST) {
- final DetailAST objBlockAST = objBlockTokensStack.peek();
- return objBlockAST != null
- && methodDefAST.getParent() == objBlockAST;
- }
- /**
- * Return true if AST has abstract modifier.
- * @param classDefAST
- * AST which has modifier
- * @return true if AST has abstract modifier, false otherwise.
- */
- private static boolean hasAbstractModifier(final DetailAST classDefAST) {
- final DetailAST modifiers =
- classDefAST.findFirstToken(TokenTypes.MODIFIERS);
- return hasChildToken(modifiers, TokenTypes.ABSTRACT);
- }
- /**
- * Verifies that the given DetailAST is a main method.
- * @param methodAST
- * DetailAST instance.
- * @return true if aMethodAST is a main method, false otherwise.
- */
- private static boolean isMainMethod(final DetailAST methodAST) {
- boolean result = true;
- final String methodName = getIdentifier(methodAST);
- if ("main".equals(methodName)) {
- result = isVoidType(methodAST)
- && isMainMethodModifiers(methodAST)
- && isMainMethodParameters(methodAST);
- }
- else {
- result = false;
- }
- return result;
- }
- /**
- * Get identifier of AST. These can be names of types, subpackages,
- * fields, methods, parameters, and local variables.
- * @param ast
- * DetailAST instance
- * @return identifier of AST, null if AST does not have name.
- */
- private static String getIdentifier(final DetailAST ast) {
- final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
- return ident.getText();
- }
- /**
- * Verifies that given AST has appropriate modifiers for main method.
- * @param methodAST
- * DetailAST instance.
- * @return true if aMethodAST has (public & static & !abstract) modifiers,
- * false otherwise.
- */
- private static boolean isMainMethodModifiers(final DetailAST methodAST) {
- final DetailAST modifiers =
- methodAST.findFirstToken(TokenTypes.MODIFIERS);
- return hasChildToken(modifiers, TokenTypes.LITERAL_PUBLIC)
- && hasChildToken(modifiers, TokenTypes.LITERAL_STATIC);
- }
- /**
- * Verifies that given AST has type and this type is void.
- * @param methodAST
- * DetailAST instance.
- * @return true if AST's type void, false otherwise.
- */
- private static boolean isVoidType(final DetailAST methodAST) {
- final DetailAST methodTypeAST = methodAST.findFirstToken(TokenTypes.TYPE);
- return hasChildToken(methodTypeAST, TokenTypes.LITERAL_VOID);
- }
- /**
- * Verifies that given AST has appropriate for main method parameters.
- * @param methodAST
- * instance of a method
- * @return true if parameters of aMethodAST are appropriate for main method,
- * false otherwise.
- */
- private static boolean isMainMethodParameters(final DetailAST methodAST) {
- final DetailAST params =
- methodAST.findFirstToken(TokenTypes.PARAMETERS);
- return hasOnlyStringArrayParameter(params)
- || hasOnlyStringEllipsisParameter(params);
- }
- /**
- * Return true if AST of method parameters has String[] parameter child
- * token.
- * @param parametersAST
- * DetailAST of method parameters.
- * @return true if AST has String[] parameter child token, false otherwise.
- */
- private static boolean hasOnlyStringArrayParameter(final DetailAST parametersAST) {
- boolean result = true;
- if (parametersAST.getChildCount(TokenTypes.PARAMETER_DEF) == 1) {
- final DetailAST parameterDefinitionAST =
- parametersAST.findFirstToken(TokenTypes.PARAMETER_DEF);
- final DetailAST parameterTypeAST = parameterDefinitionAST
- .findFirstToken(TokenTypes.TYPE);
- if (hasChildToken(parameterTypeAST, TokenTypes.ARRAY_DECLARATOR)) {
- final DetailAST arrayDeclaratorAST = parameterTypeAST
- .findFirstToken(TokenTypes.ARRAY_DECLARATOR);
- final String parameterName =
- getIdentifier(arrayDeclaratorAST);
- result = STRING_CLASS.equals(parameterName);
- }
- else {
- result = false;
- }
- }
- else {
- // there is none or multiple parameters
- result = false;
- }
- return result;
- }
- /**
- * Return true if AST of method parameters has String... parameter child
- * token.
- * @param parametersAST
- * DetailAST of method parameters.
- * @return true if aParametersAST has String... parameter child token, false
- * otherwise.
- */
- private static boolean hasOnlyStringEllipsisParameter(final DetailAST parametersAST) {
- boolean result = true;
- if (parametersAST.getChildCount(TokenTypes.PARAMETER_DEF) == 1) {
- final DetailAST parameterDefinitionAST =
- parametersAST.findFirstToken(TokenTypes.PARAMETER_DEF);
- if (hasChildToken(parameterDefinitionAST, TokenTypes.ELLIPSIS)) {
- final DetailAST parameterTypeAST =
- parameterDefinitionAST.findFirstToken(TokenTypes.TYPE);
- final String parameterName =
- getIdentifier(parameterTypeAST);
- result = STRING_CLASS.equals(parameterName);
- }
- else {
- result = false;
- }
- }
- else {
- // there is none or multiple parameters
- result = false;
- }
- return result;
- }
- /**
- * Return true if aAST has token of aTokenType type.
- * @param ast
- * DetailAST instance.
- * @param tokenType
- * one of TokenTypes
- * @return true if aAST has token of given type, or false otherwise.
- */
- private static boolean hasChildToken(DetailAST ast, int tokenType) {
- return ast.findFirstToken(tokenType) != null;
- }
- }