/******************************************************************************* * 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.Flags; import org.eclipse.jdt.core.IField; 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.Signature; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Assignment; import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.ExpressionStatement; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.search.FieldReferenceMatch; import org.eclipse.jdt.internal.core.ResolvedSourceMethod; 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 AssignedWithNewMapping extends JavaMapping { public AssignedWithNewMapping(EObject element, EStructuralFeature feature, EAnnotation annotation, EClass concreteChildType, JavaMappingInterpreter interpreter, IProgressMonitor progressMonitor) throws FSMLMappingException { super(element, feature, annotation, concreteChildType, interpreter, progressMonitor); subtypeOf = ! detailSubtypeOf.isEmpty(); } public AssignedWithNewMapping(SyncItem syncItem, EAnnotation annotation, JavaMappingInterpreter interpreter, IProgressMonitor progressMonitor) throws FSMLMappingException { super(syncItem, annotation, interpreter, progressMonitor); // exactly one of them must be non-null if ((contextMethodDeclaration == null && contextTypeDeclaration == null) || (contextMethodDeclaration != null && contextTypeDeclaration != null)) throw new FSMLMappingException(Cause.MISSING_CONTEXT, "MethodDeclaration XOR TypeDeclaration"); 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); } } subtypeOf = ! detailSubtypeOf.isEmpty(); if (subtypeOf) { // need to retrieve the type name EStructuralFeature fqNameFeature = FSMLEcoreUtil.findEStructuralFeatureWithAnnotations(element.eClass(), new String[] { "fullyQualifiedName" } ); if (fqNameFeature != null) { String fqName = (String) element.eGet(fqNameFeature); if (fqName == null || fqName.isEmpty()) throw new FSMLMappingException(Cause.MISSING_ATTRIBUTE, JavaMappingInterpreter.QUERY_FULLY_QUALIFIED_NAME); detailClass = fqName; } } } @Context(mode=Mode.REVERSE) public IField contextIField; @Context(mode=Mode.FORWARD_AND_REVERSE) public VariableDeclarationFragment contextVariableDeclarationFragment; @Context(mode=Mode.FORWARD_AND_REVERSE, excludes="contextMethodDeclaration") public TypeDeclaration contextTypeDeclaration; @Context(mode=Mode.FORWARD_AND_REVERSE, excludes="contextTypeDeclaration") public MethodDeclaration contextMethodDeclaration; @Parameter(name=JavaMappingInterpreter.DETAIL_CLASS, mode=Mode.ALL) public String detailClass; @Parameter(name=JavaMappingInterpreter.DETAIL_SIGNATURE, mode=Mode.FORWARD, defaultValue="()V") public String detailSignature; @Parameter(name=JavaMappingInterpreter.DETAIL_LOCATION_NAME, mode=Mode.FORWARD) public String locationName; @Parameter(name=JavaMappingInterpreter.DETAIL_LOCATION_SIG, mode=Mode.FORWARD, defaultValue="()V") public String locationSig; @Parameter(name=JavaMappingInterpreter.DETAIL_POSITION, mode=Mode.FORWARD, defaultValue="after") public String detailPosition; int position = 0; @Parameter(name=JavaMappingInterpreter.DETAIL_INITIALIZER, mode=Mode.FORWARD, defaultValue="false") public String initializer; @Parameter(name=JavaMappingInterpreter.DETAIL_SUBTYPE_OF, mode=Mode.ALL, defaultValue="") public String detailSubtypeOf; boolean subtypeOf; @Override protected boolean forward() throws FSMLMappingException { // prepare class instance creation String simpleName = Signature.getSimpleName(detailClass); int index = simpleName.indexOf('$'); if (index > 0) // for a nested class take the name without the top-level class name and the '$' sign simpleName = simpleName.substring(index + 1); String newExpression = "new " + simpleName + CodeTransforms.constructDefaultCallParameters(detailSignature); try { // construct the new instance creation statement IType instanceType; if (subtypeOf) // the detailClass does not exist yet, but it will be the subtype of detailSubtypeOf instanceType = contextIJavaProject.findType(detailSubtypeOf); else instanceType = contextIJavaProject.findType(detailClass); if (instanceType.isInterface() || (instanceType.isClass() && Flags.isAbstract(instanceType.getFlags()))) { // create anonymous subclass for interfaces and abstract classes newExpression += " {\n\t"; IMethod[] instanceMethods = instanceType.getMethods(); StringBuffer contents = new StringBuffer(); /* Michal: temporarily remove * for (IMethod method : instanceMethods) { if (!method.isConstructor() && (instanceType.isInterface() || Flags.isAbstract(method.getFlags()))) { // if this is an interface, provide empty stub of methods // if this is an abstract class, ONLY provide empty stubs for abstract methods String visibility = ""; if (Flags.isPublic(method.getFlags())) { visibility = "public"; } else if (Flags.isProtected(method.getFlags())) { visibility = "protected"; } contents.append(CodeTransforms.createMethodString(contextIJavaProject, null, contextTypeDeclaration, visibility, method.getElementName(), method.getSignature(), null, progressMonitor)); contents.append("\n"); } } */ contents.append("}"); newExpression += contents.toString(); } String defaultVariant = ""; if (JavaMappingInterpreter.analyzeImplVariants() && feature!=null){ EObject defaultVariantParameters = javaImplVariantManager.getDefault(element.eClass().getName(),feature.getName(),annotation.getSource()); if (defaultVariantParameters==null){ defaultVariant = (initializer!=null && Boolean.parseBoolean(initializer))?JavaImplVariantManagerConstants.INITIALIZER:JavaImplVariantManagerConstants.IN_METHOD; } else { defaultVariant = javaImplVariantManager.getVariantName(defaultVariantParameters); //populate all the relevant values from implModel if (defaultVariant.equals(JavaImplVariantManagerConstants.IN_METHOD)) { locationName = (String)defaultVariantParameters.eGet(defaultVariantParameters.eClass().getEStructuralFeature(JavaImplVariantManagerConstants.METHOD_NAME)); } } } else { defaultVariant = (initializer!=null && Boolean.parseBoolean(initializer))?JavaImplVariantManagerConstants.INITIALIZER:JavaImplVariantManagerConstants.IN_METHOD; } if (defaultVariant.equals(JavaImplVariantManagerConstants.INITIALIZER)) { //variant1 ClassInstanceCreation cic = (ClassInstanceCreation) CodeTransforms.setFieldsInitializer(null, contextVariableDeclarationFragment, newExpression, progressMonitor); contextManager.associateContext(syncItem.getModel(), cic); return true; } else if (defaultVariant.equals(JavaImplVariantManagerConstants.IN_METHOD)){ // variant2 // construct an assignment // construct a call String contents = contextVariableDeclarationFragment.getName().getIdentifier() + " = " + newExpression + ";"; // field assignment can be created in the context method or a context class if (contextMethodDeclaration != null) { List<ASTNode> result = CodeTransforms.weaveAdvice(position, null, contextMethodDeclaration, contents, progressMonitor); contextManager.associateContext(syncItem.getModel(), (ClassInstanceCreation) result.get(0)); return true; } else { // no context method, try context class if (contextTypeDeclaration != null) { MethodDeclaration methodDeclaration = ASTUtils.findMethod(javaAstManager, locationName, locationSig, contextTypeDeclaration, true, false, progressMonitor); if (methodDeclaration == null) // create the method methodDeclaration = CodeTransforms.createMethod(contextIJavaProject, null, contextTypeDeclaration, "public", locationName, locationSig, null, progressMonitor); List<ASTNode> result = CodeTransforms.weaveAdvice(position, null, methodDeclaration, contents, progressMonitor); if (result.get(0) instanceof ExpressionStatement) { ExpressionStatement expressionStatement = (ExpressionStatement) result.get(0); Assignment assignment = (Assignment) expressionStatement.getExpression(); ClassInstanceCreation cic = (ClassInstanceCreation) assignment.getRightHandSide(); contextManager.associateContext(syncItem.getModel(), cic); return true; } } } } } catch (JavaModelException e) { e.printStackTrace(); } return false; } @Override protected boolean reverse() throws FSMLMappingException { if (contextIField == null) return false; try { if (contextVariableDeclarationFragment != null) { // variant 1: initializer Expression initializer = contextVariableDeclarationFragment.getInitializer(); if (initializer instanceof ClassInstanceCreation) { ClassInstanceCreation cic = (ClassInstanceCreation)initializer; // we assume that the type of the constructor call is correct (assignable to the field type) boolean correctType = true; ITypeBinding instanceITypeBinding = cic.getType().resolveBinding(); if (subtypeOf) correctType = ! instanceITypeBinding.getQualifiedName().equals(detailSubtypeOf); else correctType = instanceITypeBinding.getQualifiedName().equals(detailClass); if (correctType) { // begin: implementation variants String name = cic.getType().toString(); Stats.INSTANCE.logImplVariant(element.eClass(), feature, annotation, "initializer"); EClass targetClass = javaImplVariantManager.getVariantEClass(JavaImplVariantManagerConstants.VARIANT+ JavaImplVariantManagerConstants.DELIMITER+ annotation.getSource() + JavaImplVariantManagerConstants.DELIMITER + JavaImplVariantManagerConstants.INITIALIZER); EObject child = EcoreUtil.create(targetClass); ((EList) javaImplVariantManager.getModel().eGet(javaImplVariantManager.getVariantsReference())).add(child); child.eSet(targetClass.getEStructuralFeature(JavaImplVariantManagerConstants.INITIALIZED_TYPE), name); child.eSet(targetClass.getEStructuralFeature(JavaImplVariantManagerConstants.ECLASS), element.eClass().getName()); child.eSet(targetClass.getEStructuralFeature(JavaImplVariantManagerConstants.ESTRUCTURALFEATURE), feature.getName()); // end: implementation variants if (subtypeOf) { IJavaElement fieldIType = cic.getType().resolveBinding().getJavaElement(); if (setFeatureContextAndMarker(true, fieldIType, fieldIType, null, "type")) { setContextAndMarker(cic, cic, "creation"); return true; } } else return setFeatureContextAndMarker(true, cic, cic, null); } } // variant 2: assignment in a method Collection<FieldReferenceMatch> result = JavaModelUtils.writesToField(contextIJavaProject, contextIField, progressMonitor); for (FieldReferenceMatch fieldReferenceMatch : result) { Expression rhs = CodeQueries.retrieveRHSFromMatch(javaAstManager, fieldReferenceMatch, progressMonitor); if (rhs instanceof ClassInstanceCreation) { ClassInstanceCreation cic = (ClassInstanceCreation) rhs; boolean correctType = true; ITypeBinding instanceITypeBinding = cic.getType().resolveBinding(); if (subtypeOf) correctType = ! instanceITypeBinding.getQualifiedName().equals(detailSubtypeOf); else correctType = instanceITypeBinding.getQualifiedName().equals(detailClass); if (correctType) { // end: implementation variants 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.METHOD_NAME), ((ResolvedSourceMethod)fieldReferenceMatch.getElement()).getElementName() ); child.eSet(targetClass.getEStructuralFeature(JavaImplVariantManagerConstants.ECLASS), element.eClass().getName()); child.eSet(targetClass.getEStructuralFeature(JavaImplVariantManagerConstants.ESTRUCTURALFEATURE), feature.getName()); Stats.INSTANCE.logImplVariant(element.eClass(), feature, annotation,"InMethod: "+((ResolvedSourceMethod)fieldReferenceMatch.getElement()).getElementName()); // end: implementation variants if (subtypeOf) { IJavaElement fieldIType = cic.getType().resolveBinding().getJavaElement(); if (setFeatureContextAndMarker(true, fieldIType, fieldIType, null, "type")) { setContextAndMarker(cic, cic, "creation"); return true; } } else return setFeatureContextAndMarker(true, cic, cic, null); } } } } } catch (JavaModelException e) { e.printStackTrace(); } return setFeature(false); } }