Bumping manifests a=b2g-bump
[gecko.git] / toolkit / modules / Dict.jsm
blob2d276113adfa9167b52f6a2d9f02916ed167b2cc
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 "use strict";
7 this.EXPORTED_SYMBOLS = ["Dict"];
9 /**
10  * Transforms a given key into a property name guaranteed not to collide with
11  * any built-ins.
12  */
13 function convert(aKey) {
14   return ":" + aKey;
17 /**
18  * Transforms a property into a key suitable for providing to the outside world.
19  */
20 function unconvert(aProp) {
21   return aProp.substr(1);
24 /**
25  * A dictionary of strings to arbitrary JS objects. This should be used whenever
26  * the keys are potentially arbitrary, to avoid collisions with built-in
27  * properties.
28  *
29  * @param aInitial An object containing the initial keys and values of this
30  *                 dictionary. Only the "own" enumerable properties of the
31  *                 object are considered.
32  *                 If |aInitial| is a string, it is assumed to be JSON and parsed into an object.
33  */
34 this.Dict = function Dict(aInitial) {
35   if (aInitial === undefined)
36     aInitial = {};
37   if (typeof aInitial == "string")
38     aInitial = JSON.parse(aInitial);
39   var items = {}, count = 0;
40   // That we don't look up the prototype chain is guaranteed by Iterator.
41   for (var [key, val] in Iterator(aInitial)) {
42     items[convert(key)] = val;
43     count++;
44   }
45   this._state = {count: count, items: items};
46   return Object.freeze(this);
49 Dict.prototype = Object.freeze({
50   /**
51    * The number of items in the dictionary.
52    */
53   get count() {
54     return this._state.count;
55   },
57   /**
58    * Gets the value for a key from the dictionary. If the key is not a string,
59    * it will be converted to a string before the lookup happens.
60    *
61    * @param aKey The key to look up
62    * @param [aDefault] An optional default value to return if the key is not
63    *                   present. Defaults to |undefined|.
64    * @returns The item, or aDefault if it isn't found.
65    */
66   get: function Dict_get(aKey, aDefault) {
67     var prop = convert(aKey);
68     var items = this._state.items;
69     return items.hasOwnProperty(prop) ? items[prop] : aDefault;
70   },
72   /**
73    * Sets the value for a key in the dictionary. If the key is a not a string,
74    * it will be converted to a string before the set happens.
75    */
76   set: function Dict_set(aKey, aValue) {
77     var prop = convert(aKey);
78     var items = this._state.items;
79     if (!items.hasOwnProperty(prop))
80       this._state.count++;
81     items[prop] = aValue;
82   },
84   /**
85    * Sets a lazy getter function for a key's value. If the key is a not a string,
86    * it will be converted to a string before the set happens.
87    * @param aKey
88    *        The key to set
89    * @param aThunk
90    *        A getter function to be called the first time the value for aKey is
91    *        retrieved. It is guaranteed that aThunk wouldn't be called more
92    *        than once.  Note that the key value may be retrieved either
93    *        directly, by |get|, or indirectly, by |listvalues| or by iterating
94    *        |values|.  For the later, the value is only retrieved if and when
95    *        the iterator gets to the value in question.  Also note that calling
96    *        |has| for a lazy-key does not invoke aThunk.
97    *
98    * @note No context is provided for aThunk when it's invoked.
99    *       Use Function.bind if you wish to run it in a certain context.
100    */
101   setAsLazyGetter: function Dict_setAsLazyGetter(aKey, aThunk) {
102     let prop = convert(aKey);
103     let items = this._state.items;
104     if (!items.hasOwnProperty(prop))
105       this._state.count++;
107     Object.defineProperty(items, prop, {
108       get: function() {
109         delete items[prop];
110         return items[prop] = aThunk();
111       },
112       configurable: true,
113       enumerable: true     
114     });
115   },
117   /**
118    * Returns whether a key is set as a lazy getter.  This returns
119    * true only if the getter function was not called already.
120    * @param aKey
121    *        The key to look up.
122    * @returns whether aKey is set as a lazy getter.
123    */
124   isLazyGetter: function Dict_isLazyGetter(aKey) {
125     let descriptor = Object.getOwnPropertyDescriptor(this._state.items,
126                                                      convert(aKey));
127     return (descriptor && descriptor.get != null);
128   },
130   /**
131    * Returns whether a key is in the dictionary. If the key is a not a string,
132    * it will be converted to a string before the lookup happens.
133    */
134   has: function Dict_has(aKey) {
135     return (this._state.items.hasOwnProperty(convert(aKey)));
136   },
138   /**
139    * Deletes a key from the dictionary. If the key is a not a string, it will be
140    * converted to a string before the delete happens.
141    *
142    * @returns true if the key was found, false if it wasn't.
143    */
144   del: function Dict_del(aKey) {
145     var prop = convert(aKey);
146     if (this._state.items.hasOwnProperty(prop)) {
147       delete this._state.items[prop];
148       this._state.count--;
149       return true;
150     }
151     return false;
152   },
154   /**
155    * Returns a shallow copy of this dictionary.
156    */
157   copy: function Dict_copy() {
158     var newItems = {};
159     for (var [key, val] in this.items)
160       newItems[key] = val;
161     return new Dict(newItems);
162   },
164   /*
165    * List and iterator functions
166    *
167    * No guarantees whatsoever are made about the order of elements.
168    */
170   /**
171    * Returns a list of all the keys in the dictionary in an arbitrary order.
172    */
173   listkeys: function Dict_listkeys() {
174     return [unconvert(k) for (k in this._state.items)];
175   },
177   /**
178    * Returns a list of all the values in the dictionary in an arbitrary order.
179    */
180   listvalues: function Dict_listvalues() {
181     var items = this._state.items;
182     return [items[k] for (k in items)];
183   },
185   /**
186    * Returns a list of all the items in the dictionary as key-value pairs
187    * in an arbitrary order.
188    */
189   listitems: function Dict_listitems() {
190     var items = this._state.items;
191     return [[unconvert(k), items[k]] for (k in items)];
192   },
194   /**
195    * Returns an iterator over all the keys in the dictionary in an arbitrary
196    * order. No guarantees are made about what happens if the dictionary is
197    * mutated during iteration.
198    */
199   get keys() {
200     // If we don't capture this._state.items here then the this-binding will be
201     // incorrect when the generator is executed
202     var items = this._state.items;
203     return (unconvert(k) for (k in items));
204   },
206   /**
207    * Returns an iterator over all the values in the dictionary in an arbitrary
208    * order. No guarantees are made about what happens if the dictionary is
209    * mutated during iteration.
210    */
211   get values() {
212     // If we don't capture this._state.items here then the this-binding will be
213     // incorrect when the generator is executed
214     var items = this._state.items;
215     return (items[k] for (k in items));
216   },
218   /**
219    * Returns an iterator over all the items in the dictionary as key-value pairs
220    * in an arbitrary order. No guarantees are made about what happens if the
221    * dictionary is mutated during iteration.
222    */
223   get items() {
224     // If we don't capture this._state.items here then the this-binding will be
225     // incorrect when the generator is executed
226     var items = this._state.items;
227     return ([unconvert(k), items[k]] for (k in items));
228   },
230   /**
231    * Returns a String representation of this dictionary.
232    */
233   toString: function Dict_toString() {
234     return "{" +
235       [(key + ": " + val) for ([key, val] in this.items)].join(", ") +
236       "}";
237   },
239   /**
240    * Returns a JSON representation of this dictionary.
241    */
242   toJSON: function Dict_toJSON() {
243     let obj = {};
244     for (let [key, item] of Iterator(this._state.items)) {
245       obj[unconvert(key)] = item;
246     }
247     return JSON.stringify(obj);
248   },