/** * */ package codemining.java.codeutils.binding; import static com.google.common.base.Preconditions.checkArgument; import java.io.File; import java.util.Collection; import java.util.Map.Entry; import java.util.Set; import java.util.Stack; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.ImportDeclaration; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.SimpleType; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.TypeDeclaration; import codemining.java.codedata.metrics.CyclomaticCalculator; import codemining.java.codeutils.MethodUtils; import codemining.java.codeutils.ProjectTypeInformation; import codemining.java.tokenizers.JavaTokenizer; import codemining.languagetools.ITokenizer; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; /** * Extract bindings (and features) for method delarations. * * @author Miltos Allamanis <[email protected]> * */ public class JavaMethodDeclarationBindingExtractor extends AbstractJavaNameBindingsExtractor { public static enum AvailableFeatures { ARGUMENTS, EXCEPTIONS, RETURN_TYPE, MODIFIERS, ANCESTRY, METHOD_TOPICS, IMPLEMENTOR_VOCABULARY, FIELDS, SIBLING_METHODS, CYCLOMATIC } private class MethodBindings extends ASTVisitor { Stack<String> className = new Stack<String>(); private String currentPackageName; /** * A map from the method name to the position. */ Multimap<String, ASTNode> methodNamePostions = HashMultimap.create(); @Override public void endVisit(final TypeDeclaration node) { className.pop(); super.endVisit(node); } /** * @param node * @return */ public boolean methodOverrides(final MethodDeclaration node) { final boolean hasAnnotation = MethodUtils .hasOverrideAnnotation(node); final boolean isOverride = pti.isMethodOverride(className.peek(), node); return hasAnnotation || isOverride; } @Override public boolean visit(final CompilationUnit node) { if (node.getPackage() != null) { currentPackageName = node.getPackage().getName() .getFullyQualifiedName(); } else { currentPackageName = ""; } return super.visit(node); } @Override public boolean visit(final ImportDeclaration node) { // Don't visit. It's boring return false; } @Override public boolean visit(final MethodDeclaration node) { if (node.isConstructor()) { return super.visit(node); } else if (!includeOverrides && methodOverrides(node)) { return super.visit(node); } final String name = node.getName().toString(); methodNamePostions.put(name, node.getName()); return super.visit(node); } @Override public boolean visit(final TypeDeclaration node) { if (className.isEmpty()) { className.push(currentPackageName + "." + node.getName().getIdentifier()); } else { className.push(className.peek() + "." + node.getName().getIdentifier()); } return super.visit(node); } } private final boolean includeOverrides; private final Set<AvailableFeatures> activeFeatures = Sets .newHashSet(AvailableFeatures.values()); private final ProjectTypeInformation pti; public JavaMethodDeclarationBindingExtractor() { super(new JavaTokenizer()); this.includeOverrides = true; pti = null; } public JavaMethodDeclarationBindingExtractor( final boolean includeOverrides, final File inputFolder) { super(new JavaTokenizer()); this.includeOverrides = includeOverrides; if (!includeOverrides) { pti = buildProjectTypeInformation(inputFolder); } else { pti = null; } } public JavaMethodDeclarationBindingExtractor(final ITokenizer tokenizer) { super(tokenizer); this.includeOverrides = true; pti = null; } public JavaMethodDeclarationBindingExtractor(final ITokenizer tokenizer, final boolean includeOverrides, final File inputFolder) { super(tokenizer); this.includeOverrides = includeOverrides; if (!includeOverrides) { pti = buildProjectTypeInformation(inputFolder); } else { pti = null; } } /** * Add argument-related features. * * @param md * @param features */ private void addArgumentFeatures(final MethodDeclaration md, final Set<String> features) { checkArgument(activeFeatures.contains(AvailableFeatures.ARGUMENTS)); features.add("nParams:" + md.parameters().size()); for (int i = 0; i < md.parameters().size(); i++) { final SingleVariableDeclaration varDecl = (SingleVariableDeclaration) md .parameters().get(i); features.add("param" + i + "Type:" + varDecl.getType().toString()); for (final String namepart : JavaFeatureExtractor .getNameParts(varDecl.getName().toString())) { features.add("paramName:" + namepart); } } if (md.isVarargs()) { features.add("isVarArg"); } } /** * Add exception related features. * * @param md * @param features */ private void addExceptionFeatures(final MethodDeclaration md, final Set<String> features) { checkArgument(activeFeatures.contains(AvailableFeatures.EXCEPTIONS)); for (final Object exception : md.thrownExceptionTypes()) { final SimpleType ex = (SimpleType) exception; features.add("thrownException:" + ex.toString()); } } /** * Add modifier-related features. * * @param md * @param features */ private void addModifierFeatures(final MethodDeclaration md, final Set<String> features) { checkArgument(activeFeatures.contains(AvailableFeatures.MODIFIERS)); JavaFeatureExtractor.addModifierFeatures(features, md.modifiers()); if (md.getBody() == null) { features.add("isInterfaceDeclaration"); } } private ProjectTypeInformation buildProjectTypeInformation( final File inputFolder) { final ProjectTypeInformation pti = new ProjectTypeInformation( inputFolder); pti.collect(); return pti; } @Override public Set<?> getAvailableFeatures() { return Sets.newHashSet(AvailableFeatures.values()); } @Override protected Set<String> getFeatures(final Set<ASTNode> boundNodes) { checkArgument(boundNodes.size() == 1); final ASTNode method = boundNodes.iterator().next().getParent(); final Set<String> features = Sets.newHashSet(); checkArgument(method instanceof MethodDeclaration); final MethodDeclaration md = (MethodDeclaration) method; if (activeFeatures.contains(AvailableFeatures.ARGUMENTS)) { addArgumentFeatures(md, features); } if (activeFeatures.contains(AvailableFeatures.EXCEPTIONS)) { addExceptionFeatures(md, features); } if (activeFeatures.contains(AvailableFeatures.RETURN_TYPE)) { features.add("returnType:" + md.getReturnType2()); } if (activeFeatures.contains(AvailableFeatures.MODIFIERS)) { addModifierFeatures(md, features); } if (activeFeatures.contains(AvailableFeatures.ANCESTRY)) { JavaFeatureExtractor.addAstAncestryFeatures(features, method); } if (activeFeatures.contains(AvailableFeatures.METHOD_TOPICS)) { JavaFeatureExtractor.addMethodTopicFeatures(md, features); } if (activeFeatures.contains(AvailableFeatures.IMPLEMENTOR_VOCABULARY)) { JavaFeatureExtractor.addImplementorVocab(method, features); } if (activeFeatures.contains(AvailableFeatures.FIELDS)) { JavaFeatureExtractor.addFields(method, features); } if (activeFeatures.contains(AvailableFeatures.SIBLING_METHODS)) { JavaFeatureExtractor.addSiblingMethodNames(md, features); } if (activeFeatures.contains(AvailableFeatures.CYCLOMATIC)) { features.add("cyclomatic:" + (int) (new CyclomaticCalculator() .getMetricForASTNode(method))); } return features; } @Override public Set<Set<ASTNode>> getNameBindings(final ASTNode node) { final MethodBindings mb = new MethodBindings(); node.accept(mb); final Set<Set<ASTNode>> nameBindings = Sets.newHashSet(); for (final Entry<String, ASTNode> entry : mb.methodNamePostions .entries()) { final Set<ASTNode> boundNodes = Sets.newIdentityHashSet(); boundNodes.add(entry.getValue()); nameBindings.add(boundNodes); } return nameBindings; } @Override public void setActiveFeatures(final Set<?> activeFeatures) { this.activeFeatures.clear(); this.activeFeatures .addAll((Collection<? extends AvailableFeatures>) activeFeatures); } }