blob: 3d15f67feba3f5099d3e1302f1372b07f9ad9822 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 Willink Transformations, University of York and others.
* 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:
* Adolfo Sanchez-Barbudo Herrera (University of York)
*******************************************************************************/
package org.eclipse.ocl.examples.autogen.lookup;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.codegen.ecore.genmodel.GenPackage;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.examples.autogen.java.AutoCG2JavaVisitor;
import org.eclipse.ocl.examples.autogen.java.AutoCodeGenerator;
import org.eclipse.ocl.examples.codegen.cgmodel.CGPackage;
import org.eclipse.ocl.examples.codegen.cgmodel.CGProperty;
import org.eclipse.ocl.examples.codegen.cgmodel.CGValuedElement;
import org.eclipse.ocl.examples.codegen.library.NativeStaticOperation;
import org.eclipse.ocl.pivot.CallExp;
import org.eclipse.ocl.pivot.CompleteClass;
import org.eclipse.ocl.pivot.Element;
import org.eclipse.ocl.pivot.ExpressionInOCL;
import org.eclipse.ocl.pivot.IfExp;
import org.eclipse.ocl.pivot.LetExp;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.OperationCallExp;
import org.eclipse.ocl.pivot.Package;
import org.eclipse.ocl.pivot.PivotPackage;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.PropertyCallExp;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.Variable;
import org.eclipse.ocl.pivot.VariableExp;
import org.eclipse.ocl.pivot.ids.IdManager;
import org.eclipse.ocl.pivot.ids.OperationId;
import org.eclipse.ocl.pivot.ids.ParametersId;
import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.ParserException;
import org.eclipse.ocl.pivot.utilities.PivotHelper;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
public class LookupUnqualifiedCodeGenerator extends LookupVisitorsCodeGenerator {
protected final @NonNull PivotHelper helper;
//
// Expected AS elements
//
protected final @NonNull Operation asEnvironmentHasFinalResultOperation;
protected final @NonNull Operation asEnvironmentNestedEnvOperation;
protected final @NonNull Operation asElementParentEnvOperation;
//
// New AS elements
//
protected Property asChildProperty;
protected Operation asVisitorEnvOperation;
protected Operation asVisitorParentEnvOperation;
// Further required CG elements
private @Nullable CGProperty cgChildProperty = null;
protected LookupUnqualifiedCodeGenerator(
@NonNull EnvironmentFactoryInternal environmentFactory,
@NonNull Package asPackage, @Nullable Package asSuperPackage,
@NonNull Package asBasePackage, @NonNull GenPackage genPackage,
@Nullable GenPackage superGenPackage,
@Nullable GenPackage baseGenPackage) {
this(environmentFactory, asPackage, asSuperPackage, asBasePackage, genPackage,
superGenPackage, baseGenPackage, LookupVisitorsClassContext.UNQUALIFIED_ENV_NAME);
}
protected LookupUnqualifiedCodeGenerator(
@NonNull EnvironmentFactoryInternal environmentFactory,
@NonNull Package asPackage, @Nullable Package asSuperPackage,
@NonNull Package asBasePackage, @NonNull GenPackage genPackage,
@Nullable GenPackage superGenPackage,
@Nullable GenPackage baseGenPackage,
@NonNull String envOperationName) {
super(environmentFactory, asPackage, asSuperPackage, asBasePackage, genPackage,
superGenPackage, baseGenPackage, envOperationName);
this.helper = new PivotHelper(environmentFactory);
ParametersId emptyParametersId = IdManager.getParametersId();
org.eclipse.ocl.pivot.Class asOclElement = metamodelManager.getStandardLibrary().getOclElementType();
CompleteClass asElementCompleteClass = metamodelManager.getCompletePackage(metamodelManager.getStandardLibrary().getPackage()).getCompleteClass(asOclElement);
String parentEnvOpName = ClassUtil.nonNull(envOperationName.replace(LookupVisitorsClassContext.UNQUALIFIED_ENV_NAME, LookupVisitorsClassContext.PARENT_ENV_NAME));
OperationId parentEnvOperationId = asOclElement.getTypeId().getOperationId(0, parentEnvOpName , emptyParametersId);
this.asElementParentEnvOperation = ClassUtil.nonNullState(asElementCompleteClass.getOperation(parentEnvOperationId));
CompleteClass asEnvironmentCompleteClass = metamodelManager.getCompleteClass(asEnvironmentType);
OperationId nestedEnvOperationId = asEnvironmentType.getTypeId().getOperationId(0, LookupVisitorsClassContext.NESTED_ENV_NAME, emptyParametersId);
this.asEnvironmentNestedEnvOperation = ClassUtil.nonNullState(asEnvironmentCompleteClass.getOperation(nestedEnvOperationId));
OperationId hasFinalResultOperationId = asEnvironmentType.getTypeId().getOperationId(0, LookupVisitorsClassContext.HAS_FINAL_RESULT_NAME, emptyParametersId);
this.asEnvironmentHasFinalResultOperation = ClassUtil.nonNullState(asEnvironmentCompleteClass.getOperation(hasFinalResultOperationId));
}
@Override
protected @NonNull AutoCG2JavaVisitor<@NonNull ? extends AutoCodeGenerator> createCG2JavaVisitor(
@NonNull CGPackage cgPackage,
@Nullable List<CGValuedElement> sortedGlobals) {
return new LookupUnqualifiedCG2JavaVisitor(this, cgPackage, sortedGlobals);
}
@Override
@NonNull
protected String getLookupVisitorClassName(@NonNull String prefix) {
// Extract type name.
String typeName = extractTypeNameFromEnvOp(LookupVisitorsClassContext.UNQUALIFIED_ENV_NAME);
return prefix + "Unqualified" + typeName + "LookupVisitor";
}
@Override
protected List<Property> createAdditionalASProperties() {
Type asOclElement = metamodelManager.getStandardLibrary().getOclElementType();
this.asChildProperty = createNativeProperty(LookupVisitorsClassContext.CHILD_NAME, asOclElement, false, true);
return Collections.singletonList(asChildProperty);
}
@Override
protected Collection<? extends Operation> createAdditionalASOperations() {
List<Operation> result = new ArrayList<Operation>();
Type asOclElement = metamodelManager.getStandardLibrary().getOclElementType();
this.asVisitorEnvOperation = PivotUtil.createOperation(envOperationName, asEnvironmentType, null, null);
asVisitorEnvOperation.getOwnedParameters().add(PivotUtil.createParameter(LookupVisitorsClassContext.ELEMENT_NAME, asOclElement, true));
asVisitorEnvOperation.getOwnedParameters().add(PivotUtil.createParameter(LookupVisitorsClassContext.CHILD_NAME, asOclElement, false));
this.asVisitorParentEnvOperation = PivotUtil.createOperation(LookupVisitorsClassContext.PARENT_ENV_NAME, asEnvironmentType, null, null);
asVisitorParentEnvOperation.getOwnedParameters().add(PivotUtil.createParameter(LookupVisitorsClassContext.ELEMENT_NAME, asOclElement, true));
asVisitorParentEnvOperation.setImplementation(NativeStaticOperation.INSTANCE);
asVisitorParentEnvOperation.setIsRequired(false);
result.add(asVisitorEnvOperation);
result.add(asVisitorParentEnvOperation);
return result;
}
/**
* Convert source.env(child) to this.env(source, child)
*/
protected void rewriteEnvOperationCall(@NonNull OperationCallExp asOperationCallExp, @NonNull Operation asVisitorEnvOperation, @NonNull Variable asThisVariable) {
OCLExpression asSource = asOperationCallExp.getOwnedSource();
asOperationCallExp.setOwnedSource(createThisVariableExp(asThisVariable));
asOperationCallExp.getOwnedArguments().add(0, asSource);
asOperationCallExp.setReferredOperation(asVisitorEnvOperation);
}
/**
* Convert "source.nestedEnv().r.e.s.i.d.u.e" to
* "let innerEnv = this.context.r.e.s.i.d.u.e in if innerEnv.hasFinalResult() then innerEnv else source endif"
* where r.e.s.i.d.u.e does not include any nestedEnv() call.
*
* "source.nestedEnv()" to "source"
*/
protected void rewriteNestedEnvOperationCall(@NonNull OperationCallExp asOperationCallExp) {
CallExp asOuterCallExp = asOperationCallExp;
for (EObject eContainer; (asOuterCallExp.eContainmentFeature() == PivotPackage.Literals.CALL_EXP__OWNED_SOURCE) && ((eContainer = asOuterCallExp.eContainer()) != null); asOuterCallExp = (CallExp)eContainer) {
if (eContainer instanceof OperationCallExp) {
OperationCallExp asParentOperationCallExp = (OperationCallExp)eContainer;
Operation asReferredOperation = asParentOperationCallExp.getReferredOperation();
if (asReferredOperation == asEnvironmentNestedEnvOperation) {
break;
}
}
}
EObject eContainer = asOuterCallExp.eContainer();
EReference eContainingFeature = asOuterCallExp.eContainmentFeature(); // This is not isMany()
eContainer.eSet(eContainingFeature, null); // asOuterCallExp becomes an orphan
OCLExpression asSource = ClassUtil.nonNullState(asOperationCallExp.getOwnedSource());
if (asOuterCallExp != asOperationCallExp) {
CallExp asInnerCallExp = (CallExp)asOperationCallExp.eContainer();
VariableExp asContextExp = helper.createVariableExp(asContextVariable);
asInnerCallExp.setOwnedSource(asContextExp); // asOperationCallExp becomes an orphan
Variable asInnerEnv = helper.createLetVariable("inner", asOuterCallExp);
VariableExp asInnerEnvExp1 = helper.createVariableExp(asInnerEnv);
VariableExp asInnerEnvExp2 = helper.createVariableExp(asInnerEnv);
OperationCallExp asCondition = helper.createOperationCallExp(asInnerEnvExp1, asEnvironmentHasFinalResultOperation, Collections.emptyList());
IfExp asIfExp = metamodelManager.createIfExp(asCondition, asInnerEnvExp2, asSource);
LetExp asLetExp = PivotUtil.createLetExp(asInnerEnv, asIfExp);
eContainer.eSet(eContainingFeature, asLetExp);
}
else {
eContainer.eSet(eContainingFeature, asSource);
}
}
/**
* Convert source.parentEnv() to this.parentEnv(source)
*/
protected void rewriteParentEnvOperationCall(@NonNull OperationCallExp asOperationCallExp) {
OCLExpression asSource = asOperationCallExp.getOwnedSource();
asOperationCallExp.setOwnedSource(createThisVariableExp(asThisVariable));
asOperationCallExp.getOwnedArguments().add(asSource);
asOperationCallExp.setReferredOperation(asVisitorParentEnvOperation);
}
protected boolean sameOrRedefiningOperation(@NonNull Operation redefiningOperation, @NonNull Operation baseOperation) {
// calledOperation.getRedefinedOperations().contains(baseOperation);
// Note: the own operation seems to be an "overload"
for (Operation redefinedOp : metamodelManager.getOperationOverloads(redefiningOperation)) {
if (baseOperation == redefinedOp) {
return true;
}
}
return false;
}
@Override
protected void rewriteOperationCalls(@NonNull Collection<? extends EObject> allContents) {
for (Map.Entry<EObject, Collection<EStructuralFeature.Setting>> entry : EcoreUtil.CrossReferencer.find(allContents).entrySet()) {
EObject crossReference = entry.getKey();
if (crossReference instanceof Operation) {
Operation asOperation = metamodelManager.getPrimaryOperation((Operation)crossReference);
if (sameOrRedefiningOperation(asOperation, asElementEnvOperation)) {
for (EStructuralFeature.Setting setting : entry.getValue()) {
EObject eObject = setting.getEObject();
if (eObject instanceof OperationCallExp) {
rewriteEnvOperationCall((OperationCallExp)eObject, ClassUtil.nonNullState(asVisitorEnvOperation)
, asThisVariable);
}
}
}
else if (sameOrRedefiningOperation(asOperation, asEnvironmentNestedEnvOperation)) {
for (EStructuralFeature.Setting setting : entry.getValue()) {
EObject eObject = setting.getEObject();
if (eObject instanceof OperationCallExp) {
rewriteNestedEnvOperationCall((OperationCallExp)eObject);
}
}
}
else if (sameOrRedefiningOperation(asOperation, asElementParentEnvOperation)) {
for (EStructuralFeature.Setting setting : entry.getValue()) {
EObject eObject = setting.getEObject();
if (eObject instanceof OperationCallExp) {
rewriteParentEnvOperationCall((OperationCallExp)eObject);
}
}
}
}
}
}
@Override
protected boolean isRewrittenOperation(Operation operation) {
return envOperationName.equals(operation.getName())
&& operation != asElementEnvOperation
&& operation.getOwnedParameters().size() ==1;
}
/**
* Convert 'Element'::_env(child : Element) : Environment
* to AutoPivotLookupVisitor::visit'Element'(element : 'Element') : Environment
*
* with
* - self accessed as element.
* - child accessed as this.child.
* @throws ParserException
*/
@Override
protected @NonNull Operation createVisitOperationDeclaration(
Map<Element, Element> reDefinitions, Operation operation) {
ExpressionInOCL envExpressionInOCL = getExpressionInOCL(operation);
//
org.eclipse.ocl.pivot.Class asType = ClassUtil.nonNullState(operation.getOwningClass());
Variable asElement = helper.createParameterVariable(LookupVisitorsClassContext.ELEMENT_NAME, asType, true);
reDefinitions.put(envExpressionInOCL.getOwnedContext(), asElement);
//
VariableExp asChildSource = createThisVariableExp(asThisVariable);
PropertyCallExp asChildAccess = PivotUtil.createPropertyCallExp(asChildSource, ClassUtil.nonNullState(asChildProperty));
Variable asChild = helper.createLetVariable(LookupVisitorsClassContext.CHILD_NAME, asChildAccess);
reDefinitions.put(envExpressionInOCL.getOwnedParameters().get(0), asChild);
//
Operation asOperation = createVisitorOperation("visit" + asType.getName(), operation.getType());
reDefinitions.put(operation, asOperation);
return asOperation;
}
@Override
protected void trackCGProperty(Property asProperty, CGProperty cgProperty) {
if (asProperty == asChildProperty) {
cgChildProperty = cgProperty;
}
}
public @NonNull CGProperty getChildProperty() {
return ClassUtil.nonNullState(cgChildProperty);
}
}