/*******************************************************************************
* Copyright (c) 2014, 2015 Willink Transformations 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:
* E.D.Willink - Initial API and implementation
* Adolfo Sanchez-Barbudo Herrera (University of York) - Lookup Environment/Visitor
*******************************************************************************/
package org.eclipse.ocl.examples.autogen.lookup;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
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.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.examples.autogen.java.AutoCG2JavaPreVisitor;
import org.eclipse.ocl.examples.autogen.java.AutoVisitorsCodeGenerator;
import org.eclipse.ocl.examples.codegen.analyzer.AS2CGVisitor;
import org.eclipse.ocl.examples.codegen.cgmodel.CGClass;
import org.eclipse.ocl.examples.codegen.cgmodel.CGModel;
import org.eclipse.ocl.examples.codegen.cgmodel.CGModelFactory;
import org.eclipse.ocl.examples.codegen.cgmodel.CGOperation;
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.cgmodel.CGVariable;
import org.eclipse.ocl.examples.codegen.java.CG2JavaPreVisitor;
import org.eclipse.ocl.examples.codegen.java.JavaConstants;
import org.eclipse.ocl.examples.codegen.library.NativeVisitorOperation;
import org.eclipse.ocl.examples.codegen.utilities.RereferencingCopier;
import org.eclipse.ocl.pivot.CompleteClass;
import org.eclipse.ocl.pivot.Element;
import org.eclipse.ocl.pivot.ExpressionInOCL;
import org.eclipse.ocl.pivot.LanguageExpression;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.PivotFactory;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.Variable;
import org.eclipse.ocl.pivot.VariableExp;
import org.eclipse.ocl.pivot.evaluation.Executor;
import org.eclipse.ocl.pivot.ids.IdManager;
import org.eclipse.ocl.pivot.ids.IdResolver;
import org.eclipse.ocl.pivot.ids.OperationId;
import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.NameUtil;
import org.eclipse.ocl.pivot.utilities.ParserException;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
/**
* LookupCodeGenerator supports generation of the content of a JavaClassFile for the Lookup visitor.
*/
public abstract class LookupVisitorsCodeGenerator extends AutoVisitorsCodeGenerator
{
protected final @NonNull String packageName;
protected final @NonNull String visitorClassName;
protected final @NonNull LookupVisitorsClassContext classContext;
protected final @NonNull AS2CGVisitor as2cgVisitor;
protected final @NonNull String envOperationName;
//
// Expected AS elements
//
protected final @NonNull Operation asElementEnvOperation;
protected final org.eclipse.ocl.pivot.@NonNull Class asEnvironmentType;
//
// New AS elements
//
protected final org.eclipse.ocl.pivot.@NonNull Class asVisitorClass;
protected final @NonNull Variable asThisVariable;
protected final @NonNull Variable asContextVariable;
protected final @NonNull Property asEvaluatorProperty;
protected final @NonNull Property asIdResolverProperty;
//
// Important CG elements
//
private @Nullable CGProperty cgEvaluatorVariable = null;
private @Nullable CGProperty cgIdResolverVariable = null;
protected LookupVisitorsCodeGenerator(@NonNull EnvironmentFactoryInternal environmentFactory, org.eclipse.ocl.pivot.@NonNull Package asPackage,
org.eclipse.ocl.pivot.@Nullable Package asSuperPackage, org.eclipse.ocl.pivot.@NonNull Package asBasePackage, @NonNull GenPackage genPackage,
@Nullable GenPackage superGenPackage, @Nullable GenPackage baseGenPackage, @NonNull String envOpName) {
super(environmentFactory, asPackage, asSuperPackage, genPackage, superGenPackage, baseGenPackage);
this.envOperationName = envOpName;
this.packageName = getSourcePackageName();
this.visitorClassName = getLookupVisitorClassName(getProjectPrefix());
this.classContext = new LookupVisitorsClassContext(this, asPackage);
this.as2cgVisitor = createAS2CGVisitor();
//
// Find expected AS elements
//
org.eclipse.ocl.pivot.Class asOclElement = metamodelManager.getStandardLibrary().getOclElementType();
// org.eclipse.ocl.pivot.Class asOclAny = metamodelManager.getStandardLibrary().getOclAnyType();
CompleteClass asElementCompleteClass = metamodelManager.getCompletePackage(metamodelManager.getStandardLibrary().getPackage()).getCompleteClass(asOclElement);
OperationId envOperationId = asOclElement.getTypeId().getOperationId(0, envOpName, IdManager.getParametersId(asOclElement.getTypeId()));
this.asElementEnvOperation = ClassUtil.nonNullState(asElementCompleteClass.getOperation(envOperationId));
this.asEnvironmentType = ClassUtil.nonNullState(asElementEnvOperation.getType().isClass());
//
// Create new AS elements
//
org.eclipse.ocl.pivot.Package asVisitorPackage = createASPackage(packageName);
this.asVisitorClass = createASClass(asVisitorPackage, visitorClassName);
this.asThisVariable = PivotUtil.createVariable("this", asVisitorClass, true, null);
this.asContextVariable = PivotUtil.createVariable(LookupVisitorsClassContext.CONTEXT_NAME, asEnvironmentType, true, null);
CGVariable cgVariable = as2cgVisitor.getVariable(asContextVariable);
nameManager.reserveName(LookupVisitorsClassContext.CONTEXT_NAME, cgVariable);
//
// Create new AS Visitor helper properties
//
this.asEvaluatorProperty = createNativeProperty(JavaConstants.EXECUTOR_NAME, Executor.class, true, true);
this.asIdResolverProperty = createNativeProperty(JavaConstants.ID_RESOLVER_NAME, IdResolver.class, true, true);
List<Property> asVisitorProperties = asVisitorClass.getOwnedProperties();
asVisitorProperties.add(asEvaluatorProperty);
asVisitorProperties.add(asIdResolverProperty);
asVisitorProperties.addAll(createAdditionalASProperties());
//
// Create new AS Visitor helper operations
//
List<Operation> asVisitorOperations = asVisitorClass.getOwnedOperations();
asVisitorOperations.addAll(createAdditionalASOperations());
}
protected Collection<? extends Operation> createAdditionalASOperations() {
// By default no additional properties
return Collections.emptyList();
}
protected List<Property> createAdditionalASProperties() {
// By default no additional properties
return Collections.emptyList();
}
/**
* Convert the construction context to supertypes/interfaces of cgClass.
*/
protected void convertSuperTypes(@NonNull CGClass cgClass) {
String superClassName = "Abstract" + getProjectPrefix() + "CommonLookupVisitor"; // The default Abstract Visitor generated for the language
CGClass superClass = getExternalClass(getVisitorPackageName(), superClassName, false);
cgClass.getSuperTypes().add(superClass);
}
@Override
public @NonNull CG2JavaPreVisitor createCG2JavaPreVisitor() {
return new AutoCG2JavaPreVisitor(classContext);
}
/**
* Synthesize an AS package by simple AS2AS conversions and convert the AS package to a CG package for onward code generation.
* @throws ParserException
*/
@Override
protected @NonNull List<CGPackage> createCGPackages() throws ParserException {
CGModel cgModel = CGModelFactory.eINSTANCE.createCGModel();
String visitorPackage = getSourcePackageName();
Map<Element,Element> reDefinitions = new HashMap<Element,Element>();
createCGPackageForVisitor(cgModel, visitorPackage, visitorClassName, asVisitorClass,
createVisitOperationDeclarations(reDefinitions), reDefinitions);
List<CGPackage> result = new ArrayList<CGPackage>();
result.addAll(cgModel.getPackages());
return result;
}
protected void createCGPackageForVisitor(@NonNull CGModel cgModel, @NonNull String packageName, @NonNull String classNAme, org.eclipse.ocl.pivot.@NonNull Class asClass,
@NonNull Map<Operation, @NonNull Operation> envOperation2asOperation, @NonNull Map<Element,Element> reDefinitions) throws ParserException {
CGPackage cgPackage = CGModelFactory.eINSTANCE.createCGPackage();
cgPackage.setName(getVisitorPackageName());
cgModel.getPackages().add(cgPackage);
CGClass cgClass = CGModelFactory.eINSTANCE.createCGClass();
cgClass.setName(classNAme);
cgPackage.getClasses().add(cgClass);
convertSuperTypes(cgClass);
convertProperties(cgClass, asClass.getOwnedProperties());
rewriteVisitOperationBodies(reDefinitions, envOperation2asOperation);
Collection<Operation> asOperations = envOperation2asOperation.values();
rewriteOperationCalls(asOperations);
convertOperations(cgClass, asOperations);
/*Resource dummyResource = EssentialOCLASResourceFactory.getInstance().createResource(URI.createURI("dummy.essentialocl"));
dummyResource.getContents().addAll(asOperations); // PrettyPrinter needs containment*/
}
/**
* Convert the asOperations to cgOperations of cgClass.
*/
protected void convertOperations(@NonNull CGClass cgClass, @NonNull Collection<Operation> asOperations) {
List<Operation> sortedOperations = new ArrayList<Operation>(asOperations);
Collections.sort(sortedOperations, NameUtil.NAMEABLE_COMPARATOR);
for (Operation asOperation : sortedOperations) {
CGOperation cgOperation = as2cgVisitor.doVisit(CGOperation.class, asOperation);
cgClass.getOperations().add(cgOperation);
}
}
/**
* Convert the asProperties to cgProperties of cgClass.
*/
protected void convertProperties(@NonNull CGClass cgClass, @NonNull List<Property> asProperties) {
for (Property asProperty : asProperties) {
CGProperty cgProperty = as2cgVisitor.doVisit(CGProperty.class, asProperty);
cgClass.getProperties().add(cgProperty);
if (asProperty == asEvaluatorProperty) {
cgEvaluatorVariable = cgProperty;
}
else if (asProperty == asIdResolverProperty) {
cgIdResolverVariable = cgProperty;
}
else {
trackCGProperty(asProperty, cgProperty);
}
}
}
/**
* Give a chance to derived lookup visitors to track the created CGProperty
*
* @param asProperty
* @param cgProperty
*/
protected void trackCGProperty(Property asProperty, CGProperty cgProperty) {
// By default do nothing
}
protected @NonNull Map<Operation, @NonNull Operation> createVisitOperationDeclarations(
Map<Element, Element> reDefinitions) {
Map<Operation, @NonNull Operation> oldOperation2rewrittenOperation = new HashMap<Operation, @NonNull Operation>();
for (@SuppressWarnings("null")org.eclipse.ocl.pivot.@NonNull Class asType : asPackage.getOwnedClasses()) {
for (Operation oldOperation : asType.getOwnedOperations()) {
if (isRewrittenOperation(oldOperation)) {
Operation asOperation = createVisitOperationDeclaration(reDefinitions, oldOperation);
oldOperation2rewrittenOperation.put(oldOperation, asOperation);
reDefinitions.put(oldOperation, asOperation);
}
}
}
return oldOperation2rewrittenOperation;
}
abstract protected boolean isRewrittenOperation(Operation operation);
abstract protected @NonNull Operation createVisitOperationDeclaration(Map<Element, Element> reDefinitions, Operation operation);
/**
* Helper operation to be used by specific lookup visitor generators
*
* @param opName
* @param resultType
* @return
*/
protected @NonNull Operation createVisitorOperation(String opName, Type resultType) {
Operation asOperation = PivotFactory.eINSTANCE.createOperation();
asOperation.setName(opName);
asOperation.setImplementation(NativeVisitorOperation.INSTANCE);
asOperation.setType(resultType);
return asOperation;
}
protected @NonNull VariableExp createThisVariableExp(@NonNull Variable thisVariable) {
return PivotUtil.createVariableExp(thisVariable);
}
abstract protected @NonNull String getLookupVisitorClassName(@NonNull String prefix);
public @NonNull CGValuedElement getEvaluatorVariable() {
return ClassUtil.nonNullState(cgEvaluatorVariable);
}
@Override
public @NonNull LookupVisitorsClassContext getGlobalContext() {
return classContext;
}
@Override
public @NonNull CGValuedElement getIdResolverVariable() {
return ClassUtil.nonNullState(cgIdResolverVariable);
}
protected @NonNull String getSuperLookupVisitorClassName() {
String superProjectPrefix = ClassUtil.nonNullState(getSuperProjectPrefix());
String superLangVisitorName = getLookupVisitorClassName(superProjectPrefix);
return getSuperVisitorPackageName() + "." + superLangVisitorName;
}
@Override
public @NonNull Class<?> getVisitorResultClass() {
return getEnvironmentClass();
}
public @NonNull Class<?> getEnvironmentClass() {
return genModelHelper.getEcoreInterfaceClass(asEnvironmentType);
}
@Override
protected @NonNull String getSourcePackageName() {
return getVisitorPackageName();
}
protected @NonNull String getSuperSourcePackageName() {
return getSuperVisitorPackageName();
}
@SuppressWarnings("null")
protected @NonNull String extractTypeNameFromEnvOp(@NonNull String envOpName) {
boolean isGeneralLookup = envOperationName.equals(envOpName);
return isGeneralLookup ? "" : envOperationName.substring(envOpName.length() + 1 /*extra underscore */);
}
/**
* Copy all the visitXXX operation bodies from the _env bodies replacing references to redefined parameters.
*/
protected void rewriteVisitOperationBodies(@NonNull Map<Element, Element> reDefinitions, @NonNull Map<Operation, @NonNull Operation> envOperation2asOperation) throws ParserException {
for (@SuppressWarnings("null")@NonNull Operation envOperation : envOperation2asOperation.keySet()) {
Operation asOperation = envOperation2asOperation.get(envOperation);
assert asOperation != null;
LanguageExpression envSpecification = ClassUtil.nonNullState(envOperation.getBodyExpression());
ExpressionInOCL envExpressionInOCL = metamodelManager.parseSpecification(envSpecification);
Variable asElement = (Variable) reDefinitions.get(envExpressionInOCL.getOwnedContext());
OCLExpression asExpression = RereferencingCopier.copy(ClassUtil.nonNullState(envExpressionInOCL.getOwnedBody()), reDefinitions);
ExpressionInOCL asExpressionInOCL = PivotUtil.createExpressionInOCL(null, asExpression, asElement);
PivotUtil.initOperation(asOperation, asExpressionInOCL);
asOperation.setType(asEnvironmentType);
asOperation.setIsRequired(false);
}
}
/**
* Replace selected OperationCallExps by alternative implementations
*
* Gives a chance to specific lookup visitor to also rewrite operation calls of rewrite operations
*
* By default no rewrite takes place
*/
protected void rewriteOperationCalls(@NonNull Collection<? extends EObject> allContents) {
// By default no operation call rewrite takes place
}
}