/******************************************************************************* * Copyright (c) 2008 SAP * see https://research.qkal.sap.corp/mediawiki/index.php/CoMONET * * Date: $Date: 2009-08-21 13:10:46 +0200 (Fr, 21 Aug 2009) $ * @version $Revision: 7738 $ * @author: $Author: c5106462 $ *******************************************************************************/ package com.sap.furcas.runtime.tcs; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.emf.ecore.EObject; import org.eclipse.ocl.ecore.OCL; import com.sap.furcas.metamodel.FURCAS.TCS.ClassTemplate; import com.sap.furcas.metamodel.FURCAS.TCS.ConcreteSyntax; import com.sap.furcas.metamodel.FURCAS.TCS.FunctionTemplate; import com.sap.furcas.metamodel.FURCAS.TCS.Keyword; import com.sap.furcas.metamodel.FURCAS.TCS.OperatorList; import com.sap.furcas.metamodel.FURCAS.TCS.OperatorTemplate; import com.sap.furcas.metamodel.FURCAS.TCS.PrimitiveTemplate; import com.sap.furcas.metamodel.FURCAS.TCS.QualifiedNamedElement; import com.sap.furcas.metamodel.FURCAS.TCS.Symbol; import com.sap.furcas.metamodel.FURCAS.TCS.Template; import com.sap.furcas.runtime.common.exceptions.MetaModelLookupException; import com.sap.furcas.runtime.common.exceptions.NameResolutionFailedException; import com.sap.furcas.runtime.common.exceptions.SyntaxElementException; import com.sap.furcas.runtime.common.interfaces.IMetaModelLookup; import com.sap.furcas.runtime.common.interfaces.ResolvedNameAndReferenceBean; import com.sap.furcas.runtime.common.util.MessageUtil; /** * Convenience class for looking up information in a SyntaxDefinition. * * @author C5107456 */ public class SyntaxLookup { private final ConcreteSyntax syntax; private final MetaModelElementResolutionHelper<?> resolutionHelper; private List<PrimitiveTemplate> primitiveTemplates; private final Map<QualifiedNamedElement, List<String>> qualifiednamesCache = new HashMap<QualifiedNamedElement, List<String>>(); private final Map<String, Collection<Template>> templateCache = new HashMap<String, Collection<Template>>(); /** * Instantiates a new syntax lookup. * * @param syntax the syntax * @param keywordSet the keyword set * @param resolutionHelper * @param keywordSet */ public SyntaxLookup(ConcreteSyntax syntax, MetaModelElementResolutionHelper<?> resolutionHelper) { super(); this.syntax = syntax; this.resolutionHelper = resolutionHelper; initializePrimitiveTemplatesList(syntax); } private void initializePrimitiveTemplatesList(ConcreteSyntax syntax) { primitiveTemplates = new ArrayList<PrimitiveTemplate>(); if (syntax != null) { Collection<Template> templates = syntax.getTemplates(); for (Template template : templates) { if (template instanceof PrimitiveTemplate) { primitiveTemplates.add((PrimitiveTemplate) template); } } } } /** * Filter duplicates. */ public Set<Keyword> getAllKeywords() { HashSet<String> strings = new HashSet<String>(); HashSet<Keyword> keywords = new HashSet<Keyword>(); for (Keyword k : syntax.getKeywords()) { if (!strings.contains(k.getValue())) { keywords.add(k); strings.add(k.getValue()); } } return keywords; } /** * If the type passed is a primitive, then this allows to get the right primitveTemplate. * If there is a default primitive template rule for this type, this method returns the template for this. * Otherwise, a random primitive template for this type is returned. * * @param metaModelTypeOfPropertyReference the property type. The type can be either a metamodel primitive or a * primitive as used by OCL. */ public PrimitiveTemplate getDefaultPrimitiveTemplateRule(ResolvedNameAndReferenceBean<?> metaModelTypeOfPropertyReference) throws SyntaxElementException { if (metaModelTypeOfPropertyReference == null || metaModelTypeOfPropertyReference.getNames() == null) { return null; } PrimitiveTemplate returnTemplate = null; for (PrimitiveTemplate candidatePrimTemp : primitiveTemplates) { // Retrieve the equivalent OCL primitive for this type (e.g., OCL maps ecore::EString to oclstdlib::String). // Comparing to OCL primitives allows clients to find templates suitable for the return types of OCL expressions Object oclType = OCL.newInstance().getEnvironment().getUMLReflection().asOCLType(candidatePrimTemp.getMetaReference()); boolean isSuitableTemplateForOCLPrimitive = oclType != null && oclType.equals(metaModelTypeOfPropertyReference.getReference()); // TODO compare references instead of names when the "->" referencing hack is removed List<String> candidateQualifiedName = getQualifiedNameOfMetaModelElement(candidatePrimTemp); if (metaModelTypeOfPropertyReference.getNames().equals(candidateQualifiedName) || isSuitableTemplateForOCLPrimitive) { returnTemplate = candidatePrimTemp; if (candidatePrimTemp.isDefault()) { break; } } } return returnTemplate; } public String getSymbolRule(String literal) { String symbolRule = null; Collection<Symbol> list = syntax.getSymbols(); if (list != null) { Symbol symbol; for (Iterator<Symbol> iterator = list.iterator(); iterator.hasNext();) { symbol = iterator.next(); if (literal.equals(symbol.getValue())) { symbolRule = ' ' + symbol.getName().toUpperCase() + ' '; break; } } } if (symbolRule == null) { symbolRule = '\'' + literal + '\''; } return symbolRule; } public Symbol getSymbolByValue(String value) { for (Symbol sym : syntax.getSymbols()) { if (sym.getValue().equals(value)) { return sym; } } return null; } /** * Teturns TCS template in the syntax for a given type name of a metamodel element * * @param typeName the sub type * * @return the TCS template * @throws SyntaxElementException */ public Collection<Template> getTCSTemplate(ResolvedNameAndReferenceBean<?> resolvedName, String mode) throws SyntaxElementException { String cacheKey = MessageUtil.asModelName(resolvedName.getNames()) + "#" + mode; if (templateCache.containsKey(cacheKey)) { return templateCache.get(cacheKey); } Collection<Template> returnTemplate = new ArrayList<Template>(1); // loop over all templates and return the first with the same name. Collection<Template> templates = syntax.getTemplates(); if (templates != null) { for (Template template : templates) { if (template instanceof FunctionTemplate) { continue; } if (template instanceof ClassTemplate) { ClassTemplate ct = (ClassTemplate) template; if (mode != null) { if (!mode.equals(ct.getMode())) { continue; } } else { if (ct.getMode() != null) { continue; } } } if (template instanceof OperatorTemplate) { //operators do not support modes yet if (resolvedName.getOperators() != null) { if (!resolvedName.getOperators().containsAll(((OperatorTemplate) template).getOperators())) { continue; } } if (mode != null) { continue; } } List<String> name = getQualifiedNameOfMetaModelElement(template); if (name != null && name.equals(resolvedName.getNames())) { returnTemplate.add(template); // if (returnTemplate == null) { // returnTemplate = template; // } else { // // FIXME trying to manage multiple operatorTemplate specifications for distinct operator sets // throw new SyntaxElementException("Duplicate Template referring to " + name, template); // } } } } templateCache.put(cacheKey, returnTemplate); return returnTemplate; } public <Type> List<ClassTemplate> getNonPrimaries(List<ResolvedNameAndReferenceBean<Type>> subtypes) throws SyntaxElementException { ArrayList<ClassTemplate> nonPrimaries = new ArrayList<ClassTemplate>(); for (ResolvedNameAndReferenceBean<Type> subType : subtypes) { Collection<Template> subtemps = null; subtemps = getTCSTemplate(subType, null); for (Template subtemp : subtemps) { if (subtemp instanceof ClassTemplate) { ClassTemplate classSubTemp = (ClassTemplate) subtemp; if (classSubTemp.isIsNonPrimary()) { // Non Primaries not added to primary rule nonPrimaries.add(classSubTemp); continue; } } } } return nonPrimaries; } public <Type> List<ClassTemplate> getPrimaries(List<ResolvedNameAndReferenceBean<Type>> subtypes, IMetaModelLookup<Type> metaLookup) throws SyntaxElementException, NameResolutionFailedException, MetaModelLookupException { ArrayList<ClassTemplate> primaries = new ArrayList<ClassTemplate>(); for (ResolvedNameAndReferenceBean<Type> subType : subtypes) { Collection<Template> subtemps = null; subtemps = getTCSTemplate(subType, null); // If a template is a class template or if no template found at all, // continue to look for primaries recursively in subtypes of subType for (Template subtemp : subtemps) { if (subtemp instanceof ClassTemplate) { ClassTemplate classSubTemp = (ClassTemplate) subtemp; if (!classSubTemp.isIsNonPrimary()) { // Non Primaries not added to primary rule primaries.add(classSubTemp); continue; } } else { primaries.addAll(getPrimaries(metaLookup.getDirectSubTypes(subType), metaLookup)); } } if (subtemps.isEmpty()) { primaries.addAll(getPrimaries(metaLookup.getDirectSubTypes(subType), metaLookup)); } } return primaries; } /** * since TCS treats the anonymous OperatorList special, we * need to retrieve it from the Syntax in ClassTemplateHandler. * * @return the anonymous operator list */ public OperatorList getAnonymousOperatorList() { OperatorList returnOpList = null; Collection<OperatorList> opListList = syntax.getOperatorLists(); if (opListList != null) { for (OperatorList opList : opListList) { if (opList.getName() == null || opList.getName().equals("")) { returnOpList = opList; break; } } } return returnOpList; } /** * For the syntaxElement (i.e. a property, or a SeuqenceElement), find the qualified name of the metamodel element, * in whose template the syntaxElement has been used.. * To achieve this, get the first Template going upwards in the containment hierarchy. * The template should have a qualified name pointing to a metamodel class to which this property * belongs to. * * @param syntaxElement the prop * * @return the property class * @throws SyntaxParsingException */ public Template getEnclosingQualifiedElement(EObject syntaxElement) { // TODO refactor SyntaxModel to have explicit reference to parent template? EObject parent = syntaxElement.eContainer(); while (parent != null && !(parent instanceof Template)) { if (parent instanceof EObject) { EObject refParent = parent; parent = refParent.eContainer(); } else { throw new RuntimeException("Bug: composite parent of Element " + parent + " not instance of RefObject."); } } if (parent != null) { return (Template) parent; } else { throw new RuntimeException("Template parent of Element " + syntaxElement + " expected, but not found."); } } /** * Util method to call namingHelper.getQualifiedTemplateReferenceName */ private List<String> getQualifiedNameOfMetaModelElement(QualifiedNamedElement template) throws SyntaxElementException { List<String> qualifiedName = qualifiednamesCache.get(template); if (qualifiedName == null) { ResolvedNameAndReferenceBean<?> reference; try { reference = resolutionHelper.resolve(template); } catch (NameResolutionFailedException e) { throw new SyntaxElementException(e.getMessage(), template, e); } if (reference != null) { qualifiedName = reference.getNames(); qualifiednamesCache.put(template, qualifiedName); } } return qualifiedName; } public boolean hasPrimitiveRule(String templateName) { for (PrimitiveTemplate primTemp : primitiveTemplates) { if (templateName.equals(primTemp.getTemplateName())) { return true; } } return false; } }