blob: d4309ddb1592e1335f4920a9f15e487fd56a591f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 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
******************************************************************************/
package org.eclipse.qvtd.pivot.qvtimperative.evaluation;
import java.io.IOException;
import java.util.Map;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.CallExp;
import org.eclipse.ocl.pivot.NamedElement;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.PivotFactory;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.TypedElement;
import org.eclipse.ocl.pivot.Variable;
import org.eclipse.ocl.pivot.evaluation.EvaluationEnvironment;
import org.eclipse.ocl.pivot.evaluation.EvaluationVisitor;
import org.eclipse.ocl.pivot.ids.CollectionTypeId;
import org.eclipse.ocl.pivot.internal.complete.StandardLibraryInternal;
import org.eclipse.ocl.pivot.internal.evaluation.AbstractExecutor;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.NameUtil;
import org.eclipse.ocl.pivot.utilities.ValueUtil;
import org.eclipse.ocl.pivot.values.CollectionValue;
import org.eclipse.ocl.pivot.values.InvalidValueException;
import org.eclipse.qvtd.pivot.qvtbase.Domain;
import org.eclipse.qvtd.pivot.qvtbase.Predicate;
import org.eclipse.qvtd.pivot.qvtbase.Rule;
import org.eclipse.qvtd.pivot.qvtbase.Transformation;
import org.eclipse.qvtd.pivot.qvtbase.TypedModel;
import org.eclipse.qvtd.pivot.qvtbase.utilities.QVTbaseUtil;
import org.eclipse.qvtd.pivot.qvtcorebase.Area;
import org.eclipse.qvtd.pivot.qvtcorebase.Assignment;
import org.eclipse.qvtd.pivot.qvtcorebase.BottomPattern;
import org.eclipse.qvtd.pivot.qvtcorebase.CoreDomain;
import org.eclipse.qvtd.pivot.qvtcorebase.EnforcementOperation;
import org.eclipse.qvtd.pivot.qvtcorebase.GuardPattern;
import org.eclipse.qvtd.pivot.qvtcorebase.NavigationAssignment;
import org.eclipse.qvtd.pivot.qvtcorebase.RealizedVariable;
import org.eclipse.qvtd.pivot.qvtcorebase.VariableAssignment;
import org.eclipse.qvtd.pivot.qvtcorebase.utilities.QVTcoreBaseUtil;
import org.eclipse.qvtd.pivot.qvtimperative.ConnectionVariable;
import org.eclipse.qvtd.pivot.qvtimperative.Mapping;
import org.eclipse.qvtd.pivot.qvtimperative.MappingCall;
import org.eclipse.qvtd.pivot.qvtimperative.MappingStatement;
import org.eclipse.qvtd.pivot.qvtimperative.utilities.QVTimperativeUtil;
import org.eclipse.qvtd.runtime.evaluation.AbstractTransformer;
import org.eclipse.qvtd.runtime.evaluation.InvocationFailedException;
public class BasicQVTiExecutor extends AbstractExecutor implements QVTiExecutor
{
protected final @NonNull Transformation transformation;
private @Nullable QVTiTransformationAnalysis transformationAnalysis = null;
private @Nullable QVTiModelManager modelManager = null;
public BasicQVTiExecutor(@NonNull QVTiEnvironmentFactory environmentFactory, @NonNull Transformation transformation) {
super(environmentFactory);
this.transformation = transformation;
}
/**
* Adds the model.
*/
public void addModel(@NonNull TypedModel typedModel, @NonNull Resource resource) {
getModelManager().addModel(typedModel, resource);
}
@Override
protected EvaluationVisitor.@NonNull EvaluationVisitorExtension createEvaluationVisitor() {
IQVTiEvaluationVisitor visitor = new QVTiEvaluationVisitor(this);
if (environmentFactory.isEvaluationTracingEnabled()) {
// decorate the evaluation visitor with tracing support
visitor = new QVTiTracingEvaluationVisitor(visitor);
// ((QVTiTracingEvaluationVisitor)visitor).setVerboseLevel(QVTiTracingEvaluationVisitor.VERBOSE_LEVEL_HIGH);
}
return visitor;
}
@Override
public void createModel(@NonNull String name, @NonNull URI modelURI, @Nullable String contentType) {
TypedModel typedModel = NameUtil.getNameable(transformation.getModelParameter(), name);
if (typedModel == null) {
throw new IllegalStateException("Unknown TypedModel '" + name + "'");
}
Resource resource = environmentFactory.getResourceSet().createResource(modelURI, contentType);
if (resource != null) {
getModelManager().addModel(typedModel, resource);
}
}
@Override
protected EvaluationEnvironment.@NonNull EvaluationEnvironmentExtension createNestedEvaluationEnvironment(EvaluationEnvironment.@NonNull EvaluationEnvironmentExtension evaluationEnvironment, @NonNull NamedElement executableObject, @Nullable OCLExpression callingObject) {
if (evaluationEnvironment instanceof QVTiEvaluationEnvironment) {
return new QVTiNestedEvaluationEnvironment((QVTiEvaluationEnvironment) evaluationEnvironment, executableObject, callingObject);
}
else{
return super.createNestedEvaluationEnvironment(evaluationEnvironment, executableObject, callingObject);
}
}
@Override
protected EvaluationEnvironment.@NonNull EvaluationEnvironmentExtension createRootEvaluationEnvironment(@NonNull NamedElement executableObject) {
if (executableObject instanceof Transformation) {
return new QVTiRootEvaluationEnvironment(this, (Transformation) executableObject);
}
else {
return super.createRootEvaluationEnvironment(executableObject);
}
}
@Override
public void dispose() {
if (modelManager != null) {
modelManager.dispose();
}
super.dispose();
}
protected void doCommits(@NonNull Mapping mapping, @NonNull EvaluationVisitor undecoratedVisitor) {
//
// creations
//
for (Domain domain : mapping.getDomain()) {
if (domain.isIsEnforceable()) {
CoreDomain enforceableDomain = (CoreDomain)domain;
BottomPattern enforceableBottomPattern = enforceableDomain.getBottomPattern();
for (RealizedVariable realizedVariable : enforceableBottomPattern.getRealizedVariable()) {
OCLExpression ownedInit = realizedVariable.getOwnedInit();
if (ownedInit != null) {
Object initValue = ownedInit.accept(undecoratedVisitor);
getEvaluationEnvironment().add(realizedVariable, initValue);
replace(realizedVariable, initValue);
Area area = ((BottomPattern)realizedVariable.eContainer()).getArea();
TypedModel typedModel = QVTcoreBaseUtil.getTypedModel(area);
assert typedModel != null;
Object ecoreValue = getIdResolver().ecoreValueOf(null, initValue);
assert ecoreValue != null;
getModelManager().addModelElement(typedModel, ecoreValue);
}
}
for (RealizedVariable realizedVariable : enforceableBottomPattern.getRealizedVariable()) {
if (realizedVariable.getOwnedInit() == null) {
realizedVariable.accept(undecoratedVisitor);
}
}
}
}
//
// property and connection assignments
//
BottomPattern middleBottomPattern = mapping.getBottomPattern();
for (Assignment assignment : middleBottomPattern.getAssignment()) {
if (!(assignment instanceof VariableAssignment)) {
assignment.accept(undecoratedVisitor);
}
}
//
// enforcement operations
//
for (Domain domain : mapping.getDomain()) {
if (domain.isIsEnforceable()) {
CoreDomain enforceableDomain = (CoreDomain)domain;
BottomPattern enforceableBottomPattern = enforceableDomain.getBottomPattern();
for (EnforcementOperation enforceOp : enforceableBottomPattern.getEnforcementOperation()) {
enforceOp.accept(undecoratedVisitor);
}
}
}
// for (EnforcementOperation enforceOp : middleBottomPattern.getEnforcementOperation()) {
// enforceOp.accept(undecoratedVisitor);
// }
}
protected boolean doPredicatesAndEvaluations(@NonNull Mapping mapping, @NonNull EvaluationVisitor undecoratedVisitor) {
//
// middle guard predicates
//
GuardPattern middleGuardPattern = mapping.getGuardPattern();
// assert middleGuardPattern.getVariable().isEmpty(); middle guards are connection variables
for (@NonNull Predicate predicate : ClassUtil.nullFree(middleGuardPattern.getPredicate())) {
// If the predicate is not true, the binding is not valid
Object result = predicate.accept(undecoratedVisitor);
if (result != Boolean.TRUE) {
return false;
}
}
for (@NonNull Domain domain : ClassUtil.nullFree(mapping.getDomain())) {
if (!domain.isIsEnforceable()) {
CoreDomain checkableDomain = (CoreDomain)domain;
GuardPattern checkableGuardPattern = checkableDomain.getGuardPattern();
assert checkableGuardPattern.getPredicate().isEmpty();
BottomPattern checkableBottomPattern = checkableDomain.getBottomPattern();
assert checkableBottomPattern.getAssignment().isEmpty();
assert checkableBottomPattern.getEnforcementOperation().isEmpty();
assert checkableBottomPattern.getPredicate().isEmpty();
assert checkableBottomPattern.getRealizedVariable().isEmpty();
// assert checkableBottomPattern.getVariable().isEmpty();
for (@NonNull Variable rVar : ClassUtil.nullFree(checkableBottomPattern.getVariable())) {
OCLExpression ownedInit = rVar.getOwnedInit();
assert ownedInit == null;
}
}
else {
CoreDomain enforceableDomain = (CoreDomain)domain;
GuardPattern enforceableGuardPattern = enforceableDomain.getGuardPattern();
assert enforceableGuardPattern.getPredicate().isEmpty();
BottomPattern enforceableBottomPattern = enforceableDomain.getBottomPattern();
assert enforceableBottomPattern.getAssignment().isEmpty();
assert enforceableBottomPattern.getPredicate().isEmpty();
for (@NonNull Variable rVar : ClassUtil.nullFree(enforceableBottomPattern.getVariable())) {
OCLExpression ownedInit = rVar.getOwnedInit();
if (ownedInit != null) {
Object initValue = ownedInit.accept(undecoratedVisitor);
replace(rVar, initValue);
}
}
}
}
BottomPattern middleBottomPattern = mapping.getBottomPattern();
assert middleBottomPattern.getEnforcementOperation().isEmpty();
assert middleBottomPattern.getRealizedVariable().isEmpty();
//
// variable declarations/initializations
//
for (@NonNull Variable rVar : ClassUtil.nullFree(middleBottomPattern.getVariable())) {
if (rVar instanceof ConnectionVariable) {
CollectionValue.Accumulator accumulator = ValueUtil.createCollectionAccumulatorValue((CollectionTypeId) rVar.getTypeId());
OCLExpression ownedInit = rVar.getOwnedInit();
if (ownedInit != null) {
Object initValue = ownedInit.accept(undecoratedVisitor);
accumulator = ValueUtil.createCollectionAccumulatorValue((CollectionTypeId) ownedInit.getTypeId());
if (initValue != null) {
for (Object value : (Iterable<?>)initValue) {
accumulator.add(value);
}
}
}
else {
accumulator = ValueUtil.createCollectionAccumulatorValue((CollectionTypeId) rVar.getTypeId());
}
replace(rVar, accumulator);
}
else {
OCLExpression ownedInit = rVar.getOwnedInit();
if (ownedInit != null) {
Object initValue = ownedInit.accept(undecoratedVisitor);
replace(rVar, initValue);
}
}
}
//
// variable assignments
//
for (@NonNull Assignment assignment : ClassUtil.nullFree(middleBottomPattern.getAssignment())) {
if (assignment instanceof VariableAssignment) {
assignment.accept(undecoratedVisitor);
}
}
//
// middle bottom predicates
//
for (@NonNull Predicate predicate : ClassUtil.nullFree(middleBottomPattern.getPredicate())) {
// If the predicate is not true, the binding is not valid
Object result = predicate.accept(undecoratedVisitor);
if (result != Boolean.TRUE) {
return false;
}
}
return true;
}
@Override
public Boolean execute() {
initializeEvaluationEnvironment(transformation);
getRootEvaluationEnvironment();
StandardLibraryInternal standardLibrary = environmentFactory.getStandardLibrary();
Variable ownedContext = QVTbaseUtil.getContextVariable(standardLibrary, transformation);
QVTiModelManager modelManager = getModelManager();
add(ownedContext, modelManager.getTransformationInstance(transformation));
for (TypedModel typedModel : transformation.getModelParameter()) {
if (typedModel != null) {
ownedContext = QVTbaseUtil.getContextVariable(standardLibrary, typedModel);
add(ownedContext, modelManager.getTypedModelInstance(typedModel));
}
}
return executeInternal();
}
protected Boolean executeInternal() {
return (Boolean) getEvaluationVisitor().visit(transformation);
}
@Override
public @NonNull QVTiEnvironmentFactory getEnvironmentFactory() {
return (QVTiEnvironmentFactory) super.getEnvironmentFactory();
}
@Override
public @NonNull QVTiEvaluationEnvironment getEvaluationEnvironment() {
return (QVTiEvaluationEnvironment) super.getEvaluationEnvironment();
}
/**
* Gets the named TypedModel.
*/
@Override
public @Nullable Resource getModel(@NonNull String name) {
TypedModel typedModel = NameUtil.getNameable(transformation.getModelParameter(), name);
if (typedModel == null) {
throw new IllegalStateException("Unknown TypedModel '" + name + "'");
}
return getModelManager().getModel(typedModel);
}
@Override
public @NonNull QVTiModelManager getModelManager() {
QVTiModelManager modelManager2 = modelManager;
if (modelManager2 == null) {
modelManager = modelManager2 = new QVTiModelManager(getTransformationAnalysis());
}
return modelManager2;
}
public @NonNull Transformation getTransformation() {
return transformation;
}
public @NonNull QVTiTransformationAnalysis getTransformationAnalysis() {
QVTiTransformationAnalysis transformationAnalysis2 = transformationAnalysis;
if (transformationAnalysis2 == null) {
transformationAnalysis = transformationAnalysis2 = getEnvironmentFactory().createTransformationAnalysis();
transformationAnalysis2.analyzeTransformation(transformation);
}
return transformationAnalysis2;
}
@Override
public @Nullable Object internalExecuteMappingCall(@NonNull MappingCall mappingCall, @NonNull Map<Variable, Object> variable2value, @NonNull EvaluationVisitor undecoratedVisitor) {
Mapping calledMapping = mappingCall.getReferredMapping();
if (calledMapping != null) {
pushEvaluationEnvironment(calledMapping, mappingCall);
try {
for (Map.Entry<Variable,Object> entry : variable2value.entrySet()) {
@SuppressWarnings("null")@NonNull Variable variable = entry.getKey();
replace(variable, entry.getValue());
}
calledMapping.accept(undecoratedVisitor);
}
finally {
popEvaluationEnvironment();
}
}
return true;
}
@Override
public @Nullable Object internalExecuteMapping(@NonNull Mapping mapping, @NonNull EvaluationVisitor undecoratedVisitor) {
try {
//
// Check the predicates
//
if (!doPredicatesAndEvaluations(mapping, undecoratedVisitor)) {
return false;
}
}
catch (InvocationFailedException e) {
throw e;
}
catch (Throwable e) {
// Mapping failure are just mappings that never happened.
AbstractTransformer.EXCEPTIONS.println("Execution failure in " + mapping.getName() + " : " + e);
return false;
}
//
// Perform the instance model addition and property assignment only after all expressions have been evaluated
// possibly throwing a not-ready exception that bypasses premature commits.
//
doCommits(mapping, undecoratedVisitor);
//
// Invoke any corrolaries
//
MappingStatement mappingStatement = mapping.getMappingStatement();
if (mappingStatement != null) {
mappingStatement.accept(undecoratedVisitor);
}
return true;
}
@Override
public void internalExecuteNavigationAssignment(@NonNull NavigationAssignment navigationAssignment, @NonNull Object slotObject, @Nullable Object ecoreValue, @Nullable Object childKey) {
Property targetProperty = QVTcoreBaseUtil.getTargetProperty(navigationAssignment);
targetProperty.initValue(slotObject, ecoreValue);
QVTiModelManager modelManager = getModelManager();
Integer cacheIndex = modelManager.getTransformationAnalysis().getCacheIndex(navigationAssignment);
if (cacheIndex != null) {
modelManager.setUnnavigableOpposite(cacheIndex, slotObject, ecoreValue);
}
}
@Override
public @Nullable Object internalExecuteRealizedVariable(@NonNull RealizedVariable realizedVariable, @NonNull EvaluationVisitor undecoratedVisitor) {
// Realized variables are in the mapping's target bottom pattern
// and create elements in the target model. The realized variables
// are being visited for each binding of variable in the mapping.
Type type = realizedVariable.getType();
if (!(type instanceof org.eclipse.ocl.pivot.Class)) {
return null;
}
Area area = ((BottomPattern)realizedVariable.eContainer()).getArea();
TypedModel typedModel = QVTcoreBaseUtil.getTypedModel(area);
assert typedModel != null;
Object element = ((org.eclipse.ocl.pivot.Class)type).createInstance();
// Add the realize variable binding to the environment
replace(realizedVariable, element);
getModelManager().addModelElement(typedModel, element);
return element;
}
@Override
public @Nullable Object internalExecuteTransformation(@NonNull Transformation transformation, @NonNull EvaluationVisitor undecoratedVisitor) {
Rule rule = NameUtil.getNameable(transformation.getRule(), QVTimperativeUtil.ROOT_MAPPING_NAME);
if (rule == null) {
throw new IllegalStateException("Transformation " + transformation.getName() + " has no root mapping");
}
CallExp callExp = PivotFactory.eINSTANCE.createOperationCallExp(); // FIXME TransformationCallExp
pushEvaluationEnvironment(rule, callExp);
try {
rule.accept(undecoratedVisitor);
}
finally {
popEvaluationEnvironment();
}
return true;
}
/**
* Loads the modelURI and binds it to the named TypedModel.
*/
public void loadModel(@NonNull String name, @NonNull URI modelURI) {
TypedModel typedModel = NameUtil.getNameable(transformation.getModelParameter(), name);
if (typedModel == null) {
throw new IllegalStateException("Unknown TypedModel '" + name + "'");
}
Resource resource = environmentFactory.getResourceSet().getResource(modelURI, true);
if (resource != null) {
getModelManager().addModel(typedModel, resource);
}
}
@Override
public void loadModel(@NonNull String name, @NonNull URI modelURI, @Nullable String contentType) {
TypedModel typedModel = NameUtil.getNameable(transformation.getModelParameter(), name);
if (typedModel == null) {
throw new IllegalStateException("Unknown TypedModel '" + name + "'");
}
Resource resource;
ResourceSet resourceSet = environmentFactory.getResourceSet();
if (contentType == null) {
resource = resourceSet.getResource(modelURI, true);
}
else {
resource = resourceSet.createResource(modelURI, contentType);
try {
resource.load(null);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (resource != null) {
getModelManager().addModel(typedModel, resource);
}
}
@Override
public void replace(@NonNull TypedElement asVariable, @Nullable Object value) {
if (value == null) {
if (asVariable.isIsRequired()) {
throw new InvalidValueException("Attempted to assign null value to " + asVariable);
}
}
else if (!(value instanceof Throwable)){
Type valueType = getIdResolver().getDynamicTypeOf(value);
Type variableType = ClassUtil.nonNullState(asVariable.getType());
if (!valueType.conformsTo(getStandardLibrary(), variableType) && (variableType.getESObject() != EcorePackage.Literals.EOBJECT)) { // FIXME EObject fudge for Adolfo's test models
throw new InvalidValueException("Attempted to assign incompatible value to " + asVariable);
}
}
super.replace(asVariable, value);
}
public Resource saveModel(@NonNull String name, @NonNull URI modelURI, String contentType, @Nullable Map<?, ?> savingOptions) throws IOException {
TypedModel typedModel = NameUtil.getNameable(transformation.getModelParameter(), name);
if (typedModel == null) {
throw new IllegalStateException("Unknown TypedModel '" + name + "'");
}
Resource resource = getModelManager().getModel(typedModel);
if (resource == null) {
resource = environmentFactory.getResourceSet().createResource(modelURI, contentType);
}
if (resource != null) {
resource.save(savingOptions);
}
return resource;
}
@Override
public void saveModels() {
getModelManager().saveModels();
}
public void saveModels(@Nullable Map<?, ?> savingOptions) {
getModelManager().saveModels(savingOptions);
}
public void saveModels(@NonNull URI traceURI) {
this.saveModels(traceURI, null);
}
public void saveModels(@NonNull URI traceURI, @Nullable Map<?, ?> savingOptions) {
this.saveModels(savingOptions);
getModelManager().saveMiddleModel(traceURI, savingOptions);
}
public void saveTransformation(Map<?,?> options) throws IOException {
XMLResource resource = (XMLResource) transformation.eResource();
// new AS2ID().assignIds(resource.getResourceSet());
resource.save(options);
}
}