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.*; 023 024 import org.sonar.java.signature.JvmJavaType; 025 import org.sonar.java.signature.MethodSignature; 026 import org.sonar.java.signature.MethodSignaturePrinter; 027 import org.sonar.java.signature.Parameter; 028 import org.sonar.squid.api.SourceMethod; 029 import org.sonar.squid.measures.Metric; 030 031 import antlr.collections.AST; 032 033 import com.puppycrawl.tools.checkstyle.api.DetailAST; 034 import com.puppycrawl.tools.checkstyle.api.TokenTypes; 035 036 public class MethodVisitor extends JavaAstVisitor { 037 038 private static final String CONSTRUCTOR = "<init>"; 039 040 public static final List<Integer> WANTED_TOKENS = Arrays.asList(TokenTypes.CTOR_DEF, TokenTypes.METHOD_DEF); 041 private static final Map<Integer, JvmJavaType> TOKEN_JAVA_TYPE_MAPPING = new HashMap<Integer, JvmJavaType>(); 042 043 static { 044 TOKEN_JAVA_TYPE_MAPPING.put(TokenTypes.LITERAL_BYTE, JvmJavaType.B); 045 TOKEN_JAVA_TYPE_MAPPING.put(TokenTypes.LITERAL_CHAR, JvmJavaType.C); 046 TOKEN_JAVA_TYPE_MAPPING.put(TokenTypes.LITERAL_SHORT, JvmJavaType.S); 047 TOKEN_JAVA_TYPE_MAPPING.put(TokenTypes.LITERAL_INT, JvmJavaType.I); 048 TOKEN_JAVA_TYPE_MAPPING.put(TokenTypes.LITERAL_LONG, JvmJavaType.J); 049 TOKEN_JAVA_TYPE_MAPPING.put(TokenTypes.LITERAL_BOOLEAN, JvmJavaType.Z); 050 TOKEN_JAVA_TYPE_MAPPING.put(TokenTypes.LITERAL_FLOAT, JvmJavaType.F); 051 TOKEN_JAVA_TYPE_MAPPING.put(TokenTypes.LITERAL_DOUBLE, JvmJavaType.D); 052 TOKEN_JAVA_TYPE_MAPPING.put(TokenTypes.LITERAL_VOID, JvmJavaType.V); 053 } 054 055 @Override 056 public List<Integer> getWantedTokens() { 057 return WANTED_TOKENS; 058 } 059 060 @Override 061 public void visitToken(DetailAST ast) { 062 boolean isConstructor = isConstructor(ast); 063 String methodName = buildMethodSignature(ast, isConstructor); 064 SourceMethod sourceMethod = new SourceMethod(peekParentClass(), methodName, ast.getLineNo()); 065 sourceMethod.setMeasure(Metric.METHODS, 1); 066 if (isConstructor) { 067 sourceMethod.setMeasure(Metric.CONSTRUCTORS, 1); 068 } 069 sourceMethod.setSuppressWarnings(SuppressWarningsAnnotationUtils.isSuppressAllWarnings(ast)); 070 addSourceCode(sourceMethod); 071 } 072 073 private boolean isConstructor(DetailAST ast) { 074 return ast.getType() == TokenTypes.CTOR_DEF; 075 } 076 077 @Override 078 public void leaveToken(DetailAST ast) { 079 popSourceCode(); 080 } 081 082 private String buildMethodSignature(DetailAST ast, boolean isConstructor) { 083 String methodName = extractMethodName(ast, isConstructor); 084 Parameter returnType = extractMethodReturnType(ast, isConstructor); 085 List<Parameter> argumentTypes = extractMethodArgumentTypes(ast); 086 MethodSignature signature = new MethodSignature(methodName, returnType, argumentTypes); 087 return MethodSignaturePrinter.print(signature); 088 } 089 090 private List<Parameter> extractMethodArgumentTypes(DetailAST ast) { 091 List<Parameter> argumentTypes = new ArrayList<Parameter>(); 092 DetailAST child = ast.findFirstToken(TokenTypes.PARAMETERS).findFirstToken(TokenTypes.PARAMETER_DEF); 093 while (child != null) { 094 if (child.getType() == TokenTypes.PARAMETER_DEF) { 095 Parameter argumentType = extractArgumentAndReturnType(child.findFirstToken(TokenTypes.TYPE)); 096 argumentTypes.add(new Parameter(argumentType)); 097 } 098 child = (DetailAST) child.getNextSibling(); 099 } 100 return argumentTypes; 101 } 102 103 private String extractMethodName(DetailAST ast, boolean isConstructor) { 104 if (isConstructor) { 105 return CONSTRUCTOR; 106 } 107 return ast.findFirstToken(TokenTypes.IDENT).getText(); 108 } 109 110 private Parameter extractMethodReturnType(DetailAST ast, boolean isConstructor) { 111 if (isConstructor) { 112 return new Parameter(JvmJavaType.V, false); 113 } 114 Parameter returnType = extractArgumentAndReturnType(ast.findFirstToken(TokenTypes.TYPE)); 115 return new Parameter(returnType); 116 } 117 118 private Parameter extractArgumentAndReturnType(DetailAST ast) { 119 boolean isArray = isArrayType(ast); 120 for (Integer tokenType : TOKEN_JAVA_TYPE_MAPPING.keySet()) { 121 if (ast.branchContains(tokenType)) { 122 return new Parameter(TOKEN_JAVA_TYPE_MAPPING.get(tokenType), isArray); 123 } 124 } 125 if (isObjectType(ast)) { 126 String className; 127 if (isArray) { 128 className = extractClassName(drilldownToLastArray(ast)); 129 } else { 130 className = extractClassName(ast); 131 } 132 return new Parameter(className, isArray); 133 } 134 throw new IllegalStateException("No Known TokenType has been found at line " + ast.getLineNo() + " of file " 135 + getFileContents().getFilename()); 136 } 137 138 private DetailAST drilldownToLastArray(DetailAST ast) { 139 while (ast.findFirstToken(TokenTypes.ARRAY_DECLARATOR) != null) { 140 ast = ast.findFirstToken(TokenTypes.ARRAY_DECLARATOR); 141 } 142 return ast; 143 } 144 145 private String extractClassName(DetailAST ast) { 146 if (ast.findFirstToken(TokenTypes.DOT) != null) { 147 return findLastToken(ast.findFirstToken(TokenTypes.DOT), TokenTypes.IDENT).getText(); 148 } else { 149 return findLastToken(ast, TokenTypes.IDENT).getText(); 150 } 151 } 152 153 private DetailAST findLastToken(DetailAST astNode, int tokenType) { 154 DetailAST retVal = null; 155 for (AST child = astNode.getFirstChild(); child != null; child = child.getNextSibling()) { 156 if (child.getType() == tokenType) { 157 retVal = (DetailAST) child; 158 } 159 } 160 return retVal; 161 } 162 163 private boolean isObjectType(DetailAST ast) { 164 return ast.branchContains(TokenTypes.IDENT); 165 } 166 167 private boolean isArrayType(DetailAST ast) { 168 return (ast.findFirstToken(TokenTypes.IDENT) == null) && (ast.branchContains(TokenTypes.ARRAY_DECLARATOR)); 169 } 170 }