001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2011 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.api.SourceCode; 026 import org.sonar.squid.api.SourceMethod; 027 import org.sonar.squid.measures.Metric; 028 029 import antlr.collections.AST; 030 031 import com.puppycrawl.tools.checkstyle.api.DetailAST; 032 import com.puppycrawl.tools.checkstyle.api.Scope; 033 import com.puppycrawl.tools.checkstyle.api.TokenTypes; 034 035 public class AccessorVisitor extends JavaAstVisitor { 036 037 private static final List<Integer> TOKENS = Arrays.asList(TokenTypes.METHOD_DEF); 038 039 @Override 040 public List<Integer> getWantedTokens() { 041 return TOKENS; 042 } 043 044 @Override 045 public void visitToken(DetailAST methodAst) { 046 SourceCode currentMethod = peekSourceCode(); 047 AstUtils.ensureResourceType(currentMethod, SourceMethod.class); 048 Scope methodScope = AstUtils.getScope(methodAst); 049 if (AstUtils.isScope(methodScope, Scope.PUBLIC) && isAccessor(methodAst, currentMethod.getName())) { 050 currentMethod.setMeasure(Metric.ACCESSORS, 1); 051 } 052 } 053 054 @Override 055 public void leaveToken(DetailAST ast) { 056 SourceMethod currentMethod = (SourceMethod) peekSourceCode(); 057 if (currentMethod.isAccessor()) { 058 currentMethod.setMeasure(Metric.PUBLIC_API, 0); 059 currentMethod.setMeasure(Metric.PUBLIC_DOC_API, 0); 060 currentMethod.setMeasure(Metric.METHODS, 0); 061 currentMethod.setMeasure(Metric.COMPLEXITY, 0); 062 } 063 } 064 065 private boolean isAccessor(DetailAST methodAst, String methodName) { 066 boolean methodWithoutParams = isMethodWithoutParameters(methodAst); 067 return isValidGetter(methodAst, methodName, methodWithoutParams) || isValidSetter(methodAst, methodName, methodWithoutParams) 068 || isValidBooleanGetter(methodAst, methodName, methodWithoutParams); 069 } 070 071 private boolean isMethodWithoutParameters(DetailAST methodAst) { 072 return methodAst.findFirstToken(TokenTypes.PARAMETERS).getChildCount() == 0; 073 } 074 075 private boolean isValidBooleanGetter(DetailAST method, String methodName, boolean methodWithoutParams) { 076 if (methodName.startsWith("is") && methodWithoutParams) { 077 AST type = AstUtils.findType(method); 078 if (isAstType(type, TokenTypes.LITERAL_BOOLEAN)) { 079 return isValidGetter(method, methodName.replace("is", "get"), methodWithoutParams); 080 } 081 } 082 return false; 083 } 084 085 private boolean isValidSetter(DetailAST method, String methodName, boolean methodWithoutParams) { 086 if (methodName.startsWith("set") && !methodWithoutParams) { 087 AST type = AstUtils.findType(method); 088 if (isVoidMethodReturn(type)) { 089 DetailAST methodParams = method.findFirstToken(TokenTypes.PARAMETERS); 090 if (methodParams.getChildCount(TokenTypes.PARAMETER_DEF) == 1) { 091 DetailAST methodBody = method.getLastChild(); 092 if (isAstType(methodBody, TokenTypes.SLIST) && methodBody.getChildCount() == 3) { 093 return inspectSetterMethodBody(method, methodParams, methodBody); 094 } 095 } 096 } 097 } 098 return false; 099 } 100 101 private boolean isValidGetter(DetailAST method, String methodName, boolean methodWithoutParams) { 102 if (methodName.startsWith("get") && methodWithoutParams) { 103 AST type = AstUtils.findType(method); 104 if (!isVoidMethodReturn(type)) { 105 DetailAST methodBody = method.getLastChild(); 106 if (isAstType(methodBody, TokenTypes.SLIST) && methodBody.getChildCount() == 2) { 107 return inspectGetterMethodBody(method, methodBody); 108 } 109 } 110 } 111 return false; 112 } 113 114 private boolean inspectGetterMethodBody(DetailAST method, DetailAST methodBody) { 115 AST returnAST = methodBody.getFirstChild(); 116 if (isAstType(returnAST, TokenTypes.LITERAL_RETURN)) { 117 AST expr = returnAST.getFirstChild(); 118 if (isAstType(expr, TokenTypes.EXPR) && isAstType(expr.getNextSibling(), TokenTypes.SEMI)) { 119 AST varReturned = expr.getFirstChild(); 120 if (isAstType(varReturned, TokenTypes.IDENT)) { 121 return findPrivateClassVariable(method, varReturned.getText()); 122 } 123 } 124 } 125 return false; 126 } 127 128 private boolean inspectSetterMethodBody(DetailAST method, DetailAST methodParams, DetailAST methodBody) { 129 DetailAST expr = (DetailAST) methodBody.getFirstChild(); 130 if (isAstType(expr, TokenTypes.EXPR)) { 131 DetailAST assignment = expr.findFirstToken(TokenTypes.ASSIGN); 132 if (assignment != null) { 133 DetailAST dotAst = assignment.findFirstToken(TokenTypes.DOT); 134 DetailAST varAssigned = assignment.getLastChild(); 135 DetailAST varAssignedMethodParam = methodParams.findFirstToken(TokenTypes.PARAMETER_DEF).findFirstToken(TokenTypes.IDENT); 136 // check that the var assigned is the var from the method param 137 if (isAstType(varAssigned, TokenTypes.IDENT) && varAssigned.getText().equals(varAssignedMethodParam.getText())) { 138 DetailAST varToAssign = dotAst != null ? dotAst.findFirstToken(TokenTypes.IDENT) : assignment.findFirstToken(TokenTypes.IDENT); 139 return findPrivateClassVariable(method, varToAssign.getText()); 140 } 141 } 142 } 143 return false; 144 } 145 146 private boolean findPrivateClassVariable(DetailAST method, String varName) { 147 DetailAST objBlock = AstUtils.findParent(method, TokenTypes.OBJBLOCK); 148 for (AST i = objBlock.getFirstChild(); i != null; i = i.getNextSibling()) { 149 if (isAstType(i, TokenTypes.VARIABLE_DEF)) { 150 DetailAST varDef = (DetailAST) i; 151 if (AstUtils.isScope(AstUtils.getScope(varDef), Scope.PRIVATE) 152 && varDef.findFirstToken(TokenTypes.IDENT).getText().equals(varName)) { 153 return true; 154 } 155 } 156 } 157 return false; 158 } 159 160 private boolean isVoidMethodReturn(AST type) { 161 return isAstType(type, TokenTypes.LITERAL_VOID); 162 } 163 164 private boolean isAstType(AST ast, int type) { 165 return ast.getType() == type; 166 } 167 168 }