toolkit/modules/Dict.jsm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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;
    15 }
    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);
    22 }
    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);
    47 }
    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   },
   249 });

mercurial