blob: f0dd6187d2b30d32deab652546382fba2b409acc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2021 Willink Transformations and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* C.Damus, K.Hussey, E.D.Willink - Initial API and implementation
*******************************************************************************/
package org.eclipse.ocl.pivot.internal.delegate;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.util.BasicInvocationDelegate;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.Constraint;
import org.eclipse.ocl.pivot.ExpressionInOCL;
import org.eclipse.ocl.pivot.LanguageExpression;
import org.eclipse.ocl.pivot.NamedElement;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.Variable;
import org.eclipse.ocl.pivot.VariableDeclaration;
import org.eclipse.ocl.pivot.evaluation.EvaluationEnvironment;
import org.eclipse.ocl.pivot.evaluation.EvaluationException;
import org.eclipse.ocl.pivot.evaluation.EvaluationVisitor;
import org.eclipse.ocl.pivot.evaluation.Executor;
import org.eclipse.ocl.pivot.evaluation.ModelManager;
import org.eclipse.ocl.pivot.ids.IdResolver;
import org.eclipse.ocl.pivot.internal.messages.PivotMessagesInternal;
import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal;
import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal.EnvironmentFactoryInternalExtension;
import org.eclipse.ocl.pivot.internal.utilities.PivotConstantsInternal;
import org.eclipse.ocl.pivot.internal.utilities.PivotUtilInternal;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.EnvironmentFactory;
import org.eclipse.ocl.pivot.utilities.MetamodelManager;
import org.eclipse.ocl.pivot.utilities.OCL;
import org.eclipse.ocl.pivot.utilities.ParserException;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
import org.eclipse.ocl.pivot.utilities.SemanticException;
import org.eclipse.ocl.pivot.utilities.ThreadLocalExecutor;
/**
* An implementation of an operation-invocation delegate for OCL body
* expressions.
*/
public class OCLInvocationDelegate extends BasicInvocationDelegate
{
protected final @NonNull OCLDelegateDomain delegateDomain;
private Operation operation = null;
private @Nullable ExpressionInOCL query = null;
/**
* Initializes me with my operation.
*
* @param operation
* the operation that I handle
*/
public OCLInvocationDelegate(@NonNull OCLDelegateDomain delegateDomain, @NonNull EOperation operation) {
super(operation);
this.delegateDomain = delegateDomain;
}
@Override
public Object dynamicInvoke(InternalEObject target, EList<?> arguments) throws InvocationTargetException {
assert target != null;
try {
EnvironmentFactoryInternal environmentFactory = PivotUtilInternal.getEnvironmentFactory(target);
Executor executor = PivotUtil.getExecutor(target);
ModelManager modelManager = executor.getModelManager();
MetamodelManager metamodelManager = environmentFactory.getMetamodelManager();
ExpressionInOCL query2 = query;
if (query2 == null) {
Operation operation2 = operation;
NamedElement namedElement = delegateDomain.getPivot(NamedElement.class, ClassUtil.nonNullEMF(eOperation));
if (namedElement instanceof Operation) {
operation2 = operation = (Operation) namedElement;
query2 = query = InvocationBehavior.INSTANCE.getQueryOrThrow(metamodelManager, operation2);
InvocationBehavior.INSTANCE.validate(operation2);
}
else if (namedElement instanceof Constraint) {
Constraint constraint = (Constraint)namedElement;
query2 = query = ValidationBehavior.INSTANCE.getQueryOrThrow(metamodelManager, constraint);
ValidationBehavior.INSTANCE.validate(constraint);
}
else if (namedElement != null) {
throw new OCLDelegateException(new SemanticException("Unsupported InvocationDelegate for a " + namedElement.eClass().getName())) ;
}
else {
throw new OCLDelegateException(new SemanticException("Unsupported InvocationDelegate for a null")) ;
}
}
return evaluate(environmentFactory, modelManager, target, arguments);
}
catch (EvaluationException e) {
throw new OCLDelegateException(new EvaluationException(e, PivotMessagesInternal.EvaluationResultIsInvalid_ERROR_, operation));
}
}
@Deprecated /* @deprecated not usedc */
protected Object evaluate(@NonNull OCL ocl, @NonNull ExpressionInOCL query2, InternalEObject ecoreObject, List<?> arguments) {
return null;
}
/**
* @since 1.7
*/
protected Object evaluate(@NonNull EnvironmentFactory environmentFactory, @NonNull ModelManager modelManager, InternalEObject ecoreObject, List<?> arguments) {
ExpressionInOCL query2 = query;
assert query2 != null;
Executor savedExecutor = ThreadLocalExecutor.basicGetExecutor();
try {
if (savedExecutor != null) {
ThreadLocalExecutor.setExecutor(null); // New evaluation needs new root EvaluationEnvironment and so new Executor, but old modelManager
}
IdResolver idResolver = environmentFactory.getIdResolver();
EvaluationEnvironment evaluationEnvironment = environmentFactory.createEvaluationEnvironment(query2, modelManager);
Object boxedObject = idResolver.boxedValueOf(ecoreObject);
VariableDeclaration contextVariable = PivotUtil.getOwnedContext(query2);
OCLExpression expression = PivotUtil.getOwnedBody(query2);
evaluationEnvironment.add(contextVariable, boxedObject);
List<Variable> parms = query2.getOwnedParameters();
if (!parms.isEmpty()) {
// bind arguments to parameter names
for (int i = 0; i < parms.size(); i++) {
Object argument = arguments.get(i);
Object boxedArgument = idResolver.boxedValueOf(argument);
evaluationEnvironment.add(ClassUtil.nonNullModel(parms.get(i)), boxedArgument);
}
}
// Variable resultVariable = specification.getResultVariable();
// if (resultVariable != null) {
// myEnv.add(resultVariable, null);
// }
EvaluationVisitor evaluationVisitor = environmentFactory.createEvaluationVisitor(evaluationEnvironment);
// try {
Object boxedResult = expression.accept(evaluationVisitor);
return idResolver.ecoreValueOf(eOperation.getEType().getInstanceClass(), boxedResult);
// } catch (EvaluationHaltedException e) {
// throw e;
// }
}
finally {
ThreadLocalExecutor.setExecutor(savedExecutor); // Restore invoker's executor
}
}
public @NonNull Operation getOperation() {
Operation operation2 = operation;
if (operation2 == null) {
NamedElement pivot = delegateDomain.getPivot(NamedElement.class, ClassUtil.nonNullEMF(eOperation));
if (pivot instanceof Operation) {
operation2 = operation = (Operation) pivot;
}
if (operation2 == null) {
throw new OCLDelegateException(new SemanticException("No pivot property for " + eOperation)) ;
}
}
return operation2;
}
public @NonNull ExpressionInOCL getQueryOrThrow(@NonNull MetamodelManager metamodelManager, @NonNull Constraint constraint) {
LanguageExpression specification = constraint.getOwnedSpecification();
if (specification == null) {
throw new OCLDelegateException(new SemanticException(PivotMessagesInternal.MissingSpecificationBody_ERROR_, constraint.getContext(), PivotConstantsInternal.BODY_ROLE));
}
try {
return ((EnvironmentFactoryInternalExtension)metamodelManager.getEnvironmentFactory()).parseSpecification(specification);
} catch (ParserException e) {
throw new OCLDelegateException(e);
}
}
@Override
public String toString() {
if (operation != null) {
return "<" + delegateDomain.getURI() + ":invocation> " + operation; //$NON-NLS-1$ //$NON-NLS-2$
}
else {
String name = eOperation.getEContainingClass().getEPackage().getName()
+ "::" + eOperation.getEContainingClass().getName()
+ "." + eOperation.getName();
return "<" + delegateDomain.getURI() + ":invocation> " + name; //$NON-NLS-1$ //$NON-NLS-2$
}
}
}