blob: 2f3cb06ac91cf042ba3fd8051d2076b968f3822a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2014 The University of York, 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:
* Horacio Hoyos - initial API and implementation
* Adolfo Sanchez-Barbudo Herrera (University of York) - Bug 456900
******************************************************************************/
package org.eclipse.qvtd.pivot.qvtimperative.evaluation;
import java.util.List;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.Import;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.Variable;
import org.eclipse.ocl.pivot.ids.IdResolver;
import org.eclipse.ocl.pivot.internal.evaluation.OCLEvaluationVisitor;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.values.InvalidValueException;
import org.eclipse.qvtd.pivot.qvtbase.BaseModel;
import org.eclipse.qvtd.pivot.qvtbase.Domain;
import org.eclipse.qvtd.pivot.qvtbase.Function;
import org.eclipse.qvtd.pivot.qvtbase.FunctionParameter;
import org.eclipse.qvtd.pivot.qvtbase.Pattern;
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.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.CorePattern;
import org.eclipse.qvtd.pivot.qvtcorebase.EnforcementOperation;
import org.eclipse.qvtd.pivot.qvtcorebase.GuardPattern;
import org.eclipse.qvtd.pivot.qvtcorebase.PropertyAssignment;
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.ImperativeModel;
import org.eclipse.qvtd.pivot.qvtimperative.Mapping;
import org.eclipse.qvtd.pivot.qvtimperative.MappingCall;
import org.eclipse.qvtd.pivot.qvtimperative.MappingCallBinding;
import org.eclipse.qvtd.pivot.qvtimperative.MappingLoop;
import org.eclipse.qvtd.pivot.qvtimperative.MappingSequence;
import org.eclipse.qvtd.pivot.qvtimperative.MappingStatement;
import org.eclipse.qvtd.pivot.qvtimperative.MiddlePropertyAssignment;
import org.eclipse.qvtd.pivot.qvtimperative.MiddlePropertyCallExp;
import org.eclipse.qvtd.pivot.qvtimperative.VariablePredicate;
/**
* QVTimperativeAbstractEvaluationVisitor is the base abstract class for QVTi
* evaluation visitors.
*
* @author Horacio Hoyos
*/
public abstract class QVTiAbstractEvaluationVisitor extends OCLEvaluationVisitor implements IQVTiEvaluationVisitor
{
// private static final Logger logger = Logger.getLogger(QVTiAbstractEvaluationVisitor.class);
/**
* Instantiates a new QVT imperative abstract visitor.
*
* @param env The environment
* @param evalEnv The evaluation environment
*/
public QVTiAbstractEvaluationVisitor(@NonNull IQVTiEvaluationEnvironment evalEnv) {
super(evalEnv);
}
/* (non-Javadoc)
* @see org.eclipse.ocl.pivot.evaluation.EvaluationVisitorImpl#createNestedEvaluator()
*/
@Override
public abstract @NonNull IQVTiEvaluationVisitor createNestedEvaluator();
protected void doMappingStatements(@NonNull List<MappingStatement> mappingStatements) {
}
protected void doPropertyAssignment(@NonNull PropertyAssignment propertyAssignment, @Nullable Integer cacheIndex) {
Object slotExpValue = safeVisit(propertyAssignment.getSlotExpression());
if (slotExpValue instanceof EObject) {
Object boxedValue = safeVisit(propertyAssignment.getValue());
Property targetProperty = propertyAssignment.getTargetProperty();
Class<?> instanceClass = null;
EObject eTarget = targetProperty.getESObject();
if (eTarget instanceof EStructuralFeature) {
EClassifier eType = ((EStructuralFeature)eTarget).getEType();
if (eType != null) {
instanceClass = eType.getInstanceClass();
}
}
Object ecoreValue = environmentFactory.getIdResolver().ecoreValueOf(instanceClass, boxedValue);
targetProperty.initValue((EObject) slotExpValue, ecoreValue);
if (cacheIndex != null) {
getModelManager().setMiddleOpposite(cacheIndex, slotExpValue, ecoreValue);
}
} else {
throw new IllegalArgumentException("Unsupported " + propertyAssignment.eClass().getName()
+ " specification. The assigment slot expression evaluates to non-ecore value");
}
}
@Override
public @NonNull QVTiEnvironmentFactory getEnvironmentFactory() {
return (QVTiEnvironmentFactory) environmentFactory;
}
@Override
public @NonNull IQVTiEvaluationEnvironment getEvaluationEnvironment() {
return (IQVTiEvaluationEnvironment) super.getEvaluationEnvironment();
}
/* (non-Javadoc)
* @see org.eclipse.ocl.pivot.evaluation.AbstractEvaluationVisitor#getModelManager()
*/
@Override
public @NonNull QVTiModelManager getModelManager() {
return (QVTiModelManager) modelManager;
}
/**
* Checks if the mapping is a middle to right mapping. Middle to Right mappings
* must have enforce domains
*
* @param mapping the mapping
* @return true, if is m to r mapping
*
protected boolean isMtoRMapping(@NonNull Mapping mapping) {
if (mapping.getDomain().size() == 0) {
return false;
}
for (Domain domain : mapping.getDomain()) {
if (!domain.isIsEnforceable()) {
return false;
}
}
return true;
} */
/**
* Checks if is middle to middle mapping.
*
* @param mapping the mapping
* @return true, if is middle to middle mapping
*
protected boolean isMtoMMapping(@NonNull Mapping mapping) {
if (mapping.getDomain().size() == 0) {
return true;
}
return false;
} */
/**
* Checks if the mapping is a left to middle mapping. Left to middle mappings
* can not have enforce domains
*
* @param mapping the mapping
* @return true, if is left to middle mapping
*
protected boolean isLtoMMapping(@NonNull Mapping mapping) {
if (mapping.getDomain().size() == 0) {
return false;
}
for (Domain domain : mapping.getDomain()) {
if (domain.isIsEnforceable()) {
return false;
}
}
return true;
} */
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtcorebase.util.QVTcoreBaseVisitor#visitAssignment(org.eclipse.qvtd.pivot.qvtcorebase.Assignment)
*/
@Override
public @Nullable Object visitAssignment(@NonNull Assignment object) {
return visiting(object);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtbase.util.QVTbaseVisitor#visitBaseModel(org.eclipse.qvtd.pivot.qvtbase.BaseModel)
*/
@Override
public @Nullable Object visitBaseModel(@NonNull BaseModel object) {
return visiting(object);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtcorebase.util.QVTcoreBaseVisitor#visitBottomPattern(org.eclipse.qvtd.pivot.qvtcorebase.BottomPattern)
*/
@Override
public @Nullable Object visitBottomPattern(@NonNull BottomPattern object) {
return visiting(object);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtcorebase.util.QVTcoreBaseVisitor#visitCoreDomain(org.eclipse.qvtd.pivot.qvtcorebase.CoreDomain)
*/
@Override
public @Nullable Object visitCoreDomain(@NonNull CoreDomain object) {
return visiting(object);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtbase.util.QVTbaseVisitor#visitDomain(org.eclipse.qvtd.pivot.qvtbase.Domain)
*/
@Override
public @Nullable Object visitDomain(@NonNull Domain object) {
return visiting(object);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtcorebase.util.QVTcoreBaseVisitor#visitCorePattern(org.eclipse.qvtd.pivot.qvtcorebase.CorePattern)
*/
@Override
public @Nullable Object visitCorePattern(@NonNull CorePattern object) {
return visiting(object);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtcorebase.util.QVTcoreBaseVisitor#visitEnforcementOperation(org.eclipse.qvtd.pivot.qvtcorebase.EnforcementOperation)
*/
@Override
public @Nullable Object visitEnforcementOperation(@NonNull EnforcementOperation object) {
return visiting(object);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtbase.util.QVTbaseVisitor#visitFunction(org.eclipse.qvtd.pivot.qvtbase.Function)
*/
@Override
public @Nullable Object visitFunction(@NonNull Function object) {
return visiting(object);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtbase.util.QVTbaseVisitor#visitFunctionParameter(org.eclipse.qvtd.pivot.qvtbase.FunctionParameter)
*/
@Override
public @Nullable Object visitFunctionParameter(@NonNull FunctionParameter object) {
return visiting(object);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.qvtd.pivot.qvtcore.util.QVTcoreVisitor#visitGuardPattern(
* org.eclipse.qvtd.pivot.qvtcore.GuardPattern)
*/
@Override
public @Nullable Object visitGuardPattern(@NonNull GuardPattern guardPattern) {
for (Predicate predicate : guardPattern.getPredicate()) {
// If the predicate is not true, the binding is not valid
Object result = predicate.accept(undecoratedVisitor);
if (result != Boolean.TRUE) {
return false;
}
}
// NB guard 1 variable may be initialized by guard 2 VariablePredicate
/* for (Variable v : guardPattern.getVariable()) {
// Check for binding initialization
if (evaluationEnvironment.getValueOf(v) == null) {
logger.warn("Missing binding for " + v + " in " + QVTimperativeUtil.getContainingMapping(guardPattern));
return false;
}
} */
return true;
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtbase.util.QVTbaseVisitor#visitUnit(org.eclipse.qvtd.pivot.qvtbase.Unit)
*/
@Override
public @Nullable Object visitImport(@NonNull Import object) {
return visiting(object);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtimperative.util.QVTimperativeVisitor#visitImperativeModel(org.eclipse.qvtd.pivot.qvtimperative.ImperativeModel)
*/
@Override
public @Nullable Object visitImperativeModel(@NonNull ImperativeModel object) {
return visiting(object);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtimperative.util.QVTimperativeVisitor#visitMappingCall(org.eclipse.qvtd.pivot.qvtimperative.MappingCall)
*/
@Override
public @Nullable Object visitMappingCall(@NonNull MappingCall mappingCall) {
Mapping calledMapping = ClassUtil.nonNullModel(mappingCall.getReferredMapping());
//
// Initialise nested environment directly with the bound values for non-looped bindings,
// and build matching lists of boundVariables and boundIterables for looped bindings.
//
// List<Variable> loopedVariables = null;
// List<Iterable<?>> loopedValues = null;
for (MappingCallBinding binding : mappingCall.getBinding()) {
Variable boundVariable = ClassUtil.nonNullModel(binding.getBoundVariable());
Object valueOrValues = null;
try {
valueOrValues = ((IQVTiEvaluationVisitor)undecoratedVisitor).safeVisit(binding.getValue());
} catch (InvalidValueException ex) {
// There was an OCLVoid value being navigated or any other/similar OCL error
// evaluating the binding value
return null;
}
Type valueType = environmentFactory.getIdResolver().getDynamicTypeOf(valueOrValues);
Type varType = boundVariable.getType();
if ((varType != null) && valueType.conformsTo(metamodelManager.getStandardLibrary(), varType)) {
evaluationEnvironment.replace(boundVariable, valueOrValues);
}
else {
return null;
}
}
calledMapping.accept(undecoratedVisitor);
return null;
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtimperative.util.QVTimperativeVisitor#visitMappingCallBinding(org.eclipse.qvtd.pivot.qvtimperative.MappingCallBinding)
*/
@Override
public @Nullable Object visitMappingCallBinding(@NonNull MappingCallBinding object) {
return visiting(object); // MappingCallBinding is serviced by the parent MappingCall
}
@Override
public @Nullable Object visitMappingLoop(@NonNull MappingLoop mappingLoop) {
Object inValues = mappingLoop.getOwnedSource().accept(undecoratedVisitor);
if (inValues instanceof Iterable<?>) {
List<Variable> iterators = mappingLoop.getOwnedIterators();
if (iterators.size() > 0) {
Variable iterator = ClassUtil.nonNullState(iterators.get(0));
for (Object object : (Iterable<?>)inValues) {
getEvaluationEnvironment().replace(iterator, object);
mappingLoop.getOwnedBody().accept(undecoratedVisitor);
}
}
}
return true;
}
@Override
public @Nullable Object visitMappingSequence(@NonNull MappingSequence mappingSequence) {
for (MappingStatement mappingStatement : mappingSequence.getMappingStatements()) {
IQVTiEvaluationVisitor nv = ((IQVTiEvaluationVisitor) undecoratedVisitor).createNestedEvaluator();
// The Undecorated visitor createNestedEvaluator should return the undecorated, so no need
// to call the getUndecoratedVisitor.
try {
mappingStatement.accept(nv);
}
finally {
nv.dispose();
}
}
return true;
}
@Override
public @Nullable Object visitMappingStatement(@NonNull MappingStatement object) {
return visiting(object); // MappingStatement is abstract
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtimperative.util.QVTimperativeVisitor#visitMiddlePropertyAssignment(org.eclipse.qvtd.pivot.qvtimperative.MiddlePropertyAssignment)
*/
@Override
public @Nullable Object visitMiddlePropertyAssignment(@NonNull MiddlePropertyAssignment propertyAssignment) {
Area area = ((BottomPattern)propertyAssignment.eContainer()).getArea();
if (area instanceof Mapping) {
// TODO Check this approach
doPropertyAssignment(propertyAssignment, propertyAssignment.getCacheIndex());
}
return true;
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtimperative.util.QVTimperativeVisitor#visitMiddlePropertyCallExp(org.eclipse.qvtd.pivot.qvtimperative.MiddlePropertyCallExp)
*/
@Override
public @Nullable Object visitMiddlePropertyCallExp(@NonNull MiddlePropertyCallExp pPropertyCallExp) {
OCLExpression source = pPropertyCallExp.getOwnedSource();
Object sourceValue = source != null ? undecoratedVisitor.evaluate(source) : null;
if (sourceValue != null) {
Integer cacheIndex = ClassUtil.nonNullState(pPropertyCallExp.getCacheIndex());
Object middleOpposite = getModelManager().getMiddleOpposite(cacheIndex, sourceValue);
return ClassUtil.nonNullState(middleOpposite);
}
throw new InvalidValueException("Failed to evaluate '" + pPropertyCallExp.getReferredProperty() + "'", sourceValue, pPropertyCallExp);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtbase.util.QVTbaseVisitor#visitPattern(org.eclipse.qvtd.pivot.qvtbase.Pattern)
*/
@Override
public @Nullable Object visitPattern(@NonNull Pattern object) {
return visiting(object);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtbase.util.QVTbaseVisitor#visitPredicate(org.eclipse.qvtd.pivot.qvtbase.Predicate)
*/
@Override
public @Nullable Object visitPredicate(@NonNull Predicate predicate) {
// Each predicate has a conditionExpression that is an OCLExpression
OCLExpression exp = predicate.getConditionExpression();
// The predicated is visited with a nested environment
Object expResult = exp.accept(undecoratedVisitor);
return expResult;
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtcorebase.util.QVTcoreBaseVisitor#visitPropertyAssignment(org.eclipse.qvtd.pivot.qvtcorebase.PropertyAssignment)
*/
@Override
public @Nullable Object visitPropertyAssignment(@NonNull PropertyAssignment propertyAssignment) {
doPropertyAssignment(propertyAssignment, null);
return true;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.qvtd.pivot.qvtcore.util.QVTcoreVisitor#visitRealizedVariable
* (org.eclipse.qvtd.pivot.qvtcore.RealizedVariable)
*/
@Override
public @Nullable Object visitRealizedVariable(@NonNull RealizedVariable realizedVariable) {
// 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.
Area area = ((BottomPattern)realizedVariable.eContainer()).getArea();
Type type = realizedVariable.getType();
Object element = type instanceof org.eclipse.ocl.pivot.Class ? ((org.eclipse.ocl.pivot.Class)type).createInstance() : null;
if (element != null) {
TypedModel tm = QVTcoreBaseUtil.getTypedModel(area);
assert tm != null;
((QVTiModelManager)modelManager).addModelElement(tm, element);
// Add the realize variable binding to the environment
evaluationEnvironment.replace(realizedVariable, element);
}
return element;
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtbase.util.QVTbaseVisitor#visitRule(org.eclipse.qvtd.pivot.qvtbase.Rule)
*/
@Override
public @Nullable Object visitRule(@NonNull Rule object) {
return visiting(object);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtbase.util.QVTbaseVisitor#visitTransformation(org.eclipse.qvtd.pivot.qvtbase.Transformation)
*/
@Override
public @Nullable Object visitTransformation(@NonNull Transformation object) {
return visiting(object);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtbase.util.QVTbaseVisitor#visitTypedModel(org.eclipse.qvtd.pivot.qvtbase.TypedModel)
*/
@Override
public @Nullable Object visitTypedModel(@NonNull TypedModel object) {
return visiting(object);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtcorebase.util.QVTcoreBaseVisitor#visitVariableAssignment(org.eclipse.qvtd.pivot.qvtcorebase.VariableAssignment)
*/
@Override
public @Nullable Object visitVariableAssignment(@NonNull VariableAssignment variableAssignment) {
Variable targetVariable = variableAssignment.getTargetVariable() ;
Object value = ((IQVTiEvaluationVisitor)undecoratedVisitor).safeVisit(variableAssignment.getValue());
// The variable had been added to the environment before the mapping call
if (targetVariable != null) {
evaluationEnvironment.replace(targetVariable, value);
}
return null;
}
@Override
public @Nullable Object visitVariablePredicate(@NonNull VariablePredicate variablePredicate) {
IdResolver idResolver = environmentFactory.getIdResolver();
// Each predicate has a conditionExpression that is an OCLExpression
OCLExpression exp = variablePredicate.getConditionExpression();
Object value = ((IQVTiEvaluationVisitor)undecoratedVisitor).safeVisit(exp);
Variable variable = variablePredicate.getTargetVariable();
Type guardType = variable.getType();
Type valueType = idResolver.getDynamicTypeOf(value);
if ((guardType != null) && valueType.conformsTo(metamodelManager.getStandardLibrary(), guardType)) {
evaluationEnvironment.replace(variable, value);
} else {
// The initialisation fails, the guard is not met
return false;
}
return true;
}
}