<@doc> Implements a predictive parser for dynamic expressions (c) SAP AG 2003-2006. All rights reserved. #INCLUDE[defs.inc] /////////////////////////////////////////////////////////////////////// // CLASS DEFINITION class DynExpParserPtn inherit dev:DynExpParser; /////////////////////////////////////////////////////////////////////// // PROPERTIES //FORMAT in A1S is translatable, so there are 2 obligatory args override readonly property FORMAT_OBLIGATORY_ARGS = 2; readonly property translationCounter = 0; <@doc>Lists the supported function groups override readonly property supportedFuncGroups = {s:true, n:true, d:true, t:true, q:true, b:true, m:true, o:true}; /////////////////////////////////////////////////////////////////////// // PUBLIC METHODS <@doc scope="protected"> in VC A1S empty boolean field is not allowed override method emptyExpression() //check if expression is empty var empty = this.supercall(); if (empty && this.expectedType == #[BOOLEAN]) throw new Error(-1, '#TEXT[XMSG_EMPTY_BOOLEAN]'); return empty; end <@doc scope="protected"> reset translation properties override method resetTranslation() this.translationCounter = 0; end <@doc scope="protected"> Validates the syntax of TRANSLATE/FORMAT TRANLSATE and FORMAT behaves differently in CE and in A1S FORMAT in A1S ============= 1)2 obligatory args which are the text and the tranlsation type. the text might contains place holders for the rest of FORMAT's arguments (FORMAT in CE has 1 obligatory arg). 2)The first FORMAT arg can be only text. 3)FORMAT is translatable in A1S thus the second param is the translation type and it is validated. TRANSLATE in A1S ============== 1)validate TRANSLATE arguments only on parseByName mode, since the macro's syntax is different in parseById mode (the persistence of TRANSLATE is different then the display to the user) - the same goes for the FORMAT 2)if the trans type is missing it is added automatically. so, for the user the second arg for TRANSLATE is optional. the func handled override method validateTransFunc(token) if(!this.parseByID && token.token == this.TOKEN_FUNC) { var tokenValue = token.value; var child0 = token.children && token.children[0]; var child1 = token.children && token.children[1] && token.children[1].value; if(tokenValue == this.TOKEN_FUNC_TRANSLATE || tokenValue == this.TOKEN_FUNC_FORMAT) { //first argument for TRANSLATE/FORMAT should be literal. if(child0 && child0.token != 'VAL') throw new Error(-1, '#TEXT[XMSG_ERROR_INVALID_ARG_FORMAT] ' + tokenValue); //validate translation type for TRANSLATE/FORMAT, is missing add it automaticaly if(child1==null || child1==undefined) { token.children[1] = {token:'VAL', value:this.transType, type:'s'}; token.size=2; this.warning= '#TEXT[XMSG_INVALID_TRANSLATION_TYPE] '.replace('{0}',this.transType); } //if trans type is missing - add the deafult else if(!this.checkTranslationType(child1)) { token.children[1].value = this.transType; this.warning= '#TEXT[XMSG_INVALID_TRANSLATION_TYPE] '.replace('{0}',this.transType); } } //validate no. of argument vs. place holders for FORMAT if(tokenValue == this.TOKEN_FUNC_FORMAT) { var total = this.handleDynamicArgs(token, child0.value); if (token.size > total) throw new Error(-1, '#TEXT[XMSG_ERROR_TOO_MANY_ARGS] '+tokenValue); if (token.size < total ) throw new Error(-1, '#TEXT[XMSG_ERROR_MISSING_ARG_FUNC] '+tokenValue); } } end <@doc scope="private"> set the rule for validating field expression field full name token represents the field override method isBOField(expr) if(expr.indexOf("@")>=0 && expr.match(/^(\[([^\]]+)\]|([\w\.^\]]+))?\@(\[([^\]]+)\]|(\@)|([\w\.]+))+/)) { this.lexpos += RegExp.lastIndex; return this.validateField(RegExp.lastMatch); } return null; end <@doc scope="private"> checks if field name is valid the field name structure: infoshape@field.subtype1.subtype2 (no cluster infosjapes for pattern) when parsing by id - the structure is: AH@AB.subtype1.subtype2 where AH = infoshape id, AB = field id field full name (by name of id) token represents the field method validateField(expression) //TODO: change getAlias to getName in patterns code !!! var theInfosets = [], theInfoShapes = [], theField = null; var identifier = null; var F, fieldName="", fieldId=""; var pair = [], names = [], givenInfoshapeName = "", givenFieldName = ""; var i = 0; //split into 2 sections: infoshapes names and fields names this.split(expression, '@', pair, true); givenInfoshapeName = pair[0]; givenFieldName = pair[1]; //root infoset //======================= if(givenInfoshapeName.length > 0) { try { identifier = this.handleReservedChar(givenInfoshapeName, true); //get the infoshape obj var allInfosets = this.infosetEnum.getInfosetList(false); if(this.parseByID) { theInfosets.push(allInfosets[identifier]); theInfoShapes.push(allInfosets[identifier].getInfoshape()); } else { //get a list of all infoshape by that name for(var set in allInfosets) if(allInfosets[set].getInfosetAlias() == identifier) { theInfosets.push(allInfosets[set]); theInfoShapes.push(allInfosets[set].getInfoshape()); } } } catch(e) //catch the error of wrong infoshape name { } //not found the infoset sent if(!theInfosets || theInfosets.length==0) { fieldName = givenInfoshapeName; fieldId = givenInfoshapeName; throw new Error(-1, '#TEXT[XMSG_ERROR_MISSING_ITERACTOR] '+ fieldName); } } else { //check if sub infoshape of current infoset theInfosets.push(this.currInfoset); theInfoShapes.push(this.currInfoset && this.currInfoset.getInfoshape()); } //child infoshapes & fields //============= identifier = givenFieldName; if (identifier == '@') { if(null == this.field) { throw new Error(-1, '#TEXT[XMSG_ERROR_CUR_FIELD_SUPPORT]'); } if(this.parseByID) { identifier = this.field.getId(); } else { identifier = this.field.getAlias(); //TODO: change to getName() } } //================================== //split infoshapes and fields names this.split(identifier, '.', names, false); for(var k in names) names[k] = this.handleReservedChar(names[k], true); var infoset = null; var infoShape = null; //when for UI - we get the last child id after the @ - infoset_id@flat_field_id for(var j=0, len=theInfosets.length; j for TRANLSATE/FORMAT persistence is different from UI the text and the translation type are replaced with a key token currently handles displying the expr in UI or for persistence holds the name of macro and arguments override method setDisplayTransFunc(token, b4UI, buf) var t=token.token, v=token.value, c=token.children; var tmpIndex = 0; if( (v==this.TOKEN_FUNC_TRANSLATE || v==this.TOKEN_FUNC_FORMAT) && #SYS[SUPPORT_TRANSLATION] && c[0].token == 'VAL' && this.element) { ////// Support Inner DE Translations //////// var outStr = ''; tmpIndex = 1; var tType = null; // Get needed trasnlations params: var tText = TRIM(c[0].value); if(!tText) throw new Error(-1, '#TEXT[XMSG_ERR_TRANSLATION_PARAMETERS]'); // Is there already a translation Text item for the property given: var key = this.element.getTransKey(this.propName); if(!b4UI) { tType = c[1] && c[1].value; // Validate translation type: if(!this.checkTranslationType(tType)) tType = this.transType; // Persisting the DE with the translation support. // The Type should not be perstisted in the property value but in the translation section: tmpIndex = 2; if(this.translationCounter == 0) { // Start handling the translations by ignoring history: if(key) { this.element.removeTranslation(key); this.element.removeTransKey(this.propName); } key = []; } key[this.translationCounter] = this.element.insertTranslation(tType, tText); if(key[this.translationCounter]) {// Now let the element know the property has a translation: this.element.setTransKey( this.propName, key[this.translationCounter], true); } if(!key[this.translationCounter])throw new Error(-1, '#TEXT[XMSG_ERROR_TRANS]'); // The value of the text should be replaced by the returned translation key for persistency: outStr = key[this.translationCounter]; } else { // Presentation purposes (b4UI) // Replace keys with values: // Get the transalation: (here the tText is actually a translation key) outStr = this.element.getTranslation(tText, true); if(!ISARRAY(outStr)) throw new Error(-1, '#TEXT[XMSG_ERROR_TRANS]'); // Get the right type from the translation object itself: tType = outStr[1]; outStr = outStr[0]; if(!key || this.translationCounter > 0) // Now let the element know the property has a translation: this.element.setTransKey( this.propName, tText, true) } // In case more than one translation in a DE: this.translationCounter++; // FORMAT anv TRANSLATION are built differently: buf.push('"' + outStr + '"'); if(b4UI) { buf.push('"' + tType + '"'); } } return tmpIndex; end <@doc scope="public"> check if the translation type is correct If not valid, an exception is thrown translation type override method checkTranslationType(ttype) if(!this.Class.metadata.translationTypes[ttype]) //throw new Error(-1, '#TEXT[XMSG_ERROR_MISSING_TRANS_TYPE]'.replace('{0}','/'+this.TOKEN_FUNC_FORMAT)); return false; return true; end /////////////////////////////////////////////////////////////////////////// // END OF CLASS