001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
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.java.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 JavaAstVisitor {
031
032 private static final List<Integer> WANTED_TOKENS = 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 WANTED_TOKENS;
043 }
044
045 @Override
046 public void visitToken(DetailAST ast) {
047 if (isCountable(ast)) {
048 peekSourceCode().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 the AST
103 * @return true if the expression is countable, false otherwise
104 */
105 private boolean isExpressionCountable(DetailAST aAST) {
106 boolean countable;
107 // count expressions only if they are direct child to a slist (method
108 // body, for loop...)
109 // or direct child of label,if,else,do,while,for
110 final int parentType = aAST.getParent().getType();
111 switch (parentType) {
112 case TokenTypes.SLIST:
113 case TokenTypes.LABELED_STAT:
114 case TokenTypes.LITERAL_FOR:
115 case TokenTypes.LITERAL_DO:
116 case TokenTypes.LITERAL_WHILE:
117 case TokenTypes.LITERAL_IF:
118 case TokenTypes.LITERAL_ELSE:
119 // don't count if or loop conditions
120 final DetailAST prevSibling = aAST.getPreviousSibling();
121 countable = (prevSibling == null) || (TokenTypes.LPAREN != prevSibling.getType());
122 break;
123 default:
124 countable = false;
125 break;
126 }
127 return countable;
128 }
129 }