/*******************************************************************************
* Copyright (c) 2010 Michal Antkiewicz.
* 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:
* Michal Antkiewicz - initial API and implementation
******************************************************************************/
package ca.uwaterloo.gsd.fsml.javaMappingInterpreter.mappings;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import ca.uwaterloo.gsd.fsml.core.Cause;
import ca.uwaterloo.gsd.fsml.core.FSMLMappingException;
import ca.uwaterloo.gsd.fsml.core.Mode;
import ca.uwaterloo.gsd.fsml.core.Parameter;
import ca.uwaterloo.gsd.fsml.ecore.FSMLEcoreUtil;
import ca.uwaterloo.gsd.fsml.ecore.FSMLEcoreUtil.NavigationResult;
import ca.uwaterloo.gsd.fsml.javaMappingInterpreter.JavaMappingInterpreter;
import ca.uwaterloo.gsd.fsml.javaMappingInterpreter.analysis.ASTUtils;
import ca.uwaterloo.gsd.fsml.sync.SyncItem;
/**
* Mapping: method call x occurs before method call y in the control flow of class c given callback sequence s.
* @author Michal Antkiewicz <mantkiew@gsd.uwaterloo.ca>
*/
public class MethodCallMapping extends JavaMapping {
public MethodCallMapping(EObject element, EStructuralFeature feature, EAnnotation annotation, EClass concreteChildType, JavaMappingInterpreter interpreter, IProgressMonitor progressMonitor) throws FSMLMappingException {
super(element, feature, annotation, concreteChildType, interpreter, progressMonitor);
NavigationResult navigationResult1 = FSMLEcoreUtil.navigateToEObject(element, call);
if (navigationResult1.errorMessage == null)
methodInvocation1 = contextManager.getContextMethodInvocation(navigationResult1.eObject, true, progressMonitor);
else
throw new FSMLMappingException(Cause.INCORRECT_VALUE, JavaMappingInterpreter.DETAIL_OF_METHOD_CALL);
NavigationResult navigationResult2 = FSMLEcoreUtil.navigateToEObject(element, before);
if (navigationResult2.errorMessage == null)
methodInvocation2 = contextManager.getContextMethodInvocation(navigationResult2.eObject, true, progressMonitor);
else
throw new FSMLMappingException(Cause.INCORRECT_VALUE, JavaMappingInterpreter.DETAIL_OF_CALL);
}
public MethodCallMapping(SyncItem syncItem, EAnnotation annotation, JavaMappingInterpreter interpreter, IProgressMonitor progressMonitor) throws FSMLMappingException {
super(syncItem, annotation, interpreter, progressMonitor);
}
@Parameter(name=JavaMappingInterpreter.DETAIL_CALL, mode=Mode.REVERSE, required=true)
public String call;
@Parameter(name=JavaMappingInterpreter.DETAIL_BEFORE, mode=Mode.REVERSE, required=true)
public String before;
@Parameter(name=JavaMappingInterpreter.DETAIL_GIVEN_CALLBACK_SEQ, mode=Mode.REVERSE, required=true)
public String givenCallbackSeq;
MethodInvocation methodInvocation1;
MethodInvocation methodInvocation2;
@Override
protected boolean forward() throws FSMLMappingException {
return true;
}
@Override
protected boolean reverse() throws FSMLMappingException {
// see if the calls are in the bodies of callback methods
MethodDeclaration methodDeclaration1 = (MethodDeclaration) ASTUtils.getAncestorOfType(methodInvocation1, ASTNode.METHOD_DECLARATION);
CompilationUnit compilationUnit1 = (CompilationUnit) methodInvocation1.getRoot();
MethodDeclaration methodDeclaration2 = (MethodDeclaration) ASTUtils.getAncestorOfType(methodInvocation2, ASTNode.METHOD_DECLARATION);
CompilationUnit compilationUnit2 = (CompilationUnit) methodInvocation2.getRoot();
if (methodDeclaration1.equals(methodDeclaration2)) {
// intraprocedural analysis
Block block1 = (Block) ASTUtils.getAncestorOfType(methodInvocation1, ASTNode.BLOCK);
Block block2 = (Block) ASTUtils.getAncestorOfType(methodInvocation2, ASTNode.BLOCK);
if (block1 != block2)
// most likely are alternative
return setFeature(false);
else {
// in the same block
if (methodInvocation1.getParent() == methodInvocation2.getParent() && methodInvocation1.getParent().getNodeType() == ASTNode.IF_STATEMENT)
// directly under if statement, must be in different branches
return setFeature(false);
else
// in the same block, not directly in an if statement
if (methodInvocation1.getStartPosition() < methodInvocation2.getStartPosition())
return setFeature(true);
}
}
else if (compilationUnit1 == compilationUnit2){
try {
IType contextIType = (IType) javaInterpreter.getContext(element, IType.class, true);
IMethod iMethod1 = (IMethod) methodDeclaration1.resolveBinding().getJavaElement();
// see if any is a constructor
boolean methodInvocation1InConstructor = methodDeclaration1.isConstructor();
List<IMethod> allMethods = callGraphManager.getAvailableMethods(contextIType);
// get all constructors
if (!methodInvocation1InConstructor) {
// maybe in cflow of a constructor?
for (IMethod method : allMethods) {
if (method.isConstructor()) {
if (callGraphManager.calls(method, iMethod1))
methodInvocation1InConstructor = true;
}
}
}
IMethod iMethod2 = (IMethod) methodDeclaration2.resolveBinding().getJavaElement();
boolean methodInvocation2InConstructor = methodDeclaration2.isConstructor();
// get all constructors
if (!methodInvocation2InConstructor) {
// maybe in cflow of a constructor?
for (IMethod method : allMethods) {
if (method.isConstructor()) {
if (callGraphManager.calls(method, iMethod2))
methodInvocation2InConstructor = true;
}
}
}
if (methodInvocation1InConstructor && !methodInvocation2InConstructor)
return setFeature(true);
else if (methodInvocation1InConstructor && methodInvocation2InConstructor)
// the calls are in different constructors of the same compilation unit.
;
else if (!methodInvocation1InConstructor && methodInvocation2InConstructor)
// the second call is first
;
else {
// both method calls are not in constructors: check if both are directly in the callback sequence
int index1 = givenCallbackSeq.indexOf(methodDeclaration1.getName().getIdentifier());
int index2 = givenCallbackSeq.indexOf(methodDeclaration2.getName().getIdentifier());
if (index1 != -1 && index2 != -1) {
if (index1 < index2)
return setFeature(true);
}
else {
ArrayList<IMethod> callbackMethods = new ArrayList<IMethod>();
// tokenize
StringTokenizer tokenizer = new StringTokenizer(givenCallbackSeq);
for ( ; tokenizer.hasMoreTokens() ;) {
String methodName = tokenizer.nextToken();
for (IMethod method : allMethods) {
if (methodName.equals(method.getElementName()))
callbackMethods.add(method);
}
}
// need to see whether methodDeclarations are reachable from the callback methods
for (int i = 0; i < callbackMethods.size(); i++) {
IMethod callbackMethod = callbackMethods.get(i);
if (callGraphManager.calls(callbackMethod, iMethod1)) {
index1 = i;
break;
}
}
for (int i = 0; i < callbackMethods.size(); i++) {
IMethod callbackMethod = callbackMethods.get(i);
if (callGraphManager.calls(callbackMethod, iMethod2)) {
index2 = i;
break;
}
}
if (index1 != -1 && index2 != -1 && index1 < index2)
return setFeature(true);
}
}
} catch (JavaModelException e) {
e.printStackTrace();
}
}
return setFeature(false);
}
}