001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2014 Oliver Burn 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//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle.checks.modifier; 020 021import java.util.ArrayList; 022import java.util.List; 023 024import com.puppycrawl.tools.checkstyle.api.Check; 025import com.puppycrawl.tools.checkstyle.api.DetailAST; 026import com.puppycrawl.tools.checkstyle.api.TokenTypes; 027 028/** 029 * Checks for redundant modifiers in interface and annotation definitions. 030 * Also checks for redundant final modifiers on methods of final classes. 031 * 032 * @author lkuehne 033 */ 034public class RedundantModifierCheck 035 extends Check 036{ 037 @Override 038 public int[] getDefaultTokens() 039 { 040 return new int[] { 041 TokenTypes.METHOD_DEF, 042 TokenTypes.VARIABLE_DEF, 043 TokenTypes.ANNOTATION_FIELD_DEF, 044 TokenTypes.INTERFACE_DEF, 045 }; 046 } 047 048 @Override 049 public int[] getRequiredTokens() 050 { 051 return new int[] {}; 052 } 053 054 @Override 055 public void visitToken(DetailAST ast) 056 { 057 if (TokenTypes.INTERFACE_DEF == ast.getType()) { 058 final DetailAST modifiers = 059 ast.findFirstToken(TokenTypes.MODIFIERS); 060 if (null != modifiers) { 061 for (final int tokenType : new int[] { 062 TokenTypes.LITERAL_STATIC, 063 TokenTypes.ABSTRACT, }) 064 { 065 final DetailAST modifier = 066 modifiers.findFirstToken(tokenType); 067 if (null != modifier) { 068 log(modifier.getLineNo(), modifier.getColumnNo(), 069 "redundantModifier", modifier.getText()); 070 } 071 } 072 } 073 } 074 else if (isInterfaceOrAnnotationMember(ast)) { 075 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS); 076 DetailAST modifier = modifiers.getFirstChild(); 077 while (modifier != null) { 078 079 // javac does not allow final or static in interface methods 080 // order annotation fields hence no need to check that this 081 // is not a method or annotation field 082 083 final int type = modifier.getType(); 084 if ((type == TokenTypes.LITERAL_PUBLIC) 085 || ((type == TokenTypes.LITERAL_STATIC) 086 && ast.getType() != TokenTypes.METHOD_DEF) 087 || (type == TokenTypes.ABSTRACT) 088 || (type == TokenTypes.FINAL)) 089 { 090 log(modifier.getLineNo(), modifier.getColumnNo(), 091 "redundantModifier", modifier.getText()); 092 break; 093 } 094 095 modifier = modifier.getNextSibling(); 096 } 097 } 098 else if (ast.getType() == TokenTypes.METHOD_DEF) { 099 final DetailAST modifiers = 100 ast.findFirstToken(TokenTypes.MODIFIERS); 101 // private method? 102 boolean checkFinal = 103 modifiers.branchContains(TokenTypes.LITERAL_PRIVATE); 104 // declared in a final class? 105 DetailAST parent = ast.getParent(); 106 while (parent != null) { 107 if (parent.getType() == TokenTypes.CLASS_DEF) { 108 final DetailAST classModifiers = 109 parent.findFirstToken(TokenTypes.MODIFIERS); 110 checkFinal |= 111 classModifiers.branchContains(TokenTypes.FINAL); 112 break; 113 } 114 parent = parent.getParent(); 115 } 116 if (checkFinal && !isAnnotatedWithSafeVarargs(ast)) { 117 DetailAST modifier = modifiers.getFirstChild(); 118 while (modifier != null) { 119 final int type = modifier.getType(); 120 if (type == TokenTypes.FINAL) { 121 log(modifier.getLineNo(), modifier.getColumnNo(), 122 "redundantModifier", modifier.getText()); 123 break; 124 } 125 modifier = modifier.getNextSibling(); 126 } 127 } 128 } 129 } 130 131 /** 132 * Checks if current AST node is member of Interface or Annotation, not of their subnodes. 133 * @param ast AST node 134 * @return true or false 135 */ 136 private static boolean isInterfaceOrAnnotationMember(DetailAST ast) 137 { 138 final DetailAST parentTypeDef = ast.getParent().getParent(); 139 return parentTypeDef.getType() == TokenTypes.INTERFACE_DEF 140 || parentTypeDef.getType() == TokenTypes.ANNOTATION_DEF; 141 } 142 143 /** 144 * Checks if method definition is annotated with 145 * <a href="https://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html"> 146 * SafeVarargs</a> annotation 147 * @param methodDef method definition node 148 * @return true or false 149 */ 150 private static boolean isAnnotatedWithSafeVarargs(DetailAST methodDef) 151 { 152 boolean result = false; 153 final List<DetailAST> methodAnnotationsList = getMethodAnnotationsList(methodDef); 154 for (DetailAST annotationNode : methodAnnotationsList) { 155 if ("SafeVarargs".equals(annotationNode.getLastChild().getText())) { 156 result = true; 157 break; 158 } 159 } 160 return result; 161 } 162 163 /** 164 * Gets the list of annotations on method definition 165 * @param methodDef method definition node 166 * @return List of annotations 167 */ 168 private static List<DetailAST> getMethodAnnotationsList(DetailAST methodDef) 169 { 170 final List<DetailAST> annotationsList = new ArrayList<>(); 171 final DetailAST modifiers = methodDef.findFirstToken(TokenTypes.MODIFIERS); 172 DetailAST modifier = modifiers.getFirstChild(); 173 while (modifier != null) { 174 if (modifier.getType() == TokenTypes.ANNOTATION) { 175 annotationsList.add(modifier); 176 } 177 modifier = modifier.getNextSibling(); 178 } 179 return annotationsList; 180 } 181}