/*cc:CT added CC comment to this file*/ /*cc:er worden 2 soorten parsers geschreven 1 parser om de expertquery te parsen, en 1 parser om de zoektekst van een zoekgroep te parsen in afzonderlijke termen en om taalafhankelijkheid in te bouwen*/ /*cc:in english: the seperate parser are written, 1 parser to for parsing the expert query, and another parser to parse the text of a search group and split it into seperate terms and to support language independency*/ /*cc:CT.2004.01.06 specialized for Framework, only used translation from external to internal, all OJ/S and internal to external code is removed specialized cases for: a near 5 b -> a ~5 b > a -> >= a -> [a;> < a -> <;a> <= a -> <;a] a till b -> [a;b] */ function Resources() { this.r2522 = 'expression'; this.r30000 = 'AND'; this.r30001 = 'OR'; this.r30002 = 'NOT'; this.r30003 = 'NEAROPERATORWILLBEIGNORED'; this.r30006 = 'TILLOPERATORWILLBEIGNORED'; } var resource = new Resources(); function alertQTPError(gname, query, errcode, errinfo) { var code = errcode; // var group = hForm.elements['errorgroup'].value; var errorInGroup = "Error in field '" + gname + "'"; var errorQuery = "\nYour query:\n " + query; var errorMessage = new String(); errorMessage += "\nError message:\n "; var errorTerm = new String(); if (code >= 8 && code <= 11) /*cc:error in operator*/ errorTerm += "\nError in operator:\n "; else errorTerm += "\nError in term:\n "; var errorExample = new String(); errorExample += "\nExample:\n "; var errorInfo = errinfo.split('^'); errorTerm += errorInfo[0]; errorExample += errorInfo[1]; switch (parseInt(code, 10)) { case 1 : case 4 : { errorMessage += "error 1 or 4"; break; } case 2 : case 3 : case 5 : { errorMessage += "error 2,3 or 5"; break; } case 6 : { errorMessage += "Incorrect format"; break; } case 7 : { errorMessage += "error 7"; break; } case 8 : { errorMessage += "Double operator in the expression"; break; } case 9 : case 11: { errorMessage += "Wrong operator in the expression"; break; } case 10 : { errorMessage += "Term expected after operator"; break; } case 12 : { errorMessage += "Operator expected"; break; } case 13 : { errorMessage += "Simultaneous right and left truncation is not permitted."; break; } case 14 : { errorMessage += "Incorrect use of near operator."; break; } case 15 : { errorMessage += "Incorrect bracket structure"; break; } default : { errorMessage += "An unknown error occurred."; errorTerm += '-'; errorExample += '-'; break; } } var errormsg = new String(); errormsg += errorInGroup + errorQuery + errorMessage + errorTerm + errorExample; alert(errormsg); } function allnumbers(inp) { for (var i = 0; i < inp.length; i++) if (inp.charAt(i) >= '0' && inp.charAt(i) <= '9') ; //cc:skip else return false; return true; } function stringReplace(aString, strOrg, strNew) { if (strOrg.length > 0) { var index = aString.indexOf(strOrg); while(index>-1) { var begin = aString.substr(0, index); aString = aString.substr(index + strOrg.length, aString.length - index + strOrg.length); aString = begin + strNew + aString; index = aString.indexOf(strOrg); } } return aString; } function removeChar(input, aChar) /*cc:removes a character char from the string input*/ { var ind = input.indexOf(aChar); while (ind != -1) { var begin = input.substr(0, ind); input = input.substr(ind+1, input.length - ind + 1); input = begin + input; ind = input.indexOf(aChar); } return input; } function removeDelimiters(input, delimiter) /*cc:removes delimiter from the beginning of the string*/ { var ind = 0; while (input.charAt(ind) == delimiter) ind++; if (ind > 0) input = input.substr(ind, input.length - ind); return input; } function addDelimiters(input, delimiter, sz) /*cc:adds leading delimters until size sz is reached*/ { var prefix = new String(); while ((input.length + prefix.length) < sz) { prefix += delimiter; } return (prefix + input); } function QTP_GetInternalRepresentation(query, grcode, translateoperands, allowadjacent, groupname) { this.error = 0; this.errorTerm = null; this.numberOfBrackets = 0; this.currentCode = grcode; this.inputQuery = new String(query); this.parseQuery(allowadjacent); this.translateFromExternalToInternal(translateoperands); this.outputQuery = stringReplace(this.outputQuery, '"', '^'); return "{'" + groupname + "' " + this.outputQuery + "}"; } function QTP_ParseQuery(allowadjacent) { /*cc:initialization of the member variables*/ this.previousToken = new Array('', 0); /*cc:the previous token*/ this.currentToken = new Array('', 0); /*cc:the current token*/ this.lookAheadToken = new Array('', 0); /*cc:the next token*/ this.currentTerms = new Array(); this.currentQuery = new String(); /*cc:end initialisation*/ this.inputQuery = removeDelimiters(this.inputQuery, this.delimiter); if (this.inputQuery == "") return ""; /*cc:no tokens*/ this.storeLookAheadToken(); this.copytokens(false); while (this.currentToken[1] != 0) /*cc:as long as there is input*/ { this.inputQuery = removeDelimiters(this.inputQuery, this.delimiter); this.storeLookAheadToken(); this.processCurrentToken(allowadjacent); /*cc: process token */ if (this.error > 0) return; this.copytokens(true); } if (this.numberOfBrackets > 0) { this.error = 15; this.errorTerm = '(^a and (b or c)'; } } function QTP_CopyTokens(copycurrenttoprevious) { if (copycurrenttoprevious) this.previousToken = new Array(this.currentToken[0], this.currentToken[1]); this.currentToken = new Array(this.lookAheadToken[0], this.lookAheadToken[1]); } function QTP_StoreLookAheadToken() /*cc:reads the first token and stores it in lookAheadToken. This token is stripped from the input furthermore, the precondition is that the delimiters are left stripped from input (using removeDelimiters*/ { if (this.inputQuery == "") { this.lookAheadToken[1] = 0; return; } var index = 0; var first = this.inputQuery.charAt(0); if (first == this.openToken) { this.lookAheadToken[0] = this.openToken; this.lookAheadToken[1] = 2; index++; } else if (first == this.closeToken) { this.lookAheadToken[0] = this.closeToken; this.lookAheadToken[1] = 3; index++; } else if (first == this.quoteToken) { index++; while ((this.inputQuery.charAt(index) != this.quoteToken || (this.inputQuery.charAt(index) == this.quoteToken && index > 0 && this.inputQuery.charAt(index-1) == this.escape)) && index < this.inputQuery.length) index++; if (index < this.inputQuery.length && this.inputQuery.charAt(index) == this.quoteToken) index++; this.lookAheadToken[0] = this.inputQuery.substr(0, index); this.lookAheadToken[1] = 1; } else if (first == '<' || first == '>') /*cc:special case for numerical operators <, <=, >, >=, << */ { ++index; var lowerCase = first; if (index < this.inputQuery.length) { second = this.inputQuery.charAt(index); if (second == '=' || (first == '<' && second == '<')) { ++index; lowerCase += second; } } this.lookAheadToken[0] = this.inputQuery.substr(0, index); if (lowerCase == this.operatorRange.toLowerCase()) this.lookAheadToken[1] = 9; else if (lowerCase == this.operatorSmall.toLowerCase()) this.lookAheadToken[1] = 10; else if (lowerCase == this.operatorSmallEqual.toLowerCase()) this.lookAheadToken[1] = 11 else if (lowerCase == this.operatorGreat.toLowerCase()) this.lookAheadToken[1] = 12; else if (lowerCase == this.operatorGreatEqual.toLowerCase()) this.lookAheadToken[1] = 13; } else { while (this.inputQuery.charAt(index) != this.openToken && this.inputQuery.charAt(index) != this.closeToken && this.inputQuery.charAt(index) != this.delimiter && this.inputQuery.charAt(index) != '<' && this.inputQuery.charAt(index) != '>' && index < this.inputQuery.length) index++; this.lookAheadToken[0] = this.inputQuery.substr(0, index); var lowerCase = this.lookAheadToken[0].toLowerCase(); if (lowerCase == this.operatorNot.toLowerCase()) this.lookAheadToken[1] = 4; else if (lowerCase == this.operatorAnd.toLowerCase()) this.lookAheadToken[1] = 5; else if (lowerCase == this.operatorOr.toLowerCase()) this.lookAheadToken[1] = 6; else if (lowerCase == this.operatorNear.toLowerCase()) this.lookAheadToken[1] = 8; else if (lowerCase == this.operatorRange.toLowerCase()) this.lookAheadToken[1] = 9; else this.lookAheadToken[1] = 1; } this.inputQuery = this.inputQuery.substr(index, this.inputQuery.length-index); } function QTP_ProcessCurrentToken(allowadjacent) { if (this.currentToken[1] == 1) /*cc:data*/ { /*cc:CT.2004.01.07 if the next operator is a range the insert a [ before the term */ var nextisrange = this.lookAheadToken[1] == 9; if (this.previousToken[1] == 1 || this.previousToken[1] == 9) //cc: CT.2004.01.07 uf previous is range then treat as adjacent { if (allowadjacent) { this.currentTerms[this.currentTerms.length-1] += this.delimiter + this.currentToken[0]; if (nextisrange) //cc:CT.2004.01.07 this.currentTerms[this.currentTerms.length-1] = "[" + this.currentTerms[this.currentTerms.length-1]; } else { this.error = 12; this.errorTerm = this.currentToken[0] + '^' + this.previousToken[0] + ' ' + this.operatorAnd + ' ' + this.currentToken[0]; } } else { this.currentTerms.length += 1; this.currentTerms[this.currentTerms.length-1] = this.currentToken[0]; if (nextisrange) //cc:CT.2004.01.07 this.currentTerms[this.currentTerms.length-1] = "[" + this.currentTerms[this.currentTerms.length-1]; if (this.currentQuery.length > 0 && this.previousToken[1] != 2) this.currentQuery += ' '; this.currentQuery += '@data' + new String(this.currentTerms.length-1) + '@'; } } else if (this.currentToken[1] == 2) /*cc:opentoken*/ { if (this.previousToken[1] == 1) { this.error = 6; this.errorTerm = '(^a AND (b or c)'; } if (this.currentQuery.length > 0) this.currentQuery += ' '; this.currentQuery += this.currentToken[0]; ++this.numberOfBrackets; } else if (this.currentToken[1] == 3) /*cc:closetoken*/ { this.currentQuery += this.currentToken[0]; --this.numberOfBrackets; if (this.numberOfBrackets < 0) { this.error = 15; this.errorTerm = ')^a and (b or c)'; } } else if (this.currentToken[1] == 4 || (this.currentToken[1] >= 10 && this.currentToken[1] <= 13)) /*cc:unary operators*/ { if (this.previousToken[1] == 1 || this.previousToken[1] == 3) /*cc:when previous token is close or a term then an error occurred*/ { this.error = 9; this.errorTerm = this.currentToken[0] + '^' + this.getPreviousExpression() + ' ' + this.operatorAnd + ' ' + this.currentToken[0] + ' ' + this.getLookAheadExpression(); } if (this.lookAheadToken[1] == 0) { this.error = 10; this.errorTerm = this.currentToken[0] + '^' + this.currentToken[0] + ' ' + resource.r2522; } /*cc: CT.2004.01.07 if a numerical operator (type 10 to 13) then rewrite the data of the lookaheadtoken according to the scheme > a -> >= a -> [a;> < a -> <;a> <= a -> <;a] */ if (this.currentQuery.length > 0) this.currentQuery += ' '; switch (this.currentToken[1]) { case 4 : {this.currentQuery += '@operNot@'; break;} case 10 : {this.lookAheadToken[0] = "<;" + this.lookAheadToken[0] + ">"; break;} case 11 : {this.lookAheadToken[0] = "<;" + this.lookAheadToken[0] + "]"; break;} case 12 : {this.lookAheadToken[0] = "<" + this.lookAheadToken[0] + ";>"; break;} case 13 : {this.lookAheadToken[0] = "[" + this.lookAheadToken[0] + ";>"; break;} } } else if (this.currentToken[1] == 5 || this.currentToken[1] == 6 || this.currentToken[1] == 9) /*cc:binary operators*/ { if (this.previousToken[1] == 0) { this.error = 11; this.errorTerm = this.currentToken[0] + '^' + resource.r2522 + ' ' + this.currentToken[0] + ' ' + this.getLookAheadExpression(); } else if (this.previousToken[1] != 1 && this.previousToken[1] != 3)/*cc:when previous token is not close and not a term then an error occurred*/ { this.error = 8; this.errorTerm = this.currentToken[0] + '^' + resource.r2522 + ' ' + this.currentToken[0] + ' ' + this.getLookAheadExpression(); } else if (this.lookAheadToken[1] == 0 || this.lookAheadToken[1] == 3) { this.error = 10; this.errorTerm = this.currentToken[0] + '^' + this.getPreviousExpression() + ' ' + this.currentToken[0] + ' ' + resource.r2522; } if (this.currentQuery.length > 0) this.currentQuery += ' '; switch (this.currentToken[1]) { case 5 : {this.currentQuery += '@operAnd@'; break;} case 6 : {this.currentQuery += '@operOr@'; break;} case 8 : {this.currentQuery += '@operNear@'; break;} case 9 : {this.lookAheadToken[0] = ";" + this.lookAheadToken[0] + "]"; break;} } } else if (this.currentToken[1] == 8) //CT.2004.01.06 NEAR operator should be something like a NEAR 5 b { //first the lookahead token must be all numbers if (this.lookAheadToken[1] != 1 || !allnumbers(this.lookAheadToken[0])) { this.error = 14; this.errorTerm = 'a near 5 b'; } //store tokens var previousToken = this.previousToken; var currentToken = this.currentToken; var number = this.lookAheadToken[0]; this.storeLookAheadToken(); this.previousToken = previousToken; this.currentToken = currentToken; if (this.previousToken[1] == 0) { this.error = 11; this.errorTerm = this.currentToken[0] + '^' + resource.r2522 + ' ' + this.currentToken[0] + ' ' + this.getLookAheadExpression(); } else if (this.previousToken[1] != 1 && this.previousToken[1] != 3)/*cc:when previous token is not close and not a term then an error occurred*/ { this.error = 8; this.errorTerm = this.currentToken[0] + '^' + resource.r2522 + ' ' + this.currentToken[0] + ' ' + this.getLookAheadExpression(); } else if (this.lookAheadToken[1] == 0) { this.error = 10; this.errorTerm = this.currentToken[0] + '^' + this.getPreviousExpression() + ' ' + this.currentToken[0] + ' ' + resource.r2522; } if (this.currentQuery.length > 0) this.currentQuery += ' '; this.currentQuery += '@operNear@' + number; } } function isalnum(a) { if (isdigit(a) || (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z')) return true; return false; } function isdigit(a) { if (a >= '0' && a <= '9') return true; return false; } /*cc:this function checks whether the input is exactly maxlength chars or shorter using wildcards, onlydigits implies whether the term should be digits only*/ function QTP_CheckTranslationLength(trm, maxlength, onlydigits, maybesmaller) { var term = new String(trm); var cp = 0; var nr_digits = 0; var star_found = false; while (cp != term.length) { if ((!isalnum(term.charAt(cp)) || (onlydigits && !isdigit(term.charAt(cp)))) && term.charAt(cp) != '*' && term.charAt(cp) != '?') return false; if (term.charAt(cp) == '*') star_found = true; else if (++nr_digits > maxlength) return false; ++cp; } if (nr_digits < maxlength && !star_found && !maybesmaller) return false; return true; } function QTP_TranslateFromExternalToInternal(translateoper) { this.outputQuery = new String(this.currentQuery); //alert(this.outputQuery); for (var i = 0; i < this.currentTerms.length; i++) { var tterm = this.translateTermFromExternalToInternal(this.currentTerms[i]); this.outputQuery = stringReplace(this.outputQuery, '@data' + new String(i) + '@', tterm); } if (translateoper) { this.outputQuery = stringReplace(this.outputQuery, '@operNot@', '!'); this.outputQuery = stringReplace(this.outputQuery, '@operAnd@', '&'); this.outputQuery = stringReplace(this.outputQuery, '@operOr@', '|'); this.outputQuery = stringReplace(this.outputQuery, '@operNear@', '~'); this.outputQuery = stringReplace(this.outputQuery, '@operRange@', this.operatorRange); this.outputQuery = stringReplace(this.outputQuery, '@operSmall@', this.operatorSmall); this.outputQuery = stringReplace(this.outputQuery, '@operSmallEqual@', this.operatorSmallEqual); this.outputQuery = stringReplace(this.outputQuery, '@operGreat@', this.operatorGreat); this.outputQuery = stringReplace(this.outputQuery, '@operGreatEqual@', this.operatorGreatEqual); } } /*cc: checks should only be done from external to internal (e.g. for CY an OL) */ function QTP_TranslateTermFromExternalToInternal(termin) { //cc:first check for left and right truncation: /* NE.2005.11.24 disable this check if ((termin.charAt(0) == '?' || termin.charAt(0) == '*') && (termin.charAt(termin.length-1) == '?' || termin.charAt(termin.length-1) == '*')) { this.error = 13; this.errorTerm = termin + '^' + termin; return(termin); //cc:error }*/ termin = stringReplace(termin, "'", "@quot@"); termin = stringReplace(termin, "@quot@", "\\'"); //CT.2004.01.06 return ("'" + termin + "'"); //CT.2004.01.06 } function QTP_GetPreviousExpression() { if (this.previousToken && this.previousToken[1] && this.previousToken[1] == 1) return this.previousToken[0]; else return resource.r2522; } function QTP_GetLookAheadExpression() { if (this.lookAheadToken && this.lookAheadToken[1] && this.lookAheadToken[1] == 1) return this.lookAheadToken[0]; else return resource.r2522; } function QueryTermParser() //cc:parses the contents of the search groups { this.getInternalRepresentation = QTP_GetInternalRepresentation; //this.getExternalRepresentation = QTP_GetExternalRepresentation; this.parseQuery = QTP_ParseQuery; this.copytokens = QTP_CopyTokens; this.storeLookAheadToken = QTP_StoreLookAheadToken; this.processCurrentToken = QTP_ProcessCurrentToken; this.checkTranslationLength = QTP_CheckTranslationLength; this.translateFromExternalToInternal = QTP_TranslateFromExternalToInternal; this.translateTermFromExternalToInternal = QTP_TranslateTermFromExternalToInternal; //this.translateArrayTermFromExternalToInternal = QTP_TranslateArrayTermFromExternalToInternal; //this.translateFromInternalToExternal = QTP_TranslateFromInternalToExternal; //this.translateTermFromInternalToExternal = QTP_TranslateTermFromInternalToExternal; //this.translateArrayTermFromInternalToExternal = QTP_TranslateArrayTermFromInternalToExternal; this.getPreviousExpression = QTP_GetPreviousExpression; this.getLookAheadExpression = QTP_GetLookAheadExpression; //this.max_OJ = '260'; /*cc:the maximum OJ number, value as agreed upon by Markus Welsch 20011030*/ //this.min_year = '1993' /*cc:the minimum OJ year, value as agreed upon by Markus Welsch 20011030*/ //this.max_year = '2010' /*cc:the maximum OJ year, value as agreed upon by Markus Welsch 20011030*/ this.quoteToken = '"'; /*cc:the character for creating a tring*/ this.openToken = '('; /*cc:opentoken*/ this.closeToken = ')';/*cc:closetoken*/ this.delimiter = ' '; /*cc:delimiter*/ this.escape = '\\'; /*cc:the escape character*/ this.numberOfBrackets; /*cc: the number of opened brackets*/ this.operatorNot = resource.r30002; /*cc:the not operator*/ this.operatorAnd = resource.r30000; /*cc:the and operator*/ this.operatorOr = resource.r30001; /*cc:the or operator*/ this.operatorNear= resource.r30003; /*cc:the near operator*/ this.operatorRange = resource.r30006; /*cc:the till operator*/ this.operatorSmall = '<'; this.operatorSmallEqual = '<='; this.operatorGreat = '>'; this.operatorGreatEqual = '>='; this.currentTerms; /*cc:an array with all terms*/ this.currentQuery; /*cc:the parsed query*/ this.currentCode; /*cc:the current code of the search group*/ this.inputQuery; /*cc:the (temporary) input query*/ this.translatedTerms; this.outputQuery; this.previousToken; /*cc:previous token*/ this.currentToken; /*cc:current token*/ this.lookAheadToken; /*cc:the next token*/ /*cc:tokencode's 0 = empty, 1 = data, 2 = opentoken, 3 = closetoken, 4 = operatorNot, 5 = operatorAnd, 6 = operatorOr, 7 = opendata (quote), 8 = operatorNear, 9 = operatorRange, 10 = operatorSmall, 11 = operatorSmallEqual 12 = operatorGreat, 13 = operatorGreatEqual*/ /*cc:error codes: 0 = no error 1 = incorrect issue number 2 = issue year is too early 3 = issue year is too high 4 = expected issue number contains alphanumeric chars 5 = expected year contains alphanumeric chars 6 = incorrect format 7 = wrong code 8 = geen term voor and/or operator 9 = wrong operator in the expression, e.g. incorrect use of unary operator 10= term expected after operator 11= wrong operator in the expression, e.g. incorrect use of binary operator 12= operator expected 13 = left and right truncation is nt supported 14 = incorrect use of near operator 15 = incorrect bracket structure */ this.error = 0; this.errorTerm = null; }