source: Dev/trunk/src/client/dojox/data/AndOrReadStore.js @ 532

Last change on this file since 532 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

  • Property svn:executable set to *
File size: 10.4 KB
Line 
1define(["dojo/_base/declare", "dojo/_base/lang", "dojo/data/ItemFileReadStore", "dojo/data/util/filter", "dojo/_base/array", "dojo/_base/json"],
2  function(declare, lang, ItemFileReadStore, filterUtil, array, json) {
3 
4// module:
5//              dojox/data/AndOrReadStore
6// summary:
7//              TODOC
8
9return declare("dojox.data.AndOrReadStore", [ItemFileReadStore], {
10        // summary:
11        //              AndOrReadStore uses ItemFileReadStore as a base, modifying only the query (_fetchItems) section.
12        //              Supports queries of the form: query:"id:1* OR dept:'Sales Department' || (id:2* && NOT dept:S*)"
13        //              Includes legacy/widget support via:
14        // |            query:{complexQuery:"id:1* OR dept:'Sales Department' || (id:2* && NOT dept:S*)"}
15        //              The ItemFileReadStore implements the dojo/data/api/Read API and reads
16        //              data from JSON files that have contents in this format --
17        // |    { items: [
18        // |            { name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]},
19        // |            { name:'Fozzie Bear', wears:['hat', 'tie']},
20        // |            { name:'Miss Piggy', pets:'Foo-Foo'}
21        // |    ]}
22        //              Note that it can also contain an 'identifier' property that specified which attribute on the items
23        //              in the array of items that acts as the unique identifier for that item.
24
25        _containsValue: function(/*dojo/data/api/Item*/ item, /*attribute-name-string */ attribute, /*anything*/ value,
26                        /*String|RegExp?*/ regexp){
27                // summary:
28                //              Internal function for looking at the values contained by the item.
29                // description:
30                //              Internal function for looking at the values contained by the item.  This
31                //              function allows for denoting if the comparison should be case sensitive for
32                //              strings or not (for handling filtering cases where string case should not matter)
33                // item:
34                //              The data item to examine for attribute values.
35                // attribute:
36                //              The attribute to inspect.
37                // value:
38                //              The value to match.
39                // regexp:
40                //              Optional string or regular expression generated off value if value was of string type to handle wildcarding.
41                //              If present and attribute values are string, then it can be used for comparison instead of 'value'
42                //              If RegExp is a string, it is treated as an comparison statement and eval for number comparisons
43                return array.some(this.getValues(item, attribute), function(possibleValue){
44                        // if string... eval for math operator comparisons
45                        if(lang.isString(regexp)){
46                                return eval(regexp);
47                        }else if(possibleValue !== null && !lang.isObject(possibleValue) && regexp){
48                                if(possibleValue.toString().match(regexp)){
49                                        return true; // Boolean
50                                }
51                        } else if(value === possibleValue){
52                                return true; // Boolean
53                        } else {
54                                return false;
55                        }
56                });
57        },
58
59        filter: function(requestArgs, arrayOfItems, findCallback){
60                var items = [];
61                if(requestArgs.query){
62                        //Complete copy, we may have to mess with it.
63                        //Safer than clone, which does a shallow copy, I believe.
64                        var query = json.fromJson(json.toJson(requestArgs.query));
65                        //Okay, object form query, we have to check to see if someone mixed query methods (such as using FilteringSelect
66                        //with a complexQuery).  In that case, the params need to be anded to the complex query statement.
67                        //See defect #7980
68                        if(typeof query == "object" ){
69                                var count = 0;
70                                var p;
71                                for(p in query){
72                                        count++;
73                                }
74                                if(count > 1 && query.complexQuery){
75                                        var cq = query.complexQuery;
76                                        var wrapped = false;
77                                        for(p in query){
78                                                if(p !== "complexQuery"){
79                                                        //We should wrap this in () as it should and with the entire complex query
80                                                        //Not just part of it.
81                                                        if(!wrapped){
82                                                                cq = "( " + cq + " )";
83                                                                wrapped = true;
84                                                        }
85                                                        //Make sure strings are quoted when going into complexQuery merge.
86                                                        var v = requestArgs.query[p];
87                                                        if(lang.isString(v)){
88                                                                v = "'" + v + "'";
89                                                        }
90                                                        cq += " AND " + p + ":" + v;
91                                                        delete query[p];
92
93                                                }
94                                        }
95                                        query.complexQuery = cq;
96                                }
97                        }
98
99                        var ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
100                        //for complex queries only:  pattern = query[:|=]"NOT id:23* AND (type:'test*' OR dept:'bob') && !filed:true"
101                        //logical operators are case insensitive:  , NOT AND OR ( ) ! && ||  // "," included for quoted/string legacy queries.
102                        if(typeof query != "string"){
103                                query = json.toJson(query);
104                                query = query.replace(/\\\\/g,"\\"); //counter toJson expansion of backslashes, e.g., foo\\*bar test.
105                        }
106                        query = query.replace(/\\"/g,"\"");   //ditto, for embedded \" in lieu of " availability.
107                        var complexQuery = lang.trim(query.replace(/{|}/g,"")); //we can handle these, too.
108                        var pos2, i;
109                        if(complexQuery.match(/"? *complexQuery *"?:/)){ //case where widget required a json object, so use complexQuery:'the real query'
110                                complexQuery = lang.trim(complexQuery.replace(/"?\s*complexQuery\s*"?:/,""));
111                                var quotes = ["'",'"'];
112                                var pos1,colon;
113                                var flag = false;
114                                for(i = 0; i<quotes.length; i++){
115                                        pos1 = complexQuery.indexOf(quotes[i]);
116                                        pos2 = complexQuery.indexOf(quotes[i],1);
117                                        colon = complexQuery.indexOf(":",1);
118                                        if(pos1 === 0 && pos2 != -1 && colon < pos2){
119                                                flag = true;
120                                                break;
121                                        } //first two sets of quotes don't occur before the first colon.
122                                }
123                                if(flag){       //dojo.toJson, and maybe user, adds surrounding quotes, which we need to remove.
124                                        complexQuery = complexQuery.replace(/^\"|^\'|\"$|\'$/g,"");
125                                }
126                        } //end query="{complexQuery:'id:1* || dept:Sales'}" parsing (for when widget required json object query).
127                        var complexQuerySave = complexQuery;
128                        //valid logical operators.
129                        var begRegExp = /^>=|^<=|^<|^>|^,|^NOT |^AND |^OR |^\(|^\)|^!|^&&|^\|\|/i; //trailing space on some tokens on purpose.
130                        var sQuery = ""; //will be eval'ed for each i-th candidateItem, based on query components.
131                        var op = "";
132                        var val = "";
133                        var pos = -1;
134                        var err = false;
135                        var key = "";
136                        var value = "";
137                        var tok = "";
138                        pos2 = -1;
139                        for(i = 0; i < arrayOfItems.length; ++i){
140                                var match = true;
141                                var candidateItem = arrayOfItems[i];
142                                if(candidateItem === null){
143                                        match = false;
144                                }else{
145                                        //process entire string for this i-th candidateItem.
146                                        complexQuery = complexQuerySave; //restore query for next candidateItem.
147                                        sQuery = "";
148                                        //work left to right, finding either key:value pair or logical operator at the beginning of the complexQuery string.
149                                        //when found, concatenate to sQuery and remove from complexQuery and loop back.
150                                        while(complexQuery.length > 0 && !err){
151                                                op = complexQuery.match(begRegExp);
152
153                                                //get/process/append one or two leading logical operators.
154                                                while(op && !err){ //look for leading logical operators.
155                                                        complexQuery = lang.trim(complexQuery.replace(op[0],""));
156                                                        op = lang.trim(op[0]).toUpperCase();
157                                                        //convert some logical operators to their javascript equivalents for later eval.
158                                                        op = op == "NOT" ? "!" : op == "AND" || op == "," ? "&&" : op == "OR" ? "||" : op;
159                                                        op = " " + op + " ";
160                                                        sQuery += op;
161                                                        op = complexQuery.match(begRegExp);
162                                                }//end op && !err
163
164                                                //now get/process/append one key:value pair.
165                                                if(complexQuery.length > 0){
166                                                        var opsRegex = /:|>=|<=|>|</g,
167                                                                matches = complexQuery.match(opsRegex),
168                                                                match = matches && matches.shift(),
169                                                                regex;
170
171                                                        pos = complexQuery.indexOf(match);
172                                                        if(pos == -1){
173                                                                err = true;
174                                                                break;
175                                                        }else{
176                                                                key = lang.trim(complexQuery.substring(0,pos).replace(/\"|\'/g,""));
177                                                                complexQuery = lang.trim(complexQuery.substring(pos + match.length));
178                                                                tok = complexQuery.match(/^\'|^\"/);    //quoted?
179                                                                if(tok){
180                                                                        tok = tok[0];
181                                                                        pos = complexQuery.indexOf(tok);
182                                                                        pos2 = complexQuery.indexOf(tok,pos + 1);
183                                                                        if(pos2 == -1){
184                                                                                err = true;
185                                                                                break;
186                                                                        }
187                                                                        value = complexQuery.substring(pos + match.length,pos2);
188                                                                        if(pos2 == complexQuery.length - 1){ //quote is last character
189                                                                                complexQuery = "";
190                                                                        }else{
191                                                                                complexQuery = lang.trim(complexQuery.substring(pos2 + 1));
192                                                                        }
193                                                                        if (match != ':') {
194                                                                                regex = this.getValue(candidateItem, key) + match + value;
195                                                                        } else {
196                                                                                regex = filterUtil.patternToRegExp(value, ignoreCase);
197                                                                        }
198                                                                        sQuery += this._containsValue(candidateItem, key, value, regex);
199                                                                }
200                                                                else{ //not quoted, so a space, comma, or closing parens (or the end) will be the break.
201                                                                        tok = complexQuery.match(/\s|\)|,/);
202                                                                        if(tok){
203                                                                                var pos3 = new Array(tok.length);
204                                                                                for(var j = 0;j<tok.length;j++){
205                                                                                        pos3[j] = complexQuery.indexOf(tok[j]);
206                                                                                }
207                                                                                pos = pos3[0];
208                                                                                if(pos3.length > 1){
209                                                                                        for(var j=1;j<pos3.length;j++){
210                                                                                                pos = Math.min(pos,pos3[j]);
211                                                                                        }
212                                                                                }
213                                                                                value = lang.trim(complexQuery.substring(0,pos));
214                                                                                complexQuery = lang.trim(complexQuery.substring(pos));
215                                                                        }else{ //not a space, so must be at the end of the complexQuery.
216                                                                                value = lang.trim(complexQuery);
217                                                                                complexQuery = "";
218                                                                        } //end  inner if(tok) else
219                                                                        if (match != ':') {
220                                                                                regex = this.getValue(candidateItem, key) + match + value;
221                                                                        } else {
222                                                                                regex = filterUtil.patternToRegExp(value, ignoreCase);
223                                                                                console.log("regex value: ", value, " regex pattern: ", regex);
224                                                                        }
225                                                                        sQuery += this._containsValue(candidateItem, key, value, regex);
226                                                                } //end outer if(tok) else
227                                                        } //end found ":"
228                                                } //end if(complexQuery.length > 0)
229                                        } //end while complexQuery.length > 0 && !err, so finished the i-th item.
230                                        match = eval(sQuery);
231                                } //end else is non-null candidateItem.
232                                if(match){
233                                        items.push(candidateItem);
234                                }
235                        } //end for/next of all items.
236                        if(err){
237                                //soft fail.
238                                items = [];
239                                console.log("The store's _fetchItems failed, probably due to a syntax error in query.");
240                        }
241                }else{
242                        // No query...
243                        // We want a copy to pass back in case the parent wishes to sort the array.
244                        // We shouldn't allow resort of the internal list, so that multiple callers
245                        // can get lists and sort without affecting each other.  We also need to
246                        // filter out any null values that have been left as a result of deleteItem()
247                        // calls in ItemFileWriteStore.
248                        for(var i = 0; i < arrayOfItems.length; ++i){
249                                var item = arrayOfItems[i];
250                                if(item !== null){
251                                        items.push(item);
252                                }
253                        }
254                } //end if there is a query.
255                findCallback(items, requestArgs);
256        } //end filter function
257
258});
259
260});
Note: See TracBrowser for help on using the repository browser.