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 }