001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2009 SonarSource SA 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.squid.ast.visitor; 021 022 import java.util.Arrays; 023 import java.util.List; 024 025 import org.sonar.squid.measures.Metric; 026 027 import com.puppycrawl.tools.checkstyle.api.DetailAST; 028 import com.puppycrawl.tools.checkstyle.api.TokenTypes; 029 030 public class StatementVisitor extends AstVisitor { 031 032 private static final List<Integer> wantedTokens = Arrays.asList(TokenTypes.VARIABLE_DEF, TokenTypes.CTOR_CALL, TokenTypes.LITERAL_IF, 033 TokenTypes.LITERAL_ELSE, TokenTypes.LITERAL_WHILE, TokenTypes.LITERAL_DO, 034 TokenTypes.LITERAL_FOR, TokenTypes.LITERAL_SWITCH, TokenTypes.LITERAL_BREAK, 035 TokenTypes.LITERAL_CONTINUE, TokenTypes.LITERAL_RETURN, TokenTypes.LITERAL_THROW, 036 TokenTypes.LITERAL_SYNCHRONIZED, TokenTypes.LITERAL_CATCH, 037 TokenTypes.LITERAL_FINALLY, TokenTypes.EXPR, TokenTypes.LABELED_STAT, 038 TokenTypes.LITERAL_CASE, TokenTypes.LITERAL_DEFAULT, TokenTypes.LITERAL_ASSERT); 039 040 @Override 041 public List<Integer> getWantedTokens() { 042 return wantedTokens; 043 } 044 045 @Override 046 public void visitToken(DetailAST ast) { 047 if (isCountable(ast)) { 048 peekResource().add(Metric.STATEMENTS, 1); 049 } 050 } 051 052 /** 053 * Checks if a token is countable for the ncss metric 054 * 055 * @param ast 056 * the AST 057 * @return true if the token is countable 058 */ 059 private boolean isCountable(DetailAST ast) { 060 boolean countable = true; 061 final int tokenType = ast.getType(); 062 // check if an expression is countable 063 if (TokenTypes.EXPR == tokenType) { 064 countable = isExpressionCountable(ast); 065 } 066 // check if an variable definition is countable, only for non class 067 // variables 068 else if (TokenTypes.VARIABLE_DEF == tokenType) { 069 countable = isVariableDefCountable(ast); 070 } 071 return countable; 072 } 073 074 /** 075 * Checks if a variable definition is countable. 076 * 077 * @param aAST 078 * the AST 079 * @return true if the variable definition is countable, false otherwise 080 */ 081 private boolean isVariableDefCountable(DetailAST aAST) { 082 boolean countable = false; 083 if (!AstUtils.isClassVariable(aAST) && !AstUtils.isInterfaceVariable(aAST)) { 084 // count variable defs only if they are direct child to a slist or 085 // object block 086 final int parentType = aAST.getParent().getType(); 087 if ((TokenTypes.SLIST == parentType) || (TokenTypes.OBJBLOCK == parentType)) { 088 final DetailAST prevSibling = aAST.getPreviousSibling(); 089 // is countable if no previous sibling is found or 090 // the sibling is no COMMA. 091 // This is done because multiple assignment on one line are 092 // countes as 1 093 countable = (prevSibling == null) || (TokenTypes.COMMA != prevSibling.getType()); 094 } 095 } 096 return countable; 097 } 098 099 /** 100 * Checks if an expression is countable for the ncss metric. 101 * 102 * @param aAST 103 * the AST 104 * @return true if the expression is countable, false otherwise 105 */ 106 private boolean isExpressionCountable(DetailAST aAST) { 107 boolean countable; 108 // count expressions only if they are direct child to a slist (method 109 // body, for loop...) 110 // or direct child of label,if,else,do,while,for 111 final int parentType = aAST.getParent().getType(); 112 switch (parentType) { 113 case TokenTypes.SLIST: 114 case TokenTypes.LABELED_STAT: 115 case TokenTypes.LITERAL_FOR: 116 case TokenTypes.LITERAL_DO: 117 case TokenTypes.LITERAL_WHILE: 118 case TokenTypes.LITERAL_IF: 119 case TokenTypes.LITERAL_ELSE: 120 // don't count if or loop conditions 121 final DetailAST prevSibling = aAST.getPreviousSibling(); 122 countable = (prevSibling == null) || (TokenTypes.LPAREN != prevSibling.getType()); 123 break; 124 default: 125 countable = false; 126 break; 127 } 128 return countable; 129 } 130 }