function indent(n) {
var s = "";
for (var i = 0; i < n; i++)
s = s + " ";
return s;
}
// CQLModifier
var CQLModifier = function () {
this.name = null;
this.relation = null;
this.value = null;
}
CQLModifier.prototype = {
toXCQL: function (n) {
var s = indent(n+1) + "\n";
s = s + indent(n+2) + "" + this.name + "\n";
if (this.relation != null)
s = s + indent(n+2)
+ "" + this.relation + "\n";
if (this.value != null)
s = s + indent(n+2)
+ "" + this.value +"\n";
s = s + indent(n+1) + "\n";
return s;
}
}
// CQLSearchClause
var CQLSearchClause = function (field, fielduri, relation, relationuri,
modifiers, term) {
this.field = field;
this.fielduri = fielduri;
this.relation = relation;
this.relationuri = relationuri;
this.modifiers = modifiers;
this.term = term;
}
CQLSearchClause.prototype = {
toXCQL: function (n) {
var s = indent(n) + "\n";
if (this.fielduri.length > 0)
{
s = s + indent(n+1) + "\n" +
indent(n+2) + "\n" +
indent(n+3) + "" + this.fielduri +
"\n" +
indent(n+2) + "\n" +
indent(n+1) + "\n";
}
s = s + indent(n+1) + "" + this.field + "\n";
s = s + indent(n+1) + "\n";
if (this.relationuri.length > 0) {
s = s + indent(n+2) +
"" + this.relationuri + "\n";
}
s = s + indent(n+2) + "" + this.relation + "\n";
if (this.modifiers.length > 0) {
s = s + indent(n+2) + "\n";
for (var i = 0; i < this.modifiers.length; i++)
s = s + this.modifiers[i].toXCQL(n+2);
s = s + indent(n+2) + "\n";
}
s = s + indent(n+1) + "\n";
s = s + indent(n+1) + "" + this.term + "\n";
s = s + indent(n) + "\n";
return s;
}
}
// CQLBoolean
var CQLBoolean = function() {
this.op = null;
this.modifiers = null;
this.left = null;
this.right = null;
}
CQLBoolean.prototype = {
toXCQL: function (n) {
var s = indent(n) + "\n";
s = s + indent(n+1) + "\n" +
indent(n+2) + "" + this.op + "\n";
if (this.modifiers.length > 0) {
s = s + indent(n+2) + "\n";
for (var i = 0; i < this.modifiers.length; i++)
s = s + this.modifiers[i].toXCQL(n+2);
s = s + indent(n+2) + "\n";
}
s = s + indent(n+1) + "\n";
s = s + indent(n+1) + "\n" +
this.left.toXCQL(n+2) + indent(n+1) + "\n";
s = s + indent(n+1) + "\n" +
this.right.toXCQL(n+2) + indent(n+1) + "\n";
s = s + indent(n) + "\n";
return s;
}
}
// CQLParser
var CQLParser = function () {
this.qi = null;
this.ql = null;
this.qs = null;
this.look = null;
this.lval = null;
this.val = null;
this.prefixes = new Object();
this.tree = null;
}
CQLParser.prototype = {
parse: function (query) {
if (!query)
throw new Error("The query to be parsed cannot be empty");
this.qs = query;
this.ql = this.qs.length;
this.qi = 0;
this._move();
this.tree = this._parseQuery("cql.serverChoice", "scr", new Array());
if (this.look != "")
throw new Error("EOF expected");
},
toXCQL: function () {
return this.tree.toXCQL();
},
_parseQuery: function(field, relation, modifiers) {
var left = this._parseSearchClause(field, relation, modifiers);
while (this.look == "s" && (
this.lval == "and" ||
this.lval == "or" ||
this.lval == "not" ||
this.lval == "prox")) {
var b = new CQLBoolean();
b.op = this.lval;
this._move();
b.modifiers = this._parseModifiers();
b.left = left;
b.right = this._parseSearchClause(field, relation, modifiers);
left = b;
}
return left;
},
_parseModifiers: function() {
var ar = new Array();
while (this.look == "/") {
this._move();
if (this.look != "s" && this.look != "q")
throw new Error("Invalid modifier.")
var name = this.lval;
this._move();
if (this.look.length > 0
&& this._strchr("<>=", this.look.charAt(0))) {
var rel = this.look;
this._move();
if (this.look != "s" && this.look != "q")
throw new Error("Invalid relation within the modifier.");
var m = new CQLModifier();
m.name = name;
m.relation = rel;
m.value = this.val;
ar.push(m);
this._move();
} else {
var m = new CQLModifier();
m.name = name;
m.relation = "";
m.value = "";
ar.push(m);
}
}
return ar;
},
_parseSearchClause: function(field, relation, modifiers) {
if (this.look == "(") {
this._move();
var b = this._parseQuery(field, relation, modifiers);
if (this.look == ")")
this._move();
else
throw new Error("Missing closing parenthesis.");
return b;
} else if (this.look == "s" || this.look == "q") {
var first = this.val; // dont know if field or term yet
this._move();
if (this.look == "q" ||
(this.look == "s" &&
this.lval != "and" &&
this.lval != "or" &&
this.lval != "not" &&
this.lval != "prox")) {
var rel = this.val; // string relation
this._move();
return this._parseSearchClause(first, rel,
this._parseModifiers());
} else if (this.look.length > 0
&& this._strchr("<>=", this.look.charAt(0))) {
var rel = this.look; // other relation <, = ,etc
this._move();
return this._parseSearchClause(first, rel,
this._parseModifiers());
} else {
// it's a search term
var pos = field.indexOf('.');
var pre = "";
if (pos != -1)
pre = field.substring(0, pos);
var uri = this._lookupPrefix(pre);
if (uri.length > 0)
field = field.substring(pos+1);
pos = relation.indexOf('.');
if (pos == -1)
pre = "cql";
else
pre = relation.substring(0, pos);
var reluri = this._lookupPrefix(pre);
if (reluri.Length > 0)
relation = relation.Substring(pos+1);
var sc = new CQLSearchClause(field,
uri,
relation,
reluri,
modifiers,
first);
return sc;
}
// prefixes
} else if (this.look == ">") {
this._move();
if (this.look != "s" && this.look != "q")
throw new Error("Expecting string or a quoted expression.");
var first = this.lval;
this._move();
if (this.look == "=")
{
this._move();
if (this.look != "s" && this.look != "q")
throw new Error("Expecting string or a quoted expression.");
this._addPrefix(first, this.lval);
this._move();
return this._parseQuery(field, relation, modifiers);
} else {
this._addPrefix("default", first);
return this._parseQuery(field, relation, modifiers);
}
} else {
throw new Error("Invalid search clause.");
}
},
_move: function () {
while (this.qi < this.ql
&& this._strchr(" \t\r\n", this.qs.charAt(this.qi)))
this.qi++;
if (this.qi == this.ql) {
this.look = "";
return;
}
var c = this.qs.charAt(this.qi);
if (this._strchr("()/", c)) {
this.look = c;
this.qi++;
} else if (this._strchr("<>=", c)) {
this.look = c;
this.qi++;
while (this.qi < this.ql
&& this._strchr("<>=", this.qs.charAt(this.qi))) {
this.look = this.look + this.qs.charAt(this.qi);
this.qi++;
}
} else if (this._strchr("\"'", c)) {
this.look = "q";
var mark = c;
this.qi++;
this.val = "";
while (this.qi < this.ql
&& this.qs.charAt(this.qi) != mark) {
if (this.qs.charAt(this.qi) == '\\'
&& this.qi < this.ql-1)
this.qi++;
this.val = this.val + this.qs.charAt(this.qi);
this.qi++;
}
this.lval = this.val.toLowerCase();
if (this.qi < this.ql)
this.qi++;
} else {
this.look = "s";
this.val = "";
while (this.qi < this.ql
&& !this._strchr("()/<>= \t\r\n", this.qs.charAt(this.qi))) {
this.val = this.val + this.qs.charAt(this.qi);
this.qi++;
}
this.lval = this.val.toLowerCase();
}
},
_strchr: function (s, ch) {
return s.indexOf(ch) >= 0
},
_lookupPrefix: function(name) {
return this.prefixes[name] ? this.prefixes[name] : "";
},
_addPrefix: function(name, value) {
//overwrite existing items
this.prefixes[name] = value;
}
}