blob: 2fd810dcdc3f02cce313c8a074e93725ed17ff26 [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.HashMap;
import java.util.List;
import java.util.Map;
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.NavigationCallExp;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.OperationCallExp;
import org.eclipse.ocl.pivot.OppositePropertyCallExp;
import org.eclipse.ocl.pivot.Property;
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.PivotUtil;
import org.eclipse.ocl.pivot.utilities.ValueUtil;
import org.eclipse.ocl.pivot.values.InvalidValueException;
import org.eclipse.qvtd.pivot.qvtbase.Function;
import org.eclipse.qvtd.pivot.qvtbase.graphs.GraphStringBuilder;
import org.eclipse.qvtd.pivot.qvtimperative.ImperativeTransformation;
import org.eclipse.qvtd.pivot.qvtimperative.Mapping;
import org.eclipse.qvtd.pivot.qvtimperative.MappingCall;
import org.eclipse.qvtd.pivot.qvtimperative.NewStatement;
import org.eclipse.qvtd.pivot.qvtimperative.ObservableStatement;
import org.eclipse.qvtd.pivot.qvtimperative.SetStatement;
import org.eclipse.qvtd.pivot.qvtimperative.utilities.QVTimperativeUtil;
import org.eclipse.qvtd.runtime.evaluation.AbstractComputation;
import org.eclipse.qvtd.runtime.evaluation.AbstractInvocation;
import org.eclipse.qvtd.runtime.evaluation.AbstractTransformer;
import org.eclipse.qvtd.runtime.evaluation.Computation;
import org.eclipse.qvtd.runtime.evaluation.Invocation;
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.AbstractComputationConstructor;
import org.eclipse.qvtd.runtime.internal.evaluation.AbstractInvocationConstructor;
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;
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 Invocation.@Nullable Incremental currentInvocation = null;
private @Nullable Map<@NonNull Mapping, Invocation.@NonNull Constructor> mapping2invocationConstructor = null;
private @Nullable Map<@NonNull Operation, Computation.@NonNull Constructor> operation2computationConstructor = null;
public QVTiIncrementalExecutor(@NonNull QVTiEnvironmentFactory environmentFactory, @NonNull ImperativeTransformation transformation, @NonNull Mode mode) {
super(environmentFactory, transformation);
this.mode = mode;
this.transformationAnalysis = getModelManager().getTransformationAnalysis();
boolean isLazy = mode == Mode.LAZY;
this.invocationManager = isLazy ? new LazyInvocationManager() : new IncrementalInvocationManager();
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
protected @Nullable Object internalExecuteFunctionCallExp(@NonNull OperationCallExp operationCallExp,
@NonNull Function asFunction, @Nullable Object @NonNull [] boxedSourceAndArgumentValues) {
Map<@NonNull Operation, Computation.@NonNull Constructor> operation2computationConstructor2 = operation2computationConstructor;
if (operation2computationConstructor2 == null) {
operation2computationConstructor = operation2computationConstructor2 = new HashMap<@NonNull Operation, Computation.@NonNull Constructor>();
}
Computation.Constructor computationConstructor = operation2computationConstructor2.get(asFunction);
if (computationConstructor == null) {
computationConstructor = new AbstractComputationConstructor(idResolver)
{
@Override
public @NonNull Computation newInstance(@Nullable Object @NonNull [] theseValues) {
Computation.Incremental computation = new AbstractComputation.Incremental()
{
protected Object result = QVTiIncrementalExecutor.super.internalExecuteFunctionCallExp(operationCallExp, asFunction, theseValues);
@Override
public @Nullable Object getResult() {
return result;
}
@Override
public boolean isEqual(@NonNull IdResolver idResolver, @Nullable Object @NonNull [] thoseValues) {
int iMax = thoseValues.length;
if (iMax != theseValues.length) {
return false;
}
for (int i = 0; i < iMax; i++) {
if (!idResolver.oclEquals(theseValues[i], thoseValues[i])) {
return false;
}
}
return true;
}
@Override
public String toString() {
return operationCallExp.getReferredOperation().getName() + "@" + Integer.toHexString(System.identityHashCode(this));
}
};
return computation;
}
};
operation2computationConstructor2.put(asFunction, computationConstructor);
}
Computation computation = computationConstructor.getUniqueComputation(boxedSourceAndArgumentValues);
return computation.getResult();
}
@Override
public @Nullable Object internalExecuteMappingCall(final @NonNull MappingCall mappingCall, @NonNull Object @NonNull [] boundValues, final @NonNull EvaluationVisitor undecoratedVisitor) {
Mapping asMapping = ClassUtil.nonNullState(mappingCall.getReferredMapping());
if (mode == Mode.LAZY) {
if (!transformationAnalysis.isHazardous(asMapping)) {
return super.internalExecuteMappingCall(mappingCall, boundValues, undecoratedVisitor);
}
}
Map<@NonNull Mapping, Invocation.@NonNull Constructor> mapping2invocationConstructor2 = mapping2invocationConstructor;
if (mapping2invocationConstructor2 == null) {
mapping2invocationConstructor = mapping2invocationConstructor2 = new HashMap<@NonNull Mapping, Invocation.@NonNull Constructor>();
}
Invocation.Constructor invocationConstructor = mapping2invocationConstructor2.get(asMapping);
if (invocationConstructor == null) {
invocationConstructor = new AbstractInvocationConstructor(idResolver)
{
@Override
public @NonNull Invocation newInstance(@NonNull Object @NonNull [] theseValues) {
Invocation.Incremental invocation = new AbstractInvocation.Incremental()
{
protected Object returnStatus;
@Override
public boolean execute() throws InvocationFailedException {
currentInvocation = this;
try {
returnStatus = QVTiIncrementalExecutor.super.internalExecuteMappingCall(mappingCall, theseValues, undecoratedVisitor);
return returnStatus == ValueUtil.TRUE_VALUE;
}
finally {
currentInvocation = null;
}
}
@Override
public boolean isEqual(@NonNull IdResolver idResolver, @NonNull Object @NonNull [] thoseValues) {
int iMax = thoseValues.length;
if (iMax != theseValues.length) {
return false;
}
for (int i = 0; i < iMax; i++) {
if (!idResolver.oclEquals(theseValues[i], thoseValues[i])) {
return false;
}
}
return true;
}
@Override
public String toString() {
return mappingCall.getReferredMapping().getName() + "@" + Integer.toHexString(System.identityHashCode(this));
}
};
return invocation;
}
};
mapping2invocationConstructor2.put(asMapping, invocationConstructor);
}
Invocation invocation = invocationConstructor.getFirstInvocation(boundValues);
if (invocation != null) {
if (debugInvocations) {
AbstractTransformer.INVOCATIONS.println("invoke " + invocation);
}
invocationManager.invoke(invocation, true);
}
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
if ((asMapping != null) && isHazardous2(navigationCallExp)) { // null within queries
// assert false; // Should use an AccessStatement
// 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);
if (debugInvocations) {
AbstractTransformer.INVOCATIONS.println("got " + eFeature.getEContainingClass().getName() + "::" + eFeature.getName() + " for " + sourceValue + " = " + ecoreValue);
}
}
else {
ecoreValue = super.internalExecuteNavigationCallExp(navigationCallExp, referredProperty, sourceValue);
}
if (mode != Mode.LAZY) {
Invocation.Incremental 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;
}
private boolean isHazardous2(@NonNull NavigationCallExp asNavigationCallExp) {
for (EObject eObject = asNavigationCallExp; eObject != null; eObject = eObject.eContainer()) {
if (eObject instanceof ObservableStatement) {
List<Property> observedProperties = ((ObservableStatement)eObject).getObservedProperties();
Property navigatedProperty = PivotUtil.getReferredProperty(asNavigationCallExp);
return observedProperties.contains(navigatedProperty);
}
}
return false;
}
@Override
public @Nullable Object internalExecuteNewStatement(@NonNull NewStatement asNewStatement, @NonNull EvaluationVisitor undecoratedVisitor) {
Object element = super.internalExecuteNewStatement(asNewStatement, undecoratedVisitor);
if ((element != null) && (mode == Mode.INCREMENTAL)) {
Invocation.Incremental 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;
}
@Override
public void internalExecuteSetStatement(@NonNull SetStatement setStatement, @NonNull Object sourceObject, @Nullable Object ecoreValue, @Nullable Object childKey) {
super.internalExecuteSetStatement(setStatement, sourceObject, ecoreValue, childKey);
if (mode == Mode.LAZY) {
if (setStatement.isIsNotify()) {
Property targetProperty = QVTimperativeUtil.getTargetProperty(setStatement);
assert targetProperty != null;
EStructuralFeature eFeature = (EStructuralFeature)targetProperty.getESObject();
objectManager.assigned(sourceObject, eFeature, ecoreValue, childKey);
}
}
else {
Property targetProperty = QVTimperativeUtil.getTargetProperty(setStatement);
assert targetProperty != null;
EStructuralFeature eFeature = (EStructuralFeature)targetProperty.getESObject();
Invocation.Incremental currentInvocation2 = currentInvocation;
assert currentInvocation2 != null;
objectManager.assigned(currentInvocation2, sourceObject, eFeature, ecoreValue, childKey);
}
}
}