/***************************************************************************** * Copyright (c) 2010 CEA LIST. * * 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: * Remi Schnekenburger (CEA LIST) remi.schnekenburger@cea.fr - Initial API and implementation *****************************************************************************/ package org.eclipse.papyrus.infra.queries.core.modisco; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Status; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.facet.infra.query.ModelQuery; import org.eclipse.emf.facet.infra.query.ModelQueryParameter; import org.eclipse.emf.facet.infra.query.ModelQuerySet; import org.eclipse.emf.facet.infra.query.core.AbstractModelQuery; import org.eclipse.emf.facet.infra.query.core.ModelQuerySetCatalog; import org.eclipse.emf.facet.infra.query.core.java.ParameterValueList; import org.eclipse.emf.facet.infra.query.runtime.ModelQueryParameterValue; import org.eclipse.emf.facet.infra.query.runtime.ModelQueryResult; import org.eclipse.papyrus.infra.queries.core.Activator; import org.eclipse.papyrus.infra.queries.core.configuration.ConstantParameterValue; import org.eclipse.papyrus.infra.queries.core.configuration.ListParameterValue; import org.eclipse.papyrus.infra.queries.core.configuration.ModiscoQueryConfiguration; import org.eclipse.papyrus.infra.queries.core.configuration.ParameterValue; import org.eclipse.papyrus.infra.queries.core.configuration.QueryConfiguration; import org.eclipse.papyrus.infra.queries.core.configuration.QueryExecutionValue; import org.eclipse.papyrus.infra.queries.core.configuration.util.ConfigurationSwitch; import org.eclipse.papyrus.infra.queries.core.converter.ConverterNotfoundException; import org.eclipse.papyrus.infra.queries.core.converter.ConverterRegistry; /** * Utility class for modisco queries */ public class QueryUtil { /** default value in case the query is not valid */ protected static final boolean defaultReturnValue = false; /** * Constructor. * Note: can not be instanciated, as it provides only static methods. */ protected QueryUtil() { } /** * Evaluates a modisco query, that should only return a boolean value. * * @param query * the query to check and evaluate * @param context * the eobject on which the query is evaluated * @param parameters * a map containing the name of parameters and their values * @return <code>true</code> if the context fills the condition given by the query * @throws Exception * exception thrown when the query could not be correctly evaluated */ public static final boolean evaluateBooleanQuery(final ModelQuery query, EObject context, Map<String, Object> parameters) throws Exception { // construct the structure parameterValues if the query is valid IStatus queryStatus = isValidQuery(query); if(!queryStatus.isOK()) { throw new Exception(queryStatus.getMessage()); } // if no parameters, evaluate List<ModelQueryParameter> queryParameters = query.getParameters(); List<ModelQueryParameterValue> parameterValues = new ArrayList<ModelQueryParameterValue>(); if(queryParameters == null || queryParameters.isEmpty()) { return evaluateBooleanQuery(query, context, parameterValues); } // check the parameters given and the set of required parameters IStatus parameterCheck = isValidParameterSet(query, parameters); if(IStatus.CANCEL == parameterCheck.getSeverity() || IStatus.ERROR == parameterCheck.getSeverity()) { Activator.log.error(parameterCheck.getMessage(), null); throw new Exception(parameterCheck.getMessage()); } else if(IStatus.WARNING == parameterCheck.getSeverity()) { Activator.log.warn(parameterCheck.getMessage()); } else if(IStatus.INFO == parameterCheck.getSeverity()) { Activator.log.info(parameterCheck.getMessage()); } // construct parameter structure for Modisco solver for(ModelQueryParameter parameter : queryParameters) { String parametername = parameter.getName(); Object value = parameters.get(parametername); ModelQueryParameterValue parameterValue = ParameterValueList.createParameterValue(value, parameter); // should try to bind elements here.. parameterValues.add(parameterValue); } // execute the query return evaluateBooleanQuery(query, context, parameterValues); } /** * Evaluates a query. * * @param context * the eobject on which the query is evaluated * @param queryConfiguration * the query configuration, containing all values for the parameters of the query * @return the result of the execution of the query * @throws Exception * exception thrown when the query could not be correctly evaluated */ public static final Object evaluateQuery(EObject context, QueryConfiguration queryConfiguration) throws Exception { if(queryConfiguration instanceof ModiscoQueryConfiguration) { return evaluateQuery(context, (ModiscoQueryConfiguration)queryConfiguration); } throw new Exception("Unhandled kind of query"); } /** * Evaluates a query, that should only return a boolean value. * * @param context * the eobject on which the query is evaluated * @param queryConfiguration * the query configuration, containing all values for the parameters of the query * @return <code>true</code> if the context fills the condition given by the query * @throws Exception * exception thrown when the query could not be correctly evaluated */ public static final boolean evaluateBooleanQuery(EObject context, QueryConfiguration queryConfiguration) throws Exception { if(queryConfiguration instanceof ModiscoQueryConfiguration) { return evaluateBooleanQuery(context, (ModiscoQueryConfiguration)queryConfiguration); } throw new Exception("Unhandled kind of query"); } /** * Evaluates a modisco query, that should only return a boolean value. * * @param context * the eobject on which the query is evaluated * @param queryConfiguration * the query configuration, containing all values for the parameters of the query * @return <code>true</code> if the context fills the condition given by the query * @throws Exception * exception thrown when the query could not be correctly evaluated */ public static final boolean evaluateBooleanQuery(EObject context, ModiscoQueryConfiguration queryConfiguration) throws Exception { Object result = evaluateQuery(context, queryConfiguration); if(result instanceof Boolean) { return (Boolean)result; } Activator.log.error("Not a boolean result: " + result, null); return defaultReturnValue; } /** * Evaluates a modisco query, that should only return a boolean value. * * @param context * the eobject on which the query is evaluated * @param queryConfiguration * the query configuration, containing all values for the parameters of the query * @return <code>true</code> if the context fills the condition given by the query * @throws Exception * exception thrown when the query could not be correctly evaluated */ public static final Object evaluateQuery(EObject context, ModiscoQueryConfiguration queryConfiguration) throws Exception { // retrieve query (problem of bundle loader if not used correctly) ModelQuery query = queryConfiguration.getQuery(); String modelQuerySetName = query.getModelQuerySet().getName(); String modelQueryName = query.getName(); ModelQuery finalModelQuery = retrieveModelQuery(modelQueryName, modelQuerySetName); // construct the structure parameterValues if the query is valid IStatus queryValidation = isValidQuery(finalModelQuery); if(!queryValidation.isOK()) { throw new Exception(queryValidation.getMessage(), queryValidation.getException()); } // create the set of parameter values List<ModelQueryParameterValue> parameterValues = createParameterValuesListFromConfiguration(context, queryConfiguration); // execute the query return evaluateQuery(finalModelQuery, context, parameterValues); } /** * Creates the {@link ModelQueryParameterValue} list from a configuration * * @param queryConfiguration * the configuration to check * @return the list of parameter values */ private static List<ModelQueryParameterValue> createParameterValuesListFromConfiguration(EObject context, ModiscoQueryConfiguration queryConfiguration) { List<ModelQueryParameterValue> parameterValues = new ArrayList<ModelQueryParameterValue>(); List<ModelQueryParameter> parameters = queryConfiguration.getQuery().getParameters(); for(ModelQueryParameter parameter : parameters) { Object value = retrieveParameterValue(context, parameter, queryConfiguration); // resolve the value for the parameters ModelQueryParameterValue parameterValue = ParameterValueList.createParameterValue(value, parameter); parameterValues.add(parameterValue); } return parameterValues; } /** * Retrieves the value of a parameter. * * @param parameter * the parameter for which the value is computed * @return the parameter value associated to the parameter */ private static Object retrieveParameterValue(final EObject context, final ModelQueryParameter parameter, ModiscoQueryConfiguration configuration) { for(final ParameterValue parameterValue : configuration.getParameterValues()) { if(parameter.equals(parameterValue.getParameter())) { // This is the right parameter. Compute the value, depending on the type of the parameters return new ConfigurationSwitch<Object>() { /** * {@inheritDoc} */ @Override public Object caseListParameterValue(ListParameterValue listParameterValue) { ArrayList<Object> values = new ArrayList<Object>(); Class<?> parameterType = parameter.getType().getInstanceClass(); for(ParameterValue parameterValue : listParameterValue.getValues()) { values.addAll(ConverterRegistry.getSingleton().convertToList(context, parameterType, parameterValue)); } return values; }; /** * {@inheritDoc} */ @Override public Object caseConstantParameterValue(ConstantParameterValue constantParameterValue) { Class<?> parameterType = parameter.getType().getInstanceClass(); try { Object convertedValue = ConverterRegistry.getSingleton().convert(parameterType, constantParameterValue.getValueInstance()); return convertedValue; } catch (ConverterNotfoundException e) { Activator.log.error(e); } return null; }; /** * {@inheritDoc} */ @Override public Object caseQueryExecutionValue(QueryExecutionValue queryExecutionValue) { // retrieves the associated configuration and launch it. QueryConfiguration configuration = queryExecutionValue.getConfiguration(); try { return QueryUtil.evaluateQuery(context, configuration); } catch (Exception e) { Activator.log.error(e); } return null; }; }.doSwitch(parameterValue); } } return null; } /** * Evaluates a modisco query. * * @param query * the query to evaluate * @param context * the context element against which the query is evaluated * @param parameterValues * the list of parameters for the query * @return the result of the query evaluation * @throws Exception * exception thrown in case of problem during evaluation of the query */ public static final Object evaluateQuery(final ModelQuery query, EObject context, List<ModelQueryParameterValue> parameterValues) throws Exception { // checks that the query is valid if(isValidQuery(query).isOK()) { AbstractModelQuery myModelQuery; myModelQuery = ModelQuerySetCatalog.getSingleton().getModelQueryImpl(query); //the model query set evaluation ModelQueryResult result = myModelQuery.evaluate(context, parameterValues); if(result.getException() != null) { throw new Exception(); } return result.getValue(); } throw new Exception("Query was not a valid query: " + query, null); } /** * Evaluates a modisco query, that should only return a boolean value. * * @param query * the query to check and evaluate * @param context * the eobject on which the query is evaluated * @param parameterValues * the list of parameter values for the evaluation * @return <code>true</code> if the context fills the condition given by the query * @throws Exception * exception thrown when the query can not be correctly evaluated */ public static final boolean evaluateBooleanQuery(final ModelQuery query, EObject context, List<ModelQueryParameterValue> parameterValues) throws Exception { Object result = evaluateQuery(query, context, parameterValues); if(result instanceof Boolean) { // try to cast into a boolean return (Boolean)result; } Activator.log.error("Query did not return a boolean: " + result, null); return defaultReturnValue; } /** * Retrieves a model query, given its name and its query set. * * @param queryName * the name of the query * @param querySetName * the name of the query set * @return the found model query or <code>null</code>. * @throws ModelQueryNotFoundException * exception thrown when the the query set was found but not the model query was not found. * @throws ModelQuerySetNotFoundException * exception thrown when the the query set was not found. */ public static final ModelQuery retrieveModelQuery(String queryName, String querySetName) throws ModelQueryNotFoundException, ModelQuerySetNotFoundException { ModelQuerySet querySet = ModelQuerySetCatalog.getSingleton().getModelQuerySet(querySetName); if(querySet == null) { throw new ModelQuerySetNotFoundException(querySetName); } ModelQuery query = querySet.getQuery(queryName); if(query == null) { throw new ModelQueryNotFoundException(queryName); } return query; } /** * Checks if the query is valid, i.e. not <code>null</code>, returning a boolean value, etc. * * @param query * the query to test * @return <code>true</code> if the query is valid */ public static final IStatus isValidQuery(ModelQuery query) { if(query == null) { return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Query should not be null."); } return Status.OK_STATUS; } /** * Checks if the query is valid, i.e. not <code>null</code>, returning a boolean value, etc. * @param query the query to test * @return <code>true</code> if the query is valid */ public static final IStatus isValidBooleanQuery(ModelQuery query) { IStatus status = isValidQuery(query); if(status.isOK()) { // check the instance type is compatible with boolean EClassifier returnType = query.getReturnType(); if(returnType == null) { return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Query " + query.getName() + " does not have a return value, whereas it should have a boolean return status"); } else { Class<?> instanceClass = returnType.getInstanceClass(); if(!boolean.class.isAssignableFrom(instanceClass)) { return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Query " + query.getName() + " should return a value that can be cast into Boolean value. Currently: " + returnType.getInstanceClass()); } // check return size of the query... should be 0..1 or 1..1 (even 0..1 could be difficult to handle, but we let this possible) if(query.getUpperBound() > 1 || query.getUpperBound() < 0) { return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Query " + query.getName() + " should return only one value. Currently: " + query.getUpperBound()); } } } return status; } /** * Check if the set of given parameters is compatible with the query * @param query the query against which the set of parameters is tested * @param parameters the map of parameter names and their value * @return the status corresponding to the validation */ public static final IStatus isValidParameterSet(ModelQuery query, Map<String, Object> parameters) { // check size, which should be equal. int querySize = query.getParameters().size(); int parameterSize = parameters.keySet().size(); if(querySize != parameterSize) { return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "The given parameters set do not have the same size (" + parameterSize + ") than the query parameter set (" + querySize + ")"); } List<IStatus> status = new ArrayList<IStatus>(); // size is ok, checking names, type, multiplicity for(String parameterName : parameters.keySet()) { // retrieve the equivalent parameter in the query ModelQueryParameter queryParameter = retrieveParameterByName(query, parameterName); if(queryParameter == null) { status.add(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Impossible to find the parameter " + parameterName + " in the query parameters list for query: " + query.getName())); } } if(status.size() > 0) { return new MultiStatus(Activator.PLUGIN_ID, IStatus.OK, status.toArray(new IStatus[status.size()]), "", null); } return Status.OK_STATUS; } /** * Retrieves a parameter, given the query to look into and the name of the parameter * * @param query * the query where the parameter is stored * @param parameterName * the name of the parameter * @return the parameter */ private final static ModelQueryParameter retrieveParameterByName(ModelQuery query, String parameterName) { for(ModelQueryParameter queryParameter : query.getParameters()) { if(parameterName.equals(queryParameter.getName())) { return queryParameter; } } return null; } }