| /******************************************************************************* |
| * 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$ |
| } |
| } |