blob: 986c01398421fd3e634ab12bf711e4a3127cc7dd [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014, 2019 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:
* E.D.Willink - Initial API and implementation
*******************************************************************************/
package org.eclipse.ocl.examples.debug.delegate;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.util.QueryDelegate;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.ExpressionInOCL;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.Variable;
import org.eclipse.ocl.pivot.evaluation.EvaluationEnvironment;
import org.eclipse.ocl.pivot.evaluation.EvaluationException;
import org.eclipse.ocl.pivot.ids.IdResolver;
import org.eclipse.ocl.pivot.internal.context.EInvocationContext;
import org.eclipse.ocl.pivot.internal.delegate.OCLDelegateDomain;
import org.eclipse.ocl.pivot.internal.delegate.OCLDelegateException;
import org.eclipse.ocl.pivot.internal.delegate.OCLQueryDelegateFactory;
import org.eclipse.ocl.pivot.internal.messages.PivotMessagesInternal;
import org.eclipse.ocl.pivot.utilities.OCL;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
import org.eclipse.ocl.pivot.utilities.Query;
import org.eclipse.ocl.pivot.utilities.SemanticException;
import org.eclipse.ocl.pivot.utilities.StringUtil;
/**
* An implementation of a query delegate for OCL expressions.
*
* @see OCLQueryDelegateFactory
* @since 3.1
*/
public class OCLQueryDelegate implements QueryDelegate
{
protected @NonNull OCLDelegateDomain delegateDomain;
protected final @NonNull EInvocationContext parserContext;
protected final @NonNull String expression;
private ExpressionInOCL specification = null;
/**
* Initializes me with my domain, context, variables, and expression.
*
* @param delegateDomain
* my domain
* @param context
* my context
* @param parameters
* name and types of variables used in my expression
* @param expression
* the expression that I handle
*/
public OCLQueryDelegate(@NonNull OCLDelegateDomain delegateDomain, @NonNull EClassifier context, @Nullable Map<String, EClassifier> parameters, @NonNull String expression) {
this.delegateDomain = delegateDomain;
this.parserContext = new EInvocationContext(delegateDomain.getOCL().getEnvironmentFactory(), null, context, parameters);
this.expression = expression;
}
/**
* Executes the query for the specified <tt>target</tt> object. The result
* is the OCL evaluation result which may be a Number, String, Collection or
* other object for normal returns or a NullLiteralExp for null, or an
* InvalidLiteralExp for invalid.
*
* @param target
* the object on which to execute the query; this must be an
* instance of the context with which the delegate was created
* @param arguments
* a map of variable names to values; these must correspond to
* the variables with which the delegate was created
* @return the query's result
* @throws InvocationTargetException
* in case of failure to prepare or execute the query, usually
* because of an exception
*/
@Override
public Object execute(@Nullable Object target, Map<String, ?> arguments) throws InvocationTargetException {
@NonNull Map<String, ?> nonNullArguments = (arguments != null ? arguments : (Map<String, ?>)Collections.<String, Object>emptyMap());
try {
if (specification == null) {
prepare();
}
@SuppressWarnings("null")
@NonNull ExpressionInOCL nonNullSpecification = specification;
OCL ocl = delegateDomain.getOCL();
IdResolver idResolver = ocl.getIdResolver();
Object targetValue = idResolver.boxedValueOf(target);
Type requiredType = PivotUtil.getType(PivotUtil.getOwnedContext(nonNullSpecification));
Type targetType = idResolver.getStaticTypeOfValue(requiredType, targetValue);
if ((requiredType == null) || !targetType.conformsTo(ocl.getStandardLibrary(), requiredType)) {
String message = StringUtil.bind(PivotMessagesInternal.WrongContextClassifier_ERROR_, targetType, requiredType);
throw new OCLDelegateException(new SemanticException(message));
}
List<Variable> parameterVariables = nonNullSpecification.getOwnedParameters();
int argCount = arguments != null ? arguments.size() : 0;
if (parameterVariables.size() != argCount) {
String message = StringUtil.bind(PivotMessagesInternal.MismatchedArgumentCount_ERROR_, argCount, parameterVariables.size());
throw new OCLDelegateException(new SemanticException(message));
}
Query query = ocl.createQuery(nonNullSpecification);
EvaluationEnvironment env = query.getEvaluationEnvironment(target);
for (Variable parameterVariable : parameterVariables) {
// bind arguments to parameter names
String name = parameterVariable.getName();
Object object = nonNullArguments.get(name);
if ((object == null) && !nonNullArguments.containsKey(name)) {
String message = StringUtil.bind(PivotMessagesInternal.EvaluationResultIsInvalid_ERROR_, nonNullSpecification.getBody());
throw new OCLDelegateException(new SemanticException(message));
}
Object value = idResolver.boxedValueOf(object);
requiredType = PivotUtil.getType(parameterVariable);
targetType = idResolver.getStaticTypeOfValue(requiredType, value);
if (!targetType.conformsTo(ocl.getStandardLibrary(), requiredType)) {
String message = StringUtil.bind(PivotMessagesInternal.MismatchedArgumentType_ERROR_, name, targetType, requiredType);
throw new OCLDelegateException(new SemanticException(message));
}
env.add(parameterVariable, value);
}
Object result = query.evaluateEcore(target);
return result;
}
catch (InvocationTargetException e) {
throw e;
}
catch (EvaluationException e) {
String message = StringUtil.bind(PivotMessagesInternal.EvaluationResultIsInvalid_ERROR_, expression);
throw new InvocationTargetException(new EvaluationException(message));
}
catch (WrappedException e) {
throw new InvocationTargetException(e.getCause());
}
catch (Exception e) {
throw new InvocationTargetException(e);
}
}
/**
* Prepares the query wrapping any exceptions as InvocationTargetException.
* This method is lazily invoked from execute, but may be invoked eagerly
* to detect compilation errors earlier or incur compilation costs at a more
* convenient time.
*
* @throws InvocationTargetException wrapping any parser, io exceptions
*/
@Override
public void prepare() throws InvocationTargetException {
try {
specification = parserContext.parse(parserContext.getClassContext(), expression);
} catch (Exception e) {
throw new InvocationTargetException(e);
}
}
@Override
public String toString() {
ExpressionInOCL specification2 = specification;
OCLExpression bodyExpression = specification2 != null ? specification2.getOwnedBody() : null;
if (bodyExpression != null) {
return "<" + delegateDomain.getURI() + ":query> " + bodyExpression; //$NON-NLS-1$ //$NON-NLS-2$
}
else {
return "<" + delegateDomain.getURI() + ":query> " + expression; //$NON-NLS-1$ //$NON-NLS-2$
}
}
}