/******************************************************************************* * 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.PivotUtil; public class LookupUnqualifiedCodeGenerator extends LookupVisitorsCodeGenerator { // // 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); 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 = PivotUtil.createVariableExp(asContextVariable); asInnerCallExp.setOwnedSource(asContextExp); // asOperationCallExp becomes an orphan Variable asInnerEnv = PivotUtil.createVariable("inner", asOuterCallExp); VariableExp asInnerEnvExp1 = PivotUtil.createVariableExp(asInnerEnv); VariableExp asInnerEnvExp2 = PivotUtil.createVariableExp(asInnerEnv); OperationCallExp asCondition = PivotUtil.createOperationCallExp(asInnerEnvExp1, asEnvironmentHasFinalResultOperation); 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 = PivotUtil.createVariable(LookupVisitorsClassContext.ELEMENT_NAME, asType, true, null); reDefinitions.put(envExpressionInOCL.getOwnedContext(), asElement); // VariableExp asChildSource = createThisVariableExp(asThisVariable); PropertyCallExp asChildAccess = PivotUtil.createPropertyCallExp(asChildSource, ClassUtil.nonNullState(asChildProperty)); Variable asChild = PivotUtil.createVariable(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); } }