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