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