package com.feenk.jdt2famix.injava; import java.util.Arrays; import java.util.List; import java.util.function.Consumer; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration; import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; import org.eclipse.jdt.core.dom.Assignment; import org.eclipse.jdt.core.dom.CatchClause; import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.ConditionalExpression; import org.eclipse.jdt.core.dom.ConstructorInvocation; import org.eclipse.jdt.core.dom.DoStatement; import org.eclipse.jdt.core.dom.EnhancedForStatement; import org.eclipse.jdt.core.dom.EnumConstantDeclaration; import org.eclipse.jdt.core.dom.EnumDeclaration; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.FieldAccess; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.ForStatement; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.IfStatement; import org.eclipse.jdt.core.dom.InfixExpression; import org.eclipse.jdt.core.dom.InfixExpression.Operator; import org.eclipse.jdt.core.dom.Initializer; import org.eclipse.jdt.core.dom.MarkerAnnotation; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.NormalAnnotation; import org.eclipse.jdt.core.dom.ParenthesizedExpression; import org.eclipse.jdt.core.dom.ReturnStatement; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleMemberAnnotation; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.SuperConstructorInvocation; import org.eclipse.jdt.core.dom.SuperMethodInvocation; import org.eclipse.jdt.core.dom.SwitchCase; import org.eclipse.jdt.core.dom.SwitchStatement; import org.eclipse.jdt.core.dom.SynchronizedStatement; import org.eclipse.jdt.core.dom.ThrowStatement; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.VariableDeclarationStatement; import org.eclipse.jdt.core.dom.WhileStatement; import com.feenk.jdt2famix.model.famix.Access; import com.feenk.jdt2famix.model.famix.AnnotationInstance; import com.feenk.jdt2famix.model.famix.AnnotationType; import com.feenk.jdt2famix.model.famix.AnnotationTypeAttribute; import com.feenk.jdt2famix.model.famix.Attribute; import com.feenk.jdt2famix.model.famix.CaughtException; import com.feenk.jdt2famix.model.famix.Class; import com.feenk.jdt2famix.model.famix.Enum; import com.feenk.jdt2famix.model.famix.EnumValue; import com.feenk.jdt2famix.model.famix.Invocation; import com.feenk.jdt2famix.model.famix.Method; import com.feenk.jdt2famix.model.famix.NamedEntity; import com.feenk.jdt2famix.model.famix.Namespace; import com.feenk.jdt2famix.model.famix.ParameterizedType; import com.feenk.jdt2famix.model.famix.ThrownException; import com.feenk.jdt2famix.model.famix.Type; /** * Responsible for visiting the AST of one Java file. It works in close * relationship with the {@link InJavaImporter} which - provides ensure and * create methods, and - keeps track of the overall model. * * Each method that visits a container entity (e.g., Type, Method ...), - we * push the resulting Famix entity in the importer stack, and - we pop it in a * corresponding endVisit method * * @author girba * */ public class AstVisitor extends ASTVisitor { private InJavaImporter importer; public AstVisitor(InJavaImporter importer) { this.importer = importer; } public void logNullBinding(String string, Object extraData, int lineNumber) { importer.logNullBinding(string, extraData, lineNumber); } //////// PACKAGES @Override public boolean visit(CompilationUnit node) { Namespace namespace; if (node.getPackage() == null) /* This is the default package */ namespace = importer.ensureNamespaceNamed(""); else namespace = importer.ensureNamespaceFromPackageBinding(node.getPackage().resolveBinding()); namespace.setIsStub(false); importer.pushOnContainerStack(namespace); return true; } /** * Needed for keeping track of the current container */ @Override public void endVisit(CompilationUnit node) { importer.popFromContainerStack(); } //////// TYPES @SuppressWarnings("unchecked") @Override public boolean visit(TypeDeclaration node) { ITypeBinding binding = node.resolveBinding(); if (binding == null) { logNullBinding("type declaration", node.getName(), ((CompilationUnit) node.getRoot()).getLineNumber(node.getStartPosition())); return false; } Type type = importer.ensureTypeFromTypeBinding(binding); org.eclipse.jdt.core.dom.Type superclassType = node.getSuperclassType(); /* * This is an ugly patch. When the binding to the superclass or super interfaces * cannot be resolved, we try to recover as much info as possible We do it here * because it is hard to pass around the dom type */ if (binding.getSuperclass() == null && superclassType != null) importer.createInheritanceFromSubtypeToSuperDomType(type, superclassType); if (superclassType != null) type.getSuperInheritances().stream().filter(inheritance -> (inheritance.getSuperclass() instanceof Class && !((Class) inheritance.getSuperclass()).getIsInterface()) || (inheritance.getSuperclass() instanceof ParameterizedType && ((ParameterizedType) inheritance.getSuperclass()).getParameterizableClass() != null && !((ParameterizedType) inheritance.getSuperclass()).getParameterizableClass() .getIsInterface())) .findFirst().ifPresent(in -> importer.createLightweightSourceAnchor(in, superclassType)); if (binding.getInterfaces().length == 0 && !node.superInterfaceTypes().isEmpty()) node.superInterfaceTypes().stream().forEach(t -> { importer.createInheritanceFromSubtypeToSuperDomType(type, (org.eclipse.jdt.core.dom.Type) t); }); // create source anchors for implemented interfaces references createSourceAnchorsForInterfaceInheritance(node, type); type.setIsStub(false); importer.createSourceAnchor(type, node); importer.createLightweightSourceAnchor(type, node.getName()); importer.ensureCommentFromBodyDeclaration(type, node); importer.pushOnContainerStack(type); return true; } private void createSourceAnchorsForInterfaceInheritance(EnumDeclaration node, Type type) { if (!node.superInterfaceTypes().isEmpty()) node.superInterfaceTypes().stream().forEach(createSourceAnchorForInheritance(type)); } private void createSourceAnchorsForInterfaceInheritance(TypeDeclaration node, Type type) { if (!node.superInterfaceTypes().isEmpty()) node.superInterfaceTypes().stream().forEach(createSourceAnchorForInheritance(type)); } private Consumer createSourceAnchorForInheritance(Type type) { return t -> { org.eclipse.jdt.core.dom.Type currentInterface = (org.eclipse.jdt.core.dom.Type) t; type.getSuperInheritances().stream() .filter(superInheritance -> currentInterface.resolveBinding() != null && superInheritance .getSuperclass().getName().equals(currentInterface.resolveBinding().getName())) .findFirst().ifPresent(superInheritance -> importer.createLightweightSourceAnchor(superInheritance, currentInterface)); }; } @Override public void endVisit(TypeDeclaration node) { if (importer.topOfContainerStack() instanceof Type) importer.popFromContainerStack(); } @Override public boolean visit(AnonymousClassDeclaration node) { ITypeBinding binding = node.resolveBinding(); Type type; if (binding != null) type = importer.createTypeFromTypeBinding(binding); else { type = importer.createTypeNamedInUnknownNamespace(""); logNullBinding("anonymous type declaration", node.getParent().toString().replaceAll("\n", " "), ((CompilationUnit) node.getRoot()).getLineNumber(node.getStartPosition())); } importer.ensureTypeFromAnonymousDeclaration(type, node); type.setIsStub(false); importer.createSourceAnchor(type, node); importer.pushOnContainerStack(type); return true; } @Override public void endVisit(AnonymousClassDeclaration node) { importer.popFromContainerStack(); } @Override public boolean visit(EnumDeclaration node) { ITypeBinding binding = node.resolveBinding(); if (binding == null) { logNullBinding("enum declaration", node.getName(), ((CompilationUnit) node.getRoot()).getLineNumber(node.getStartPosition())); return false; } Type ensureTypeFromTypeBinding = importer.ensureTypeFromTypeBinding(binding); if (ensureTypeFromTypeBinding instanceof Enum) { Enum famixEnum = (Enum) ensureTypeFromTypeBinding; createSourceAnchorsForInterfaceInheritance(node, famixEnum); famixEnum.setIsStub(false); importer.createSourceAnchor(famixEnum, node); importer.ensureCommentFromBodyDeclaration(famixEnum, node); importer.pushOnContainerStack(famixEnum); } return true; } @Override public void endVisit(EnumDeclaration node) { if (importer.topOfContainerStack() instanceof Enum) importer.popFromContainerStack(); } @Override public boolean visit(EnumConstantDeclaration node) { if (!node.arguments().isEmpty()) importer.pushOnContainerStack(importer.ensureInitializerMethod()); EnumValue enumValue = importer.ensureEnumValueFromDeclaration(node); importer.createSourceAnchor(enumValue, node); importer.ensureCommentFromBodyDeclaration(enumValue, node); return true; } @Override public void endVisit(EnumConstantDeclaration node) { if (importer.topOfContainerStack().getName().equals(InJavaImporter.INITIALIZER_NAME)) importer.popFromContainerStack(); } //////// ANNOTATIONS @Override public boolean visit(AnnotationTypeDeclaration node) { ITypeBinding binding = node.resolveBinding(); if (binding == null) { logNullBinding("annotation type declaration", node.getName(), ((CompilationUnit) node.getRoot()).getLineNumber(node.getStartPosition())); return false; } Type type = importer.ensureTypeFromTypeBinding(binding); type.setIsStub(false); importer.createSourceAnchor(type, node); importer.pushOnContainerStack(type); importer.ensureCommentFromBodyDeclaration(type, node); return true; } @Override public void endVisit(AnnotationTypeDeclaration node) { if (importer.topOfContainerStack() instanceof AnnotationType) importer.popFromContainerStack(); } @Override public boolean visit(AnnotationTypeMemberDeclaration node) { AnnotationTypeAttribute attribute = importer.ensureAnnotationTypeAttributeFromDeclaration(node); attribute.setIsStub(false); importer.ensureCommentFromBodyDeclaration(attribute, node); return super.visit(node); } @Override public void endVisit(AnnotationTypeMemberDeclaration node) { super.endVisit(node); } /** * handles: @ TypeName We do not use this one because we want to tie the * creation of annotation instances with the ensuring of bindings (e.g., * {@link InJavaImporter#ensureTypeFromTypeBinding(ITypeBinding)}). Thus, we * prefer to call the annotation creation explicitly from the other visit * methods (e.g., {link {@link #visit(TypeDeclaration)} */ @Override public boolean visit(MarkerAnnotation node) { addTypeAnnotationSourceAnchor(node); return true; } /** * handles: @ TypeName ( [ MemberValuePair { , MemberValuePair } ] ) see comment * from {@link #visit(MarkerAnnotation)} */ @Override public boolean visit(NormalAnnotation node) { addTypeAnnotationSourceAnchor(node); return true; } private void addTypeAnnotationSourceAnchor(Annotation node) { ASTNode parent = node.getParent(); NamedEntity namedEntity = null; if ((parent instanceof AbstractTypeDeclaration) && (((AbstractTypeDeclaration) parent).resolveBinding() != null)) { namedEntity = importer.ensureTypeFromTypeBinding(((AbstractTypeDeclaration) parent).resolveBinding()); } if ((parent instanceof MethodDeclaration) && (((MethodDeclaration) parent).resolveBinding() != null)) { IMethodBinding resolveBinding = ((MethodDeclaration) parent).resolveBinding(); if (resolveBinding.getMethodDeclaration() != null) namedEntity = importer.ensureMethodFromMethodBinding(resolveBinding, importer.ensureTypeFromTypeBinding(resolveBinding.getMethodDeclaration().getDeclaringClass())); } if ((parent instanceof SingleVariableDeclaration) && (importer.topOfContainerStack() instanceof Method)) namedEntity = importer.ensureParameterFromSingleVariableDeclaration((SingleVariableDeclaration) parent, (Method) importer.topOfContainerStack()); if (namedEntity != null && node.resolveAnnotationBinding() != null) { AnnotationInstance annotationInstance = importer.createAnnotationInstanceFromAnnotationBinding(namedEntity, node.resolveAnnotationBinding()); importer.createLightweightSourceAnchor(annotationInstance, node); } if (parent instanceof FieldDeclaration) { List fragments = ((FieldDeclaration) parent).fragments(); for (Object object : fragments) { if (((VariableDeclarationFragment) object).resolveBinding() != null && node.resolveAnnotationBinding() != null) { Attribute attribute = importer .ensureAttributeForVariableBinding(((VariableDeclarationFragment) object).resolveBinding()); AnnotationInstance annotationInstance = importer .createAnnotationInstanceFromAnnotationBinding(attribute, node.resolveAnnotationBinding()); importer.createLightweightSourceAnchor(annotationInstance, node); } } } } /** * handles: @ TypeName ( Expression ) see comment from * {@link #visit(MarkerAnnotation)} */ @Override public boolean visit(SingleMemberAnnotation node) { addTypeAnnotationSourceAnchor(node); return true; } //////// METHODS @SuppressWarnings("unchecked") @Override public boolean visit(MethodDeclaration node) { if (importer.topOfContainerStack() instanceof Type) { IMethodBinding binding = node.resolveBinding(); Method method; if (binding != null) { method = importer.ensureMethodFromMethodBindingToCurrentContainer(binding); Arrays.stream(binding.getExceptionTypes()) .forEach(e -> importer.createDeclaredExceptionFromTypeBinding(e, method)); } else { logNullBinding("method declaration", node.getName(), ((CompilationUnit) node.getRoot()).getLineNumber(node.getStartPosition())); method = importer.ensureMethodFromMethodDeclaration(node); } method.setIsStub(false); method.setCyclomaticComplexity(1); importer.pushOnContainerStack(method); node.parameters().stream().forEach( p -> importer.ensureParameterFromSingleVariableDeclaration((SingleVariableDeclaration) p, method)); importer.createSourceAnchor(method, node); importer.createLightweightSourceAnchor(method, node.getName()); importer.ensureCommentFromBodyDeclaration(method, node); } return true; } @Override public void endVisit(MethodDeclaration node) { if (importer.topOfContainerStack() instanceof Method) importer.popFromContainerStack(); } @Override public boolean visit(Initializer node) { if (importer.topOfContainerStack() instanceof Type) { Method method = importer.ensureInitializerMethod(); importer.pushOnContainerStack(method); importer.createSourceAnchor(method, node); importer.ensureCommentFromBodyDeclaration(method, node); } return true; } @Override public void endVisit(Initializer node) { if (importer.topOfContainerStack() instanceof Method) importer.popFromContainerStack(); } //////// ATTRIBUTES @SuppressWarnings("unchecked") @Override public boolean visit(FieldDeclaration node) { if (node.fragments().stream().anyMatch(f -> ((VariableDeclarationFragment) f).getInitializer() != null)) importer.pushOnContainerStack(importer.ensureInitializerMethod()); node.fragments().stream().forEach(f -> visitFragment((VariableDeclarationFragment) f, node)); return true; } private void visitFragment(VariableDeclarationFragment fragment, FieldDeclaration field) { Attribute attribute = importer.ensureAttributeForFragment(fragment, field); importer.createSourceAnchor(attribute, fragment); importer.ensureCommentFromBodyDeclaration(attribute, field); /* * only the last fragment of a field contains the initializer code. thus, to * create the access to each variable in the fragment we need to ask that last * fragment we do not have to check the existence of that last fragment, because * we already know that the field has at least one fragment */ VariableDeclarationFragment lastFragment = (VariableDeclarationFragment) field.fragments() .get(field.fragments().size() - 1); if (lastFragment.getInitializer() != null) { Access access = importer.createAccessFromExpression(fragment.getName()); access.setIsWrite(true); importer.createAccessFromExpression((Expression) lastFragment.getInitializer()); } attribute.setIsStub(false); } @Override public void endVisit(FieldDeclaration node) { if (importer.topOfContainerStack().getName().equals(InJavaImporter.INITIALIZER_NAME)) importer.popFromContainerStack(); } //////// LOCAL VARIABLES // I do not know when this one is triggered // public boolean visit(VariableDeclarationExpression node) { // node.fragments().stream().forEach( // fragment -> // importer.ensureLocalVariableFromFragment((VariableDeclarationFragment) // fragment, node.getType())); // return true; // } @SuppressWarnings("unchecked") public boolean visit(VariableDeclarationStatement node) { node.fragments().stream().forEach(fragment -> importer .ensureLocalVariableFromFragment((VariableDeclarationFragment) fragment, node.getType())); return true; } //////// INVOCATIONS /** * handles object.method(parameter) */ @SuppressWarnings("unchecked") @Override public boolean visit(MethodInvocation node) { if (importer.topOfContainerStack() instanceof Method) { Invocation invocation = importer.createInvocationFromMethodBinding(node.resolveMethodBinding(), node.toString().trim()); importer.createLightweightSourceAnchor(invocation, node.getName()); importer.createAccessFromExpression(node.getExpression()); invocation.setReceiver(importer.ensureStructuralEntityFromExpression(node.getExpression())); node.arguments().stream().forEach(arg -> importer.createAccessFromExpression((Expression) arg)); } return true; } /** * handles super.method(parameter) */ @SuppressWarnings("unchecked") @Override public boolean visit(SuperMethodInvocation node) { Invocation invocation = importer.createInvocationFromMethodBinding(node.resolveMethodBinding(), node.toString().trim()); importer.createLightweightSourceAnchor(invocation, node); node.arguments().stream().forEach(arg -> importer.createAccessFromExpression((Expression) arg)); return true; } /** * handles this(parameter) */ @SuppressWarnings("unchecked") @Override public boolean visit(ConstructorInvocation node) { Invocation invocation = importer.createInvocationFromMethodBinding(node.resolveConstructorBinding(), node.toString().trim()); importer.createLightweightSourceAnchor(invocation, node); node.arguments().stream().forEach(arg -> importer.createAccessFromExpression((Expression) arg)); return true; } /** * handles super(parameter) */ @SuppressWarnings("unchecked") @Override public boolean visit(SuperConstructorInvocation node) { Invocation invocation = importer.createInvocationFromMethodBinding(node.resolveConstructorBinding(), node.toString().trim()); importer.createLightweightSourceAnchor(invocation, node); node.arguments().stream().forEach(arg -> importer.createAccessFromExpression((Expression) arg)); return true; } /** * handles new Class() */ @SuppressWarnings("unchecked") @Override public boolean visit(ClassInstanceCreation node) { IMethodBinding binding = node.resolveConstructorBinding(); if (binding != null) { Invocation invocation = importer.createInvocationFromMethodBinding(binding, node.toString().trim()); importer.createLightweightSourceAnchor(invocation, node.getType()); } else { String name = node.getType().toString(); importer.ensureBasicMethod(name, name, importer.ensureTypeNamedInUnknownNamespace(name), m -> importer.createInvocationToMethod(m, node.toString().trim())); } node.arguments().stream().forEach(arg -> importer.createAccessFromExpression((Expression) arg)); return true; } //////// ACCESSES /** * This one looks highly interesting, but I do not know in which situation this * is invoked. Funny, no? */ @Override public boolean visit(FieldAccess node) { return true; } @Override public boolean visit(Assignment node) { Access writeAccess = importer.createAccessFromExpression((Expression) node.getLeftHandSide()); writeAccess.setIsWrite(true); importer.createAccessFromExpression((Expression) node.getRightHandSide()); return true; } @Override public boolean visit(ReturnStatement node) { importer.createAccessFromExpression((Expression) node.getExpression()); return true; } /** * We create this access explicitly to catch a boolean variable used in * condition. Complicated expressions are handled in * {@link #visit(InfixExpression)} */ @Override public boolean visit(WhileStatement node) { if (importer.topFromContainerStack(Method.class) != null) { importer.topFromContainerStack(Method.class).incCyclomaticComplexity(); importer.createAccessFromExpression((Expression) node.getExpression()); } return true; } /** * We create this access explicitly to catch a boolean variable used in * condition. Complicated expressions are handled in * {@link #visit(InfixExpression)} */ @Override public boolean visit(DoStatement node) { if (importer.topFromContainerStack(Method.class) != null) { importer.topFromContainerStack(Method.class).incCyclomaticComplexity(); importer.createAccessFromExpression((Expression) node.getExpression()); } return true; } /** * We create this access explicitly to catch a boolean variable used in * condition. Complicated expressions are handled in * {@link #visit(InfixExpression)} */ @Override public boolean visit(IfStatement node) { if (importer.topFromContainerStack(Method.class) != null) { importer.topFromContainerStack(Method.class).incCyclomaticComplexity(); importer.createAccessFromExpression((Expression) node.getExpression()); } return true; } /** * We create this access explicitly to catch a boolean variable used in * condition. Complicated expressions are handled in * {@link #visit(InfixExpression)} */ @Override public boolean visit(SwitchStatement node) { importer.createAccessFromExpression((Expression) node.getExpression()); return true; } /** * Handles for (int i = init; i < n; i++) * * We create this access explicitly to catch a boolean variable used in * condition. Complicated expressions are handled in * {@link #visit(InfixExpression)} */ @Override public boolean visit(ForStatement node) { if (importer.topFromContainerStack(Method.class) != null) { importer.topFromContainerStack(Method.class).incCyclomaticComplexity(); importer.createAccessFromExpression((Expression) node.getExpression()); } return true; } /** * Handles for ( Object x : list ) We create this access explicitly to catch a * boolean variable used in condition. Complicated expressions are handled in * {@link #visit(InfixExpression)} */ @Override public boolean visit(EnhancedForStatement node) { if (importer.topFromContainerStack(Method.class) != null) { importer.topFromContainerStack(Method.class).incCyclomaticComplexity(); importer.createAccessFromExpression((Expression) node.getExpression()); } return true; } /** * We create this access explicitly to catch a boolean variable used in * condition. Complicated expressions are handled in * {@link #visit(InfixExpression)} */ @Override public boolean visit(ConditionalExpression node) { if (importer.topFromContainerStack(Method.class) != null) { importer.topFromContainerStack(Method.class).incCyclomaticComplexity(); importer.createAccessFromExpression((Expression) node.getExpression()); } return true; } /** * This one also takes care of expanded expressions like true || (41 == 42) && * booleanAttribute */ @Override public boolean visit(InfixExpression node) { if ((node.getOperator().equals(Operator.AND) || node.getOperator().equals(Operator.OR)) && importer.topFromContainerStack(Method.class) != null) { importer.topFromContainerStack(Method.class).incCyclomaticComplexity(); } importer.createAccessFromExpression((Expression) node.getLeftOperand()); importer.createAccessFromExpression((Expression) node.getRightOperand()); return true; } /** * Handles expressions like (a && b || c) */ @Override public boolean visit(ParenthesizedExpression node) { importer.createAccessFromExpression((Expression) node.getExpression()); return true; } /** * Handles synchronized(a) {} */ @Override public boolean visit(SynchronizedStatement node) { importer.createAccessFromExpression((Expression) node.getExpression()); return true; } /** * It would be ideal to find a way to use this method for creating accesses. * However without having the context, we do not know whether this is actually * an access. That is why we have to resort to spreading these * createAccessFromExpression calls everywhere. */ @Override public boolean visit(SimpleName node) { return false; } @Override public boolean visit(CatchClause node) { if (importer.topFromContainerStack(Method.class) != null) { importer.topFromContainerStack(Method.class).incCyclomaticComplexity(); } CaughtException caughtException = new CaughtException(); ITypeBinding binding = node.getException().getType().resolveBinding(); if (binding != null) { Type caughtType = importer.ensureTypeFromTypeBinding(binding); caughtException.setExceptionClass((com.feenk.jdt2famix.model.famix.Class) caughtType); caughtException.setDefiningMethod((Method) importer.topOfContainerStack()); importer.repository().add(caughtException); } return true; } @Override public boolean visit(ThrowStatement node) { ITypeBinding binding = node.getExpression().resolveTypeBinding(); if (binding != null) { ThrownException thrownException = new ThrownException(); Type thrownType = importer.ensureTypeFromTypeBinding(binding); thrownException.setExceptionClass((com.feenk.jdt2famix.model.famix.Class) thrownType); thrownException.setDefiningMethod((Method) importer.topOfContainerStack()); importer.repository().add(thrownException); } return true; } @Override public boolean visit(SwitchCase node) { if (importer.topFromContainerStack(Method.class) != null) importer.topFromContainerStack(Method.class).incCyclomaticComplexity(); return true; } }