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; 020 021import java.util.Set; 022 023import com.google.common.collect.ImmutableSet; 024import com.puppycrawl.tools.checkstyle.api.Check; 025import com.puppycrawl.tools.checkstyle.api.DetailAST; 026import com.puppycrawl.tools.checkstyle.api.TokenTypes; 027 028/** 029 * Check that method/constructor/catch/foreach parameters are final. 030 * The user can set the token set to METHOD_DEF, CONSTRUCTOR_DEF, 031 * LITERAL_CATCH, FOR_EACH_CLAUSE or any combination of these token 032 * types, to control the scope of this check. 033 * Default scope is both METHOD_DEF and CONSTRUCTOR_DEF. 034 * <p> 035 * Check has an option <b>ignorePrimitiveTypes</b> which allows ignoring lack of 036 * final modifier at 037 * <a href="http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html"> 038 * primitive datatype</a> parameter. Default value <b>false</b>. 039 * </p> 040 * E.g.: 041 * <p> 042 * <code> 043 * private void foo(int x) { ... } //parameter is of primitive type 044 * </code> 045 * </p> 046 * 047 * @author lkuehne 048 * @author o_sukhodolsky 049 * @author Michael Studman 050 * @author <a href="mailto:nesterenko-aleksey@list.ru">Aleksey Nesterenko</a> 051 */ 052public class FinalParametersCheck extends Check 053{ 054 /** 055 * Option to ignore primitive types as params. 056 */ 057 private boolean ignorePrimitiveTypes; 058 059 /** 060 * Sets ignoring primitive types as params. 061 * @param ignorePrimitiveTypes true or false. 062 */ 063 public void setIgnorePrimitiveTypes(boolean ignorePrimitiveTypes) 064 { 065 this.ignorePrimitiveTypes = ignorePrimitiveTypes; 066 } 067 068 /** 069 * Contains 070 * <a href="http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html"> 071 * primitive datatypes</a>. 072 */ 073 private final Set<Integer> primitiveDataTypes = ImmutableSet.of( 074 TokenTypes.LITERAL_BYTE, 075 TokenTypes.LITERAL_SHORT, 076 TokenTypes.LITERAL_INT, 077 TokenTypes.LITERAL_LONG, 078 TokenTypes.LITERAL_FLOAT, 079 TokenTypes.LITERAL_DOUBLE, 080 TokenTypes.LITERAL_BOOLEAN, 081 TokenTypes.LITERAL_CHAR); 082 083 @Override 084 public int[] getDefaultTokens() 085 { 086 return new int[] { 087 TokenTypes.METHOD_DEF, 088 TokenTypes.CTOR_DEF, 089 }; 090 } 091 092 @Override 093 public int[] getAcceptableTokens() 094 { 095 return new int[] { 096 TokenTypes.METHOD_DEF, 097 TokenTypes.CTOR_DEF, 098 TokenTypes.LITERAL_CATCH, 099 TokenTypes.FOR_EACH_CLAUSE, 100 }; 101 } 102 103 @Override 104 public void visitToken(DetailAST ast) 105 { 106 // don't flag interfaces 107 final DetailAST container = ast.getParent().getParent(); 108 if (container.getType() == TokenTypes.INTERFACE_DEF) { 109 return; 110 } 111 112 if (ast.getType() == TokenTypes.LITERAL_CATCH) { 113 visitCatch(ast); 114 } 115 else if (ast.getType() == TokenTypes.FOR_EACH_CLAUSE) { 116 visitForEachClause(ast); 117 } 118 else { 119 visitMethod(ast); 120 } 121 } 122 123 /** 124 * Checks parameters of the method or ctor. 125 * @param method method or ctor to check. 126 */ 127 private void visitMethod(final DetailAST method) 128 { 129 // exit on fast lane if there is nothing to check here 130 if (!method.branchContains(TokenTypes.PARAMETER_DEF)) { 131 return; 132 } 133 134 // ignore abstract method 135 final DetailAST modifiers = 136 method.findFirstToken(TokenTypes.MODIFIERS); 137 if (modifiers.branchContains(TokenTypes.ABSTRACT)) { 138 return; 139 } 140 141 // we can now be sure that there is at least one parameter 142 final DetailAST parameters = 143 method.findFirstToken(TokenTypes.PARAMETERS); 144 DetailAST child = parameters.getFirstChild(); 145 while (child != null) { 146 // childs are PARAMETER_DEF and COMMA 147 if (child.getType() == TokenTypes.PARAMETER_DEF) { 148 checkParam(child); 149 } 150 child = child.getNextSibling(); 151 } 152 } 153 154 /** 155 * Checks parameter of the catch block. 156 * @param catchClause catch block to check. 157 */ 158 private void visitCatch(final DetailAST catchClause) 159 { 160 checkParam(catchClause.findFirstToken(TokenTypes.PARAMETER_DEF)); 161 } 162 163 /** 164 * Checks parameter of the for each clause. 165 * @param forEachClause for each clause to check. 166 */ 167 private void visitForEachClause(final DetailAST forEachClause) 168 { 169 checkParam(forEachClause.findFirstToken(TokenTypes.VARIABLE_DEF)); 170 } 171 172 /** 173 * Checks if the given parameter is final. 174 * @param param parameter to check. 175 */ 176 private void checkParam(final DetailAST param) 177 { 178 if (!param.branchContains(TokenTypes.FINAL) && !isIgnoredParam(param)) { 179 final DetailAST paramName = param.findFirstToken(TokenTypes.IDENT); 180 final DetailAST firstNode = CheckUtils.getFirstNode(param); 181 log(firstNode.getLineNo(), firstNode.getColumnNo(), 182 "final.parameter", paramName.getText()); 183 } 184 } 185 186 /** 187 * Checks for skip current param due to <b>ignorePrimitiveTypes</b> option. 188 * @param paramDef {@link TokenTypes#PARAMETER_DEF PARAMETER_DEF} 189 * @return true if param has to be skipped. 190 */ 191 private boolean isIgnoredParam(DetailAST paramDef) 192 { 193 boolean result = false; 194 if (ignorePrimitiveTypes) { 195 final DetailAST parameterType = paramDef. 196 findFirstToken(TokenTypes.TYPE).getFirstChild(); 197 if (primitiveDataTypes.contains(parameterType.getType())) { 198 result = true; 199 } 200 } 201 return result; 202 } 203}