blob: 2fcefe7a4271a1da94e8d78acc95d7b4c4de166d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 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 v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Adolfo Sanchez-Barbudo Herrera (University of York) - initial API and implementation
*******************************************************************************/
package org.eclipse.ocl.examples.autogen.xtend
import java.util.ArrayList
import java.util.List
import org.eclipse.emf.codegen.ecore.genmodel.GenClassifier
import org.eclipse.emf.codegen.ecore.genmodel.GenPackage
import org.eclipse.emf.ecore.EPackage
import org.eclipse.emf.mwe.core.issues.Issues
import org.eclipse.ocl.examples.codegen.generator.AbstractGenModelHelper
import org.eclipse.ocl.examples.codegen.generator.GenModelHelper
import org.eclipse.ocl.pivot.Class
import org.eclipse.ocl.pivot.Operation
import org.eclipse.ocl.pivot.Type
import org.eclipse.ocl.pivot.internal.manager.PivotMetamodelManager
import org.eclipse.ocl.pivot.internal.utilities.PivotUtilInternal
import org.eclipse.ocl.pivot.utilities.ClassUtil
import org.eclipse.ocl.pivot.utilities.EnvironmentFactory
import org.eclipse.ocl.pivot.CollectionType
import org.eclipse.ocl.pivot.utilities.OCL
import org.eclipse.ocl.examples.build.utilities.GenPackageHelper
import org.eclipse.ocl.examples.autogen.lookup.LookupCodeGenerator
import org.eclipse.ocl.xtext.completeocl.CompleteOCLStandaloneSetup
import org.eclipse.ocl.pivot.model.OCLstdlib
import java.util.Set
import org.eclipse.ocl.examples.autogen.lookup.LookupCGUtil
import org.eclipse.ocl.examples.build.xtend.GenerateVisitorsXtend
import org.eclipse.ocl.examples.build.xtend.MergeWriter
class GenerateAutoLookupInfrastructureXtend extends GenerateVisitorsXtend
{
protected String lookupFilePath;
// FIXME The lookupVisitor will be in the normal visitors folder, but all the artifacts generated by this component
// will be in a different one
protected String lookupArtifactsJavaPackage;
protected String lookupArtifactsOutputFolder;
protected String lookupPackageName;
protected String superLookupPackageName
protected String baseLookupPackageName
protected GenModelHelper genModelHelper;
override checkConfiguration(Issues issues) {
super.checkConfiguration(issues);
if (!isDefined(lookupFilePath)) {
issues.addError(this, "lookupFilePath must be specified");
}
}
/**
* The path inside the projectName to the Complete OCL file which contains the
* name resolution description (e.g. "model/NameResolution.ocl"). It
* can't be null
*/
def setLookupFilePath(String lookupFilePath) {
this.lookupFilePath = lookupFilePath;
}
def setLookupPackageName(String lookupPackageName) {
this.lookupPackageName = lookupPackageName;
}
def setSuperLookupPackageName(String superLookupPackageName) {
this.superLookupPackageName = superLookupPackageName;
}
def setBaseLookupPackageName(String baseLookupPackageName) {
this.baseLookupPackageName = baseLookupPackageName;
}
protected override doSetup() {
CompleteOCLStandaloneSetup.doSetup();
OCLstdlib.install();
}
override protected doPropertiesConfiguration(OCL ocl) {
super.doPropertiesConfiguration(ocl);
val genModelURI = getGenModelURI(projectName, genModelFile);
val genModelResource = getGenModelResource(ocl, genModelURI);
val genPackage = getGenPackage(genModelResource);
var helper = new GenPackageHelper(genPackage);
if (!lookupPackageName.isDefined || lookupPackageName.length == 0) {
lookupPackageName = helper.modelPackageName + ".lookup"
}
if (isDerived()) {
val superGenModelURI = getGenModelURI(superProjectName, superGenModelFile);
val superGenModelResource = getGenModelResource(ocl, superGenModelURI);
val superGenPackage = getGenPackage(superGenModelResource);
helper = new GenPackageHelper(superGenPackage)
if (!superLookupPackageName.isDefined || superLookupPackageName.length == 0) {
superLookupPackageName = helper.modelPackageName + ".lookup"
}
val baseGenModelURI = getGenModelURI(baseProjectName, baseGenModelFile);
val baseGenModelResource = getGenModelResource(ocl, baseGenModelURI);
val baseGenPackage = getGenPackage(baseGenModelResource);
helper = new GenPackageHelper(baseGenPackage);
if (!baseLookupPackageName.isDefined || baseLookupPackageName.length == 0) {
baseLookupPackageName = helper.modelPackageName + ".lookup"
}
} else {
if (!baseLookupPackageName.isDefined || baseLookupPackageName.length == 0) {
baseLookupPackageName = lookupPackageName;
}
}
}
@SuppressWarnings("null")
protected def doGenerateVisitors(/*@NonNull*/ GenPackage genPackage) {
LookupCodeGenerator.generate(genPackage, superGenPackage, baseGenPackage, projectName, lookupFilePath,
lookupPackageName, lookupPackageName,null); // FIXME
}
protected def void generateAutoLookupResultItf(/*@NonNull*/ EPackage ePackage) {
var boolean isDerived = isDerived();
if (!isDerived) {
var MergeWriter writer = new MergeWriter(lookupArtifactsOutputFolder + projectPrefix + "LookupResult.java");
writer.append('''
«ePackage.generateHeaderWithTemplate(lookupArtifactsJavaPackage)»
import java.util.List;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
/**
* The lookup result returned by the name lookup solver
*/
public interface «projectPrefix»LookupResult<NE> {
@Nullable
NE getSingleResult();
@NonNull
List<NE> getAllResults();
int size();
}
''');
writer.close();
}
}
protected def void generateAutoLookupResultClass(/*@NonNull*/ EPackage ePackage) {
var boolean isDerived = isDerived();
if (!isDerived) {
var MergeWriter writer = new MergeWriter(lookupArtifactsOutputFolder + projectPrefix + "LookupResultImpl.java");
writer.append('''
«ePackage.generateHeaderWithTemplate(lookupArtifactsJavaPackage)»
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
public class «projectPrefix»LookupResultImpl<NE> implements «projectPrefix»LookupResult<NE> {
private @NonNull List<NE> results = new ArrayList<NE>();
public «projectPrefix»LookupResultImpl(List<NE> results){
this.results.addAll(results);
}
@Override
public @NonNull List<NE> getAllResults() {
return Collections.unmodifiableList(results);
}
@Override
public @Nullable NE getSingleResult() {
return results.size() == 0 ? null : results.get(0);
}
@Override
public int size() {
return results.size();
}
}
''');
writer.close();
}
}
protected def void generateAutoLookupFilterItf(/*@NonNull*/ EPackage ePackage) {
val boolean isDerived = isDerived();
if (!isDerived) {
val itfName = '''«projectPrefix»LookupFilter'''
val MergeWriter writer = new MergeWriter(lookupArtifactsOutputFolder + itfName + ".java");
writer.append('''
«ePackage.generateHeaderWithTemplate(lookupArtifactsJavaPackage)»
import org.eclipse.jdt.annotation.NonNull;
import «modelPackageName».NamedElement;
/**
*
*/
public interface «itfName» {
boolean matches(@NonNull NamedElement namedElement);
}
''');
writer.close();
}
}
protected def void generateAutoLookupFilterClass(/*@NonNull*/ EPackage ePackage) {
val boolean isDerived = isDerived();
if (!isDerived) {
val className = '''Abstract«projectPrefix»LookupFilter''';
val itfName = '''«projectPrefix»LookupFilter'''
val MergeWriter writer = new MergeWriter(lookupArtifactsOutputFolder + className + ".java");
writer.append('''
«ePackage.generateHeaderWithTemplate(lookupArtifactsJavaPackage)»
import org.eclipse.jdt.annotation.NonNull;
import «modelPackageName».NamedElement;
/**
*
*/
public abstract class «className»<C extends NamedElement> implements «itfName» {
@NonNull private Class<C> _class;
public «className»(@NonNull Class<C> _class) {
this._class = _class;
}
@SuppressWarnings("unchecked")
@Override
public boolean matches(@NonNull NamedElement namedElement) {
return _class.isInstance(namedElement) && _matches((C)namedElement);
}
abstract protected Boolean _matches(@NonNull C element);
}
''');
writer.close();
}
}
protected def void generateSingleResultLookupEnvironment(/*@NonNull*/ EPackage ePackage) {
var boolean isDerived = isDerived();
var className = projectPrefix + "SingleResultLookupEnvironment";
if (!isDerived) {
var MergeWriter writer = new MergeWriter(lookupArtifactsOutputFolder + className+ ".java");
writer.append('''
«ePackage.generateHeaderWithTemplate(lookupArtifactsJavaPackage)»
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.evaluation.Executor;
import «modelPackageName».NamedElement;
import «lookupPackageName».LookupEnvironment;
import «lookupPackageName».impl.LookupEnvironmentImpl;
public class «className» extends LookupEnvironmentImpl {
private @NonNull Executor executor;
private @NonNull String name;
private @NonNull EClass typeFilter;
private @Nullable «projectPrefix»LookupFilter expFilter;
public «className»(@NonNull Executor executor, /*@NonNull*/ EClass typeFilter, /*@NonNull*/ String name, @Nullable «projectPrefix»LookupFilter expFilter) {
assert typeFilter != null;
assert name != null;
this.executor = executor;
this.name = name;
this.typeFilter = typeFilter;
this.expFilter = expFilter;
}
public «className»(@NonNull Executor executor, /*@NonNull*/ EClass typeFilter, /*@NonNull*/ String name) {
this(executor, typeFilter, name, null);
}
@Override
@NonNull
public Executor getExecutor() {
return executor;
}
@Override
public boolean hasFinalResult() {
for (NamedElement element : getNamedElements()) {
if (name.equals(element.getName())) {
return true;
}
}
return false;
}
@Override
@NonNull
public LookupEnvironment addElement(@Nullable NamedElement namedElement) {
if (namedElement != null) {
if (name.equals(namedElement.getName())) {
if (typeFilter.isInstance(namedElement)) {
«projectPrefix»LookupFilter expFilter2 = expFilter;
if (expFilter2 == null || expFilter2.matches(namedElement)) {
List<NamedElement> elements = getNamedElements();
if (!elements.contains(namedElement)) { // FIXME use a set ?
elements.add(namedElement);
}
}
}
}
}
return this;
}
@Override
@NonNull
public <NE extends NamedElement > LookupEnvironment addElements(
@Nullable Collection<NE> namedElements) {
if (namedElements != null) {
for (NamedElement namedElement : namedElements) {
addElement(namedElement);
}
}
return this;
}
@SuppressWarnings("unchecked")
public <NE extends NamedElement> List<NE> getNamedElementsByKind(Class<NE> class_) {
List<NE> result = new ArrayList<NE>();
for (NamedElement namedElement : getNamedElements()) {
if (class_.isAssignableFrom(namedElement.getClass())) {
result.add((NE)namedElement);
}
}
return result;
}
}
''');
writer.close();
}
}
protected def void generateAutoLookupSolver(GenPackage genPackage, GenPackage basePackage) {
var EPackage ePackage = genPackage.getEcorePackage;
var String fqPackageItf = genModelHelper.getQualifiedPackageInterfaceName(ePackage)
var List<Operation> lookupOps = basePackage.lookupMethods;
var boolean isDerived = isDerived();
var String className = projectPrefix + "LookupSolver";
var String superClassName = superProjectPrefix + "LookupSolver";
var MergeWriter writer = new MergeWriter(lookupArtifactsOutputFolder + className + ".java");
writer.append('''
«ePackage.generateHeaderWithTemplate(lookupArtifactsJavaPackage)»
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.ocl.pivot.evaluation.Executor;
«FOR typeName : lookupOps.getLookupTypeNames("Unqualified")»
import «visitorPackageName».«projectPrefix»Unqualified«typeName»LookupVisitor;
«ENDFOR»
«FOR typeName : lookupOps.getLookupTypeNames("Exported")»
import «visitorPackageName».«projectPrefix»Exported«typeName»LookupVisitor;
«ENDFOR»
«FOR typeName : lookupOps.getLookupTypeNames("Qualified")»
import «visitorPackageName».«projectPrefix»Qualified«typeName»LookupVisitor;
«ENDFOR»
«IF isDerived»
import «superLookupPackageName».util.«superClassName»;
import «baseLookupPackageName».util.«basePackage.prefix»LookupResult;
import «baseLookupPackageName».util.«basePackage.prefix»LookupResultImpl;
import «baseLookupPackageName».util.«basePackage.prefix»SingleResultLookupEnvironment;
«ENDIF»
public class «className»«IF isDerived» extends «superClassName»«ENDIF» {
«IF !isDerived»protected final @NonNull Executor executor;
«ENDIF»
public «className» (@NonNull Executor executor) {
«IF isDerived»
super(executor);
«ELSE»
this.executor = executor;
«ENDIF»
}
«FOR op : lookupOps»
«val opName = op.name»
«val isExportedLookup = op.exportedLookupOperation»
«val lookupVisitorName = op.getLookupVisitorName(op.type.name)»
«val hasAdditionalFilter = op.hasAdditionalFilterArgs»
«val lookupVars = op.getLookupArgs»
«val typeFQName = getTypeFQName(op.type)»
«val typeLiteral = getTypeLiteral(op.type)»
public «basePackage.prefix»LookupResult<«typeFQName»> «opName»(«getTypeFQName(op.owningClass)» context«FOR param:op.ownedParameters», «getTypeFQName(param.type)» «param.name»«ENDFOR») {
«IF hasAdditionalFilter»
«op.type.name»Filter filter = new «op.type.name»Filter(executor, «op.getFilterArgs»);
«ENDIF»
«basePackage.prefix»SingleResultLookupEnvironment _lookupEnv = new «basePackage.prefix»SingleResultLookupEnvironment(executor, «fqPackageItf».Literals.«typeLiteral»«lookupVars»);
«lookupVisitorName» _lookupVisitor = new «lookupVisitorName»(_lookupEnv«IF isExportedLookup», importer«ENDIF»);
context.accept(_lookupVisitor);
return new «basePackage.prefix»LookupResultImpl<«typeFQName»>
(_lookupEnv.getNamedElementsByKind(«typeFQName».class));
}
«ENDFOR»
}
''');
writer.close();
}
protected def void generateAutoCommonLookupVisitor(EPackage ePackage) {
var String fqPackageItf = genModelHelper.getQualifiedPackageInterfaceName(ePackage)
var boolean isDerived = isDerived();
var String visitorName = '''Abstract«projectPrefix»CommonLookupVisitor''';
var String superVisitorName = '''Abstract«superProjectPrefix»CommonLookupVisitor''';
var String superClassName = '''AbstractExtending«IF isDerived»«projectPrefix»«ENDIF»Visitor'''
var MergeWriter writer = new MergeWriter(outputFolder + visitorName + ".java");
writer.append('''
«ePackage.generateHeaderWithTemplate(visitorPackageName)»
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.internal.evaluation.EvaluationCache;
«IF !isDerived»
import org.eclipse.ocl.pivot.internal.evaluation.ExecutorInternal.ExecutorInternalExtension;
«ENDIF»
import «baseLookupPackageName».LookupEnvironment;
import «visitablePackageName».«visitableClassName»;
«IF isDerived»
import «superVisitorPackageName».«superVisitorName»;
«ENDIF»
public abstract class «visitorName»
extends «superClassName»<@Nullable LookupEnvironment, @NonNull LookupEnvironment> {
«IF !isDerived»protected final @NonNull EvaluationCache evaluationCache;«ENDIF»
«IF isDerived»private «superVisitorName» delegate;«ENDIF»
protected «visitorName»(@NonNull LookupEnvironment context) {
super(context);
«IF !isDerived»this.evaluationCache = ((ExecutorInternalExtension)context.getExecutor()).getEvaluationCache();«ENDIF»
«IF isDerived»this.delegate = createSuperLangVisitor();«ENDIF»
}
@Override
public final LookupEnvironment visiting(@NonNull Visitable visitable) {
«IF isDerived»
return «fqPackageItf».eINSTANCE == visitable.eClass().getEPackage()
? doVisiting(visitable)
: visitable.accept(getSuperLangVisitor());
«ELSE»
return doVisiting(visitable);
«ENDIF»
}
«IF isDerived»
protected «superVisitorName» getSuperLangVisitor(){
if (delegate == null) {
delegate = createSuperLangVisitor();
}
return delegate;
}
«ENDIF»
abstract protected LookupEnvironment doVisiting(@NonNull Visitable visitable);
«IF isDerived»abstract protected «superVisitorName» createSuperLangVisitor();«ENDIF»
}
''')
writer.close();
}
override generateVisitors(GenPackage genPackage) {
genModelHelper = createGenModelHelper(genPackage);
lookupArtifactsJavaPackage = lookupPackageName + ".util";
lookupArtifactsOutputFolder = modelFolder + lookupArtifactsJavaPackage.replace('.', '/') + "/";
var EPackage ePackage = genPackage.getEcorePackage();
// Some lookup artefacts
ePackage.generateAutoLookupResultClass;
ePackage.generateAutoLookupResultItf;
ePackage.generateAutoLookupFilterItf;
ePackage.generateAutoLookupFilterClass;
ePackage.generateSingleResultLookupEnvironment;
genPackage.generateAutoLookupSolver(baseGenPackage);
// Visitors related generation
ePackage.generateAutoCommonLookupVisitor;
doGenerateVisitors(genPackage);
}
private def List<Operation> getLookupMethods(GenPackage genPackage) {
var List<Operation> result = new ArrayList<Operation>;
var EnvironmentFactory envFact = PivotUtilInternal.getEnvironmentFactory(genPackage.getEcorePackage.eResource)
for (oclPackage : LookupCGUtil.getTargetPackages(genPackage, envFact, lookupFilePath, projectName)) {
for (oclClass : oclPackage.ownedClasses) {
for (oclOp : oclClass.ownedOperations) {
if (oclOp.isLookupOperation) {
result.add(oclOp);
}
}
}
}
return result;
}
private def Set<String> getLookupTypeNames(List<Operation> lookupOps, String lookupProtocol) {
val result = newHashSet();
for (op : lookupOps) {
if (op.name.contains(lookupProtocol)){
result.add(op.type.name);
}
}
return result;
}
private def boolean isLookupOperation(Operation op){
// internal lookup ops...
if (!op.name.startsWith("_lookup")) {
return false;
}
// ...which don't have the env parameter
// FIXME more robust check of the parameter
for (param : op.ownedParameters) {
if ("env".equals(param.name)) {
return false
}
}
return true;
}
private def boolean isExportedLookupOperation(Operation op) {
isLookupOperation(op) && op.name.contains("Exported") // FIXME more robust check?
}
private def GenModelHelper createGenModelHelper(GenPackage genPackage) {
var PivotMetamodelManager mManager = PivotUtilInternal.getEnvironmentFactory(genPackage.getEcorePackage().eResource).metamodelManager;
return new AbstractGenModelHelper(mManager);
}
private def String getTypeLiteral(Type type) {
var GenClassifier genClassifier = genModelHelper.getGenClassifier(type as Class);
return ClassUtil.nonNullState(genClassifier).classifierID;
}
private def String getTypeFQName(Type type) {
return if (type instanceof CollectionType) '''java.util.List<«getTypeFQName(type.elementType)»>'''
else genModelHelper.getEcoreInterfaceName(type as Class)
}
private def String getLookupVisitorName(Operation op, String typeName) {
if (op.name.contains("Qualified")) '''«projectPrefix»Qualified«typeName»LookupVisitor'''
else if (op.name.contains("Exported")) '''«projectPrefix»Exported«typeName»LookupVisitor'''
else '''«projectPrefix»Unqualified«typeName»LookupVisitor''';
}
private def boolean hasAdditionalFilterArgs(Operation op) {
val params = op.ownedParameters;
if (op.isExportedLookupOperation) params.size() > 2 else params.size() > 1;
}
private def String getLookupArgs(Operation op) {
val params = op.ownedParameters
val nameParam = if (op.isExportedLookupOperation) params.get(1) else params.get(0);
''',«nameParam.name»«IF op.hasAdditionalFilterArgs»,filter«ENDIF»'''
}
// We assume we are dealing with the the exported element lookup op
private def String getFilterArgs(Operation op) {
val sb = new StringBuffer();
val filterArgsIndex = if (op.isExportedLookupOperation) 2 else 1;
val params = op.ownedParameters
var first=true;
for (var i = filterArgsIndex; i < params.size; i++) {
if (first) {
first = false
} else {
sb.append(',');
}
sb.append('''«params.get(i).name»''');
}
sb.toString();
}
}