/******************************************************************************* * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, Canada. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * The Chisel Group, University of Victoria *******************************************************************************/ package ca.uvic.chisel.diver.sequencediagrams.sc.java.model; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeHierarchy; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.ConstructorInvocation; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.ReturnStatement; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SuperConstructorInvocation; import org.eclipse.jdt.core.dom.SuperMethodInvocation; import org.eclipse.jdt.core.dom.ThrowStatement; import org.eclipse.jdt.core.dom.TryStatement; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.VariableDeclarationExpression; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.VariableDeclarationStatement; /** * A class representing an activation on a java object. * @author Del Myers */ public class JavaActivation implements IAdaptable, IJavaActivation { private JavaCallTree tree; private JavaMessage callingMessage; private IMethod method; private MethodDeclaration astNode; private ArrayList<JavaMessage> messages; private class MessageFinder extends GenericVisitor { private class VariableHolder { protected SimpleName name; protected Type type; VariableHolder(Type type, SimpleName name) { this.name = name; this.type = type; } } private class ThrowResolver extends ASTVisitor { protected List<ASTNode> possibilities = new ArrayList<ASTNode>(); @Override public boolean visit(MethodInvocation node) { possibilities.add(node); return false; } @Override public boolean visit(SuperMethodInvocation node) { possibilities.add(node); return false; } @Override public boolean visit(ConstructorInvocation node) { possibilities.add(node); return false; } public boolean visit(SimpleName node) { possibilities.add(node); return false; } @Override public boolean visit(ClassInstanceCreation node) { possibilities.add(node); return false; } } private List<ASTNode> messages = new LinkedList<ASTNode>(); private HashMap<Block, List<VariableHolder>> variables = new HashMap<Block, List<VariableHolder>>(); private HashMap<ThrowStatement, List<IType>> thrownTypes = new HashMap<ThrowStatement, List<IType>>(); private LinkedList<TryStatement> openTries = new LinkedList<TryStatement>(); private HashMap<ASTNode, List<TryStatement>> invocationTries = new HashMap<ASTNode, List<TryStatement>>(); @Override public boolean visit(TryStatement node) { openTries.addLast(node); return true; } @Override public void endVisit(TryStatement node) { openTries.removeLast(); } @Override protected void endVisitNode(ASTNode node) { if ((node instanceof MethodInvocation)|| (node instanceof SuperMethodInvocation)|| (node instanceof ConstructorInvocation)|| (node instanceof SuperConstructorInvocation)|| (node instanceof ThrowStatement)|| (node instanceof ReturnStatement) || (node instanceof ClassInstanceCreation)) { messages.add(node); if (openTries.size() > 0) { invocationTries.put(node, new ArrayList<TryStatement>(openTries)); } } if (node instanceof ThrowStatement) { //try to resolve the throw statements. ThrowResolver resolver = new ThrowResolver(); node.accept(resolver); List<IType> thrown = new ArrayList<IType>(); thrownTypes.put((ThrowStatement)node, thrown); for (ASTNode unresolved : resolver.possibilities) { try { switch (unresolved.getNodeType()) { case ASTNode.METHOD_INVOCATION: //have to try and find the type for the return value. Type rtype = ASTUTils.findReturnType((MethodInvocation)unresolved); if (rtype != null) { //try and resolve it to an exception ITypeBinding binding = rtype.resolveBinding(); IType type = (IType) binding.getJavaElement(); ITypeHierarchy hierarchy = type.newSupertypeHierarchy(new NullProgressMonitor()); IJavaProject project = type.getJavaProject(); IType throwable = project.findType("java.lang.Throwable"); if (hierarchy.contains(throwable)) { thrown.add(type); } } break; case ASTNode.CONSTRUCTOR_INVOCATION: IMethodBinding methodBinding = ((ConstructorInvocation)unresolved).resolveConstructorBinding(); if (methodBinding != null) { IType type = (IType) methodBinding.getDeclaringClass().getJavaElement(); if (type != null) { ITypeHierarchy hierarchy = type.newSupertypeHierarchy(new NullProgressMonitor()); IJavaProject project = type.getJavaProject(); IType throwable = project.findType("java.lang.Throwable"); if (hierarchy.contains(throwable)) { thrown.add(type); } } } break; case ASTNode.SIMPLE_NAME: for (List<VariableHolder> localVariables : variables.values()) { for (VariableHolder variable : localVariables) { if (variable.name.equals(node)) { ITypeBinding binding = variable.type.resolveBinding(); if (binding != null) { IType type = (IType) binding.getJavaElement(); ITypeHierarchy hierarchy = type.newSupertypeHierarchy(new NullProgressMonitor()); IJavaProject project = type.getJavaProject(); IType throwable = project.findType("java.lang.Throwable"); if (hierarchy.contains(throwable)) { thrown.add(type); } } break; } } } break; case ASTNode.CLASS_INSTANCE_CREATION: ClassInstanceCreation ci = (ClassInstanceCreation) unresolved; ITypeBinding binding = ci.getType().resolveBinding(); if (binding != null) { IType type = (IType) binding.getJavaElement(); ITypeHierarchy hierarchy = type.newSupertypeHierarchy(new NullProgressMonitor()); IJavaProject project = type.getJavaProject(); IType throwable = project.findType("java.lang.Throwable"); if (hierarchy.contains(throwable)) { thrown.add(type); } } break; } } catch (JavaModelException e) { } } } super.endVisitNode(node); } //keep track of variables to resolve throw statements. @Override public boolean visit(VariableDeclarationExpression node) { Type type = node.getType(); ASTNode scope = node.getParent(); while (scope != null && !(scope instanceof Block)) { scope = scope.getParent(); } if (scope instanceof Block) { List<VariableHolder> exps = variables.get(scope); if (exps == null) { exps = new LinkedList<VariableHolder>(); variables.put((Block)scope, exps); } for (Object o : node.fragments()) { exps.add(new VariableHolder(type, ((VariableDeclarationFragment)o).getName())); } } return super.visit(node); } //keep track of variables to resolve throw statements. @SuppressWarnings("unchecked") @Override public boolean visit(VariableDeclarationStatement node) { Type type = node.getType(); ASTNode scope = node.getParent(); while (scope != null && !(scope instanceof Block)) { scope = scope.getParent(); } if (scope instanceof Block) { List<VariableHolder> exps = variables.get(scope); if (exps == null) { exps = new LinkedList<VariableHolder>(); variables.put((Block)scope, exps); } for (Object o : node.fragments()) { exps.add(new VariableHolder(type, ((VariableDeclarationFragment)o).getName())); } } return super.visit(node); } @Override public void endVisit(Block node) { //unscope all variables. variables.remove(node); super.endVisit(node); } } public JavaActivation(JavaCallTree tree, IMethod method) { this.tree = tree; this.callingMessage = null; this.method = method; } @SuppressWarnings("unchecked") public Object getAdapter(Class adapter) { if (ASTNode.class.isAssignableFrom(adapter)) { return getAST(); } //try the java element's adapter return getJavaElement().getAdapter(adapter); } public JavaMessage getCallingMessage() { return this.callingMessage; } public ASTNode getAST() { if (astNode == null) { ASTNode rootNode = tree.parse(method.getDeclaringType()); this.astNode = ASTUTils.findMethodDeclaration(rootNode, method); } return astNode; } public IJavaElement getJavaElement() { return method; } public JavaObject getLifeLine() { return new JavaObject(tree, method.getDeclaringType()); } public JavaCallTree getTree() { return tree; } /* (non-Javadoc) * @see ca.uvic.chise.diver.sequencediagrams.sc.java.model.IJavaActivation#getMessages() */ @Override public List<JavaMessage> getMessages() { if (this.messages == null) { this.messages = new ArrayList<JavaMessage>(); if (getAST() == null) { return messages; } MessageFinder finder = new MessageFinder(); getAST().accept(finder); for (ASTNode node : finder.messages) { IMethodBinding binding = null; switch (node.getNodeType()) { case ASTNode.METHOD_INVOCATION: binding = ((MethodInvocation)node).resolveMethodBinding(); break; case ASTNode.SUPER_METHOD_INVOCATION: binding = ((SuperMethodInvocation)node).resolveMethodBinding(); break; case ASTNode.CONSTRUCTOR_INVOCATION: binding = ((ConstructorInvocation)node).resolveConstructorBinding(); break; case ASTNode.SUPER_CONSTRUCTOR_INVOCATION: binding = ((SuperConstructorInvocation)node).resolveConstructorBinding(); break; case ASTNode.CLASS_INSTANCE_CREATION: binding = ((ClassInstanceCreation)node).resolveConstructorBinding(); break; } if (binding != null) { IMethod target = (IMethod) binding.getJavaElement(); if (target != null) { JavaActivation targetActivation = new JavaActivation(tree, target); JavaMessage message = new JavaMessage(tree, node, this, targetActivation); //add catches targetActivation.setCallingMessage(message); message.setTries(finder.invocationTries.get(node)); messages.add(message); } } else if (node instanceof ThrowStatement){ ThrowStatement statement = (ThrowStatement) node; List<IType> throwns = finder.thrownTypes.get(statement); for (IType thrown : throwns) { JavaMessage parentMessage = getCallingMessage(); IJavaActivation root = this; JavaMessage message = null; while (parentMessage != null) { root = parentMessage.getSource(); if (parentMessage.catches(thrown)) { break; } parentMessage = root.getCallingMessage(); } if (root != null && root != this) { message = new JavaMessage(tree, statement, this, root); message.setType(thrown); message.setException(true); messages.add(message); } } } else if (node instanceof ReturnStatement) { if (getCallingMessage() != null) { JavaMessage message = new JavaMessage(tree, node, this, getCallingMessage().getSource()); IJavaProject project = getJavaElement().getJavaProject(); if (project != null) { String returnType; try { returnType = ((IMethod)getJavaElement()).getReturnType(); if (returnType != null) { message.setType(returnType); } } catch (JavaModelException e) { } } messages.add(message); } } } } return messages; } protected void setCallingMessage(JavaMessage message) { this.callingMessage = message; } }