/******************************************************************************* * Copyright (c) 2008 SAP * see https://research.qkal.sap.corp/mediawiki/index.php/CoMONET * * Date: $Date: 2010-05-20 15:12:26 +0200 (Do, 20 Mai 2010) $ * @version $Revision: 9718 $ * @author: $Author: c5106462 $ *******************************************************************************/ package com.sap.furcas.parsergenerator.tcs.t2m.grammar; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import com.sap.furcas.metamodel.FURCAS.TCS.ClassTemplate; import com.sap.furcas.metamodel.FURCAS.TCS.ContextTags; import com.sap.furcas.metamodel.FURCAS.TCS.OperatorList; import com.sap.furcas.metamodel.FURCAS.TCS.OperatorTemplate; import com.sap.furcas.metamodel.FURCAS.TCS.Priority; import com.sap.furcas.metamodel.FURCAS.TCS.Sequence; import com.sap.furcas.metamodel.FURCAS.TCS.Template; import com.sap.furcas.parsergenerator.tcs.t2m.grammar.rules.ClassProductionRule; import com.sap.furcas.parsergenerator.util.VarStringBuffer; 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; import com.sap.furcas.runtime.parser.exceptions.SyntaxParsingException; import com.sap.furcas.runtime.tcs.MetaModelElementResolutionHelper; import com.sap.furcas.runtime.tcs.MetamodelNameResolvingException; import com.sap.furcas.runtime.tcs.SyntaxLookup; import com.sap.furcas.runtime.tcs.TcsUtil; import com.sap.furcas.runtime.tcs.TemplateNamingHelper; /** * The Class ClassTemplateHandler. */ public class ClassTemplateHandler<Type extends Object> { /** The writer. */ private ANTLR3GrammarWriter writer; /** The meta lookup. */ private IMetaModelLookup<Type> metaLookup; /** The operator handler. */ private OperatorHandler operatorHandler; /** The syntax lookup. */ private SyntaxLookup syntaxLookup; private TemplateNamingHelper<Type> namingHelper; private SemanticErrorBucket errorBucket; private MetaModelElementResolutionHelper<Type> resolutionHelper; /** * Instantiates a new class template handler. * * @param writer the writer * @param operatorHandler the operator handler * @param metaLookup the meta lookup * @param syntaxLookup the syntax lookup * @param namingHelper * @param errorBucket * @param resHelper */ protected ClassTemplateHandler(ANTLR3GrammarWriter writer, OperatorHandler operatorHandler, IMetaModelLookup<Type> metaLookup, SyntaxLookup syntaxLookup, TemplateNamingHelper<Type> namingHelper, SemanticErrorBucket errorBucket, MetaModelElementResolutionHelper<Type> resHelper) { super(); this.writer = writer; this.operatorHandler = operatorHandler; this.metaLookup = metaLookup; this.namingHelper = namingHelper; this.syntaxLookup = syntaxLookup; this.errorBucket = errorBucket; this.resolutionHelper = resHelper; } /** * @param operatorHandler2 * @param handlerConfig */ protected ClassTemplateHandler(OperatorHandler operatorHandler, SyntaxElementHandlerConfigurationBean<Type> handlerConfig) { this(handlerConfig.getWriter(), operatorHandler, handlerConfig.getMetaLookup(), handlerConfig.getSyntaxLookup(), handlerConfig.getNamingHelper(), handlerConfig.getErrorBucket(), handlerConfig.getResolutionHelper() ); } /** * Adds the template. * * @param template * the template * @param ruleBodyBufferFactory * the rule body buffer factory * * @throws SyntaxParsingException * the syntax parsing exception * @throws MetaModelLookupException * the meta model lookup exception * @throws SyntaxElementException */ public void addTemplate(ClassTemplate template, RuleBodyBufferFactory ruleBodyBufferFactory) throws MetaModelLookupException, SyntaxElementException { if (template.isIsMulti() && ! template.isIsMain()) { errorBucket.addWarning("Multi keyword can only be applied to main template, else it will be ignored.", template); } if (template.isIsAbstract() && template.isIsContext() && template.getTemplateSequence() == null) { errorBucket.addWarning("Context keyword can only be applied to non-abstract or non-empty template, else it will be ignored.", template); } if (! template.isIsAbstract() && template.isIsOperatored()) { errorBucket.addError("Only abstract templates can be made operatored.", template); } if (template.isIsDeep() ) { throw new RuntimeException("Deep not implemented yet"); } String templateRulename = null; try { templateRulename = namingHelper.getRuleNameForMode(template, template.getMode()); if (templateRulename == null) { errorBucket.addError("Template name could not be resolved.", template); } } catch (SyntaxElementException e) { errorBucket.addException(e); } if (templateRulename != null) { if (template.isIsMain() ) { // adds the ANTLR rule main : ... ; addMainRule(template, templateRulename); } String initString = null; VarStringBuffer rulebody = new VarStringBuffer(); // first add ANTLR rule to Rulebody, then Java elements boolean isAbstractRule = false; if (template.isIsAbstract()) { if (addAbstractRuleBody(template, templateRulename, rulebody, ruleBodyBufferFactory)) { initString = "Object semRef = null;\n"; initString += getInitString(template, false, true, true); } else { initString = getInitString(template, false, false, false); } isAbstractRule = true; } else { // not abstract try { List<ResolvedNameAndReferenceBean<Type>> subtypes = null; ResolvedNameAndReferenceBean<Type> resolvedBean = resolutionHelper.resolve(template); subtypes = metaLookup.getDirectSubTypes(resolvedBean); if(TcsUtil.areSubTypesWithTemplates(subtypes, template.getMode(), syntaxLookup) && !template.isIsReferenceOnly()) { //We have a concrete class with additional subclasses //first we need to create a rule for the concrete classes case initString = getInitString(template, false, false, false); String templateRulenameImpl = namingHelper.getConcreteRuleNameForTemplate(template, syntaxLookup); VarStringBuffer implRuleBody = new VarStringBuffer(); String implRuleInitString = getInitString(template, false, true, true); addTemplateSequenceToRuleBody(template, ruleBodyBufferFactory, implRuleBody); addPostActions(template, implRuleBody); writer.addRule(ClassProductionRule.getClassTemplateProductionRule(templateRulenameImpl, "Object ret2", implRuleInitString, implRuleBody.toString(), true, true)); //Now create an abstract rule that has alternatives for the //implementation rule as well as all subtypes. rulebody.append("("); rulebody.append("ret=" + templateRulenameImpl); rulebody.append("|"); addAbstractRuleBody(template, templateRulename, rulebody, ruleBodyBufferFactory); rulebody.append("){ret2 = ret;}"); isAbstractRule = true; } else { initString = getInitString(template, false, true, true); addTemplateSequenceToRuleBody(template, ruleBodyBufferFactory, rulebody); addPostActions(template, rulebody); isAbstractRule = false; } } catch (NameResolutionFailedException e) { errorBucket.addError(e.getMessage(), template); } } // templateRulename is not null writer.addRule(ClassProductionRule.getClassTemplateProductionRule(templateRulename, "Object ret2", initString, rulebody.toString(), !isAbstractRule, !isAbstractRule)); } } /** * @param template * @param templateRulename */ private void addMainRule(ClassTemplate template, String templateRulename) { String rulebody = "((ret=" + templateRulename + ")"; if (template.isIsMulti()) { rulebody += '+'; // add '+' to indicate that this main element may repeat in the syntax } rulebody += " EOF)" + " \n {\n" + "ret2=ret;\n" + "}"; writer.addRule(new ClassProductionRule("main","Object ret2", null, rulebody, null)); } private void addTemplateSequenceToRuleBody(ClassTemplate template, RuleBodyBufferFactory ruleBodyBufferFactory, VarStringBuffer rulebody) throws MetaModelLookupException { Sequence sequence = template.getTemplateSequence(); String rulefragment = ruleBodyBufferFactory.getNewRuleBodyFragment(sequence); rulebody.append(rulefragment); } private void addPostActions(ClassTemplate template, VarStringBuffer rulebody) { rulebody.append("\n {\n"); // code action here // set location command rulebody.append("ret2 = commitCreation(ret, firstToken, "+template.isIsContext()+");\n"); rulebody.append("\n }"); } /** * creates the initString, addToCurrentContext may be overridden, used for abstract templates with body. * @param template * @param addObjectCreation * @param withEnterNotification * @param overRideAddtoContext * @return * @throws JmiException */ private String getInitString(ClassTemplate template, boolean forceAddToContextFalse, boolean addObjectCreation, boolean withEnterNotification) { StringBuilder initString = new StringBuilder(128); try { if (addObjectCreation) { String metaObjectListParam = namingHelper.getMetaTypeListParameter(template); initString.append("IModelElementProxy ret;\n"+ClassTemplateHandler. createModelElementProxyString(template, forceAddToContextFalse,metaObjectListParam)); if (withEnterNotification) { initString.append(ObservationDirectivesHelper.getEnterTemplateNotification(template)); } initString.append("org.antlr.runtime.Token firstToken=input.LT(1);\n"); } } catch (SyntaxElementException e) { errorBucket.addException(e); } return initString.toString(); } public static String createModelElementProxyString(ClassTemplate template, boolean forceAddToContextFalse, String metaObjectListParam) { StringBuffer initString = new StringBuffer(); initString.append("List<String> metaType=").append( metaObjectListParam).append( ";\n"); if (template.isIsReferenceOnly()) { // determine the type of proxy to create initString.append("ret=(getBacktrackingLevel()==0) ? createReferenceProxy(metaType) : null;\n"); } else { initString.append("ret=(getBacktrackingLevel()==0) ? createModelElementProxy(metaType, "); initString.append(template.isIsContext()).append(", ").append( ( template.isIsAddToContext() && (! forceAddToContextFalse))); ContextTags tags = template.getContextTags(); if (tags != null && tags.getTags() != null && tags.getTags().size() > 0) { initString.append(", new String[]{"); for (Iterator<String> iterator = tags.getTags().iterator(); iterator .hasNext();) { String tag = iterator.next(); initString.append("\"").append(tag).append("\""); if (iterator.hasNext()) { initString.append(", "); } } initString.append("}"); } initString.append(") : null;\n"); } return initString.toString(); } /** * utility method wrapping functionality for abstract template bodies * @param template * @param rulebody * @param ruleBodyBufferFactory * @throws SyntaxParsingException * @throws MetaModelLookupException * @throws MetamodelNameResolvingException */ private boolean addAbstractRuleBody(ClassTemplate template, String templateRulename, VarStringBuffer rulebody, RuleBodyBufferFactory ruleBodyBufferFactory) throws MetaModelLookupException { boolean hasAddedSubTemplates = false; // drives writing "ret2=ret;" boolean hasSemanticDisambiguate = false; String templateMode = template.getMode(); if (template.isIsOperatored()) { if (templateMode != null) { // TODO implement this by adding mode wherever subtemplates are being invoked throw new RuntimeException("Operatored classtemplates with mode not implemented yet"); } hasAddedSubTemplates = addOperatoredRules(template, templateRulename, rulebody, ruleBodyBufferFactory); } else { // not operatored List<ResolvedNameAndReferenceBean<Type>> subtypes = null; try { ResolvedNameAndReferenceBean<Type> resolvedBean = resolutionHelper.resolve(template); subtypes = metaLookup.getDirectSubTypes(resolvedBean); } catch (NameResolutionFailedException e) { errorBucket.addError(e.getMessage(), template); } // rulebody is one of the subtypes of the abstract template rulebody.append("("); // b1 boolean isFirstAlternative = true; //get the templates for the direct subtypes if (subtypes.size() > 0) { List<Template> templates = new ArrayList<Template>(subtypes.size()); for (Iterator<ResolvedNameAndReferenceBean<Type>> iterator = subtypes.iterator(); iterator.hasNext();) { try { ResolvedNameAndReferenceBean<Type> subTypeName = iterator.next(); Collection<Template> subtemps = null; subtemps = syntaxLookup.getTCSTemplate(subTypeName, templateMode); if (subtemps.size() > 0) { templates.addAll(subtemps); } else { errorBucket.addWarning("No template present for subtype " + MessageUtil.asModelName(subTypeName.getNames()) +" with mode #"+templateMode+" of type " + MessageUtil.asModelName(resolutionHelper.resolve(template).getNames()), template); } } catch (SyntaxElementException e) { errorBucket.addException(e); } catch (NameResolutionFailedException e) { errorBucket.addException(new SyntaxElementException(e.getMessage(), template, e)); } } //first sort the templates according to their occurrence in the syntax Collections.sort(templates, new Comparator<Template>(){ @Override public int compare(Template o1, Template o2) { if(o1.equals(o2)) { return 0; } else { if(o1.getConcreteSyntax() == null || o2.getConcreteSyntax() == null) { return 0; } return o1.getConcreteSyntax().getTemplates().indexOf(o1) - o2.getConcreteSyntax().getTemplates().indexOf(o2); } } }); //add (ret = xyz | ret = abc | ...) for all templates xyz, abc, ... extending this one SemanticDisambiguateHandler semanticHandler = new SemanticDisambiguateHandler(templates, errorBucket, namingHelper); for(Template subtemp : templates){ try { if (subtemp instanceof OperatorTemplate) { continue; // ignore operatorTemplates, as they // need to be invoked from Operator // rules } if(semanticHandler.shouldUseSemanticDisambiguate(subtemp)) { semanticHandler.addSemanticDisambiguateRule(subtemp, rulebody, ruleBodyBufferFactory,null, null, false); hasSemanticDisambiguate = true; } else{ if(!isFirstAlternative) { rulebody.append("\n | "); } if (subtemp.getDisambiguateV3() != null) { // add disambiguation rule rulebody.append("(" + subtemp.getDisambiguateV3() + ")=>("); // b2 } rulebody.append("ret=" + namingHelper.getRuleNameForMode(subtemp, templateMode)); } isFirstAlternative = false; hasAddedSubTemplates = true; if (subtemp.getDisambiguateV3() != null) { // add disambiguation rule rulebody.append(")"); // b2 } } catch (SyntaxElementException e) { errorBucket.addException(e); } } } // call abstractContentsrule if sequence is not empty Sequence sequence = template.getTemplateSequence(); if (template.isIsAbstract() && sequence != null ) { if (! isFirstAlternative) { rulebody.append("\n | "); } rulebody.append("ret="+templateRulename + TemplateNamingHelper.ABSTRACT_CONTENTS_SUFFIX); // add a separate supporting rule for the abstract contents addAbstractContentsRule(template, templateRulename + TemplateNamingHelper.ABSTRACT_CONTENTS_SUFFIX, ruleBodyBufferFactory); } rulebody.append(")"); // b1 } rulebody.append("\n {\n"); // don't need to add To context, as this will be done in if (template.isIsAddToContext()) { rulebody.append("addToCurrentContext(ret);\n"); } if (hasAddedSubTemplates) { rulebody.append("ret2=ret;"); } rulebody.append("\n }"); return hasSemanticDisambiguate; } /** * adds a separate rule to the grammar which creates the given template element (cannot work if abstract template refers to abstract modelElement) * @param template * @param ruleBodyBufferFactory * @throws MetaModelLookupException */ private void addAbstractContentsRule(ClassTemplate template, String templateAbstractRulename, RuleBodyBufferFactory ruleBodyBufferFactory) throws MetaModelLookupException { String initString = getInitString(template, true, true, true); VarStringBuffer rulebody = new VarStringBuffer(); addTemplateSequenceToRuleBody(template, ruleBodyBufferFactory, rulebody); addPostActions(template, rulebody); if (templateAbstractRulename != null) { writer.addRule( ClassProductionRule.getClassTemplateProductionRule( templateAbstractRulename, "Object ret2", initString, rulebody.toString(), true, true // ! is not abstract, this is the case where the abstract template refers to a non-abstract model element )); } } /** * special case of operatored class template * @param template * @param templateRulename * @param rulebody * @param ruleBodyBufferFactory * @return true if subtemplate rules have been added * @throws MetaModelLookupException */ private boolean addOperatoredRules(ClassTemplate template, String templateRulename, VarStringBuffer rulebody, RuleBodyBufferFactory ruleBodyBufferFactory) throws MetaModelLookupException { boolean hasAddedSubTemplates = false; // need to create 2 production rules in this case, one for //<templateName>, one with primary_<templateName> // need to create production rules for the given OperatorList, need to get that list first OperatorList operatorList = template.getOperatorList(); if (operatorList == null) { // since class is operatored, it needs an operatored list, so try to find and use anonymous one OperatorList anonymousOperatorList = syntaxLookup.getAnonymousOperatorList(); if (anonymousOperatorList == null) { // Should never happen? throw new RuntimeException("Anonymous Operator List required but not defined."); } else { operatorList = anonymousOperatorList; } } else { String operatorListName = template.getOperatorList().getName(); if (operatorListName == null) { throw new RuntimeException("Syntax model inconsistent. Contains non-anonymous Operator List without name."); } } try { List<ResolvedNameAndReferenceBean<Type>> subtypes = null; subtypes = metaLookup.getDirectSubTypes(resolutionHelper.resolve(template)); List<ClassTemplate> nonPrimaries = writePrimaryRule(template, templateRulename); List<ClassTemplate> primaries = syntaxLookup.getPrimaries(subtypes, metaLookup); boolean hasPrimaries = primaries.size() > 0; operatorHandler.addOperatorList(operatorList, templateRulename, hasPrimaries, ruleBodyBufferFactory, template); // add "primary_"+name rule alternating over primary subtemplates // production rule calling the priority with the highest index int highestIndex = getHighestPriorityIndex(operatorList); if (nonPrimaries.size() > 0) { rulebody.append('('); } rulebody.append("ret=" + OperatorHandler.getPriorityPrefix(operatorList) + "priority_" + highestIndex); for (ClassTemplate nonPrimarySubTemplate : nonPrimaries) { rulebody.append("\n | "); rulebody.append("ret=" + namingHelper.getRuleName(nonPrimarySubTemplate)); } if (nonPrimaries.size() > 0) { rulebody.append(')'); } hasAddedSubTemplates = true; } catch (NameResolutionFailedException e1) { errorBucket.addError(e1.getMessage(), template); } catch (SyntaxElementException e) { errorBucket.addException(e); } return hasAddedSubTemplates; } /** * creates primary_xyz rule and returns list of all nonprimary subtemplates, * that need to be added to the non primary rule of the template * @param template * @param templateRulename * @return * @throws MetaModelLookupException * @throws SyntaxElementException */ private List<ClassTemplate> writePrimaryRule(ClassTemplate template, String templateRulename) throws MetaModelLookupException, SyntaxElementException { List<ResolvedNameAndReferenceBean<Type>> subtypes = null; try { subtypes = metaLookup.getDirectSubTypes(resolutionHelper.resolve(template)); } catch (NameResolutionFailedException e1) { errorBucket.addError(e1.getMessage(), template); } List<ClassTemplate> nonPrimaries = syntaxLookup.getNonPrimaries(subtypes); try { List<ClassTemplate> primaries = syntaxLookup.getPrimaries(subtypes, metaLookup); if(primaries.size() == 0){ errorBucket.addError("No template for concrete subclass found, required to terminate this operator template", template); } if (subtypes != null && primaries.size() > 0) { StringBuilder rulebody = new StringBuilder(); rulebody.append('('); // drives setting of '|'s, because iterator might have next but no valid ones boolean previousExists = false; if (previousExists) { rulebody.append("\n | "); } for (Template subtemp : primaries) { // if (subtemp == null) { // errorBucket.addWarning("No Template exists in syntax for SubType " + subType.getNames(), template); // continue; // } if (subtemp instanceof OperatorTemplate) { // excluding TCS!OperatorTemplate from subtypes, because they can only be called // from priority rules where the required parameters (opName, left, Token) are known continue; } if (subtemp instanceof ClassTemplate) { ClassTemplate classSubTemp = (ClassTemplate) subtemp; if (classSubTemp.isIsNonPrimary()) { // Non Primaries not added to primary rule nonPrimaries.add(classSubTemp); continue; } } if (previousExists) { rulebody.append("\n | "); } if (subtemp instanceof ClassTemplate) { ClassTemplate classSubTemp = (ClassTemplate) subtemp; if(classSubTemp.getDisambiguateV3() != null) { rulebody.append("("); rulebody.append(classSubTemp.getDisambiguateV3()); rulebody.append(")=>"); } } rulebody.append("ret=" + namingHelper.getRuleName(subtemp)); previousExists = true; } if (previousExists) { rulebody.append("\n | "); } // TODO parametrize bracket notation, use template sequence instead maybe rulebody.append("("); rulebody.append(ObservationDirectivesHelper.getEnterOperatoredBracketsSequenceNotification()); rulebody.append(ObservationDirectivesHelper.getEnterSequenceElementNotification(null)); rulebody.append("LPAREN "); rulebody.append(ObservationDirectivesHelper.getExitSequenceElementNotification()); rulebody.append(ObservationDirectivesHelper.getEnterSequenceElementNotification(null)); rulebody.append("ret=" + templateRulename); rulebody.append(ObservationDirectivesHelper.getExitSequenceElementNotification()); rulebody.append(ObservationDirectivesHelper.getEnterSequenceElementNotification(null)); rulebody.append(" RPAREN"); rulebody.append(ObservationDirectivesHelper.getExitSequenceElementNotification()); rulebody.append(")"); rulebody.append(ObservationDirectivesHelper.getExitOperatoredBracketsSequenceNotification()); rulebody.append(')'); rulebody.append("\n {\n ret2=ret;\n}"); writer.addRule(new ClassProductionRule("primary_" + templateRulename, "Object ret2", null, rulebody.toString(), null)); } // end if subtypes != null } catch (NameResolutionFailedException e1) { errorBucket.addError(e1.getMessage(), template); } return nonPrimaries; } /** * Gets the highest priority index. * * @return the highest priority index */ protected static int getHighestPriorityIndex(OperatorList opList) { int max = 0; Collection<Priority> prios = opList.getPriorities(); for (Iterator<Priority> iterator2 = prios.iterator(); iterator2.hasNext();) { Priority priority = iterator2.next(); if (priority.getValue() > max) { max = priority.getValue(); } } return max; } }