3 for (var i = 0; i < n; i++)
8 var CQLModifier = function () {
14 CQLModifier.prototype = {
15 toString: function () {
16 return this.name + this.relation + this.value;
19 toXCQL: function (n) {
20 var s = indent(n+1) + "<modifier>\n";
21 s = s + indent(n+2) + "<name>" + this.name + "</name>\n";
22 if (this.relation != null)
24 + "<relation>" + this.relation + "</relation>\n";
25 if (this.value != null)
27 + "<value>" + this.value +"</value>\n";
28 s = s + indent(n+1) + "</modifier>\n";
33 //we ignore modifier relation symbol, for value-less modifiers
35 var value = this.value.length > 0 ? this.value : "true";
36 var s = '"'+this.name+'": "'+value+'"';
42 var CQLSearchClause = function (field, fielduri, relation, relationuri,
45 this.fielduri = fielduri;
46 this.relation = relation;
47 this.relationuri = relationuri;
48 this.modifiers = modifiers;
52 CQLSearchClause.prototype = {
53 toString: function () {
54 var field = this.field;
55 var relation = this.relation;
56 if (field == 'cql.serverChoice' && relation == 'scr') {
57 //avoid redundant field/relation
61 return (field ? field + ' ' : '') +
62 (relation ? relation : '') +
63 (this.modifiers.length > 0 ? '/' + this.modifiers.join('/') : '') +
64 (relation || this.modifiers.length ? ' ' : '') +
65 '"' + this.term + '"';
68 toXCQL: function (n) {
69 var s = indent(n) + "<searchClause>\n";
70 if (this.fielduri.length > 0)
72 s = s + indent(n+1) + "<prefixes>\n" +
73 indent(n+2) + "<prefix>\n" +
74 indent(n+3) + "<identifier>" + this.fielduri +
76 indent(n+2) + "</prefix>\n" +
77 indent(n+1) + "</prefixes>\n";
79 s = s + indent(n+1) + "<index>" + this.field + "</index>\n";
80 s = s + indent(n+1) + "<relation>\n";
81 if (this.relationuri.length > 0) {
83 "<identifier>" + this.relationuri + "</identifier>\n";
85 s = s + indent(n+2) + "<value>" + this.relation + "</value>\n";
86 if (this.modifiers.length > 0) {
87 s = s + indent(n+2) + "<modifiers>\n";
88 for (var i = 0; i < this.modifiers.length; i++)
89 s = s + this.modifiers[i].toXCQL(n+2);
90 s = s + indent(n+2) + "</modifiers>\n";
92 s = s + indent(n+1) + "</relation>\n";
93 s = s + indent(n+1) + "<term>" + this.term + "</term>\n";
94 s = s + indent(n) + "</searchClause>\n";
99 var s = '{ "term": "'+this.term+'"';
100 if (this.field.length > 0 && this.field != 'cql.serverChoice')
101 s+= ', "field": "'+this.field+'"';
102 if (this.relation.length > 0 && this.relation != 'scr')
103 s+= ', "relation": "'+this._mapRelation(this.relation)+'"';
104 for (var i = 0; i < this.modifiers.length; i++) {
105 //since modifiers are mapped to keys, ignore the reserved ones
106 if (this.modifiers[i].name == "term"
107 ||this.modifiers[i].name == "field"
108 ||this.modifiers[i].name == "relation")
110 s += ', ' + this.modifiers[i].toFQ();
116 _mapRelation: function (rel) {
118 case "<" : return "lt";
119 case ">" : return "gt";
120 case "=" : return "eq";
121 case "<>" : return "ne";
122 case ">=" : return "ge";
123 case "<=" : return "le";
128 _remapRelation: function (rel) {
130 case "lt" : return "<";
131 case "gt" : return ">";
132 case "eq" : return "=";
133 case "ne" : return "<>";
134 case "ge" : return ">=";
135 case "le" : return "<=";
142 var CQLBoolean = function() {
144 this.modifiers = null;
149 CQLBoolean.prototype = {
150 toString: function () {
151 return (this.left.op ? '(' + this.left + ')' : this.left) + ' ' +
152 this.op.toUpperCase() +
153 (this.modifiers.length > 0 ? '/' + this.modifiers.join('/') : '') +
154 ' ' + (this.right.op ? '(' + this.right + ')' : this.right);;
156 toXCQL: function (n) {
157 var s = indent(n) + "<triple>\n";
158 s = s + indent(n+1) + "<boolean>\n" +
159 indent(n+2) + "<value>" + this.op + "</value>\n";
160 if (this.modifiers.length > 0) {
161 s = s + indent(n+2) + "<modifiers>\n";
162 for (var i = 0; i < this.modifiers.length; i++)
163 s = s + this.modifiers[i].toXCQL(n+2);
164 s = s + indent(n+2) + "</modifiers>\n";
166 s = s + indent(n+1) + "</boolean>\n";
167 s = s + indent(n+1) + "<leftOperand>\n" +
168 this.left.toXCQL(n+2) + indent(n+1) + "</leftOperand>\n";
170 s = s + indent(n+1) + "<rightOperand>\n" +
171 this.right.toXCQL(n+2) + indent(n+1) + "</rightOperand>\n";
172 s = s + indent(n) + "</triple>\n";
177 var s = ' { "op": "'+this.op+'"';
178 //proximity modifiers
179 for (var i = 0; i < this.modifiers.length; i++)
180 s += ', ' + this.modifiers[i].toFQ();
181 s += ', "s1": '+this.left.toFQ();
182 s += ', "s2": '+this.right.toFQ();
189 var CQLParser = function () {
196 this.prefixes = new Object();
200 CQLParser.prototype = {
201 parse: function (query) {
203 throw new Error("The query to be parsed cannot be empty");
206 this.ql = this.qs.length;
209 this.tree = this._parseQuery("cql.serverChoice", "scr", new Array());
211 throw new Error("EOF expected");
213 parseFromFQ: function (query) {
215 throw new Error("The query to be parsed cannot be empty");
216 if (typeof query == 'string')
217 query = JSON.parse(query);
218 this.tree = this._parseFromFQ(query);
220 _parseFromFQ: function (fq) {
222 if (fq.hasOwnProperty('op')
223 && fq.hasOwnProperty('s1')
224 && fq.hasOwnProperty('s2')) {
225 var node = new CQLBoolean();
227 node.left = this._parseFromFQ(fq.s1);
228 node.right = this._parseFromFQ(fq.s2);
229 //include all other members as modifiers
231 for (var key in fq) {
232 if (key == 'op' || key == 's1' || key == 's2')
234 var mod = new CQLModifier();
238 node.modifiers.push(mod);
243 if (fq.hasOwnProperty('term')) {
244 var node = new CQLSearchClause();
246 node.field = fq.hasOwnProperty('field')
247 ? fq.field : 'cql.serverChoice';
248 node.relation = fq.hasOwnProperty('relation')
249 ? node._remapRelation(fq.relation) : 'scr';
250 //include all other members as modifiers
251 node.relationuri = '';
254 for (var key in fq) {
255 if (key == 'term' || key == 'field' || key == 'relation')
257 var mod = new CQLModifier();
261 node.modifiers.push(mod);
265 throw new Error('Unknow node type; '+JSON.stringify(fq));
267 toXCQL: function () {
268 return this.tree.toXCQL();
271 return this.tree.toFQ();
273 toString: function () {
274 return this.tree.toString();
276 _parseQuery: function(field, relation, modifiers) {
277 var left = this._parseSearchClause(field, relation, modifiers);
278 while (this.look == "s" && (
279 this.lval == "and" ||
281 this.lval == "not" ||
282 this.lval == "prox")) {
283 var b = new CQLBoolean();
286 b.modifiers = this._parseModifiers();
288 b.right = this._parseSearchClause(field, relation, modifiers);
293 _parseModifiers: function() {
294 var ar = new Array();
295 while (this.look == "/") {
297 if (this.look != "s" && this.look != "q")
298 throw new Error("Invalid modifier.")
300 var name = this.lval;
302 if (this.look.length > 0
303 && this._strchr("<>=", this.look.charAt(0))) {
306 if (this.look != "s" && this.look != "q")
307 throw new Error("Invalid relation within the modifier.");
309 var m = new CQLModifier();
316 var m = new CQLModifier();
325 _parseSearchClause: function(field, relation, modifiers) {
326 if (this.look == "(") {
328 var b = this._parseQuery(field, relation, modifiers);
329 if (this.look == ")")
332 throw new Error("Missing closing parenthesis.");
335 } else if (this.look == "s" || this.look == "q") {
336 var first = this.val; // dont know if field or term yet
338 if (this.look == "q" ||
340 this.lval != "and" &&
342 this.lval != "not" &&
343 this.lval != "prox")) {
344 var rel = this.val; // string relation
346 return this._parseSearchClause(first, rel,
347 this._parseModifiers());
348 } else if (this.look.length > 0
349 && this._strchr("<>=", this.look.charAt(0))) {
350 var rel = this.look; // other relation <, = ,etc
352 return this._parseSearchClause(first, rel,
353 this._parseModifiers());
355 // it's a search term
356 var pos = field.indexOf('.');
359 pre = field.substring(0, pos);
361 var uri = this._lookupPrefix(pre);
363 field = field.substring(pos+1);
365 pos = relation.indexOf('.');
369 pre = relation.substring(0, pos);
371 var reluri = this._lookupPrefix(pre);
372 if (reluri.Length > 0)
373 relation = relation.Substring(pos+1);
375 var sc = new CQLSearchClause(field,
384 } else if (this.look == ">") {
386 if (this.look != "s" && this.look != "q")
387 throw new Error("Expecting string or a quoted expression.");
389 var first = this.lval;
391 if (this.look == "=")
394 if (this.look != "s" && this.look != "q")
395 throw new Error("Expecting string or a quoted expression.");
397 this._addPrefix(first, this.lval);
399 return this._parseQuery(field, relation, modifiers);
401 this._addPrefix("default", first);
402 return this._parseQuery(field, relation, modifiers);
405 throw new Error("Invalid search clause.");
410 while (this.qi < this.ql
411 && this._strchr(" \t\r\n", this.qs.charAt(this.qi)))
413 if (this.qi == this.ql) {
417 var c = this.qs.charAt(this.qi);
418 if (this._strchr("()/", c)) {
421 } else if (this._strchr("<>=", c)) {
424 while (this.qi < this.ql
425 && this._strchr("<>=", this.qs.charAt(this.qi))) {
426 this.look = this.look + this.qs.charAt(this.qi);
429 } else if (this._strchr("\"'", c)) {
434 while (this.qi < this.ql
435 && this.qs.charAt(this.qi) != mark) {
436 if (this.qs.charAt(this.qi) == '\\'
437 && this.qi < this.ql-1)
439 this.val = this.val + this.qs.charAt(this.qi);
442 this.lval = this.val.toLowerCase();
443 if (this.qi < this.ql)
448 while (this.qi < this.ql
449 && !this._strchr("()/<>= \t\r\n", this.qs.charAt(this.qi))) {
450 this.val = this.val + this.qs.charAt(this.qi);
453 this.lval = this.val.toLowerCase();
456 _strchr: function (s, ch) {
457 return s.indexOf(ch) >= 0
459 _lookupPrefix: function(name) {
460 return this.prefixes[name] ? this.prefixes[name] : "";
462 _addPrefix: function(name, value) {
463 //overwrite existing items
464 this.prefixes[name] = value;