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.*;
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 }