blob: a5f2694b66fa04db19b9185c8648aa22207f8e56 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2016 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.Collection;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
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.Operation;
import org.eclipse.ocl.pivot.OperationCallExp;
import org.eclipse.ocl.pivot.Parameter;
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.internal.complete.StandardLibraryInternal;
import org.eclipse.ocl.pivot.internal.evaluation.AbstractExecutor;
import org.eclipse.ocl.pivot.internal.messages.PivotMessagesInternal;
import org.eclipse.ocl.pivot.labels.ILabelGenerator;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.NameUtil;
import org.eclipse.ocl.pivot.values.InvalidValueException;
import org.eclipse.ocl.pivot.values.NullValue;
import org.eclipse.qvtd.pivot.qvtbase.Function;
import org.eclipse.qvtd.pivot.qvtbase.Transformation;
import org.eclipse.qvtd.pivot.qvtbase.utilities.QVTbaseUtil;
import org.eclipse.qvtd.pivot.qvtimperative.AppendParameterBinding;
import org.eclipse.qvtd.pivot.qvtimperative.ConnectionVariable;
import org.eclipse.qvtd.pivot.qvtimperative.GuardParameterBinding;
import org.eclipse.qvtd.pivot.qvtimperative.ImperativeTransformation;
import org.eclipse.qvtd.pivot.qvtimperative.ImperativeTypedModel;
import org.eclipse.qvtd.pivot.qvtimperative.LoopParameterBinding;
import org.eclipse.qvtd.pivot.qvtimperative.Mapping;
import org.eclipse.qvtd.pivot.qvtimperative.MappingCall;
import org.eclipse.qvtd.pivot.qvtimperative.MappingParameter;
import org.eclipse.qvtd.pivot.qvtimperative.MappingParameterBinding;
import org.eclipse.qvtd.pivot.qvtimperative.NewStatement;
import org.eclipse.qvtd.pivot.qvtimperative.SetStatement;
import org.eclipse.qvtd.pivot.qvtimperative.SimpleParameterBinding;
import org.eclipse.qvtd.pivot.qvtimperative.Statement;
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 ImperativeTransformation transformation;
private @Nullable QVTiTransformationAnalysis transformationAnalysis = null;
private @Nullable QVTiModelManager modelManager = null;
protected final boolean debugExceptions = AbstractTransformer.EXCEPTIONS.isActive();
protected final boolean debugInvocations = AbstractTransformer.INVOCATIONS.isActive();
public BasicQVTiExecutor(@NonNull QVTiEnvironmentFactory environmentFactory, @NonNull ImperativeTransformation transformation) {
super(environmentFactory);
this.transformation = transformation;
}
/**
* Adds the model.
*/
public void addModel(@NonNull ImperativeTypedModel 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 @Nullable Resource createModel(@NonNull String name, @NonNull URI modelURI, @Nullable String contentType) {
ImperativeTypedModel typedModel = getTypedModel(name);
Resource resource = environmentFactory.getResourceSet().createResource(modelURI, contentType);
if (resource != null) {
getModelManager().addModel(typedModel, resource);
}
return resource;
}
@Override
protected EvaluationEnvironment.@NonNull EvaluationEnvironmentExtension createNestedEvaluationEnvironment(EvaluationEnvironment.@NonNull EvaluationEnvironmentExtension evaluationEnvironment, @NonNull NamedElement executableObject, @Nullable Object caller) {
if (evaluationEnvironment instanceof QVTiEvaluationEnvironment) {
return new QVTiNestedEvaluationEnvironment((QVTiEvaluationEnvironment) evaluationEnvironment, executableObject, caller);
}
else{
return super.createNestedEvaluationEnvironment(evaluationEnvironment, executableObject, caller);
}
}
@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();
}
@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 (@NonNull ImperativeTypedModel typedModel : QVTimperativeUtil.getOwnedTypedModels(transformation)) {
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) {
ImperativeTypedModel typedModel = getTypedModel(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 Collection<@NonNull EObject> getRootObjects(@NonNull String name) {
return getModelManager().getRootObjects(getTypedModel(name));
}
public @NonNull Transformation getTransformation() {
return transformation;
}
public @NonNull QVTiTransformationAnalysis getTransformationAnalysis() {
QVTiTransformationAnalysis transformationAnalysis2 = transformationAnalysis;
if (transformationAnalysis2 == null) {
transformationAnalysis = transformationAnalysis2 = getEnvironmentFactory().createTransformationAnalysis(transformation);
transformationAnalysis2.analyzeTransformation();
}
return transformationAnalysis2;
}
public @NonNull ImperativeTypedModel getTypedModel(@NonNull String name) {
ImperativeTypedModel typedModel = NameUtil.getNameable(QVTimperativeUtil.getOwnedTypedModels(transformation), name);
if (typedModel == null) {
throw new IllegalStateException("Unknown TypedModel '" + name + "'");
}
return typedModel;
}
protected @Nullable Object internalExecuteFunctionCallExp(@NonNull OperationCallExp operationCallExp,
@NonNull Function referredFunction, @Nullable Object @NonNull [] boxedSourceAndArgumentValues) {
// PivotUtil.checkExpression(expressionInOCL);
EvaluationEnvironment nestedEvaluationEnvironment = pushEvaluationEnvironment(referredFunction, (TypedElement)operationCallExp);
// nestedEvaluationEnvironment.add(ClassUtil.nonNullModel(expressionInOCL.getOwnedContext()), sourceValue);
List<Parameter> parameters = referredFunction.getOwnedParameters();
if (!parameters.isEmpty()) {
for (int i = 0; i < parameters.size(); i++) {
Object value = boxedSourceAndArgumentValues[i+1];
nestedEvaluationEnvironment.add(ClassUtil.nonNullModel(parameters.get(i)), value);
}
}
try {
OCLExpression bodyExpression = referredFunction.getQueryExpression();
assert bodyExpression != null;
Object result = evaluate(bodyExpression);
assert !(result instanceof NullValue);
return result;
}
catch (InvalidValueException e) {
throw e;
}
catch (Exception e) {
// This is a backstop. Library operations should catch their own exceptions
// and produce a better reason as a result.
throw new InvalidValueException(e, PivotMessagesInternal.FailedToEvaluate_ERROR_, referredFunction, ILabelGenerator.Registry.INSTANCE.labelFor(null), operationCallExp);
}
finally {
popEvaluationEnvironment();
}
}
@Override
public @Nullable Object internalExecuteMappingCall(@NonNull MappingCall mappingCall, @NonNull Object @NonNull [] boundValues, @NonNull EvaluationVisitor undecoratedVisitor) {
Mapping calledMapping = mappingCall.getReferredMapping();
if (calledMapping != null) {
pushEvaluationEnvironment(calledMapping, mappingCall);
try {
int index = 0;
for (MappingParameterBinding binding : mappingCall.getBinding()) {
MappingParameter boundVariable = ClassUtil.nonNullState(binding.getBoundVariable());
if (binding instanceof AppendParameterBinding) { // FIXME visit the bindings
replace(boundVariable, boundValues[index++], !(boundVariable instanceof ConnectionVariable)); // FIXME static type conformance test
}
else if (binding instanceof GuardParameterBinding) {
replace(boundVariable, boundValues[index++], !(boundVariable instanceof ConnectionVariable)); // FIXME static type conformance test
}
else if (binding instanceof LoopParameterBinding) {
replace(boundVariable, boundValues[index++], !(boundVariable instanceof ConnectionVariable)); // FIXME static type conformance test
}
else if (binding instanceof SimpleParameterBinding) {
replace(boundVariable, boundValues[index++], !(boundVariable instanceof ConnectionVariable)); // FIXME static type conformance test
}
else {
assert false;
}
}
calledMapping.accept(undecoratedVisitor);
}
finally {
popEvaluationEnvironment();
}
}
return true;
}
@Override
public @Nullable Object internalExecuteMapping(@NonNull Mapping mapping, @NonNull EvaluationVisitor undecoratedVisitor) {
try {
for (Statement statement : mapping.getOwnedStatements()) {
Object result = statement.accept(undecoratedVisitor);
if (result != Boolean.TRUE) {
return false;
}
}
return true;
}
catch (InvocationFailedException e) {
throw e;
}
catch (Throwable e) {
// Mapping failures are just mappings that never happened.
if (debugExceptions) {
AbstractTransformer.EXCEPTIONS.println("Execution failure in " + mapping.getName() + " : " + e);
}
return false;
}
}
@Override
public @Nullable Object internalExecuteOperationCallExp(@NonNull OperationCallExp operationCallExp,
@Nullable Object @NonNull [] boxedSourceAndArgumentValues) {
Operation referredOperation = operationCallExp.getReferredOperation();
if (referredOperation instanceof Function) {
return internalExecuteFunctionCallExp(operationCallExp, (Function)referredOperation, boxedSourceAndArgumentValues);
}
else {
return super.internalExecuteOperationCallExp(operationCallExp, boxedSourceAndArgumentValues);
}
}
@Override
public @Nullable Object internalExecuteNewStatement(@NonNull NewStatement newStatement, @NonNull EvaluationVisitor undecoratedVisitor) {
OCLExpression ownedExpression = newStatement.getOwnedExpression();
if (ownedExpression != null) {
Object initValue = ownedExpression.accept(undecoratedVisitor);
getEvaluationEnvironment().add(newStatement, initValue);
replace(newStatement, initValue);
ImperativeTypedModel typedModel = newStatement.getReferredTypedModel();
assert typedModel != null;
Object ecoreValue = getIdResolver().ecoreValueOf(null, initValue);
assert ecoreValue != null;
getModelManager().addModelElement(typedModel, ecoreValue);
return ecoreValue;
}
else {
// 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 = newStatement.getType();
if (!(type instanceof org.eclipse.ocl.pivot.Class)) {
return null;
}
ImperativeTypedModel typedModel = newStatement.getReferredTypedModel();
assert typedModel != null;
Object element = ((org.eclipse.ocl.pivot.Class)type).createInstance();
// Add the realize variable binding to the environment
replace(newStatement, element, false);
getModelManager().addModelElement(typedModel, element);
return element;
}
}
@Override
public void internalExecuteSetStatement(@NonNull SetStatement setStatement, @NonNull Object slotObject, @Nullable Object ecoreValue, @Nullable Object childKey) {
Property targetProperty = QVTimperativeUtil.getTargetProperty(setStatement);
targetProperty.initValue(slotObject, ecoreValue);
QVTiModelManager modelManager = getModelManager();
Integer cacheIndex = modelManager.getTransformationAnalysis().getCacheIndex(setStatement);
if (cacheIndex != null) {
modelManager.setUnnavigableOpposite(cacheIndex, slotObject, ecoreValue);
}
}
@Override
public @Nullable Object internalExecuteTransformation(@NonNull ImperativeTransformation transformation, @NonNull EvaluationVisitor undecoratedVisitor) {
Mapping rule = QVTimperativeUtil.getOwnedMapping(transformation, QVTimperativeUtil.ROOT_MAPPING_NAME);
CallExp callExp = PivotFactory.eINSTANCE.createOperationCallExp(); // FIXME TransformationCallExp
pushEvaluationEnvironment(rule, (TypedElement)callExp);
try {
rule.accept(undecoratedVisitor);
}
finally {
popEvaluationEnvironment();
}
return true;
}
/**
* Loads the modelURI and binds it to the named TypedModel.
*/
public @Nullable Resource loadModel(@NonNull String name, @NonNull URI modelURI) {
ImperativeTypedModel typedModel = getTypedModel(name);
Resource resource = environmentFactory.getResourceSet().getResource(modelURI, true);
if (resource != null) {
getModelManager().addModel(typedModel, resource);
}
return resource;
}
@Override
public void loadModel(@NonNull String name, @NonNull URI modelURI, @Nullable String contentType) {
ImperativeTypedModel typedModel = getTypedModel(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);
}
}
super.replace(asVariable, value);
}
@Override
public void replace(@NonNull TypedElement asVariable, @Nullable Object value, boolean checkType) {
if (value == null) {
if (asVariable.isIsRequired()) {
throw new InvalidValueException("Attempted to assign null value to " + asVariable);
}
}
else if (checkType) {
Type valueType = getIdResolver().getDynamicTypeOf(value);
// Type valueType2 = getIdResolver().getStaticTypeOf(value);
// Type valueType = valueType1;
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);
}
// @Override
public void saveContents() {
getModelManager().saveContents();
}
public Resource saveModel(@NonNull String name, @NonNull URI modelURI, String contentType, @Nullable Map<?, ?> savingOptions) throws IOException {
ImperativeTypedModel typedModel = getTypedModel(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);
}
}