blob: af2c21ff14f14154570ccfbeb81c4e34860ce3e7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2018 Kenn Hussey 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:
* Kenn Hussey - Initial API and implementation
*******************************************************************************/
package org.eclipse.ocl.ecore.delegate;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.QueryDelegate;
import org.eclipse.ocl.Environment;
import org.eclipse.ocl.EvaluationEnvironment;
import org.eclipse.ocl.LookupException;
import org.eclipse.ocl.ParserException;
import org.eclipse.ocl.ecore.CallOperationAction;
import org.eclipse.ocl.ecore.Constraint;
import org.eclipse.ocl.ecore.OCL;
import org.eclipse.ocl.ecore.OCLExpression;
import org.eclipse.ocl.ecore.SendSignalAction;
import org.eclipse.ocl.expressions.Variable;
import org.eclipse.ocl.internal.l10n.OCLMessages;
import org.eclipse.ocl.util.TypeUtil;
import org.eclipse.ocl.utilities.OCLFactory;
import org.eclipse.ocl.utilities.UMLReflection;
/**
* An implementation of a query delegate for OCL expressions.
*
* @see OCLQueryDelegateFactory
* @since 3.1
*/
public class OCLQueryDelegate implements QueryDelegate
{
protected final OCLDelegateDomain delegateDomain;
protected final EClassifier context;
protected final Map<String, EClassifier> variables;
protected final String expression;
private OCLExpression query = null;
/**
* Initializes me with my domain, context, variables, and expression.
*
* @param delegateDomain
* my domain
* @param context
* my context
* @param variables
* name and types of variables used in my expression
* @param expression
* the expression that I handle
*
* @throws ParserException
* if the expression is invalid
*/
public OCLQueryDelegate(OCLDelegateDomain delegateDomain,
EClassifier context, Map<String, EClassifier> variables,
String expression) {
this.delegateDomain = delegateDomain;
this.context = context;
this.variables = variables;
this.expression = expression;
}
/**
* 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
*/
public void prepare() throws InvocationTargetException {
OCL ocl = delegateDomain.getOCL();
OCL.Helper helper = ocl.createOCLHelper();
helper.setContext(context);
if (variables != null) {
// create variables with specified names and types
@SuppressWarnings("unchecked")
Environment<?, EClassifier, ?, ?, ?, EParameter, ?, ?, ?, ?, ?, ?> environment = (Environment<?, EClassifier, ?, ?, ?, EParameter, ?, ?, ?, ?, ?, ?>) helper
.getEnvironment();
OCLFactory oclFactory = environment.getOCLFactory();
UMLReflection<?, EClassifier, ?, ?, ?, EParameter, ?, ?, ?, ?> umlReflection = environment
.getUMLReflection();
for (Map.Entry<String, EClassifier> entry : variables.entrySet()) {
Variable<EClassifier,EParameter> variable = oclFactory.createVariable();
variable.setName(entry.getKey());
variable.setType(umlReflection.getOCLType(entry.getValue()));
environment.addElement(entry.getKey(), variable, true);
}
}
try {
query = helper.createQuery(expression);
} catch (ParserException e) {
throw new InvocationTargetException(e);
}
}
/**
* 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
*/
public Object execute(Object target, Map<String, ?> arguments)
throws InvocationTargetException {
if (query == null) {
prepare();
}
OCL ocl = delegateDomain.getOCL();
OCL.Query oclQuery = ocl.createQuery(query);
EvaluationEnvironment<EClassifier, ?, ?, ?, ?> evalEnv = oclQuery.getEvaluationEnvironment();
Environment<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> env = ocl.getEnvironment();
EClassifier contextType = evalEnv.getType(target);
if (!TypeUtil.compatibleTypeMatch(env, contextType, context)) {
String message = OCLMessages.bind(OCLMessages.WrongContextClassifier_ERROR_,
contextType.getName(), context.getName());
throw new InvocationTargetException(new ParserException(message));
}
if (variables != null) {
// check variables defined
for (Map.Entry<String, ?> entry : variables.entrySet()) {
String key = entry.getKey();
if ((arguments == null) || !arguments.containsKey(key)) {
String message = OCLMessages.bind(OCLMessages.BadArg_ERROR_, key);
throw new InvocationTargetException(new ParserException(message));
}
}
}
if (arguments != null) {
// bind values to variable names
for (Map.Entry<String, ?> entry : arguments.entrySet()) {
String key = entry.getKey();
EClassifier variableType = variables != null ? variables.get(key) : null;
if (variableType == null) {
String message = OCLMessages.bind(OCLMessages.ExtraArg_ERROR_, key);
throw new InvocationTargetException(new LookupException(message));
}
Object newValue = entry.getValue();
EClassifier valueType = evalEnv.getType(newValue);
if (!TypeUtil.compatibleTypeMatch(env, valueType, variableType)) {
String message = OCLMessages.bind(OCLMessages.TypeConformanceInit_ERROR_, key);
throw new InvocationTargetException(new ParserException(message));
}
evalEnv.replace(key, newValue);
}
}
return oclQuery.evaluate(target);
}
@Override
public String toString() {
return "<" + delegateDomain.getURI() + ":query> " + expression; //$NON-NLS-1$ //$NON-NLS-2$
}
}