/*******************************************************************************
* 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.Collection;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.common.util.EList;
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.emf.ecore.util.EcoreUtil;
import org.eclipse.jdt.core.IJavaElement;
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.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.search.MethodReferenceMatch;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.internal.core.ResolvedSourceMethod;
import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil;
import ca.uwaterloo.gsd.fsml.core.Cause;
import ca.uwaterloo.gsd.fsml.core.Context;
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.javaMappingInterpreter.CodeQueries;
import ca.uwaterloo.gsd.fsml.javaMappingInterpreter.CodeTransforms;
import ca.uwaterloo.gsd.fsml.javaMappingInterpreter.JavaMappingInterpreter;
import ca.uwaterloo.gsd.fsml.javaMappingInterpreter.analysis.ASTUtils;
import ca.uwaterloo.gsd.fsml.javaMappingInterpreter.analysis.JavaModelUtils;
import ca.uwaterloo.gsd.fsml.javaMappingInterpreter.analysis.impl.JavaImplVariantManagerConstants;
import ca.uwaterloo.gsd.fsml.stats.Stats;
import ca.uwaterloo.gsd.fsml.sync.SyncItem;
public class CallsReceivedMapping extends JavaMapping {
/**
* Constructor used in reverse engineering.
* @param element
* @param feature
* @param annotation
* @param concreteChildType
* @param interpreter
* @param progressMonitor
* @throws FSMLMappingException
*/
public CallsReceivedMapping(EObject element, EStructuralFeature feature, EAnnotation annotation, EClass concreteChildType, JavaMappingInterpreter interpreter, IProgressMonitor progressMonitor) throws FSMLMappingException {
super(element, feature, annotation, concreteChildType, interpreter, progressMonitor);
}
/**
* Constructor used in forward engineering.
* @param syncItem
* @param annotation
* @param interpreter
* @param progressMonitor
* @throws FSMLMappingException
*/
public CallsReceivedMapping(SyncItem syncItem, EAnnotation annotation, JavaMappingInterpreter interpreter, IProgressMonitor progressMonitor) throws FSMLMappingException {
super(syncItem, annotation, interpreter, progressMonitor);
if (detailPosition == null || detailPosition.equals("before"))
position = CodeTransforms.BEFORE_ADVICE;
else if (detailPosition.equals("after"))
position = CodeTransforms.AFTER_ADVICE;
else {
try {
position = Integer.parseInt(detailPosition);
} catch (Exception e){
throw new FSMLMappingException(Cause.INCORRECT_VALUE, "for detail " + JavaMappingInterpreter.DETAIL_POSITION);
}
}
}
@Context(mode=Mode.REVERSE, required=true)
public IType contextIType;
@Context(mode=Mode.FORWARD, excludes="contextTypeDeclaration")
public MethodDeclaration contextMethodDeclaration;
@Context(mode=Mode.FORWARD, excludes="contextMethodDeclaration")
public TypeDeclaration contextTypeDeclaration;
@Parameter(name=JavaMappingInterpreter.DETAIL_CLASS, mode=Mode.ALL, required=true)
public String type;
@Parameter(name=JavaMappingInterpreter.DETAIL_NAME, mode=Mode.FORWARD, required=true)
public String name;
@Parameter(name=JavaMappingInterpreter.DETAIL_SIGNATURE, mode=Mode.FORWARD, defaultValue="()V")
public String signature;
@Parameter(name=JavaMappingInterpreter.DETAIL_LOCATION_NAME, mode=Mode.FORWARD, required=true)
public String detailLocationName;
@Parameter(name=JavaMappingInterpreter.DETAIL_LOCATION_SIG, mode=Mode.FORWARD, defaultValue="()V")
public String detailLocationSig;
@Parameter(name=JavaMappingInterpreter.DETAIL_POSITION, mode=Mode.FORWARD, defaultValue="after")
public String detailPosition;
int position = 0;
@Override
protected boolean forward() throws FSMLMappingException {
IMethod targetMethod = javaInterpreter.iMethods.get(FSMLEcoreUtil.computeAnnotationKey(annotation));
String[] parameterTypes = targetMethod.getParameterTypes();
// construct a call
String contents = name + CodeTransforms.constructDefaultCallParameters(parameterTypes) + ";";
List<ASTNode> result = null;
// method calls can be create in the context method or a context class
if (contextMethodDeclaration != null) {
result = CodeTransforms.weaveAdvice(position, null, contextMethodDeclaration, contents, progressMonitor);
}
else {
// no context method, try context class
if (contextTypeDeclaration != null) {
MethodDeclaration methodDeclaration = ASTUtils.findMethod(javaAstManager, detailLocationName, detailLocationSig, contextTypeDeclaration, true, false, progressMonitor);
if (methodDeclaration == null)
// create the method
methodDeclaration = CodeTransforms.createMethod(contextIJavaProject, null, contextTypeDeclaration, "public", detailLocationName, detailLocationSig, null, progressMonitor);
result = CodeTransforms.weaveAdvice(position, null, methodDeclaration, contents, progressMonitor);
}
}
// need to set up the call as a context and put the markers
if (result != null && !result.isEmpty()) {
// should be a single method call. However, in the future, different variants may
// require some preparation statements, e.g., variable declarations, and the call will
// the the last one
ASTNode astNode = result.get(result.size()-1);
if (astNode instanceof ExpressionStatement)
astNode = ((ExpressionStatement) astNode).getExpression();
if (astNode instanceof MethodInvocation) {
EObject element = FSMLEcoreUtil.retrieveContextElement(syncItem, JavaMappingInterpreter.CONTEXT_METHOD_CALL);
contextManager.associateContext(element, astNode);
}
}
return result != null;
}
@Override
protected boolean reverse() throws FSMLMappingException {
String key = FSMLEcoreUtil.computeAnnotationKey(annotation);
IMethod targetMethod = javaInterpreter.iMethods.get(key);
try {
// check cache first
Collection<SearchMatch> methodCalls = javaInterpreter.callsCache.get(targetMethod);
if (methodCalls == null) {
methodCalls = JavaModelUtils.getMethodCallsInProject(contextIJavaProject, targetMethod, progressMonitor);
javaInterpreter.callsCache.put(targetMethod, methodCalls);
}
for (SearchMatch match : methodCalls) {
// remove calls which are not received by the contextType
CompilationUnit matchCompilationUnit = javaAstManager.getCompilationUnit(((IJavaElement) match.getElement()).getPrimaryElement());
if (matchCompilationUnit == null)
continue;
ASTNode matchAstNode = ASTNodeSearchUtil.findNode(match, matchCompilationUnit);
if (matchAstNode == null) {
Stats.INSTANCE.logError("CallsReceivedMapping::reverse(): " + element.eClass().getName() + "::" + feature.getName()+ " ASTNode for match not found: " + match + "\n");
continue;
}
if (matchAstNode.getNodeType() != ASTNode.METHOD_INVOCATION &&
matchAstNode.getNodeType() != ASTNode.CLASS_INSTANCE_CREATION) {
Stats.INSTANCE.logMessage("CallsReceivedMapping::reverse(): matchASTNode which is either method invocation nor class instance creation found: " + matchAstNode.getClass() + "\n");
continue;
}
// check if expression is assignable to the required receiver
IType actualReceiverType = null;
String requiredReceiverName = contextIType.getFullyQualifiedName();
if (requiredReceiverName != null) {
Expression receiverExpression = null;
if (matchAstNode instanceof MethodInvocation)
receiverExpression = ((MethodInvocation) matchAstNode).getExpression();
else if (matchAstNode instanceof ClassInstanceCreation)
receiverExpression = ((ClassInstanceCreation) matchAstNode).getExpression();
if (receiverExpression == null) {
// the receiver is 'this'
// locate the containing method declaration
actualReceiverType = CodeQueries.findITypeContainingASTNode(contextIJavaProject, matchAstNode);
if (typeHierarchy.implementsOrExtendsType(actualReceiverType, requiredReceiverName)) {
// break iteration if a correct value has been set
if (javaInterpreter.analyzeImplVariants()) {
String declaringMethod = ((ResolvedSourceMethod)((MethodReferenceMatch)match).getElement()).getKey();
Stats.INSTANCE.logImplVariant(element.eClass(), feature, annotation,declaringMethod );
EClass targetClass = javaImplVariantManager.getVariantEClass(JavaImplVariantManagerConstants.VARIANT+
JavaImplVariantManagerConstants.DELIMITER+
annotation.getSource() + JavaImplVariantManagerConstants.DELIMITER
+ JavaImplVariantManagerConstants.IN_METHOD);
EObject child = EcoreUtil.create(targetClass);
((EList) javaImplVariantManager.getModel().eGet(javaImplVariantManager.getVariantsReference())).add(child);
child.eSet(targetClass.getEStructuralFeature(JavaImplVariantManagerConstants.ECLASS), element.eClass().getName());
child.eSet(targetClass.getEStructuralFeature(JavaImplVariantManagerConstants.ESTRUCTURALFEATURE), feature.getName());
child.eSet(targetClass.getEStructuralFeature(JavaImplVariantManagerConstants.METHOD_NAME), declaringMethod);
//createMarkerDescriptor((ResolvedSourceMethod)((MethodReferenceMatch)match).getElement(), JavaImplVariantManager.computeVariantKey(child), "marker for implModel");
createMarkerDescriptor((ResolvedSourceMethod)((MethodReferenceMatch)match).getElement(), child, "marker for implModel");
}
if (setFeatureContextAndMarker(true, matchAstNode, matchAstNode, null) && !feature.isMany())
break;
}
} else {
ITypeBinding typeBinding = receiverExpression.resolveTypeBinding();
actualReceiverType = (IType) typeBinding.getJavaElement();
if (typeHierarchy.implementsOrExtendsType(actualReceiverType, requiredReceiverName)) {
// correct receiver
// break iteration if a correct value has been set
if (setFeatureContextAndMarker(true, matchAstNode, matchAstNode, null) && !feature.isMany())
break;
}
else {
if (! typeHierarchy.implementsOrExtendsType(contextIType, actualReceiverType.getFullyQualifiedName()))
continue;
// need to check dataflow
if (receiverExpression.getNodeType() == ASTNode.SIMPLE_NAME) {
SimpleName simpleName = (SimpleName) receiverExpression;
IVariableBinding iVariableBinding = (IVariableBinding) simpleName.resolveBinding();
if (iVariableBinding.isParameter()) {
IVariableBinding iVariableDeclarationBinding = iVariableBinding.getVariableDeclaration();
CompilationUnit compilationUnit = (CompilationUnit) receiverExpression.getRoot();
SingleVariableDeclaration variableDeclaration = (SingleVariableDeclaration) compilationUnit.findDeclaringNode(iVariableDeclarationBinding);
MethodDeclaration methodDeclaration = (MethodDeclaration) variableDeclaration.getParent();
IMethodBinding iMethodBinding = methodDeclaration.resolveBinding();
int index = methodDeclaration.parameters().indexOf(variableDeclaration);
for (ASTNode methodInvocationName : ASTUtils.findMathodInvocations(compilationUnit, methodDeclaration, iMethodBinding)) {
ASTNode methodInvocationNode = methodInvocationName.getParent();
if (methodInvocationNode instanceof Type)
methodInvocationNode = methodInvocationNode.getParent();
if (methodInvocationNode.getNodeType() == ASTNode.METHOD_DECLARATION)
continue;
Expression argument = null;
switch (methodInvocationNode.getNodeType()) {
case ASTNode.METHOD_INVOCATION:
argument = (Expression) ((MethodInvocation) methodInvocationNode).arguments().get(index);
break;
case ASTNode.CLASS_INSTANCE_CREATION:
argument = (Expression) ((ClassInstanceCreation) methodInvocationNode).arguments().get(index);
break;
}
if (argument == null)
continue;
typeBinding = argument.resolveTypeBinding();
actualReceiverType = (IType) typeBinding.getJavaElement();
if (typeHierarchy.implementsOrExtendsType(actualReceiverType, requiredReceiverName)) {
// break iteration if a correct value has been set
if (setFeatureContextAndMarker(true, matchAstNode, matchAstNode, null))
break;
}
continue;
}
}
else if (iVariableBinding.isField()) {
continue;
}
}
}
}
}
}
} catch (JavaModelException e) {
e.printStackTrace();
}
return FSMLEcoreUtil.isFeaturePresent(element, feature);
}
}