/******************************************************************************* * Copyright (c) 2010, 2015 Kenn Hussey 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: * Kenn Hussey - Initial API and implementation *******************************************************************************/ package org.eclipse.ocl.pivot.internal.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.messages.PivotMessagesInternal; import org.eclipse.ocl.pivot.utilities.ClassUtil; import org.eclipse.ocl.pivot.utilities.OCL; import org.eclipse.ocl.pivot.utilities.ParserException; 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 */ 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 * * @throws ParserException * if the expression is invalid */ 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; } protected@Nullable Object evaluate(@NonNull Query query, @Nullable Object target) { return query.evaluateEcore(target); } /** * 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 { @SuppressWarnings("null") @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); org.eclipse.ocl.pivot.Class targetType = idResolver.getStaticTypeOf(targetValue); Type requiredType = nonNullSpecification.getOwnedContext().getType(); 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); targetType = idResolver.getStaticTypeOf(value); requiredType = ClassUtil.nonNullModel(parameterVariable.getType()); 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 = evaluate(query, target); // if (result.isInvalid()) { // String message = ClassUtil.bind(OCLMessages.EvaluationResultIsInvalid_ERROR_, getOperationName()); // throw new OCLDelegateException(message); // } // if ((result == null) / * || ocl.isInvalid(result) * /) { // String message = ClassUtil.bind(OCLMessages.EvaluationResultIsNull_ERROR_, getOperationName()); // throw new OCLDelegateException(message); // } // return converter.convert(ocl, result); // if (result == null) { // String message = NLS.bind(OCLMessages.EvaluationResultIsInvalid_ERROR_, PivotUtil.getBody(specification)); // throw new InvocationTargetException(new OCLDelegateException(message)); // } return idResolver.ecoreValueOf(null, result); } catch (InvocationTargetException e) { throw e; } catch (EvaluationException e) { String message = StringUtil.bind(PivotMessagesInternal.EvaluationResultIsInvalid_ERROR_, specification.getBody()); 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() { OCLExpression bodyExpression = specification.getOwnedBody(); if (bodyExpression != null) { return "<" + delegateDomain.getURI() + ":query> " + bodyExpression; //$NON-NLS-1$ //$NON-NLS-2$ } else { return "<" + delegateDomain.getURI() + ":query> " + specification.getBody(); //$NON-NLS-1$ //$NON-NLS-2$ } } }