blob: 18eafbe5343358641c1b199b12acd7f4e7da64f6 [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.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.NavigationCallExp;
import org.eclipse.ocl.pivot.OppositePropertyCallExp;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.Variable;
import org.eclipse.ocl.pivot.evaluation.EvaluationVisitor;
import org.eclipse.ocl.pivot.ids.IdResolver;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.ValueUtil;
import org.eclipse.ocl.pivot.values.InvalidValueException;
import org.eclipse.qvtd.pivot.qvtbase.Transformation;
import org.eclipse.qvtd.pivot.qvtcorebase.NavigationAssignment;
import org.eclipse.qvtd.pivot.qvtcorebase.RealizedVariable;
import org.eclipse.qvtd.pivot.qvtcorebase.utilities.QVTcoreBaseUtil;
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.utilities.GraphStringBuilder;
import org.eclipse.qvtd.pivot.qvtimperative.utilities.QVTimperativeUtil;
import org.eclipse.qvtd.runtime.evaluation.AbstractInvocation;
import org.eclipse.qvtd.runtime.evaluation.AbstractTransformer;
import org.eclipse.qvtd.runtime.evaluation.InvocationFailedException;
import org.eclipse.qvtd.runtime.evaluation.InvocationManager;
import org.eclipse.qvtd.runtime.evaluation.ObjectManager;
import org.eclipse.qvtd.runtime.evaluation.Transformer;
import org.eclipse.qvtd.runtime.internal.evaluation.IncrementalInvocationManager;
import org.eclipse.qvtd.runtime.internal.evaluation.IncrementalObjectManager;
import org.eclipse.qvtd.runtime.internal.evaluation.LazyInvocationManager;
import org.eclipse.qvtd.runtime.internal.evaluation.LazyObjectManager;
/**
* An evaluator implementation for OCL expressions.
*/
public class QVTiIncrementalExecutor extends BasicQVTiExecutor
{
/**
* The run-time API version.
*
* @noreference this is solely for development usage.
*/
public static int RUN_TIME_EVALUATOR_API_VERSION = Transformer.RUN_TIME_EVALUATOR_API_VERSION_1_1_0_2;
protected static abstract class InterpretedInvocation extends AbstractInvocation.Incremental
{
protected final @NonNull List<Object> theseValues;
protected Object returnStatus;
public InterpretedInvocation(@NonNull List<Object> theseValues) {
this.theseValues = theseValues;
}
@Override
public boolean isEqual(@NonNull IdResolver idResolver, @Nullable Object @NonNull [] thoseValues) {
int iMax = thoseValues.length;
if (iMax != theseValues.size()) {
return false;
}
for (int i = 0; i < iMax; i++) {
if (!ClassUtil.safeEquals(theseValues.get(i), thoseValues[i])) {
return false;
}
}
return true;
}
}
public enum Mode {
LAZY, // EvaluationStatus is created lazily where necessary
INCREMENTAL, // EvaluationStatus is created for all mapping elements
REPAIR // EvaluationStatus is updated for all mapping elements
};
protected final @NonNull Mode mode;
protected final @NonNull QVTiTransformationAnalysis transformationAnalysis;
protected final @NonNull InvocationManager invocationManager;
protected final @NonNull ObjectManager objectManager;
private @Nullable InterpretedInvocation currentInvocation = null;
public QVTiIncrementalExecutor(@NonNull QVTiEnvironmentFactory environmentFactory, @NonNull Transformation transformation, @NonNull Mode mode) {
super(environmentFactory, transformation);
this.mode = mode;
this.transformationAnalysis = getModelManager().getTransformationAnalysis();
boolean isLazy = mode == Mode.LAZY;
IdResolver idResolver = environmentFactory.getIdResolver();
this.invocationManager = isLazy ? new LazyInvocationManager(idResolver) : new IncrementalInvocationManager(idResolver);
this.objectManager = isLazy ? new LazyObjectManager((LazyInvocationManager)invocationManager) : new IncrementalObjectManager((IncrementalInvocationManager)invocationManager);
}
public @NonNull String createGraph(@NonNull GraphStringBuilder s) {
Execution2GraphVisitor execution2GraphVisitor = new Execution2GraphVisitor(s);
invocationManager.accept(execution2GraphVisitor);
objectManager.accept(execution2GraphVisitor);
String string = execution2GraphVisitor.toString();
assert string != null;
return string;
}
@Override
public @Nullable Object internalExecuteMappingCall(final @NonNull MappingCall mappingCall, final @NonNull Map<Variable, Object> variable2value, final @NonNull EvaluationVisitor undecoratedVisitor) {
if (mode == Mode.LAZY) {
Mapping asMapping = mappingCall.getReferredMapping();
assert asMapping != null;
if (!transformationAnalysis.isHazardous(asMapping)) {
return QVTiIncrementalExecutor.super.internalExecuteMappingCall(mappingCall, variable2value, undecoratedVisitor);
}
}
List<Object> newBoundValues = new ArrayList<Object>();
for (MappingCallBinding binding : mappingCall.getBinding()) {
Variable boundVariable = ClassUtil.nonNullModel(binding.getBoundVariable());
Object valueOrValues = variable2value.get(boundVariable);
newBoundValues.add(valueOrValues);
}
InterpretedInvocation invocation = new InterpretedInvocation(newBoundValues)
{
@Override
public boolean execute() throws InvocationFailedException, ReflectiveOperationException {
currentInvocation = this;
try {
returnStatus = QVTiIncrementalExecutor.super.internalExecuteMappingCall(mappingCall, variable2value, undecoratedVisitor);
return returnStatus == ValueUtil.TRUE_VALUE;
}
finally {
currentInvocation = null;
}
}
@Override
public String toString() {
return mappingCall.getReferredMapping().getName() + "@" + Integer.toHexString(System.identityHashCode(this));
}
};
AbstractTransformer.INVOCATIONS.println("invoke " + invocation);
try {
invocationManager.invoke(invocation, true);
} catch (ReflectiveOperationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
@Override
public @Nullable Object internalExecuteNavigationCallExp(@NonNull NavigationCallExp navigationCallExp, @NonNull Property referredProperty, @Nullable Object sourceValue) {
if (referredProperty.isIsImplicit()) {
QVTiModelManager modelManager = getModelManager();
Integer cacheIndex = modelManager.getTransformationAnalysis().getCacheIndex((OppositePropertyCallExp) navigationCallExp);
if (cacheIndex != null) {
if (sourceValue != null) {
Object middleOpposite = modelManager.getUnnavigableOpposite(cacheIndex, sourceValue);
if (middleOpposite != null) {
return middleOpposite;
}
}
else {
throw new InvalidValueException("Failed to evaluate '" + referredProperty + "'", sourceValue, navigationCallExp);
}
}
}
Mapping asMapping = QVTimperativeUtil.getContainingMapping(navigationCallExp);
Object ecoreValue;
if ((asMapping != null) && transformationAnalysis.isHazardousRead(asMapping, navigationCallExp)) { // null within queries
// assert sourceValue != null;
if (sourceValue == null) {
throw new InvalidValueException("Null source for '" + referredProperty + "'", sourceValue, navigationCallExp);
}
boolean isOpposite = false;
EStructuralFeature eFeature = (EStructuralFeature)referredProperty.getESObject();
if (eFeature == null) {
assert referredProperty.isIsImplicit();
isOpposite = true;
eFeature = (EStructuralFeature)referredProperty.getOpposite().getESObject();
}
objectManager.getting(sourceValue, eFeature, isOpposite);
ecoreValue = super.internalExecuteNavigationCallExp(navigationCallExp, referredProperty, sourceValue);
AbstractTransformer.INVOCATIONS.println("got " + eFeature.getEContainingClass().getName() + "::" + eFeature.getName() + " for " + sourceValue + " = " + ecoreValue);
}
else {
ecoreValue = super.internalExecuteNavigationCallExp(navigationCallExp, referredProperty, sourceValue);
}
if (mode != Mode.LAZY) {
InterpretedInvocation currentInvocation2 = currentInvocation;
if (currentInvocation2 != null) { // Null at root
assert sourceValue != null;
EStructuralFeature eFeature = (EStructuralFeature)referredProperty.getESObject();
assert currentInvocation2 != null;
objectManager.got(currentInvocation2, sourceValue, eFeature, ecoreValue);
}
}
return ecoreValue;
}
@Override
public void internalExecuteNavigationAssignment(@NonNull NavigationAssignment navigationAssignment, @NonNull Object sourceObject, @Nullable Object ecoreValue, @Nullable Object childKey) {
super.internalExecuteNavigationAssignment(navigationAssignment, sourceObject, ecoreValue, childKey);
if (mode == Mode.LAZY) {
Mapping asMapping = QVTimperativeUtil.getContainingMapping(navigationAssignment);
assert asMapping != null;
if (transformationAnalysis.isHazardousWrite(asMapping, navigationAssignment)) {
Property targetProperty = QVTcoreBaseUtil.getTargetProperty(navigationAssignment);
assert targetProperty != null;
EStructuralFeature eFeature = (EStructuralFeature)targetProperty.getESObject();
objectManager.assigned(sourceObject, eFeature, ecoreValue, childKey);
}
}
else {
Property targetProperty = QVTcoreBaseUtil.getTargetProperty(navigationAssignment);
assert targetProperty != null;
EStructuralFeature eFeature = (EStructuralFeature)targetProperty.getESObject();
InterpretedInvocation currentInvocation2 = currentInvocation;
assert currentInvocation2 != null;
objectManager.assigned(currentInvocation2, sourceObject, eFeature, ecoreValue, childKey);
}
}
@Override
public @Nullable Object internalExecuteRealizedVariable(@NonNull RealizedVariable realizedVariable, @NonNull EvaluationVisitor undecoratedVisitor) {
Object element = super.internalExecuteRealizedVariable(realizedVariable, undecoratedVisitor);
if ((element != null) && (mode == Mode.INCREMENTAL)) {
InterpretedInvocation currentInvocation2 = currentInvocation;
assert currentInvocation2 != null;
objectManager.created(currentInvocation2, element);
/* DomainUsage domainUsage = getEvaluationEnvironment().getUsageFor(realizedVariable);
ClassStatus classStatus = statusManager.getClassStatus(domainUsage, realizedVariable.getType(), (EObject)element);
MappingStatus mappingStatus = findMappingStatus();
assert mappingStatus != null;
mappingStatus.getOutputs().add(classStatus); */
}
return element;
}
}