package com.sap.furcas.modeladaptation.emf.lookup; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import org.eclipse.emf.common.notify.Notifier; import org.eclipse.emf.common.util.BasicDiagnostic; import org.eclipse.emf.common.util.Diagnostic; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.query.index.IndexFactory; import org.eclipse.emf.query2.QueryContext; import org.eclipse.emf.query2.QueryProcessor; import org.eclipse.emf.query2.QueryProcessorFactory; import org.eclipse.emf.query2.ResultSet; import org.eclipse.ocl.ParserException; import org.eclipse.ocl.ecore.opposites.OppositeEndFinder; import com.sap.furcas.metamodel.FURCAS.TCS.Template; import com.sap.furcas.runtime.common.exceptions.MetaModelLookupException; import com.sap.furcas.runtime.common.interfaces.IMetaModelLookup; import com.sap.furcas.runtime.common.interfaces.ResolvedNameAndReferenceBean; import com.sap.furcas.runtime.common.util.EcoreHelper; import com.sap.furcas.runtime.common.util.MessageUtil; import com.sap.furcas.runtime.common.util.TCSSpecificOCLEvaluator; import com.sap.ocl.oppositefinder.query2.Query2OppositeEndFinder; import de.hpi.sam.bp2009.solution.queryContextScopeProvider.QueryContextProvider; /** * A query2 based {@link IMetaModelLookup} implementations. */ public class QueryBasedEcoreMetaModelLookUp extends AbstractEcoreMetaModelLookup { /** * A simple scope provider that can only be used to navigate with interconnected * metamodels. */ private class MetamodelQueryContextProvider implements QueryContextProvider { @Override public QueryContext getForwardScopeQueryContext(Notifier context) { return EcoreHelper.getRestrictedQueryContext(resourceSet, metaModelURIs); } @Override public QueryContext getBackwardScopeQueryContext(Notifier context) { return EcoreHelper.getRestrictedQueryContext(resourceSet, metaModelURIs); } } private final ResourceSet resourceSet; private final Set<URI> metaModelURIs; private final QueryProcessor queryProcessor; private final OppositeEndFinder oppositeEndFinder; private final TCSSpecificOCLEvaluator oclEvaluator; private final Map<List<String>, EClassifier> typeCache = new WeakHashMap<List<String>, EClassifier>(); public QueryBasedEcoreMetaModelLookUp(ResourceSet resourceSet, Set<URI> metaModelURIs) { this.resourceSet = resourceSet; this.metaModelURIs = metaModelURIs; queryProcessor = QueryProcessorFactory.getDefault().createQueryProcessor(IndexFactory.getInstance()); oppositeEndFinder = new Query2OppositeEndFinder(new MetamodelQueryContextProvider()); oclEvaluator = new TCSSpecificOCLEvaluator(oppositeEndFinder); } @Override protected EClassifier findClassifiersByQualifiedName(List<String> qualifiedNameOfType) throws MetaModelLookupException { if (qualifiedNameOfType == null || qualifiedNameOfType.size() == 0) { throw new IllegalArgumentException("qualifiedNameOfType must not be empty: " + qualifiedNameOfType); } EClassifier result = typeCache .get(qualifiedNameOfType); if(result == null) { // since we cannot query for the qualified name with MQL, query for the // name instead, and then compare qualified names to filter out wrong results String name = qualifiedNameOfType.get(qualifiedNameOfType.size() - 1); List<EClassifier> classifiers = findClassifiersByUnqualifiedName(name); if (qualifiedNameOfType.size() > 1) { classifiers = filterClassifiers(qualifiedNameOfType, classifiers); } if (classifiers == null || classifiers.size() == 0) { return null; } else if (classifiers.size() == 1) { result = classifiers.get(0); typeCache.put(qualifiedNameOfType, result); } else { throw new MetaModelLookupException("Ambiguous classifier name " + MessageUtil.asModelName(qualifiedNameOfType) + ". There are more than one class of that name within the metamodels " + MessageUtil.asMetaModelNames(metaModelURIs) + ". Please use sufficiently qualified names."); } } return result; } @Override protected List<EClassifier> findClassifiersByUnqualifiedName(String name) throws MetaModelLookupException { URI uriEClassifier = EcoreUtil.getURI(EcorePackage.eINSTANCE.getEClassifier()); String query = "select instance \n" + "from [" + uriEClassifier + "] as instance \n" + "where instance.name = '" + name + "'"; ResultSet resultSet = executeQuery(query); List<EClassifier> result = new ArrayList<EClassifier>(resultSet.getSize()); for (int i = 0; i < resultSet.getSize(); i++) { URI mri = resultSet.getUri(i, "instance"); EObject object = resourceSet.getEObject(mri, true); if (object != null) { EClassifier classifier = (EClassifier) object; result.add(classifier); } } return result; } /** * Removes those classifiers from the list which do not have the correct qualified name */ protected static List<EClassifier> filterClassifiers(List<String> qualifiedNameOfType, List<EClassifier> classifiers) { if (classifiers == null || classifiers.size() == 0 || qualifiedNameOfType == null) { return Collections.emptyList(); } List<EClassifier> resultList = new ArrayList<EClassifier>(); for (EClassifier classifier : classifiers) { List<String> otherQualifiedName = EcoreHelper.getQualifiedName(classifier); if (otherQualifiedName.containsAll(qualifiedNameOfType)) { resultList.add(classifier); } } return resultList; } @Override public List<ResolvedNameAndReferenceBean<EObject>> getDirectSubTypes(ResolvedNameAndReferenceBean<EObject> reference) throws MetaModelLookupException { URI uriEClassifier = EcoreUtil.getURI(EcorePackage.eINSTANCE.getEClassifier()); URI uriEClass = EcoreUtil.getURI(EcorePackage.eINSTANCE.getEClass()); String query = "select instance \n" + "from [" + uriEClass + "] as instance , \n" + "[" + uriEClassifier + "] as supertype in elements { [" + EcoreUtil.getURI(reference.getReference())+ "] }\n" + "where instance.eSuperTypes = supertype"; List<ResolvedNameAndReferenceBean<EObject>> result = null; ResultSet resultSet = executeQuery(query); result = new ArrayList<ResolvedNameAndReferenceBean<EObject>>(resultSet.getSize()); for (int i = 0; i < resultSet.getSize(); i++) { URI object = resultSet.getUri(i, "instance"); if (object != null) { EClassifier classifier = (EClassifier) resourceSet.getEObject(object, true); result.add(createBean(classifier)); } } return result; } private ResultSet executeQuery(String query) { try { QueryContext scopeProvider = EcoreHelper.getRestrictedQueryContext(resourceSet, metaModelURIs); return queryProcessor.execute(query, scopeProvider); } catch (RuntimeException rte) { rte.printStackTrace(); throw rte; } } @Override public List<Diagnostic> validateOclQuery(Template template, String queryToValidate) { return oclEvaluator.validateOclQuery(template, queryToValidate); } @Override public List<Diagnostic> validateOclQuery(EObject parsingContext, String queryToValidate) { if (parsingContext instanceof EClassifier) { return oclEvaluator.validateOclQuery((EClassifier) parsingContext, queryToValidate); } else { return Collections.singletonList((Diagnostic) new BasicDiagnostic(getClass().getName(), Diagnostic.ERROR, "Parsing context must be of type EClassifier. Invalid OCL parsing context: \"" + parsingContext + "\"", null)); } } @Override public EObject getOclReturnType(EObject parsingContext, String oclQuery) throws MetaModelLookupException { try { return oclEvaluator.getOclReturnType((EClassifier) parsingContext, oclQuery); } catch (ParserException e) { // Client can call validate to find out what is wrong. return null; } } @Override protected OppositeEndFinder getOppositeEndFinder() { return oppositeEndFinder; } @Override public Set<URI> getMetaModelURIs() { return metaModelURIs; } }