/*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;
}