/******************************************************************************* * 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 static com.sap.furcas.parsergenerator.util.StringConcatUtil.concatBuf; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.eclipse.emf.common.util.Diagnostic; import org.eclipse.emf.ecore.EStructuralFeature; import com.sap.furcas.metamodel.FURCAS.TCS.AsPArg; import com.sap.furcas.metamodel.FURCAS.TCS.AutoCreateKind; import com.sap.furcas.metamodel.FURCAS.TCS.AutoCreatePArg; import com.sap.furcas.metamodel.FURCAS.TCS.ClassTemplate; import com.sap.furcas.metamodel.FURCAS.TCS.CreateAsPArg; import com.sap.furcas.metamodel.FURCAS.TCS.CreateInPArg; import com.sap.furcas.metamodel.FURCAS.TCS.DisambiguatePArg; import com.sap.furcas.metamodel.FURCAS.TCS.EnumerationTemplate; import com.sap.furcas.metamodel.FURCAS.TCS.ForcedLowerPArg; import com.sap.furcas.metamodel.FURCAS.TCS.ForcedUpperPArg; import com.sap.furcas.metamodel.FURCAS.TCS.ImportContextPArg; import com.sap.furcas.metamodel.FURCAS.TCS.LookInPArg; import com.sap.furcas.metamodel.FURCAS.TCS.LookupScopePArg; import com.sap.furcas.metamodel.FURCAS.TCS.ModePArg; import com.sap.furcas.metamodel.FURCAS.TCS.PartialPArg; import com.sap.furcas.metamodel.FURCAS.TCS.PostfixPArg; import com.sap.furcas.metamodel.FURCAS.TCS.PrefixPArg; import com.sap.furcas.metamodel.FURCAS.TCS.PrimitiveTemplate; import com.sap.furcas.metamodel.FURCAS.TCS.Property; import com.sap.furcas.metamodel.FURCAS.TCS.PropertyArg; import com.sap.furcas.metamodel.FURCAS.TCS.PropertyReference; import com.sap.furcas.metamodel.FURCAS.TCS.QualifiedNamedElement; import com.sap.furcas.metamodel.FURCAS.TCS.ReferenceByPArg; import com.sap.furcas.metamodel.FURCAS.TCS.RefersToPArg; import com.sap.furcas.metamodel.FURCAS.TCS.SeparatorPArg; import com.sap.furcas.metamodel.FURCAS.TCS.Sequence; import com.sap.furcas.metamodel.FURCAS.TCS.SequenceInAlternative; import com.sap.furcas.metamodel.FURCAS.TCS.Template; import com.sap.furcas.parsergenerator.tcs.t2m.grammar.constraints.PropertyQuantityConstraint; import com.sap.furcas.parsergenerator.tcs.t2m.grammar.constraints.RuleBodyPropertyConstraint; import com.sap.furcas.parsergenerator.util.TcsUtil; 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.MultiplicityBean; 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.MessageHelper; import com.sap.furcas.runtime.tcs.MetaModelElementResolutionHelper; import com.sap.furcas.runtime.tcs.MetamodelNameResolvingException; import com.sap.furcas.runtime.tcs.PropertyArgumentUtil; import com.sap.furcas.runtime.tcs.SyntaxLookup; import com.sap.furcas.runtime.tcs.TemplateNamingHelper; /** * The Class PropertyTypeHandler. * */ public class PropertyTypeHandler<Type extends Object> { private IMetaModelLookup<Type> metaLookup; private SyntaxLookup syntaxLookup; private TemplateNamingHelper<Type> namingHelper; private SemanticErrorBucket errorBucket; private MetaModelElementResolutionHelper<Type> resolutionHelper; private boolean skipDelayedReferences = false; protected PropertyTypeHandler(IMetaModelLookup<Type> metaLookup, SyntaxLookup syntaxLookup, TemplateNamingHelper<Type> namingHelper, SemanticErrorBucket errorBucket) { this.metaLookup = metaLookup; this.syntaxLookup = syntaxLookup; this.namingHelper = namingHelper; this.errorBucket = errorBucket; this.resolutionHelper = new MetaModelElementResolutionHelper<Type>(metaLookup); } protected PropertyTypeHandler(SyntaxElementHandlerConfigurationBean<Type> handlerConfig) { this(handlerConfig.getMetaLookup(), handlerConfig.getSyntaxLookup(), handlerConfig.getNamingHelper(), handlerConfig.getErrorBucket()); } public void addElement(Property prop, RuleBodyStringBuffer buffer) throws SyntaxElementException, MetaModelLookupException { try { StringBuilder ruleBodyPart = new StringBuilder(); // get Owner of this property QualifiedNamedElement propertyOwnerTypeTemplate = syntaxLookup.getEnclosingQualifiedElement(prop); // get Type of this Property String propertyName = getPropertyName(prop); ResolvedNameAndReferenceBean<Type> metaModelTypeOfProperty = TcsUtil.getReferencedType(prop, buffer, propertyName, propertyOwnerTypeTemplate, resolutionHelper, metaLookup); PropertyArgs args = new PropertyArgs(prop.getPropertyArgs()); validateArgs(args); if (args.referenceByPArg != null) { addQueriedReferenceCode(prop, ruleBodyPart, metaModelTypeOfProperty, propertyName, args); } else if (args.refersTo != null) { addRefersToCode(prop, ruleBodyPart, metaModelTypeOfProperty, propertyName, args); } else { // property not to be referenced but used within ruleBodyPart.append(" temp="); if (args.asPArg != null) { addAsTemplateCode(args, ruleBodyPart); } else { addTypeDependentTemplateCode(prop, args, metaModelTypeOfProperty, ruleBodyPart); } if (args.modePArg != null) { ruleBodyPart.append(TemplateNamingHelper.getModeSuffix(args.modePArg.getMode())); } ruleBodyPart.append(" {setProperty(ret, \"").append(propertyName).append("\", temp);\nsetParent(temp,ret,\"" + propertyName + "\");}"); } handleMultiplicity(prop, buffer, propertyName, args, propertyOwnerTypeTemplate, ruleBodyPart); } catch (MetamodelNameResolvingException e) { throw new SyntaxElementException(e.getMessage(), prop, e); } } /** * Use type name for rule name. If it is a DataTyp, then use primitive template for that datatype. */ private void addTypeDependentTemplateCode(Property prop, PropertyArgs args, ResolvedNameAndReferenceBean<Type> metaModelTypeOfProperty, StringBuilder repeatablePart) throws SyntaxElementException { PrimitiveTemplate propertyPrimitiveTemplate = syntaxLookup.getDefaultPrimitiveTemplateRule(metaModelTypeOfProperty); if (propertyPrimitiveTemplate != null) { repeatablePart.append(namingHelper.getRuleName(propertyPrimitiveTemplate)); } else { String propertyTemplateRule = namingHelper.buildRuleName(metaModelTypeOfProperty); String modeArg = args.modePArg != null ? args.modePArg.getMode() : null; if (syntaxLookup.getTCSTemplate(metaModelTypeOfProperty, modeArg).isEmpty()) { errorBucket.addError("Syntax does not define a rule for " + MessageUtil.asModelName(metaModelTypeOfProperty.getNames()) + " with mode #" + modeArg, prop); } repeatablePart.append(propertyTemplateRule); } } private void addAsTemplateCode(PropertyArgs args, StringBuilder ruleBodyPart) throws SyntaxElementException { if (args.asPArg.getTemplate() != null) { Template asTemplate = args.asPArg.getTemplate(); String ruleName = null; if (asTemplate instanceof ClassTemplate) { ClassTemplate cAsTemplate = (ClassTemplate) asTemplate; if (cAsTemplate.getMode() != null) { ruleName = namingHelper.getRuleNameForMode(cAsTemplate, cAsTemplate.getMode()); } else { ruleName = namingHelper.getRuleName(cAsTemplate); } } else if (asTemplate instanceof PrimitiveTemplate) { PrimitiveTemplate pAsTemplate = (PrimitiveTemplate) asTemplate; ruleName = namingHelper.getRuleName(pAsTemplate); } else if (asTemplate instanceof EnumerationTemplate) { EnumerationTemplate eAsTemplate = (EnumerationTemplate) asTemplate; ruleName = namingHelper.getRuleName(eAsTemplate); } ruleBodyPart.append(ruleName); } else { ruleBodyPart.append(args.asPArg.getValue()); } } private void validateArgs(PropertyArgs args) { if (args.refersTo != null) { errorBucket.addWarning("RefersTo is deprecated. Please use referenceBy and lookupScope instead.", args.refersTo); if (args.asPArg != null) { errorBucket.addWarning("As only possible without refersTo", args.asPArg); if (args.modePArg != null) { errorBucket.addWarning("Mode only possible without refersTo", args.modePArg); } } } if (args.refersTo == null) { if (args.autoCreatePArg != null) { errorBucket.addWarning("AutoCreate only possible with refersTo", args.autoCreatePArg); } if (args.createAsPArg != null) { errorBucket.addWarning("CreateAs only possible with refersTo", args.createAsPArg); } if (args.createInPArg != null) { errorBucket.addWarning("CreateIn only possible with refersTo", args.createInPArg); } if (args.lookInPArg != null) { errorBucket.addWarning("LookIn only possible with refersTo", args.lookInPArg); } if (args.importContextPArg != null) { errorBucket.addWarning("ImportContext only possible with refersTo", args.importContextPArg); } } if (args.referenceByPArg == null) { if (args.prefixPArg != null) { errorBucket.addWarning("Prefix can only be used together with referenceBy. It is ignored otherwise.", args.prefixPArg); } if (args.postfixPArg != null) { errorBucket.addWarning("Postfix can only be used together with referenceBy. It is ignored otherwise.", args.postfixPArg); } } if (args.referenceByPArg != null && args.lookupScopePArg == null) { errorBucket.addError("ReferenceBy only be used together with lookupScope.", args.referenceByPArg); } if (args.lookupScopePArg != null && (args.referenceByPArg == null)) { errorBucket.addError("LookupScope only be used together with referenceBy.", args.lookupScopePArg); } } protected static String getPropertyName(Property prop) { PropertyReference propRef = prop.getPropertyReference(); if (propRef != null) { if (propRef.getName() != null) { return propRef.getName(); } else { EStructuralFeature strucFeat = propRef.getStrucfeature(); if (strucFeat != null) { return strucFeat.getName(); } } } return null; } private void addQueriedReferenceCode(Property prop, StringBuilder ruleBodyPart, ResolvedNameAndReferenceBean<Type> metaModelTypeOfPropertyListName, String propertyName, PropertyArgs args) throws SyntaxElementException, MetaModelLookupException { // creates temp = ... appendRefersToTempPart(prop, ruleBodyPart, metaModelTypeOfPropertyListName, args, true); validateOclQuery(prop, args.lookupScopePArg, args.lookupScopePArg.getQuery()); Type type = metaModelTypeOfPropertyListName.getReference(); validateOclQuery(args.referenceByPArg, type, PropertyArgumentUtil.getReferenceByAsOCL(args.referenceByPArg)); String query = PropertyArgumentUtil.getCombinedReferenceByLookupOCLQuery(args.referenceByPArg, args.lookupScopePArg, args.prefixPArg, args.postfixPArg); query = TcsUtil.escapeMultiLineOclQuery(query); // creates the bit that sets the temp from above as reference to modelElement "ret" of this rule String propURI = ObservationDirectivesHelper.getId(prop); if (args.refersTo != null) { if (!skipDelayedReferences) { ruleBodyPart.append(concatBuf(" {setOclRef(ret, \"", propertyName, "\", \"", args.refersTo.getPropertyName(), "\", temp, \"" + query + "\", \""+propURI+"\", SyntaxRegistryFacade.getModelUpdaterRegistry());}")); } } else { if (!skipDelayedReferences) { ruleBodyPart.append(concatBuf(" {setOclRef(ret, \"", propertyName, "\", null, temp, \"" + query + "\", \""+propURI+"\", SyntaxRegistryFacade.getModelUpdaterRegistry());}")); } } } private void validateOclQuery(PropertyArg argument, Type parsingContext, String query) { List<Diagnostic> oclErrors = metaLookup.validateOclQuery(parsingContext, query); for (Diagnostic error : oclErrors) { if(error.getSeverity() == Diagnostic.ERROR) { errorBucket.addError(error.getMessage() + " in OCL query: \"" + query + "\"", argument); } else if(error.getSeverity() == Diagnostic.WARNING) { errorBucket.addWarning(error.getMessage() + " in OCL query " + query, argument); } } } private void validateOclQuery(Property prop, PropertyArg argument, String query) { List<Diagnostic> oclErrors = metaLookup.validateOclQuery(prop.getParentTemplate(), query); for (Diagnostic error : oclErrors) { if(error.getSeverity() == Diagnostic.ERROR) { errorBucket.addError(error.getMessage() + " in OCL query: \"" + query + "\"", argument); } else if(error.getSeverity() == Diagnostic.WARNING) { errorBucket.addWarning(error.getMessage() + " in OCL query " + query, argument); } } } /** * Adds to the rulepart the String "temp=\<rulename\>{setRef(ret, * propertyTemplate, propertyType, refersTo.PropertyName, temp,...)". * * This means that an Object temp is created using either the parsing rule * provided as AsPArg or inferred from the metamodel, and then this temp * object is used to reference a different object in a delayed way. Meaning * we create a delayed reference to be resolved after parsing, where we will * try to locate a ModelElement Y of type propertyType, which has an * attribute named refersTo.propertyName of value temp. If we find this * ModelElement Y, we will set the reference from ModelElment ret with name * retpropertyName. As an example, if we parsed Java: "String x;", then we * would set the property "Type" of the ModelElement Variable x to that * DataType, which has name = "String". * */ private void addRefersToCode(Property prop, StringBuilder ruleBodyPart, ResolvedNameAndReferenceBean<Type> metaModelTypeOfProperty, String propertyName, PropertyArgs args) throws MetaModelLookupException, MetamodelNameResolvingException, SyntaxElementException { // appends the temp = ... part appendRefersToTempPart(prop, ruleBodyPart, metaModelTypeOfProperty, args, false); // now append the part that sets temp as reference of modelelement "ret" of this rule String resolvedTypeOfPropertyName = namingHelper.getMetaTypeListParameter(metaModelTypeOfProperty); String lookIn = null; // may be null in the String allowed if (args.lookInPArg != null) { Collection<String> argPropertyName = args.lookInPArg.getPropertyName(); lookIn = "\"" + createDotSeparatedList(argPropertyName) + "\""; } String autoCreate = "\"never\""; // default value if (args.autoCreatePArg != null) { AutoCreateKind value = args.autoCreatePArg.getValue(); autoCreate = "\"" + value.toString() + "\""; } String createAs = null; // may be null in String allowed if (args.createAsPArg != null) { // type to be created, may be illegal name or unqualified createAs = namingHelper.getMetaTypeListParameter(args.createAsPArg.getName()); // createAs = "\"" + qualifiedname + "\""; } String createIn = null; // may be null in String allowed if (args.createInPArg != null) { Collection<String> argPropertyName = args.createInPArg.getPropertyName(); createIn = "\"" + createDotSeparatedList(argPropertyName) + "\""; } if (!skipDelayedReferences) { ruleBodyPart.append(concatBuf(" {setRef(ret, \"", propertyName, "\", ", resolvedTypeOfPropertyName, ", \"", args.refersTo.getPropertyName(), "\", temp, " + lookIn + ", " + autoCreate + ", " + createAs + ", " + (args.importContextPArg != null) + ", " + createIn + ");}")); } } private void appendRefersToTempPart(Property prop, StringBuilder ruleBodyPart, ResolvedNameAndReferenceBean<Type> metaModelTypeOfPropertyReference, PropertyArgs args, boolean primitivesOnly) throws SyntaxElementException, MetaModelLookupException { // example: // temp=identifier {setRef(ret, "author", "Author", "name", temp, null, "never", null, false, null);} // meaning create a delayed reference, where after all model elements have been created, // the model injector will set Article.author = Author x with x.name = temp ruleBodyPart.append(" temp="); // what Parser rule to call to create the key value for the referred object if (args.asPArg != null) { if (args.asPArg.getTemplate() != null) { ruleBodyPart.append(namingHelper.getRuleNameForTemplate(args.asPArg.getTemplate())); } else { String asRule = args.asPArg.getValue(); if (!syntaxLookup.hasPrimitiveRule(asRule)) { errorBucket.addError("Unknown As reference " + asRule, args.asPArg); } ruleBodyPart.append(asRule); } } else if (args.referenceByPArg != null) { Type type = metaLookup.getOclReturnType(metaModelTypeOfPropertyReference.getReference(), PropertyArgumentUtil.getReferenceByAsOCL(args.referenceByPArg)); if (type == null) { errorBucket.addError(metaModelTypeOfPropertyReference + " is no suitable context for the OCL expression " + PropertyArgumentUtil.getReferenceByAsOCL(args.referenceByPArg), prop); } else { ResolvedNameAndReferenceBean<Type> metaModelTypeOfQueryResult = metaLookup.resolveReferenceName(type); PrimitiveTemplate propertyPrimitiveTemplate = syntaxLookup.getDefaultPrimitiveTemplateRule(metaModelTypeOfQueryResult); if (propertyPrimitiveTemplate == null) { errorBucket.addError("Syntax does not define a rule for " + metaModelTypeOfQueryResult + " which is the return type of the referenceBy expression " + PropertyArgumentUtil.getReferenceByAsOCL(args.referenceByPArg), prop); } else { ruleBodyPart.append(propertyPrimitiveTemplate.getTemplateName()); } } } else { PrimitiveTemplate propertyPrimitiveTemplate = syntaxLookup.getDefaultPrimitiveTemplateRule(metaModelTypeOfPropertyReference); // if this is null, then there is no primitive template for this, so // we need to consider PropertyType as a non-primitive boolean isPrimitive = (propertyPrimitiveTemplate != null); // need to find out what type the property is of (i.e. // Article.author is of Type Author) if (isPrimitive) { // if primitive use the default primitive template ruleBodyPart.append(propertyPrimitiveTemplate.getTemplateName()); } else { String propertyName = args.refersTo.getPropertyName(); ResolvedNameAndReferenceBean<Type> referredFeatureType = metaLookup.getFeatureClassReference(metaModelTypeOfPropertyReference, propertyName); if (referredFeatureType == null) { errorBucket.addError("Type " + metaModelTypeOfPropertyReference + " has no feature " + propertyName, prop); } else { PrimitiveTemplate primTemp = syntaxLookup.getDefaultPrimitiveTemplateRule(referredFeatureType); if (primTemp != null) { ruleBodyPart.append(primTemp.getTemplateName()); } else { String calledRuleName = namingHelper.buildRuleName(referredFeatureType); // TODO: check if we need to consider mode here Collection<Template> checkTemplates = syntaxLookup.getTCSTemplate(referredFeatureType, null); if (checkTemplates.isEmpty()) { errorBucket.addError("Syntax does not define a rule for " + referredFeatureType, prop); } for (Template checkTemplate : checkTemplates) { if (primitivesOnly && !(checkTemplate instanceof PrimitiveTemplate)) { errorBucket.addError("Query only possible for primitive feature references " + referredFeatureType, prop); } } ruleBodyPart.append(calledRuleName); } } } } } private static String createDotSeparatedList(Collection<String> argPropertyName) { StringBuilder builder = new StringBuilder(); for (Iterator<String> iterator = argPropertyName.iterator(); iterator.hasNext();) { builder.append(iterator.next()); if (iterator.hasNext()) { builder.append('.'); } } return builder.toString(); } private void handleMultiplicity(Property prop, RuleBodyStringBuffer buffer, String propertyName, PropertyArgs args, QualifiedNamedElement propertyOwnerTypeTemplate, StringBuilder ruleBodyPart) throws MetaModelLookupException, SyntaxElementException { MultiplicityBean multiplicity = null; try { ResolvedNameAndReferenceBean<Type> refBean = resolutionHelper.resolve(propertyOwnerTypeTemplate); multiplicity = metaLookup.getMultiplicity(refBean, propertyName); } catch (NameResolutionFailedException e) { throw new SyntaxElementException("Type " + MessageHelper.getTemplateName(propertyOwnerTypeTemplate) + " could not be resolved.", e); } if (multiplicity == null) { throw new SyntaxElementException("Type " + propertyOwnerTypeTemplate + ", feature " + propertyName + " returned null as multiplicity.", prop, null); } if (multiplicity.getUpperBound() > -1 && multiplicity.getLowerBound() > multiplicity.getUpperBound()) { throw new SyntaxElementException("Multiplicity of type " + propertyOwnerTypeTemplate + ", feature " + propertyName + " is inconsistent in metamodel, lower bound is greater than upper bound: " + multiplicity.getLowerBound() + ">" + multiplicity.getUpperBound(), prop, null); } addRepeatableWithMultiplicity(buffer, prop, ruleBodyPart, multiplicity, args); } /** * Helper method to deal with multiplicity. Add the repeatable part to the * buffer, and indicates expected multiplicity of this part. */ protected void addRepeatableWithMultiplicity(RuleBodyStringBuffer buffer, Property prop, StringBuilder repeatablePart, MultiplicityBean multiplicity, PropertyArgs args) throws SyntaxElementException, MetaModelLookupException { boolean isInMulti = false; if (prop != null && prop.getElementSequence() instanceof SequenceInAlternative && ((SequenceInAlternative) prop.getElementSequence()).getAlternativeContainer().isIsMulti()) { if (multiplicity.getUpperBound() != -1) { errorBucket.addError("Alternative isMulti is only allows if all directly contained" + "properties have an upper multiplicity of UNBOUNDED!", prop); } isInMulti = true; } /** * now, there are several cases to consider (), not optional, not * multiple ()?, optional, not multiple * * Multiple cases: * * lowerbound = 0 means (...)? = isoptional lowerbound = 1 means ()... * lowerbound > 3 means ()()()... * * For ... in the above upperbound = -1 means ()* upperbound = 3 mean * ()()?()? for lowerbound = 1 * */ // but because of the separator, + is not really usable, // (xyz)((separator)xyz)* must be used instead, and (()*)? instead of ()* boolean isMultiple = multiplicity.getUpperBound() > 1 || multiplicity.getUpperBound() < 0; boolean isOptional = multiplicity.isOptional(); // propertyConstraints may override the native multiplicity. This is the // case when // the current sequence is within a isDefinedExp ore OneExp, where // optionality is checked. List<RuleBodyPropertyConstraint> constraints = buffer.getCurrentConstraints(); for (RuleBodyPropertyConstraint constraint : constraints) { if (constraint instanceof PropertyQuantityConstraint) { PropertyQuantityConstraint quantityConstraint = (PropertyQuantityConstraint) constraint; if (quantityConstraint.getPropertyName().equals(getPropertyName(prop))) { if (quantityConstraint.getKey() == PropertyQuantityConstraint.ISDEFINED_KEY) { if (quantityConstraint.isValue() == true) { if (!isMultiple) { isOptional = false; // Like TCS, still allow optional // multivaluedelements to remain optional. } } else { throw new SyntaxElementException("Property used within context where it is not defined.", prop); } } else if (quantityConstraint.getKey() == PropertyQuantityConstraint.ONE_KEY) { if (quantityConstraint.isValue() == true) { isMultiple = false; isOptional = false; } else { // keep multiplicity as from metamodel } } else { // should never happen throw new RuntimeException("Unknown property constraint key: " + quantityConstraint.getKey()); } } } } if (isMultiple) { // cases: (xyz ((SEP)xyz)* ), (xyz ((SEP)xyz)* ) ?, // (xyz((SEP)xyz)((SEP)xyz)((SEP)xyz)+) // lowerbound variable as combination of metamodel information and // syntax information int lowerBound = 0; if (multiplicity.getLowerBound() > lowerBound) { lowerBound = multiplicity.getLowerBound(); } if (args.forcedLower != null) { lowerBound = args.forcedLower.getValue(); } // upperbound variable as combination of metamodel information and // syntax information int upperBound = multiplicity.getUpperBound(); boolean isUpperUnbounded = multiplicity.getUpperBound() < 0; if (args.forcedUpper != null) { upperBound = args.forcedUpper.getValue(); isUpperUnbounded = false; } String disambiguation = null; if (args.disambiguatePArg != null) { disambiguation = args.disambiguatePArg.getDisambiguation(); } if (lowerBound > 1 && !isInMulti) { buffer.append('(', repeatablePart.toString()); for (int i = 0; i < lowerBound - 1; i++) { buffer.append(' '); appendBitWithSeparator(buffer, repeatablePart, args.separator, disambiguation); } // now either make the last element repeatable unlimited times, // or add ()?()?()? for the remaining optional times it may // occur if (isUpperUnbounded) { buffer.append('+'); // add + to the last brackets } else { for (int i = lowerBound; i < upperBound; i++) { buffer.append(' '); appendBitWithSeparator(buffer, repeatablePart, args.separator, disambiguation); buffer.append('?'); } } buffer.append(')'); } else { // lowerbound = 0 or 1 // TODO: Check for property Args that are being ignored (i.e. separator) buffer.append('('); if (lowerBound == 0) { if (disambiguation != null) { buffer.append('('); buffer.append(disambiguation); buffer.append(")=>"); } } buffer.append(repeatablePart.toString()); // now either add unlimited optional part including the separator, or n times () () ()? if (!isInMulti) { if (isUpperUnbounded) { buffer.append(' '); appendBitWithSeparator(buffer, repeatablePart, args.separator, disambiguation); buffer.append("* "); // add * to the inner brackets } else { for (int i = 0; i < upperBound - 1; i++) { buffer.append(' '); appendBitWithSeparator(buffer, repeatablePart, args.separator, disambiguation); buffer.append('?'); } } } buffer.append(')'); if (isOptional == true) { if (lowerBound == 0) { buffer.append("? "); } } } } else { // not multiple buffer.append('(', repeatablePart.toString(), ')'); // TODO: compare forcedLower with Metamodel, generate warning // add ? or not if (isOptional == true) { if (args.forcedLower == null || args.forcedLower.getValue() == 0) { buffer.append("? "); } } } } /** * helper method to add "((%Separator%) %repeatablePart%)" to the buffer. * * @param buffer * @param repeatablePart * @param disambiguate * If not <tt>null</tt>, this will be surrounded with "(...)=>" * and prepended to the <tt>repeatablePart</tt>. Use a non- * <tt>null</tt> value here in case the grammar specifies a * disambiguation for the property and this occurrence is * optional. Then, the disambiguation works between matching the * optional property and the right context. * @param args * @throws SyntaxParsingException * @throws MetaModelLookupException * @throws MetamodelNameResolvingException */ protected static void appendBitWithSeparator(RuleBodyStringBuffer buffer, StringBuilder repeatablePart, SeparatorPArg separatorArg, String disambiguate) throws MetaModelLookupException { buffer.append('('); // close with )* // check Separator if (separatorArg != null) { Sequence separator = null; separator = separatorArg.getSeparatorSequence(); if (separator != null) { buffer.append(ObservationDirectivesHelper.getEnterSeparatorSequenceNotification()); buffer.addToRuleFragment(separator); buffer.append(ObservationDirectivesHelper.getExitSeparatorSequenceNotification()); } } if (disambiguate != null) { buffer.append('('); buffer.append(disambiguate); buffer.append(")=>"); } buffer.append(repeatablePart.toString(), ')'); } /** * Utility wrapper for the PArg List attached to a Property in TCS. TCS has * a List of PArgs attached to Properties just as a modeling convention, in * reality only one of each PArg type can be present, therefore this is * equivalent to a bean where each type of arg is either set or not * * @author C5107456 */ protected static final class PropertyArgs { public SeparatorPArg separator; public ForcedLowerPArg forcedLower; public ForcedUpperPArg forcedUpper; public AsPArg asPArg; public RefersToPArg refersTo; public LookInPArg lookInPArg; public AutoCreatePArg autoCreatePArg; public CreateAsPArg createAsPArg; public CreateInPArg createInPArg; public ImportContextPArg importContextPArg; public ModePArg modePArg; public PartialPArg partialPArg; public LookupScopePArg lookupScopePArg; public ReferenceByPArg referenceByPArg; public PrefixPArg prefixPArg; public PostfixPArg postfixPArg; public DisambiguatePArg disambiguatePArg; protected PropertyArgs(Collection<PropertyArg> args) throws SyntaxElementException { if (args == null) { return; } for (PropertyArg propertyArg : args) { if (propertyArg instanceof SeparatorPArg) { if (separator != null) { throw new SyntaxElementException("Double definition of separator", propertyArg); } separator = (SeparatorPArg) propertyArg; // TODO validate contents here and below, add to error // bucket else } else if (propertyArg instanceof RefersToPArg) { if (refersTo != null) { throw new SyntaxElementException("Double definition of RefersToPArg", propertyArg); } refersTo = (RefersToPArg) propertyArg; } else if (propertyArg instanceof ForcedLowerPArg) { if (forcedLower != null) { throw new SyntaxElementException("Double definition of ForcedLowerPArg", propertyArg); } forcedLower = (ForcedLowerPArg) propertyArg; } else if (propertyArg instanceof ForcedUpperPArg) { if (forcedUpper != null) { throw new SyntaxElementException("Double definition of ForcedLowerPArg", propertyArg); } forcedUpper = (ForcedUpperPArg) propertyArg; } else if (propertyArg instanceof AsPArg) { if (asPArg != null) { throw new SyntaxElementException("Double definition of AsPArg", propertyArg); } asPArg = (AsPArg) propertyArg; } else if (propertyArg instanceof LookInPArg) { if (lookInPArg != null) { throw new SyntaxElementException("Double definition of LookInPArg", propertyArg); } lookInPArg = (LookInPArg) propertyArg; } else if (propertyArg instanceof AutoCreatePArg) { if (autoCreatePArg != null) { throw new SyntaxElementException("Double definition of AutoCreatePArg", propertyArg); } autoCreatePArg = (AutoCreatePArg) propertyArg; } else if (propertyArg instanceof CreateAsPArg) { if (createAsPArg != null) { throw new SyntaxElementException("Double definition of CreateAsPArg", propertyArg); } createAsPArg = (CreateAsPArg) propertyArg; } else if (propertyArg instanceof CreateInPArg) { if (createInPArg != null) { throw new SyntaxElementException("Double definition of CreateInPArg", propertyArg); } createInPArg = (CreateInPArg) propertyArg; } else if (propertyArg instanceof ImportContextPArg) { if (importContextPArg != null) { throw new SyntaxElementException("Double definition of ImportContextPArg", propertyArg); } importContextPArg = (ImportContextPArg) propertyArg; } else if (propertyArg instanceof ModePArg) { if (modePArg != null) { throw new SyntaxElementException("Double definition of ImportContextPArg", propertyArg); } modePArg = (ModePArg) propertyArg; } else if (propertyArg instanceof LookupScopePArg) { if (lookupScopePArg != null) { throw new SyntaxElementException("Double definition of lookupScopePArg", lookupScopePArg); } lookupScopePArg = (LookupScopePArg) propertyArg; } else if (propertyArg instanceof ReferenceByPArg) { if (referenceByPArg != null) { throw new SyntaxElementException("Double definition of referenceByPArg", referenceByPArg); } referenceByPArg = (ReferenceByPArg) propertyArg; } else if (propertyArg instanceof PrefixPArg) { if (prefixPArg != null) { throw new SyntaxElementException("Double definition of prefixPArg", prefixPArg); } prefixPArg = (PrefixPArg) propertyArg; } else if (propertyArg instanceof PostfixPArg) { if (postfixPArg != null) { throw new SyntaxElementException("Double definition of postfixPArg", postfixPArg); } postfixPArg = (PostfixPArg) propertyArg; } else if (propertyArg instanceof DisambiguatePArg) { if (disambiguatePArg != null) { throw new SyntaxElementException("Double definition of disambiguateParg", disambiguatePArg); } disambiguatePArg = (DisambiguatePArg) propertyArg; } else if (propertyArg instanceof PartialPArg) { if (partialPArg != null) { throw new SyntaxElementException("Double definition of partialPArg", partialPArg); } partialPArg = (PartialPArg) propertyArg; } else { throw new RuntimeException("PropertyArg type not supported yet:" + propertyArg.getClass()); } } } } public void setSkipDelayedReferences(boolean skipDelayedReferences) { this.skipDelayedReferences = skipDelayedReferences; } }