UNPKG

k2hr3-api

Version:

K2HR3 REST API is K2hdkc based Resource and Roles and policy Rules

1,568 lines (1,462 loc) 243 kB
/* * K2HR3 REST API * * Copyright 2017 Yahoo Japan Corporation. * * K2HR3 is K2hdkc based Resource and Roles and policy Rules, gathers * common management information for the cloud. * K2HR3 can dynamically manage information as "who", "what", "operate". * These are stored as roles, resources, policies in K2hdkc, and the * client system can dynamically read and modify these information. * * For the full copyright and license information, please view * the license file that was distributed with this source code. * * AUTHOR: Takeshi Nakatani * CREATE: Wed Jul 11 2017 * REVISION: * */ 'use strict'; var apiutil = require('./k2hr3apiutil'); // Debug logging objects var r3logger = require('./dbglogging'); //--------------------------------------------------------- // Keywords for K2HR3 Template Engine //--------------------------------------------------------- // Engine type Keywords const TEMPL_ENGINE_TYPE_PTN = '^{{#!(.*?)[ \t]*}}[\r\n]*'; // Regex = /^\{\{#\!(.*)[ \t]+\}\}[\r\n]*/ const TEMPL_K2HR3_TEMPLENGINE_KW = 'k2hr3template'; // This k2hr3template.js keywords // Template area Keywords const TEMPL_START_KW = '{{'; const TEMPL_END_KW = '}}'; const TEMPL_ESCAPE_START_KW = '{{{'; const TEMPL_ESCAPE_END_KW = '}}}'; // Special Keywords const TEMPL_SEMICOLON_KW = ';'; // ";" for sentence or "for" const TEMPL_COMMENT_KW = '#'; // Comment const TEMPL_PRINT_KW = '='; // Print command, this keyword after '{{' // Control syntax const TEMPL_IF_KW = 'if'; // if const TEMPL_ELIF_KW = 'elif'; // elif const TEMPL_ELSE_KW = 'else'; // else const TEMPL_ENDIF_KW = 'endif'; // endif const TEMPL_WHILE_KW = 'while'; // while const TEMPL_DO_KW = 'do'; // do(while) const TEMPL_DONE_KW = 'done'; // done const TEMPL_FOR_KW = 'for'; // for const TEMPL_FOREACH_KW = 'foreach'; // foreach const TEMPL_IN_KW = 'in'; // "in" for "foreach" const TEMPL_BREAK_KW = 'break'; // break const TEMPL_CONTINUE_KW = 'continue'; // continue // Conditions const TEMPL_AND_KW = '&&'; // Condition: AND const TEMPL_OR_KW = '||'; // Condition: OR const TEMPL_LESS_KW = '<'; // Condition: LESS THAN const TEMPL_GREAT_KW = '>'; // Condition: GREATER THAN const TEMPL_LESSEQ_KW = '<='; // Condition: LESS EQUAL THAN const TEMPL_GREATEQ_KW = '>='; // Condition: GREATER EQUAL THAN const TEMPL_EQUAL_KW = '=='; // Condition: EQUAL const TEMPL_NOTEQ_KW = '!='; // Condition: NOT EQUAL // One term operator const TEMPL_NOT_KW = '!'; // One term operator const TEMPL_INC_KW = '++'; // One term operator const TEMPL_DEC_KW = '--'; // One term operator // Binary operator const TEMPL_SET_KW = '='; // Binary operator const TEMPL_ADD_KW = '+'; // Binary operator const TEMPL_SUB_KW = '-'; // Binary operator const TEMPL_DIV_KW = '/'; // Binary operator const TEMPL_MUL_KW = '*'; // Binary operator const TEMPL_REM_KW = '%'; // Binary operator const TEMPL_AMP_KW = '&'; // Binary operator const TEMPL_VARTBAR_KW = '|'; // Binary operator const TEMPL_LSHIFT_KW = '<<'; // Binary operator const TEMPL_RSHIFT_KW = '>>'; // Binary operator // Array operator const TEMPL_SQUARE_START_KW = '['; // keywords for accessing to array member const TEMPL_SQUARE_END_KW = ']'; // const TEMPL_CURLY_START_KW = '{'; // keywords for accessing to object member const TEMPL_CURLY_END_KW = '}'; // // String area and escape for it const TEMPL_SINGLE_QUOTE_KW = '\''; // Separator for string value const TEMPL_DOUBLE_QUOTE_KW = '"'; // Separator for string value const TEMPL_ESCAPE_KW = '\\'; // escape // Variable area for K2hr3 template const TEMPL_VALUESEP_KW = '%'; // Separator for Variable // Length(size) property const TEMPL_LENGTH_KW = '.length'; // length property for array or object variable const TEMPL_SIZE_KW = '.size'; // const TEMPL_COUNT_KW = '.count'; // // Static values const TEMPL_NULL_KW = 'null'; // constant null const TEMPL_TRUE_KW = 'true'; // constant true const TEMPL_FALSE_KW = 'false'; // constant false // Radix for static number values const TEMPL_HEX_PREFIX_KW = '0x'; // hex(16) radix const TEMPL_HEX2_PREFIX_KW = 'x'; // hex(16) radix const TEMPL_OCT_PREFIX_KW = '0o'; // octal(8) radix const TEMPL_BIN_PREFIX_KW = '0b'; // binary(2) radix //--------------------------------------------------------- // Template Statement Objects for K2HR3 Template Engine //--------------------------------------------------------- // Statement object Array is an array that decomposes the k2hr3 template and holds its contents. // It is indicated as follows: // [ // { : Each template area(statement area / not statement area) // "isStatement": true/false : This is true when the template area is statement({{...}}). // "origString": <string> : original string // "cvtString": <string> : converted string(escaped keyword and trimmed when statement area includes formula) // "isReplication": true/false : true means the statement object is replicated // "formUnits": [ : formula units array for each statement area, if the statement does not have any template object, this is [[cvtString]]. // [ : One formula(separate ';') // "unit string", : One unit in formula(separate keywords...) // "unit string", // ... // ], // ... // ] // }, // ... // ] // // [NOTE] // If the statement is comment, it's statement area(object) is following as example: // { "isStatement": true, // "origString": "{{#.....}}", // "cvtString": "#....", // "formUnits": [ [ // "#", // "....." // ] ] // } // // If the statement is print, it's statement area(object) is following as example: // { "isStatement": true, // "origString": "{{=AAAA; BBBB}}", // "cvtString": "=AAAA; BBBB", // "formUnits": [ [ // "=", // "AAAA" // ], // [ // "BBBB" // ] // ] // } // //--------------------------------------------------------- // K2HR3 Template Engine Utility : Create K2HR3 Template Objects //--------------------------------------------------------- // Create one statement object tree and return top of tree object. // // return : following object // { // result: true/false // message: null or <error message string> // templObj: null or BaseTemplateObject object // } // // [NOTE] // If object tree is rechained(the parent was included into new object), // this returns result=true and templObj=null. // var createK2hr3TemplateObject = function(statementsArray, parentObject) { var resobj = {'result': false, 'message': null, 'templObj': null}; if(isEmptyStatementsArray(statementsArray)){ resobj.result = true; resobj.templObj = null; r3logger.elog('templateArray is empty or wrong : ' + JSON.stringify(statementsArray)); return resobj; } if(null !== parentObject && !(parentObject instanceof BaseTemplateObject)){ resobj.result = false; resobj.message = 'parentObject is not BaseTemplateObject class or subclass : ' + JSON.stringify(parentObject); r3logger.elog(resobj.message); return resobj; } var lastErrorString = ''; for(var cnt = 0; cnt < k2hr3StatementObjectList.length; ++cnt){ // create object var assembleObject = new k2hr3StatementObjectList[cnt](); // test assembling if(assembleObject.testAssemble(statementsArray)){ // try assemble if(null !== assembleObject.assemble(statementsArray, parentObject)){ for(var foundObject = assembleObject; foundObject; foundObject = foundObject.getParentTemplObject()){ if(parentObject === foundObject.getParentTemplObject()){ // [NOTE] allow parentObject is null break; } } if(foundObject){ // found parent object in assembleObject's parent inheritance tree. // resobj.result = true; resobj.templObj = foundObject; return resobj; }else{ // not found parent object in assembleObject's parent inheritance tree. // // This means that the parent of assembleObject points to parent object // rather than parentObject as analysis result, and assembleObject does // not exist in descendant tree of parentObject. // On this case, this function returns resobj.templObj = null, then // stop analyzing the statementArray for parentObject's child. // resobj.result = true; resobj.templObj = null; return resobj; } }else{ // could not assemble or something error occurred. // then any error message is stocked. // if(apiutil.isSafeString(lastErrorString.length)){ lastErrorString += ', '; } lastErrorString += '('; lastErrorString += assembleObject.getError() + ')'; } }else{ // statement object is not a target } } // not matched resobj.result = false; resobj.message = 'any template class is not matched for templateArray : ' + lastErrorString; r3logger.elog(resobj.message); return resobj; }; //--------------------------------------------------------- // K2HR3 Template Engine Utility : Check K2HR3 Template left //--------------------------------------------------------- // Check the first template statement's unit requires left // statements. // const REQUIRED_LEFT_NO = 0; const REQUIRED_LEFT_VARIABLE = 1; const REQUIRED_LEFT_CALCULATE = 2; const REQUIRED_LEFT_CONDITION = 4; const REQUIRED_LEFT_ALL = (REQUIRED_LEFT_VARIABLE | REQUIRED_LEFT_CALCULATE | REQUIRED_LEFT_CONDITION); var isRequireLeftTemplate = function(statementsArray, type) { if(!apiutil.isSafeEntity(type) || isNaN(type)){ r3logger.elog('type parameter is wrong : ' + JSON.stringify(type)); return false; } if(isEmptyStatementsArray(statementsArray)){ // empty return false; } // first unit(string) var unit = statementsArray[0].formUnits[0][0]; var unit_type = REQUIRED_LEFT_NO; switch(unit){ case TEMPL_INC_KW: case TEMPL_DEC_KW: unit_type = REQUIRED_LEFT_VARIABLE; break; case TEMPL_AND_KW: case TEMPL_OR_KW: case TEMPL_LESS_KW: case TEMPL_GREAT_KW: case TEMPL_LESSEQ_KW: case TEMPL_GREATEQ_KW: case TEMPL_EQUAL_KW: case TEMPL_NOTEQ_KW: unit_type = REQUIRED_LEFT_ALL; break; case TEMPL_SET_KW: unit_type = REQUIRED_LEFT_VARIABLE; break; case TEMPL_ADD_KW: case TEMPL_SUB_KW: case TEMPL_DIV_KW: case TEMPL_MUL_KW: case TEMPL_REM_KW: unit_type = REQUIRED_LEFT_VARIABLE | REQUIRED_LEFT_CALCULATE; break; case TEMPL_AMP_KW: case TEMPL_VARTBAR_KW: unit_type = REQUIRED_LEFT_ALL; break; case TEMPL_LSHIFT_KW: case TEMPL_RSHIFT_KW: unit_type = REQUIRED_LEFT_VARIABLE; break; default: break; } return (0 !== (type & unit_type)); }; //--------------------------------------------------------- // K2HR3 Template Engine Utility : Others //--------------------------------------------------------- var isEmptyStatementsArray = function(statementsArray) { if(!isSafeStatementsArray(statementsArray, true) || apiutil.isEmptyArray(statementsArray) || apiutil.isEmptyArray(statementsArray[0].formUnits)){ return true; } return false; }; var isSafeStatementsArray = function(statementsArray, is_check_units) { if(!apiutil.isArray(statementsArray)){ return false; } for(var cnt = 0; cnt < statementsArray.length; ++cnt){ if(!isSafeStatementObject(statementsArray[cnt], is_check_units)){ return false; } } return true; }; var isSafeStatementObject = function(statementObj, is_check_units) { if(!apiutil.isSafeEntity(is_check_units) || 'boolean' !== typeof is_check_units){ is_check_units = true; // default check units } if( !apiutil.isSafeEntity(statementObj) || !apiutil.isSafeEntity(statementObj.isStatement) || // check statement object 'boolean' !== typeof statementObj.isStatement || // (!apiutil.isSafeString(statementObj.origString) && '' !== statementObj.origString) || // (!apiutil.isSafeString(statementObj.cvtString) && '' !== statementObj.cvtString) || // !apiutil.isSafeEntity(statementObj.isReplication) || // 'boolean' !== typeof statementObj.isReplication || // !apiutil.isArray(statementObj.formUnits) || // (is_check_units && !isSafeFormulaUnits(statementObj.formUnits)) ) // check units array { return false; } return true; }; var isReplicationStatementObject = function(statementObj) { if( !apiutil.isSafeEntity(statementObj) || !apiutil.isSafeEntity(statementObj.isReplication) || 'boolean' !== typeof statementObj.isReplication || !statementObj.isReplication ) { return false; } return true; }; var isSafeFormulaUnits = function(unitsObj) { if(!apiutil.isArray(unitsObj)){ return false; } for(var cnt = 0; cnt < unitsObj.length; ++cnt){ if(!isSafeFormulaUnitsArray(unitsObj[cnt]) || apiutil.isEmptyArray(unitsObj[cnt])){ return false; } } return true; }; var isSafeFormulaUnitsArray = function(unitsArray) { if(!apiutil.isArray(unitsArray)){ return false; } for(var cnt = 0; cnt < unitsArray.length; ++cnt){ if(!apiutil.isSafeString(unitsArray[cnt]) && '' !== unitsArray[cnt]){ return false; } } return true; }; //--------------------------------------------------------- // K2HR3 Template Engine Utility // : Create/Modify Template Statement Objects //--------------------------------------------------------- var createEmptyK2hr3TemplateStatement = function(isStatement, isReplication) { var areaobj = {}; areaobj.isStatement = ('boolean' === typeof isStatement ? isStatement : true); // default true areaobj.origString = ''; areaobj.cvtString = ''; areaobj.isReplication = ('boolean' === typeof isReplication ? isReplication : true); // default true areaobj.formUnits = []; // empty array[] return areaobj; }; var replicateK2hr3TemplateStatement = function(statementObj) { if(!isSafeStatementObject(statementObj, false)){ // not check units return createEmptyK2hr3TemplateStatement(); } var areaobj = createEmptyK2hr3TemplateStatement(statementObj.isStatement, true); if(isSafeFormulaUnits(statementObj.formUnits)){ areaobj.formUnits = statementObj.formUnits; // reference } return areaobj; }; var createReplicatedK2hr3TemplateStatement = function(isStatement, formUnits) { var areaobj = createEmptyK2hr3TemplateStatement(isStatement, true); if(!apiutil.isEmptyArray(formUnits)){ areaobj.formUnits = formUnits; } return areaobj; }; var replaceK2hr3TemplateStatementFormUnits = function(statementObj, formUnits) { // Allowed empty formUnits and not check strict statementObj if( !apiutil.isSafeEntity(statementObj) || !apiutil.isArray(formUnits) ) { return false; } statementObj.formUnits = formUnits; return true; }; var compressStatementsArray = function(statementsArray) { if(!apiutil.isArray(statementsArray)){ return; } for(var cnt1 = 0; cnt1 < statementsArray.length; ){ if(!isSafeStatementObject(statementsArray[cnt1], false)){ // something is wrong, then remove this statement object statementsArray.splice(cnt1, 1); }else{ for(var cnt2 = 0; cnt2 < statementsArray[cnt1].formUnits.length; ){ if(apiutil.isEmptyArray(statementsArray[cnt1].formUnits[cnt2])){ // units is empty, then remove this units statementsArray[cnt1].formUnits.splice(cnt2, 1); }else{ for(var cnt3 = 0; cnt3 < statementsArray[cnt1].formUnits[cnt2].length; ){ if(!apiutil.isSafeString(statementsArray[cnt1].formUnits[cnt2][cnt3]) && '' !== statementsArray[cnt1].formUnits[cnt2][cnt3]){ // one unit is wrong, then remove this unit statementsArray[cnt1].formUnits[cnt2].splice(cnt3, 1); }else{ ++cnt3; } } if(apiutil.isEmptyArray(statementsArray[cnt1].formUnits[cnt2])){ // finally units is empty, then remove this units statementsArray[cnt1].formUnits.splice(cnt2, 1); }else{ ++cnt2; } } } if(apiutil.isEmptyArray(statementsArray[cnt1].formUnits)){ // finally units is empty, then remove this units statementsArray.splice(cnt1, 1); }else{ ++cnt1; } } } }; //--------------------------------------------------------- // K2HR3 Template Engine Utility // : Rollback Template Statement Objects //--------------------------------------------------------- // // rollback(insert) statement object to statements array // // is_add_first_pos : if true, insert statement object at first position in statementsArray. // if false, push bach units array data to statementsArray. // // return : returns statementsArray object // if input statementsArray is wrong, returned statementsArray is created in this function. // please take care for wrong input. // // [NOTE] // If statementObj is replication object, this rollbacks only formUnits in statementObj. // var rollbackStatementObjectToArray = function(statementsArray, statementObj, is_add_first_pos) { if('boolean' !== typeof is_add_first_pos){ is_add_first_pos = true; } if(!isSafeStatementObject(statementObj, false)){ // not check units // statement object is something wrong // ---> nothing to do return statementsArray; } if(!isSafeStatementsArray(statementsArray, false)){ // not check units // statementsArray is something wrong // ---> overwrite it by statement object. statementsArray = [statementObj]; }else if(apiutil.isEmptyArray(statementsArray)){ // statementsArray is empty // ---> push statement object. statementsArray.push(statementObj); }else{ var pos = (is_add_first_pos ? 0 : (statementsArray.length - 1)); var cnt; if(!isReplicationStatementObject(statementObj)){ // statement object is not replication if(isReplicationStatementObject(statementsArray[pos])){ // statementsArray[pos] is not replication if(is_add_first_pos){ // ---> insert statement object to top of statement array statementsArray.unshift(statementObj); }else{ // ---> push back statement object to last of statement array statementsArray.push(statementObj); } }else{ // statementsArray[pos] is replication, if(is_add_first_pos){ // ---> insert the statement object' formUnits to top of statementsArray[pos]'s one. // and replace statementsArray[pos] other member with statement object's one. for(cnt = 0; cnt < statementsArray[pos].formUnits.length; ++cnt){ statementObj.formUnits.push(statementsArray[pos].formUnits[cnt]); } statementsArray.shift(); statementsArray.unshift(statementObj); }else{ // ---> push back the statement object' formUnits to last of statementsArray[pos]'s one. // and replace statementsArray[pos] other member with statement object's one. for(cnt = 0; cnt < statementsArray[pos].formUnits.length; ++cnt){ statementObj.formUnits.unshift(statementsArray[pos].formUnits[statementsArray[pos].formUnits.length - cnt - 1]); } statementsArray.splice(statementsArray.length - 1, 1); statementsArray.push(statementObj); } } }else{ // statement object is replication if(isReplicationStatementObject(statementsArray[pos])){ // statementsArray[pos] is not replication if(is_add_first_pos){ // ---> insert statement object to top of statement array statementsArray.unshift(statementObj); }else{ // ---> push back statement object to last of statement array statementsArray.push(statementObj); } }else{ // statementsArray[pos] is replication if(is_add_first_pos){ // ---> insert the statement object' formUnits to top of statementsArray[pos]'s one. for(cnt = 0; cnt < statementObj.formUnits.length; ++cnt){ statementsArray[pos].formUnits.unshift(statementObj.formUnits[statementObj.formUnits.length - cnt - 1]); } }else{ // ---> push back the statement object' formUnits to last of statementsArray[pos]'s one. for(cnt = 0; cnt < statementObj.formUnits.length; ++cnt){ statementsArray[pos].formUnits.push(statementObj.formUnits[cnt]); } } } } } return statementsArray; }; //--------------------------------------------------------- // K2HR3 Template Engine Utility // : Parse Template Statement Objects //--------------------------------------------------------- // // Parse k2hr3 template to statement areas and minimum formula units. // // templString : The string is all of template string // result : Statement object Array // var parseK2hr3Template = function(templString) { // parse template to statement areas var resobj = parseK2hr3TemplateToStatements(templString); if(apiutil.isEmptyArray(resobj)){ r3logger.dlog('parsed template string result is null.'); return null; } // parse each statement area to formula units var formUnits; for(var cnt = 0; !apiutil.isEmptyArray(resobj) && cnt < resobj.length; ++cnt){ if(!apiutil.isSafeEntity(resobj[cnt])){ // why? resobj.splice(cnt, 1); if(0 === cnt){ cnt = -1; }else{ --cnt; } continue; } if(resobj[cnt].isStatement){ // in statement area formUnits = parseK2hr3TemplateForStatements(resobj[cnt].cvtString); if(!isSafeFormulaUnits(formUnits)){ // empty sentence, so remove this element. resobj.splice(cnt, 1); if(0 === cnt){ cnt = -1; }else{ --cnt; } continue; } }else{ // not statement area formUnits = [[resobj[cnt].cvtString]]; } resobj[cnt].formUnits = formUnits; } if(apiutil.isEmptyArray(resobj)){ r3logger.dlog('parsed template string result is null.'); return null; } //r3logger.dlog('parsed template string result is : ' + r3logger.dump(resobj)); return resobj; }; //--------------------------------------------------------- // K2HR3 Template Engine Utility // : Parse Template String to Template Statement Objects //--------------------------------------------------------- // // Parse input string which is k2hr3 template to statement areas array. // Each area is statement or not statement area. // // templString : The string is all of template string // result : Following object array, return empty array when error. // [ // { : Each template area(statement area / not statement area) // "isStatement": true/false : This is true when the template area is statement({{...}}). // "origString": <string> : original string // "cvtString": <string> : converted string(escaped keyword and trimmed when statement area includes formula) // }, // ... // ] // var parseK2hr3TemplateToStatements = function(templString) { var resobj = new Array(0); var oneobj = {}; if(!apiutil.isSafeString(templString)){ return null; } // parse var tmpl_cvtstr = ''; var tmpl_origin = ''; var keyword = TEMPL_START_KW; var escword = TEMPL_ESCAPE_START_KW; var oescword = TEMPL_ESCAPE_END_KW; // not keyword's escape var templ_area = false; while(apiutil.isSafeString(templString) && 0 < templString.length){ // each var key_pos = templString.indexOf(keyword); // '{{' or '}}' var esc_pos = templString.indexOf(escword); // '{{{' or '}}}' var oesc_pos = templString.indexOf(oescword); // '}}}' or '{{{' if(-1 !== oesc_pos && (-1 === key_pos || oesc_pos < key_pos)){ // found not keyword's escaped words before keyword tmpl_origin += templString.substr(0, oesc_pos + 3); tmpl_cvtstr += templString.substr(0, oesc_pos + 2); // converts '{{{'('}}}') to '{{'('}}') and appends it to end of tmpl_cvtstr. templString = templString.substr(oesc_pos + 3); // after key words('{{{' or '}}}') }else{ if(-1 != esc_pos && esc_pos === key_pos){ // found escaped key words tmpl_origin += templString.substr(0, esc_pos + 3); tmpl_cvtstr += templString.substr(0, esc_pos + 2); // converts '{{{'('}}}') to '{{'('}}') and appends it to end of tmpl_cvtstr. templString = templString.substr(esc_pos + 3); // after key words('{{{' or '}}}') }else if(-1 != key_pos){ // found key words oneobj = {}; if(templ_area){ tmpl_origin += templString.substr(0, key_pos + 2); // original string with '}}' tmpl_cvtstr += templString.substr(0, key_pos); // converted string without '}}' // make object oneobj.isStatement = true; oneobj.origString = tmpl_origin; oneobj.cvtString = tmpl_cvtstr.trim(); // trimmed string oneobj.isReplication= false; // set next templ_area = false; keyword = TEMPL_START_KW; escword = TEMPL_ESCAPE_START_KW; oescword = TEMPL_ESCAPE_END_KW; tmpl_origin = ''; tmpl_cvtstr = ''; templString = templString.substr(key_pos + 2); // after key words('{{') }else{ tmpl_origin += templString.substr(0, key_pos); // original string without '{{' tmpl_cvtstr += templString.substr(0, key_pos); // converted string without '{{' // make object oneobj.isStatement = false; oneobj.origString = tmpl_origin; oneobj.cvtString = tmpl_cvtstr; // not trimmed string oneobj.isReplication= false; // set next templ_area = true; keyword = TEMPL_END_KW; escword = TEMPL_ESCAPE_END_KW; oescword = TEMPL_ESCAPE_START_KW; tmpl_origin = TEMPL_START_KW; // start original string with '{{' tmpl_cvtstr = ''; templString = templString.substr(key_pos + 2); // after key words('}}') } // add if(apiutil.isSafeString(oneobj.origString)){ resobj.push(oneobj); } }else{ // not found keywords oneobj = {}; tmpl_origin += templString; // add rest string to original string tmpl_cvtstr += templString; // add rest string to converted string // only make object if(templ_area){ oneobj.isStatement = true; oneobj.origString = tmpl_origin; oneobj.cvtString = tmpl_cvtstr.trim(); // trimmed string }else{ oneobj.isStatement = false; oneobj.origString = tmpl_origin; oneobj.cvtString = tmpl_cvtstr; // not trimmed string } oneobj.isReplication = false; // add if(apiutil.isSafeString(oneobj.origString)){ resobj.push(oneobj); } break; } } } return resobj; }; //--------------------------------------------------------- // K2HR3 Template Engine Utility // : Parse Template String to Minimum Template Statement Objects //--------------------------------------------------------- // // Parse input string which is rough cutting statement area to minimum statement array. // Each minimum statement has formula units array which elements are minimum formula string. // Formula unit string is separated by keywords. // // target : the string in template area which is from '{{' to '}}'. // result : The array has formula units array which is separated by ':'. // The one sentence array member is array which member is // minimum opcode/operand unit. // [ // [ : One sentence(separated by ';') // "unit string", : One unit(separated by keywords...) // "unit string", // ... // ], // ... // ] // // [NOTE] // About comment and print statement which are special. // If target starts comment keyword('{{#'), this returns [ ["#", "...."] ] array. // If target starts print keyword('{{='), this returns [ ["=", "...."], ... ] array. // var parseK2hr3TemplateForStatements = function(target, is_print_statement) { if(!apiutil.isSafeString(target)){ return null; } if('boolean' !== typeof is_print_statement){ is_print_statement = false; } var mainres = new Array(0); var subres = new Array(0); var one_part = ''; // check comment/print line at first if(0 === target.indexOf(TEMPL_COMMENT_KW)){ // found comment keyword at first character position one_part = target.substr(1).trim(); subres.push(TEMPL_COMMENT_KW); if(apiutil.isSafeString(one_part)){ subres.push(one_part); } mainres.push(subres); return mainres; }else if(!is_print_statement && 0 === target.indexOf(TEMPL_PRINT_KW)){ // found print keyword at first character position one_part = target.substr(1).trim(); // reentrant for analyzing after statement string mainres = parseK2hr3TemplateForStatements(one_part, true); if(apiutil.isEmptyArray(mainres)){ // mainres array is empty, then make static for print(maybe this is error at assembling or executing) subres.push(TEMPL_PRINT_KW); mainres = new Array(0); mainres.push(subres); }else if(apiutil.isEmptyArray(mainres[0])){ // first array in mainres is empty, then make static for print(maybe this is error at assembling or executing) subres.push(TEMPL_PRINT_KW); mainres[0] = subres; }else{ // insert print keyword into first of array mainres[0].unshift(TEMPL_PRINT_KW); } return mainres; } // parse to statement string var is_squate = false; var is_dquate = false; var is_escape = false; for(var cnt = 0; cnt < target.length; ++cnt){ // check ';', '\'', '\"', '\\', '\s' if(TEMPL_SEMICOLON_KW === target[cnt]){ // ';' if(is_escape){ one_part += TEMPL_ESCAPE_KW; } is_squate = false; is_dquate = false; is_escape = false; if(0 < one_part.length){ subres.push(one_part); // finish one word one_part = ''; } if(0 < subres.length){ mainres.push(subres); // finish statement subres = new Array(0); } }else if(TEMPL_SINGLE_QUOTE_KW === target[cnt]){ // '\'' if(is_escape){ is_escape = false; one_part += TEMPL_ESCAPE_KW; one_part += target[cnt]; }else if(is_squate){ is_squate = false; one_part += target[cnt]; subres.push(one_part); // finish one word one_part = ''; }else if(is_dquate){ // nothing to do... one_part += target[cnt]; }else{ is_squate = true; if(0 < one_part.length){ subres.push(one_part); // finish one word } one_part = target[cnt]; } }else if(TEMPL_DOUBLE_QUOTE_KW === target[cnt]){ // '\"' if(is_escape){ is_escape = false; one_part += TEMPL_ESCAPE_KW; one_part += target[cnt]; }else if(is_dquate){ is_dquate = false; one_part += target[cnt]; subres.push(one_part); // finish one word one_part = ''; }else if(is_squate){ // nothing to do... one_part += target[cnt]; }else{ is_dquate = true; if(0 < one_part.length){ subres.push(one_part); // finish one word } one_part = target[cnt]; } }else if(TEMPL_ESCAPE_KW === target[cnt]){ // '\\' if(is_escape){ is_escape = false; one_part += TEMPL_ESCAPE_KW; one_part += target[cnt]; }else{ is_escape = true; } }else if(' ' === target[cnt] || '\t' === target[cnt] || '\r' === target[cnt] || '\n' === target[cnt]){ // === \s if(is_escape){ is_escape = false; one_part += TEMPL_ESCAPE_KW; subres.push(one_part); // finish one word one_part = ''; }else if(is_dquate || is_squate){ one_part += target[cnt]; }else if(0 === one_part.length){ // nothing to do }else{ subres.push(one_part); // finish one word one_part = ''; } }else{ // another char if(is_escape){ is_escape = false; one_part += TEMPL_ESCAPE_KW; } one_part += target[cnt]; } } // rest if(is_escape){ one_part += TEMPL_ESCAPE_KW; } if(0 < one_part.length){ subres.push(one_part); // finish one word } if(0 < subres.length){ mainres.push(subres); // finish statement } // next parse units(minimum formula units) mainres = parseK2hr3TemplateStatementToFomulaUnits(mainres); return mainres; }; //--------------------------------------------------------- // K2HR3 Template Engine Utility // : Parsing Template Formula String to Minimum Template Statement Units //--------------------------------------------------------- // // This parses double array which is parsed by parseK2hr3TemplateForStatements // to minimum formula units. // After this parsing, the result double array is separated minimum formula units // array divided into instruction units. // // mainres : input array which elements are each statement area. // [ // [ : array for statement separated by ';' // "unit string", : unit statement string separated by ';' or "string"(which is statement area by '\'' or '\"') // ... // ] // ] // // result : parse input unit array by keywords // [ // [ : array for statement separated by ';' // "unit string", : One unit formula string separated by ';' or "string" or any keywords // ... // ] // ] // var parseK2hr3TemplateStatementToFomulaUnits = function(mainres) { if(apiutil.isEmptyArray(mainres)){ return mainres; } for(var cnt = 0; cnt < mainres.length; ++cnt){ if(apiutil.isEmptyArray(mainres[cnt])){ // why? mainres.splice(cnt, 1); if(0 === cnt){ cnt = -1; }else{ --cnt; } continue; } // parse subres to some units var subres = mainres[cnt]; for(var cnt2 = 0; cnt2 < subres.length; ++cnt2){ // parse string to minimum unit array var subres_array = parseK2hr3TemplateStatementToMinimumUnits(subres[cnt2]); if(apiutil.isEmptyArray(subres_array)){ // why? subres.splice(cnt2, 1); if(0 === cnt2){ cnt2 = -1; }else{ --cnt2; } }else if(1 < subres_array.length){ // replace pos to array subres.splice(cnt2, 1); for(var cnt3 = 0; cnt3 < subres_array.length; ++cnt3){ subres.splice(cnt2, 0, subres_array[subres_array.length - cnt3 - 1]); } cnt2 += (subres_array.length - 1); }else{ // nothing to do } } // check empty subres if(!isSafeFormulaUnitsArray(subres) || apiutil.isEmptyArray(subres)){ // mainres[cnt] has empty subres array, then remove cnt position array. mainres.splice(cnt, 1); if(0 === cnt){ cnt = -1; }else{ --cnt; } } } // [NOTE] // if units array(subres) is empty, it is removed from mainres. // if there is no units in mainres, mainres is empty array. // return mainres; }; //--------------------------------------------------------- // K2HR3 Template Engine Utility // : Parsing Template String Including Some Formula to Minimum Template Statement Units //--------------------------------------------------------- // // If it starts '\'' or '\"', it means "string" area which maybe include ';' and '\s'. // Then this method does not check when the target is "string" and '\' character, // because it effects only "string". // The input string must not include basically ';' and '\s'. // // result : null or following array // [ // "unit string", : One unit in formula(separate keywords...) // ... // ] // var parseK2hr3TemplateStatementToMinimumUnits = function(target) { if(!apiutil.isSafeString(target)){ return null; } var resarr = new Array(0); if(TEMPL_SINGLE_QUOTE_KW === target[0] || TEMPL_DOUBLE_QUOTE_KW === target[0]){ // target is '...' or "..." string, then this can not parse. resarr.push(target); return resarr; } var one_part = ''; for(var cnt = 0; cnt < target.length; ++cnt){ if(TEMPL_VALUESEP_KW === target[cnt]){ // '%' (== TEMPL_REM_KW) // [NOTE] // Space characters are already parsed before calling this function. // if(target.length <= (cnt + 1)){ // target[cnt + 1] === \s or null, then target[cnt] is TEMPL_REM_KW if(0 !== one_part.length){ resarr.push(one_part); // set stocked string before percent } one_part = TEMPL_REM_KW; resarr.push(one_part); // set percent one_part = ''; }else{ var end_percent_pos = target.substr(cnt + 1).indexOf(TEMPL_VALUESEP_KW); if(-1 === end_percent_pos){ // not found end(2'nd) percent/or \r(\n) found before percent, then target[cnt] is TEMPL_REM_KW if(0 !== one_part.length){ resarr.push(one_part); // set stocked string before percent } one_part = TEMPL_REM_KW; resarr.push(one_part); // set percent one_part = ''; }else{ // found end(2'nd) percent, then these are TEMPL_VALUESEP_KW if(0 !== one_part.length){ resarr.push(one_part); // set stocked string before percent } one_part = TEMPL_REM_KW; one_part += target.substr(cnt + 1).substr(0, end_percent_pos); one_part += TEMPL_REM_KW; resarr.push(one_part); // set %...%(TEMPL_VALUESEP_KW area) one_part = ''; cnt += end_percent_pos + 1; // cnt is set end of TEMPL_VALUESEP_KW } } }else if(TEMPL_SQUARE_START_KW === target[cnt]){ // '[' if(0 !== one_part.length){ resarr.push(one_part); one_part= ''; } resarr.push(TEMPL_SQUARE_START_KW); }else if(TEMPL_SQUARE_END_KW === target[cnt]){ // ']' if(0 !== one_part.length){ resarr.push(one_part); one_part= ''; } resarr.push(TEMPL_SQUARE_END_KW); }else if(TEMPL_CURLY_START_KW === target[cnt]){ // '{' if(0 !== one_part.length){ resarr.push(one_part); one_part= ''; } resarr.push(TEMPL_CURLY_START_KW); }else if(TEMPL_CURLY_END_KW === target[cnt]){ // '}' if(0 !== one_part.length){ resarr.push(one_part); one_part= ''; } resarr.push(TEMPL_CURLY_END_KW); }else if(TEMPL_MUL_KW === target[cnt]){ // '*' if(0 !== one_part.length){ resarr.push(one_part); one_part= ''; } resarr.push(TEMPL_MUL_KW); }else if(TEMPL_DIV_KW === target[cnt]){ // '/' if(0 !== one_part.length){ resarr.push(one_part); one_part= ''; } resarr.push(TEMPL_DIV_KW); }else if(TEMPL_NOT_KW === target[cnt]){ // '!' if(0 !== one_part.length){ resarr.push(one_part); one_part= ''; } if((cnt + 1) < target.length && TEMPL_SET_KW === target[cnt + 1]){ ++cnt; resarr.push(TEMPL_NOTEQ_KW); // '!=' }else{ resarr.push(TEMPL_NOT_KW); // '!' } }else if(TEMPL_SET_KW === target[cnt]){ // '=' if(0 !== one_part.length){ resarr.push(one_part); one_part= ''; } if((cnt + 1) < target.length && TEMPL_SET_KW === target[cnt + 1]){ ++cnt; resarr.push(TEMPL_EQUAL_KW); // '==' }else{ resarr.push(TEMPL_SET_KW); // '=' } }else if(TEMPL_ADD_KW === target[cnt]){ // '+' if(0 !== one_part.length){ resarr.push(one_part); one_part= ''; } if((cnt + 1) < target.length && TEMPL_ADD_KW === target[cnt + 1]){ ++cnt; resarr.push(TEMPL_INC_KW); // '++' }else{ resarr.push(TEMPL_ADD_KW); // '+' } }else if(TEMPL_SUB_KW === target[cnt]){ // '-' if(0 !== one_part.length){ resarr.push(one_part); one_part= ''; } if((cnt + 1) < target.length && TEMPL_SUB_KW === target[cnt + 1]){ ++cnt; resarr.push(TEMPL_DEC_KW); // '--' }else{ resarr.push(TEMPL_SUB_KW); // '-' } }else if(TEMPL_AMP_KW === target[cnt]){ // '&' if(0 !== one_part.length){ resarr.push(one_part); one_part= ''; } if((cnt + 1) < target.length && TEMPL_AMP_KW === target[cnt + 1]){ ++cnt; resarr.push(TEMPL_AND_KW); // '&&' }else{ resarr.push(TEMPL_AMP_KW); // '&' } }else if(TEMPL_VARTBAR_KW === target[cnt]){ // '|' if(0 !== one_part.length){ resarr.push(one_part); one_part= ''; } if((cnt + 1) < target.length && TEMPL_VARTBAR_KW === target[cnt + 1]){ ++cnt; resarr.push(TEMPL_OR_KW); // '||' }else{ resarr.push(TEMPL_VARTBAR_KW); // '|' } }else if(TEMPL_LESS_KW === target[cnt]){ // '<' if(0 !== one_part.length){ resarr.push(one_part); one_part= ''; } if((cnt + 1) < target.length && TEMPL_SET_KW === target[cnt + 1]){ ++cnt; resarr.push(TEMPL_LESSEQ_KW); // '<=' }else if((cnt + 1) < target.length && TEMPL_LESS_KW === target[cnt + 1]){ ++cnt; resarr.push(TEMPL_LSHIFT_KW); // '<<' }else{ resarr.push(TEMPL_LESS_KW); // '<' } }else if(TEMPL_GREAT_KW === target[cnt]){ // '>' if(0 !== one_part.length){ resarr.push(one_part); one_part= ''; } if((cnt + 1) < target.length && TEMPL_SET_KW === target[cnt + 1]){ ++cnt; resarr.push(TEMPL_GREATEQ_KW); // '>=' }else if((cnt + 1) < target.length && TEMPL_GREAT_KW === target[cnt + 1]){ ++cnt; resarr.push(TEMPL_RSHIFT_KW); // '>>' }else{ resarr.push(TEMPL_GREAT_KW); // '>' } }else{ one_part += target[cnt]; } } // rest if(0 !== one_part.length){ resarr.push(one_part); } return resarr; }; //--------------------------------------------------------- // Execute Result Class for K2HR3 Template Class //--------------------------------------------------------- var K2hr3TemplateResult = (function() { var K2hr3TemplateResult = function(result) { if(!(this instanceof K2hr3TemplateResult)){ return new K2hr3TemplateResult(result); } // private variables(do not use directly) this._result = ('boolean' === typeof result) ? result : false; // result of all this._templString = ''; // result string value this._cond = false; // result boolean value this._lastvalue = null; // last value object which is any type value this._isbreak = false; // if break is called, true this._iscontinue = false; // if continue is called, true this._varNameArray = null; // variable name array }; var proto = K2hr3TemplateResult.prototype; // access proto.result = function() { return this._result; }; proto.isSuccess = function() { return this._result; }; proto.isFailure = function() { return !this._result; }; proto.getString = function() { return this._templString; }; proto.getCondition = function() { return this._cond; }; proto.getLastValue = function() { return this._lastvalue; }; proto.getVariableNames = function() { return this._varNameArray; }; proto.isLoopControlFlags = function() { return (this._isbreak || this._iscontinue); }; proto.isBreak = function() { return this._isbreak; }; proto.isContinue = function() { return this._iscontinue; }; // set proto.init = function(result) { this._result = ('boolean' === typeof result) ? result : false; // result of all this._templString = ''; // result string value this._cond = false; // result boolean value this._lastvalue = null; // result value object which is any type value this._isbreak = false; // if break is called, true this._iscontinue = false; // if continue is called, true this._varNameArray = null; // variable name array }; proto.setError = function(errstr) { this._result = false; // result is false this._templString += ' --> ' + apiutil.getSafeString(errstr); this._cond = false; this._lastvalue = apiutil.getSafeString(errstr); }; proto.addTemplString = function(str) { this._templString += apiutil.getSafeString(str); this.setLastString(str); }; proto.mergeTemplString = function(other) { if(!(other instanceof K2hr3TemplateResult)){ return; } this.addTemplString(other._templString); this.setLastString(other._templString); }; proto.setLastString = function(str) { this._cond = apiutil.isSafeString(str); this._lastvalue = apiutil.getSafeString(str); }; proto.setLastValue = function(value) { // check value type if(null === value){ // null ---> condition = false this._cond = false; this._lastvalue = null; }else if('boolean' === typeof value){ // boolean ---> condition = true/false this._cond = value; this._lastvalue = value; }else if(apiutil.isSafeString(value) || '' === value){ // string ---> condition = true/false this._cond = apiutil.isSafeString(value); this.setLastString(value); }else if(!isNaN(value)){ // number ---> condition = true/false this._cond = (0 !== parseInt(value)); this._lastvalue = value; }else{ // unknown ---> condition = false if(value){ this._cond = true; }else{ this._cond = false; } this._lastvalue = value; // unknown value } }; proto.setVariableNames = function() { var args = Array.prototype.slice.call(arguments, 0); if(apiutil.isEmptyArray(args)){ this._varNameArray = null; return; } for(var cnt = 0; cnt < args.length; ){ if(null === args[cnt] || ('boolean' === typeof args[cnt]) || (isNaN(args[cnt]) && !apiutil.isSafeString(args[cnt]))){ // cut not string member args.splice(cnt, 1); }else{ ++cnt; } } if(apiutil.isEmptyArray(args)){ this._varNameArray = null; }else{ this._varNameArray = args; } }; proto.setBreak = function() { this._isbreak = true; }; proto.clearBreak = function() { this._isbreak = false; }; proto.setContinue = function() { this._iscontinue = true; }; proto.clearContinue = function() { this._iscontinue = false; }; proto.clearLoopControlFlags = function() { this.clearBreak(); this.clearContinue(); }; proto.merge = function(other) { if(!(other instanceof K2hr3TemplateResult)){ return; } this._result = (this._result && other.result()); // and this._templString += other.getString(); // adding other this._cond = other.getCondition(); // other this._lastvalue = other.getLastValue(); // other this._isbreak = (this._isbreak || other.isBreak()); // or this._iscontinue = (this._iscontinue || other.isContinue()); // or this._varNameArray = other._varNameArray; // other }; proto.mergeStop = function(other) { if(!(other instanceof K2hr3TemplateResult)){ return; } this._result = (this._result && other.result()); // and this._isbreak = (this._isbreak || other.isBreak()); // or this._iscontinue = (this._iscontinue || other.isContinue()); // or }; proto.insert = function(other) { if(!(other instanceof K2hr3TemplateResult)){ return; } this._result = (this._result && other.result()); // and this._templString = other.getString() + this._templString; // inserting other this._cond = other.getCondition(); // other this._lastvalue = other.getLastValue(); // other this._isbreak = (this._isbreak || other.isBreak()); // or this._iscontinue = (this._iscontinue || other.isContinue()); // or this._varNameArray = other._varNameArray; // other }; proto.insertPrint = function(other) { if(!(other instanceof K2hr3TemplateResult)){ return; } this._result = (this._result && other.result()); // and this._templString = (undefined === other.getLastValue() || null === other.getLastValue() ? '' : other.getLastValue().toString()) + this._templString; // inserting other last value this._cond = other.getCondition(); // other this._lastvalue = other.getLastValue(); // other this._isbreak = (this._isbreak || other.isBreak()); // or this._iscontinue = (this._iscontinue || other.isContinue()); // or this._varNameArray = other._varNameArray; // other }; return K2hr3TemplateResult; })(); //--------------------------------------------------------- // K2HR3 Template Class : Common Variables //--------------------------------------------------------- // // Static Variables // const K2HR3TEMPL_ERROR_MSG_MAXLENGTH = (128 - 3); // 3 is margin "..." // // Class type string // const TEMPLATECLASS_TYPE_BASE = 'template:base'; const STATEMENTCLASS_TYPE_BASE = 'statement:base'; const STATEMENTCLASS_TYPE_STATIC = 'statement:static'; const STATEMENTCLASS_TYPE_COMMENT = 'statement:comment'; const STATEMENTCLASS_TYPE_PRINT = 'statement:print'; const FORMULACLASS_TYPE_BASE = 'formula:base'; const FORMULACLASS_TYPE_VARIABLE = 'formula:variable'; const FORMULACLASS_TYPE_CALCULATE = 'formula:calculate'; const FORMULACLASS_TYPE_CONDITION = 'formula:condition'; const FORMULACLASS_TYPE_SYNTAX = 'formula:syntax'; // // These type is used by isPossibleResultType method parameter. // // [NOTE] // If you get true from isPossibleResultType method with one of following // symbol, but it is only possibility and you get another type result from // execute method at runtime. // const TEMPLATE_RESULT_TYPE_NULL = 0; const TEMPLATE_RESULT_TYPE_BOOLEAN = 1; const TEMPLATE_RESULT_TYPE_NUMBER = 2; const TEMPLATE_RESULT_TYPE_VARIABLE = 3; const TEMPLATE_RESULT_TYPE_STRING = 4; const TEMPLATE_RESULT_TYPE_STRICT_STRING = 5; const TEMPLATE_RESULT_TYPE_NOSTATIC = 6; const TEMPLATE_RESULT_TYPE_ANYVALUE = 7; //--------------------------------------------------------- // K2HR3 Template Class : Common utility - class Inherits //--------------------------------------------------------- var inherits = function(childClass, parentClass) { Object.setPrototypeOf(childClass.prototype, parentClass.prototype); }; //--------------------------------------------------------- // K2HR3 Template Class : Base Class //--------------------------------------------------------- // // Constructor // var BaseTemplateObject = function(type) { if(!(this instanceof BaseTemplateObject)){ return new BaseTemplateObject(type); } this._type = apiutil.isSafeString(type) ? type : TEMPLATECLASS_TYPE_BASE; // type of class this._error = null; // last error string when initializing/assembling this._parentTempl = null; // if not null, this object is included from another object this._nextTempl = null; // if not null, next object after this object is existed, it means the input statement has many statement. this._resobj = new K2hr3TemplateResult(true); // result object for executing }; // // Methods // BaseTemplateObject.prototype.clear = function() { this._error = null; this.clear