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.metrics; 020 021import com.puppycrawl.tools.checkstyle.api.Check; 022import com.puppycrawl.tools.checkstyle.api.DetailAST; 023import com.puppycrawl.tools.checkstyle.api.FastStack; 024import com.puppycrawl.tools.checkstyle.api.TokenTypes; 025import com.puppycrawl.tools.checkstyle.checks.CheckUtils; 026 027/** 028 * Restricts nested boolean operators (&&, ||, &, | and ^) to 029 * a specified depth (default = 3). 030 * 031 * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a> 032 * @author o_sukhodolsky 033 */ 034public final class BooleanExpressionComplexityCheck extends Check 035{ 036 /** Default allowed complexity. */ 037 private static final int DEFAULT_MAX = 3; 038 039 /** Stack of contexts. */ 040 private final FastStack<Context> contextStack = FastStack.newInstance(); 041 /** Maximum allowed complexity. */ 042 private int max; 043 /** Current context. */ 044 private Context context; 045 046 /** Creates new instance of the check. */ 047 public BooleanExpressionComplexityCheck() 048 { 049 setMax(DEFAULT_MAX); 050 } 051 052 @Override 053 public int[] getDefaultTokens() 054 { 055 return new int[] { 056 TokenTypes.CTOR_DEF, 057 TokenTypes.METHOD_DEF, 058 TokenTypes.EXPR, 059 TokenTypes.LAND, 060 TokenTypes.BAND, 061 TokenTypes.LOR, 062 TokenTypes.BOR, 063 TokenTypes.BXOR, 064 }; 065 } 066 067 @Override 068 public int[] getRequiredTokens() 069 { 070 return new int[] { 071 TokenTypes.CTOR_DEF, 072 TokenTypes.METHOD_DEF, 073 TokenTypes.EXPR, 074 }; 075 } 076 077 /** 078 * Getter for maximum allowed complexity. 079 * @return value of maximum allowed complexity. 080 */ 081 public int getMax() 082 { 083 return max; 084 } 085 086 /** 087 * Setter for maximum allowed complexity. 088 * @param max new maximum allowed complexity. 089 */ 090 public void setMax(int max) 091 { 092 this.max = max; 093 } 094 095 @Override 096 public void visitToken(DetailAST ast) 097 { 098 switch (ast.getType()) { 099 case TokenTypes.CTOR_DEF: 100 case TokenTypes.METHOD_DEF: 101 visitMethodDef(ast); 102 break; 103 case TokenTypes.EXPR: 104 visitExpr(); 105 break; 106 case TokenTypes.LAND: 107 case TokenTypes.BAND: 108 case TokenTypes.LOR: 109 case TokenTypes.BOR: 110 case TokenTypes.BXOR: 111 context.visitBooleanOperator(); 112 break; 113 default: 114 throw new IllegalStateException(ast.toString()); 115 } 116 } 117 118 @Override 119 public void leaveToken(DetailAST ast) 120 { 121 switch (ast.getType()) { 122 case TokenTypes.CTOR_DEF: 123 case TokenTypes.METHOD_DEF: 124 leaveMethodDef(); 125 break; 126 case TokenTypes.EXPR: 127 leaveExpr(ast); 128 break; 129 default: 130 // Do nothing 131 } 132 } 133 134 /** 135 * Creates new context for a given method. 136 * @param ast a method we start to check. 137 */ 138 private void visitMethodDef(DetailAST ast) 139 { 140 contextStack.push(context); 141 context = new Context(!CheckUtils.isEqualsMethod(ast)); 142 } 143 144 /** Removes old context. */ 145 private void leaveMethodDef() 146 { 147 context = contextStack.pop(); 148 } 149 150 /** Creates and pushes new context. */ 151 private void visitExpr() 152 { 153 contextStack.push(context); 154 context = new Context((context == null) || context.isChecking()); 155 } 156 157 /** 158 * Restores previous context. 159 * @param ast expression we leave. 160 */ 161 private void leaveExpr(DetailAST ast) 162 { 163 context.checkCount(ast); 164 context = contextStack.pop(); 165 } 166 167 /** 168 * Represents context (method/expression) in which we check complexity. 169 * 170 * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a> 171 * @author o_sukhodolsky 172 */ 173 private class Context 174 { 175 /** 176 * Should we perform check in current context or not. 177 * Usually false if we are inside equals() method. 178 */ 179 private final boolean checking; 180 /** Count of boolean operators. */ 181 private int count; 182 183 /** 184 * Creates new instance. 185 * @param checking should we check in current context or not. 186 */ 187 public Context(boolean checking) 188 { 189 this.checking = checking; 190 count = 0; 191 } 192 193 /** 194 * Getter for checking property. 195 * @return should we check in current context or not. 196 */ 197 public boolean isChecking() 198 { 199 return checking; 200 } 201 202 /** Increases operator counter. */ 203 public void visitBooleanOperator() 204 { 205 ++count; 206 } 207 208 /** 209 * Checks if we violates maximum allowed complexity. 210 * @param ast a node we check now. 211 */ 212 public void checkCount(DetailAST ast) 213 { 214 if (checking && (count > getMax())) { 215 final DetailAST parentAST = ast.getParent(); 216 217 log(parentAST.getLineNo(), parentAST.getColumnNo(), 218 "booleanExpressionComplexity", count, getMax()); 219 } 220 } 221 } 222}