Changeset 7969

Show
Ignore:
Timestamp:
04/06/07 19:55:05 (21 months ago)
Author:
peller
Message:

Not thrilled with asserts - will revisit later. Also, still need to port io/xhrGet code when ready. Refs #2704

Location:
dojo/trunk/data
Files:
6 copied

Legend:

Unmodified
Added
Removed
  • dojo/trunk/data/JsonItemStore.js

    r7391 r7969  
    11dojo.provide("dojo.data.JsonItemStore"); 
    22 
    3 dojo.require("dojo.data.core.SimpleBaseStore"); 
    4 dojo.require("dojo.lang.declare"); 
    5 dojo.require("dojo.io.common"); 
    6 dojo.require("dojo.lang.common"); 
    7 dojo.require("dojo.experimental"); 
    8 dojo.experimental("dojo.data.JsonItemStore"); 
    9  
    10 dojo.declare("dojo.data.JsonItemStore", dojo.data.core.SimpleBaseStore,  
    11         function(/* object */ keywordParameters) { 
     3dojo.require("dojo.data.util.filter"); 
     4dojo.require("dojo.data.util.simpleFetch"); 
     5//FIXME: Replace with the port to the new xhr when available. 
     6//dojo.require("dojo.io.common"); 
     7 
     8dojo.declare("dojo.data.JsonItemStore", 
     9        null, 
     10        function(/* Object */ keywordParameters){ 
    1211                // summary: initializer 
    1312                // keywordParameters: {url: String} 
     
    1716                this._jsonFileUrl = keywordParameters.url; 
    1817                this._jsonData = keywordParameters.data; 
    19                 this._features = { 'dojo.data.core.Read': true }; 
     18                this._features = { 'dojo.data.api.Read': true}; 
    2019                this._itemsByIdentity = null; 
    21         }, { 
    22         /* summary: 
    23          *   The JsonItemStore implements the dojo.data.core.Read API and reads 
    24          *   data from JSON files that have contents in this format -- 
    25          *   { items: [ 
    26          *       { name:'Kermit', color:'green', age:12, friends:['Gonzo', {reference:{name:'Fozzie Bear'}}]}, 
    27          *       { name:'Fozzie Bear', wears:['hat', 'tie']}, 
    28          *       { name:'Miss Piggy', pets:'Foo-Foo'} 
    29          *   ]} 
    30          */ 
    31          
    32         _assertIsItem: function(/* item */ item) { 
    33                 if (!this.isItem(item)) {  
     20                this._itemMap = {}; // Simple associative map for making an O(1) isItem. 
     21                this._storeRef = "_S";  //Default name for the store reference to attach to every item. 
     22                this._itemId = "_0"; //Default Item Id for isItem to attach to every item. 
     23        },{ 
     24        //      summary: 
     25        //              The JsonItemStore implements the dojo.data.api.Read API and reads 
     26        //              data from JSON files that have contents in this format -- 
     27        //              { items: [ 
     28        //                      { name:'Kermit', color:'green', age:12, friends:['Gonzo', {reference:{name:'Fozzie Bear'}}]}, 
     29        //                      { name:'Fozzie Bear', wears:['hat', 'tie']}, 
     30        //                      { name:'Miss Piggy', pets:'Foo-Foo'} 
     31        //              ]} 
     32        //              Note that it can also contain an 'identifer' property that specified which attribute on the items  
     33        //              in the array of items that acts as the unique identifier for that item. 
     34        // 
     35 
     36        _assertIsItem: function(/* item */ item){ 
     37                //      summary: 
     38                //      This function tests whether the item passed in is indeed an item in the store. 
     39                //      item:  
     40                //              The item to test for being contained by the store. 
     41                if(!this.isItem(item)){  
    3442                        throw new Error("dojo.data.JsonItemStore: a function was passed an item argument that was not an item"); 
    3543                } 
    3644        }, 
    37          
    38         getValue: function(/* item */ item, /* attribute || attribute-name-string */ attribute, /* value? */ defaultValue) { 
    39                 // summary: See dojo.data.core.Read.getValue() 
     45 
     46        _assertIsAttribute: function(/* item || String */ attribute){ 
     47                //      summary: 
     48                //      This function tests whether the item passed in is indeed a valid 'attribute' like type for the store. 
     49                //      attribute:  
     50                //              The attribute to test for being contained by the store. 
     51                if(!this.isItem(attribute)){  
     52                        throw new Error("dojo.data.JsonItemStore: a function was passed an attribute argument that was not an attribute object nor an attribute name string"); 
     53                } 
     54        }, 
     55 
     56        getValue: function(     /* item */ item,  
     57                                                /* attribute || attribute-name-string */ attribute,  
     58                                                /* value? */ defaultValue){ 
     59                //      summary:  
     60                //      See dojo.data.api.Read.getValue() 
    4061                var values = this.getValues(item, attribute); 
    41                 var value = (values.length > 0) ? values[0] : defaultValue; 
    42                 return value; 
    43         }, 
    44                  
    45         getValues: function(/* item */ item, /* attribute || attribute-name-string */ attribute) { 
    46                 // summary: See dojo.data.core.Read.getValues() 
     62                return (values.length > 0)?values[0]:defaultValue; //Object || int || Boolean 
     63        }, 
     64 
     65        getValues: function(/* item */ item,  
     66                                                /* attribute || attribute-name-string */ attribute){ 
     67                //      summary:  
     68                //              See dojo.data.api.Read.getValues() 
     69                if(typeof attribute !== "string"){ 
     70                        this._assertIsAttribute(attribute); 
     71                        attribute = this.getIdentity(attribute); 
     72                } 
    4773                this._assertIsItem(item); 
    48                 var arrayOfValues = item[attribute] || []; 
    49                 return arrayOfValues; 
    50         }, 
    51          
    52         getAttributes: function(/* item */ item) { 
    53                 // summary: See dojo.data.core.Read.getAttributes() 
     74                return item[attribute] || []; //Array 
     75        }, 
     76 
     77        getAttributes: function(/* item */ item){ 
     78                //      summary:  
     79                //              See dojo.data.api.Read.getAttributes() 
    5480                this._assertIsItem(item); 
    5581                var attributes = []; 
    56                 for (var key in item) { 
    57                         attributes.push(key); 
    58                 } 
    59                 return attributes; 
    60         }, 
    61  
    62         hasAttribute: function(/* item */ item, /* attribute || attribute-name-string */ attribute) { 
    63                 // summary: See dojo.data.core.Read.hasAttribute() 
    64                 return (this.getValues(item, attribute).length > 0); 
    65         }, 
    66          
    67         containsValue: function(/* item */ item, /* attribute || attribute-name-string */ attribute, /* anything */ value) { 
    68                 // summary: See dojo.data.core.Read.containsValue() 
     82                for(var key in item){ 
     83                        //Save off only the real item attributes, not the special id marks for O(1) isItem. 
     84                        if((key !== this._storeRef) && (key !== this._itemId)){ 
     85                                attributes.push(key); 
     86                        } 
     87                } 
     88                return attributes; //Array 
     89        }, 
     90 
     91        hasAttribute: function( /* item */ item, 
     92                                                        /* attribute || attribute-name-string */ attribute) { 
     93                //      summary:  
     94                //              See dojo.data.api.Read.hasAttribute() 
     95                return this.getValues(item, attribute).length > 0; 
     96        }, 
     97 
     98        containsValue: function(/* item */ item,  
     99                                                        /* attribute || attribute-name-string */ attribute,  
     100                                                        /* anything */ value){ 
     101                //      summary:  
     102                //              See dojo.data.api.Read.containsValue() 
    69103                var values = this.getValues(item, attribute); 
    70                 for (var i = 0; i < values.length; ++i) { 
     104                for(var i = 0; i < values.length; ++i){ 
    71105                        var possibleValue = values[i]; 
    72                         if (value == possibleValue) { 
     106                        if(typeof value === "string" && typeof possibleValue === "string"){ 
     107                                return (possibleValue.match(dojo.data.util.filter.patternToRegExp(value)) !== null); 
     108                        }else{ 
     109                                //Non-string matching. 
     110                                if(value === possibleValue){ 
     111                                        return true; // Boolean 
     112                                } 
     113                        } 
     114                } 
     115                return false; // Boolean 
     116        }, 
     117 
     118        isItem: function(/* anything */ something){ 
     119                //      summary:  
     120                //              See dojo.data.api.Read.isItem() 
     121                if(something && something[this._storeRef] === this){ 
     122                        if(this._itemMap[something[this._itemId]] === something){ 
    73123                                return true; 
    74124                        } 
    75125                } 
    76                 return false; // boolean 
    77         }, 
    78          
    79         isItem: function(/* anything */ something) { 
    80                 // summary: See dojo.data.core.Read.isItem() 
    81                 for (var i = 0; i < this._arrayOfAllItems.length; ++i) { 
    82                         var possibleItem = this._arrayOfAllItems[i]; 
    83                         if (something == possibleItem) { 
    84                                 return true; 
    85                         } 
    86                 } 
    87                 return false; // boolean 
    88         }, 
    89          
    90         isItemLoaded: function(/* anything */ something) { 
    91                 // summary: See dojo.data.core.Read.isItemLoaded() 
    92                 return this.isItem(something); 
    93         }, 
    94          
    95         loadItem: function(/* item */ item) { 
    96                 // summary: See dojo.data.core.Read.loadItem() 
     126                return false; // Boolean 
     127        }, 
     128 
     129        isItemLoaded: function(/* anything */ something){ 
     130                //      summary:  
     131                //              See dojo.data.api.Read.isItemLoaded() 
     132                return this.isItem(something); //boolean 
     133        }, 
     134 
     135        loadItem: function(/* item */ item){ 
     136                //      summary:  
     137                //              See dojo.data.api.Read.loadItem() 
    97138                this._assertIsItem(item); 
    98                 return item; 
    99         }, 
    100          
    101         // find: function(/* object? */ keywordArgs) { 
    102         //     /* find() is implemented in  dojo.data.core.SimpleBaseStore */ 
    103         // }, 
    104          
    105         getFeatures: function() { 
    106                 // summary: See dojo.data.core.Read.getFeatures() 
    107                 return this._features; 
    108         }, 
    109          
    110         _findItems: function(/* object? */ keywordArgs, /* function */ findCallback, /* function */ errorCallback) { 
     139                return item; //object 
     140        }, 
     141 
     142        getFeatures: function(){ 
     143                //      summary:  
     144                //              See dojo.data.api.Read.getFeatures() 
     145                if (!this._loadFinished){ 
     146                        // This has to happen to meet the property that the identity functions are 
     147                        // denoted to work only if the store has been loaded and it had an identifier  
     148                        // property in the JSON.  So, for the feature to be found, the load had to have  
     149                        // happened. 
     150                        this._forceLoad(); 
     151                } 
     152                return this._features; //Object 
     153        }, 
     154 
     155        _fetchItems: function(  /* Object */ keywordArgs,  
     156                                                        /* Function */ findCallback,  
     157                                                        /* Function */ errorCallback){ 
     158                //      summary:  
     159                //              See dojo.data.util.simpleFetch.fetch() 
    111160                var self = this; 
    112                 var filter = function(keywordArgs, arrayOfAllItems) { 
    113                         if (keywordArgs.query) { 
    114                                 var items = []; 
    115                                 for (var i = 0; i < arrayOfAllItems.length; ++i) { 
     161                var filter = function(resultArgs, arrayOfAllItems){ 
     162                        var items = null; 
     163                        if(resultArgs.query) { 
     164                                items = []; 
     165                                for(var i = 0; i < arrayOfAllItems.length; ++i){ 
    116166                                        var match = true; 
    117167                                        var candidateItem = arrayOfAllItems[i]; 
    118                                         for (var key in keywordArgs.query) { 
    119                                                 var value = keywordArgs.query[key]; 
    120                                                 if (!self.containsValue(candidateItem, key, value)) { 
     168                                        for(var key in resultArgs.query) { 
     169                                                var value = resultArgs.query[key]; 
     170                                                if (!self.containsValue(candidateItem, key, value)){ 
    121171                                                        match = false; 
    122172                                                } 
    123173                                        } 
    124                                         if (match) { 
     174                                        if(match){ 
    125175                                                items.push(candidateItem); 
    126176                                        } 
    127177                                } 
    128                                 keywordArgs.items = items; 
    129                                 findCallback(keywordArgs); 
    130                         } else { 
    131                                 keywordArgs.items = self._arrayOfAllItems; 
    132                                 findCallback(keywordArgs); 
     178                                findCallback(resultArgs, items); 
     179                        }else{ 
     180                                // We want a copy to pass back in case the parent wishes to sort the array.  We shouldn't allow resort  
     181                                // of the internal list so that multiple callers can get listsand sort without affecting each other. 
     182                                if(self._arrayOfAllItems.length> 0){ 
     183                                        items = self._arrayOfAllItems.slice(0,self._arrayOfAllItems.length);  
     184                                } 
     185                                findCallback(resultArgs, items); 
    133186                        } 
    134187                }; 
    135                 var bindHandler = function(type, data, evt) { 
    136                         if (type == "load") { 
     188 
     189                var bindHandler = function(type, data, evt){ 
     190                        if(type == "load") { 
    137191                                self._loadFinished = true; 
    138192                                self._arrayOfAllItems = self._getItemsFromLoadedData(data); 
    139193                                filter(keywordArgs, self._arrayOfAllItems); 
    140                         } else if(type == "error" || type == 'timeout') { 
     194                        }else if(type == "error" || type == 'timeout'){ 
    141195                                var errorObject = data; 
    142196                                errorCallback(errorObject); 
    143197                        } 
    144198                }; 
    145                 if (this._loadFinished) { 
    146                         filter(keywordArgs, this._arrayOfAllItems) 
    147                 } else { 
    148                         if (this._jsonFileUrl) { 
    149                                 var bindArgs = { 
     199 
     200                if(this._loadFinished){ 
     201                        filter(keywordArgs, this._arrayOfAllItems); 
     202                }else{ 
     203                        if(this._jsonFileUrl){ 
     204                                var bindArgs ={ 
    150205                                        url: this._jsonFileUrl, // example: "muppets.json", 
    151206                                        handle: bindHandler, 
    152207                                        mimetype: "text/json" 
    153208                                        }; 
    154                                 if (keywordArgs.sync) { 
    155                                         bindArgs.sync = keywordArgs.sync;  
    156                                 } 
    157                                 var bindRequest = dojo.io.bind(bindArgs); 
    158                                 keywordArgs.abort = bindRequest.abort; 
    159                         } else if (this._jsonData) { 
     209                                //FIXME:  replace with new xhr when available.  May require changes to the above as well. 
     210                                //var bindRequest = dojo.io.bind(bindArgs); 
     211                                //keywordArgs.abort = bindRequest.abort; 
     212                        }else if(this._jsonData){ 
    160213                                this._loadFinished = true; 
    161214                                this._arrayOfAllItems = this._getItemsFromLoadedData(this._jsonData); 
    162215                                this._jsonData = null; 
    163216                                filter(keywordArgs, this._arrayOfAllItems); 
    164                         } else { 
    165                                 // FIXME: error condition? 
    166                         } 
    167                 } 
    168         }, 
    169          
    170         _getItemsFromLoadedData: function(/* object */ dataObject) { 
     217                        }else{ 
     218                                throw new Error("dojo.data.JsonItemStore: No JSON source data was provided as either URL or a nested Javascript object."); 
     219                        } 
     220                } 
     221        }, 
     222 
     223        close: function(/*dojo.data.api.Request || keywordArgs || null */ request){ 
     224                 //     summary:  
     225                 //             See dojo.data.api.Read.close() 
     226        }, 
     227 
     228        _getItemsFromLoadedData: function(/* Object */ dataObject){ 
     229                //      summary: 
     230                //              Function to parse the loaded data into item format and build the internal items array. 
     231                //      description: 
     232                //              Function to parse the loaded data into item format and build the internal items array. 
     233                // 
     234                //      dataObject: 
     235                //              The JS data object containing the raw data to convery into item format. 
     236                // 
     237                //      returns: array 
     238                //              Array of items in store item format. 
     239 
    171240                var arrayOfItems = dataObject.items; 
    172                  
     241                var i; 
     242                var item; 
     243                var attrNames = {}; 
     244 
    173245                // We need to do some transformations to convert the data structure 
    174246                // that we read from the file into a format that will be convenient 
    175247                // to work with in memory.. 
    176                  
     248 
    177249                // Step 1: We walk through all the attribute values of all the items,  
    178250                // and replace single values with arrays.  For example, we change this: 
    179                 //     { name:'Miss Piggy', pets:'Foo-Foo'} 
     251                //              { name:'Miss Piggy', pets:'Foo-Foo'} 
    180252                // into this: 
    181                 //     { name:['Miss Piggy'], pets:['Foo-Foo']} 
    182                  
    183                 for (var i = 0; i < arrayOfItems.length; ++i) { 
    184                         var item = arrayOfItems[i]; 
    185                         for (var key in item) { 
     253                //              { name:['Miss Piggy'], pets:['Foo-Foo']} 
     254                // Also store off the keys so we can validate our store reference and item  
     255                // id special properties for the O(1) isItem 
     256                for(i = 0; i < arrayOfItems.length; ++i){ 
     257                        item = arrayOfItems[i]; 
     258            for(var key in item){ 
    186259                                var value = item[key]; 
    187                                 if (!dojo.lang.isArray(value)) { 
     260                                if(!dojo.isArray(value)){ 
    188261                                        item[key] = [value]; 
    189262                                } 
    190                         } 
    191                 } 
    192                  
     263                                attrNames[key]=key; 
     264                        } 
     265                } 
     266 
     267                //Build unique keys for id and store ref. 
     268                //This should go really fast, it will generally 
     269                // never even run the loop.. 
     270                while(attrNames[this._storeRef]){ 
     271                        this._storeRef += "_"; 
     272                } 
     273                while(attrNames[this._itemId]){ 
     274                        this._itemId += "_"; 
     275                } 
     276 
    193277                // Step 2: Some data files specify an optional 'identifier', which is  
    194278                // the name of an attribute that holds the identity of each item.  If  
     
    196280                // hash table of items keyed by the identity of the items. 
    197281                var identifier = dataObject.identifier; 
    198                 if (identifier) { 
    199                         this._features['dojo.data.core.Identity'] = identifier; 
     282                var arrayOfValues = null; 
     283                if(identifier){ 
     284                        this._features['dojo.data.api.Identity'] = identifier; 
    200285                        this._itemsByIdentity = {}; 
    201                         for (i = 0; i < arrayOfItems.length; ++i) { 
     286                        for(var i = 0; i < arrayOfItems.length; ++i){ 
    202287                                item = arrayOfItems[i]; 
    203288                                arrayOfValues = item[identifier]; 
    204289                                identity = arrayOfValues[0]; 
    205                                 this._itemsByIdentity[identity] = item; 
     290                                if(!this._itemsByIdentity[identity]){ 
     291                                        this._itemsByIdentity[identity] = item; 
     292                                }else{ 
     293                                        if(this._jsonFileUrl){ 
     294                                                throw new Error("dojo.data.JsonItemStore:  The json data as specified by: [" + this._jsonFileUrl + "] is malformed.  Items within the list have identifier: [" + identifier + "].  Value collided: [" + identity + "]"); 
     295                                        }else if(this._jsonData){ 
     296                                                throw new Error("dojo.data.JsonItemStore:  The json data provided by the creation arguments is malformed.  Items within the list have identifier: [" + identifier + "].  Value collided: [" + identity + "]"); 
     297                                        } 
     298                                } 
     299 
    206300                        } 
    207301                } 
     
    209303                // Step 3: We walk through all the attribute values of all the items, 
    210304                // and replace references with pointers to items.  For example, we change: 
    211         //     { name:['Kermit'], friends:[{reference:{name:'Miss Piggy'}}] } 
     305                //              { name:['Kermit'], friends:[{reference:{name:'Miss Piggy'}}] } 
    212306                // into this: 
    213         //     { name:['Kermit'], friends:[miss_piggy] }  
     307                //              { name:['Kermit'], friends:[miss_piggy] }  
    214308                // (where miss_piggy is the object representing the 'Miss Piggy' item). 
    215                 for (i = 0; i < arrayOfItems.length; ++i) { 
    216                         item = arrayOfItems[i]; // example: { name:['Kermit'], friends:[{reference:{name:'Miss Piggy'}}] } 
    217                         for (key in item) { 
    218                                 var arrayOfValues = item[key]; // example: [{reference:{name:'Miss Piggy'}}] 
    219                                 for (var j = 0; j < arrayOfItems.length; ++j) { 
     309                // Also generate the associate map for all items for the O(1) isItem function. 
     310                for(i = 0; i < arrayOfItems.length; ++i){ 
     311            item = arrayOfItems[i]; // example: { name:['Kermit'], friends:[{reference:{name:'Miss Piggy'}}] } 
     312                        item[this._storeRef] = this; 
     313                        item[this._itemId] = i; 
     314                        this._itemMap[i] = item; 
     315                        this._itemMap.lastItem = i; 
     316                        for(key in item){ 
     317                                arrayOfValues = item[key]; // example: [{reference:{name:'Miss Piggy'}}] 
     318                                for(var j = 0; j < arrayOfItems.length; ++j) { 
    220319                                        value = arrayOfValues[j]; // example: {reference:{name:'Miss Piggy'}} 
    221                                         if (typeof value == "object" && value.reference) { 
     320                                        if(typeof value == "object" && value.reference){ 
    222321                                                var referenceDescription = value.reference; // example: {name:'Miss Piggy'} 
    223                                                 if (dojo.lang.isString(referenceDescription)) { 
     322                                                if(dojo.isString(referenceDescription)){ 
    224323                                                        // example: 'Miss Piggy' 
    225324                                                        // from an item like: { name:['Kermit'], friends:[{reference:'Miss Piggy'}]} 
    226325                                                        arrayOfValues[j] = this._itemsByIdentity[referenceDescription]; 
    227                                                 } else { 
     326                                                }else{ 
    228327                                                        // example: {name:'Miss Piggy'} 
    229328                                                        // from an item like: { name:['Kermit'], friends:[{reference:{name:'Miss Piggy'}}] } 
    230                                                         for (var k = 0; k < arrayOfItems.length; ++k) { 
     329                                                        for(var k = 0; k < arrayOfItems.length; ++k){ 
    231330                                                                var candidateItem = arrayOfItems[k]; 
    232331                                                                var found = true; 
    233                                                                 for (var refKey in referenceDescription) { 
    234                                                                         if (candidateItem[refKey] != referenceDescription[refKey]) {  
     332                                                                for(var refKey in referenceDescription){ 
     333                                                                        if(candidateItem[refKey] != referenceDescription[refKey]){  
    235334                                                                                found = false;  
    236335                                                                        } 
    237336                                                                } 
    238                                                                 if (found) {  
     337                                                                if(found){  
    239338                                                                        arrayOfValues[j] = candidateItem;  
    240339                                                                } 
     
    245344                        } 
    246345                } 
    247                  
    248                 return arrayOfItems; 
    249         }, 
    250          
    251         getIdentity: function(/* item */ item) { 
    252                 // summary: See dojo.data.core.Identity.getIdentity() 
    253                 var identifier = this._features['dojo.data.core.Identity']; 
     346                return arrayOfItems; //Array 
     347        }, 
     348 
     349        getIdentity: function(/* item */ item){ 
     350                //      summary:  
     351                //              See dojo.data.api.Identity.getIdentity() 
     352                var identifier = this._features['dojo.data.api.Identity']; 
    254353                var arrayOfValues = item[identifier];