PageRenderTime 21ms CodeModel.GetById 12ms app.highlight 6ms RepoModel.GetById 1ms app.codeStats 1ms

/toolkit/content/Dict.jsm

http://github.com/zpao/v8monkey
Unknown | 268 lines | 246 code | 22 blank | 0 comment | 0 complexity | 7b84e3842b94d77b0e91531edad620b4 MD5 | raw file
  1/* ***** BEGIN LICENSE BLOCK *****
  2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3 * 
  4 * The contents of this file are subject to the Mozilla Public License Version
  5 * 1.1 (the "License"); you may not use this file except in compliance with
  6 * the License. You may obtain a copy of the License at
  7 * http://www.mozilla.org/MPL/
  8 * 
  9 * Software distributed under the License is distributed on an "AS IS" basis,
 10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 11 * for the specific language governing rights and limitations under the
 12 * License.
 13 * 
 14 * The Original Code is dict.js.
 15 * 
 16 * The Initial Developer of the Original Code is
 17 * the Mozilla Foundation.
 18 * Portions created by the Initial Developer are Copyright (C) 2010
 19 * the Initial Developer. All Rights Reserved.
 20 * 
 21 * Contributor(s):
 22 *   Siddharth Agarwal <sid.bugzilla@gmail.com>
 23 * 
 24 * Alternatively, the contents of this file may be used under the terms of
 25 * either the GNU General Public License Version 2 or later (the "GPL"), or
 26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 27 * in which case the provisions of the GPL or the LGPL are applicable instead
 28 * of those above. If you wish to allow use of your version of this file only
 29 * under the terms of either the GPL or the LGPL, and not to allow others to
 30 * use your version of this file under the terms of the MPL, indicate your
 31 * decision by deleting the provisions above and replace them with the notice
 32 * and other provisions required by the GPL or the LGPL. If you do not delete
 33 * the provisions above, a recipient may use your version of this file under
 34 * the terms of any one of the MPL, the GPL or the LGPL.
 35 * 
 36 * ***** END LICENSE BLOCK ***** */
 37
 38"use strict";
 39
 40var EXPORTED_SYMBOLS = ["Dict"];
 41
 42/**
 43 * Transforms a given key into a property name guaranteed not to collide with
 44 * any built-ins.
 45 */
 46function convert(aKey) {
 47  return ":" + aKey;
 48}
 49
 50/**
 51 * Transforms a property into a key suitable for providing to the outside world.
 52 */
 53function unconvert(aProp) {
 54  return aProp.substr(1);
 55}
 56
 57/**
 58 * A dictionary of strings to arbitrary JS objects. This should be used whenever
 59 * the keys are potentially arbitrary, to avoid collisions with built-in
 60 * properties.
 61 *
 62 * @param aInitial An object containing the initial keys and values of this
 63 *                 dictionary. Only the "own" enumerable properties of the
 64 *                 object are considered.
 65 */
 66function Dict(aInitial) {
 67  if (aInitial === undefined)
 68    aInitial = {};
 69  var items = {}, count = 0;
 70  // That we don't look up the prototype chain is guaranteed by Iterator.
 71  for (var [key, val] in Iterator(aInitial)) {
 72    items[convert(key)] = val;
 73    count++;
 74  }
 75  this._state = {count: count, items: items};
 76  return Object.freeze(this);
 77}
 78
 79Dict.prototype = Object.freeze({
 80  /**
 81   * The number of items in the dictionary.
 82   */
 83  get count() {
 84    return this._state.count;
 85  },
 86
 87  /**
 88   * Gets the value for a key from the dictionary. If the key is not a string,
 89   * it will be converted to a string before the lookup happens.
 90   *
 91   * @param aKey The key to look up
 92   * @param [aDefault] An optional default value to return if the key is not
 93   *                   present. Defaults to |undefined|.
 94   * @returns The item, or aDefault if it isn't found.
 95   */
 96  get: function Dict_get(aKey, aDefault) {
 97    var prop = convert(aKey);
 98    var items = this._state.items;
 99    return items.hasOwnProperty(prop) ? items[prop] : aDefault;
100  },
101
102  /**
103   * Sets the value for a key in the dictionary. If the key is a not a string,
104   * it will be converted to a string before the set happens.
105   */
106  set: function Dict_set(aKey, aValue) {
107    var prop = convert(aKey);
108    var items = this._state.items;
109    if (!items.hasOwnProperty(prop))
110      this._state.count++;
111    items[prop] = aValue;
112  },
113
114  /**
115   * Sets a lazy getter function for a key's value. If the key is a not a string,
116   * it will be converted to a string before the set happens.
117   * @param aKey
118   *        The key to set
119   * @param aThunk
120   *        A getter function to be called the first time the value for aKey is
121   *        retrieved. It is guaranteed that aThunk wouldn't be called more
122   *        than once.  Note that the key value may be retrieved either
123   *        directly, by |get|, or indirectly, by |listvalues| or by iterating
124   *        |values|.  For the later, the value is only retrieved if and when
125   *        the iterator gets to the value in question.  Also note that calling
126   *        |has| for a lazy-key does not invoke aThunk.
127   *
128   * @note No context is provided for aThunk when it's invoked.
129   *       Use Function.bind if you wish to run it in a certain context.
130   */
131  setAsLazyGetter: function Dict_setAsLazyGetter(aKey, aThunk) {
132    let prop = convert(aKey);
133    let items = this._state.items;
134    if (!items.hasOwnProperty(prop))
135      this._state.count++;
136
137    Object.defineProperty(items, prop, {
138      get: function() {
139        delete items[prop];
140        return items[prop] = aThunk();
141      },
142      configurable: true,
143      enumerable: true     
144    });
145  },
146
147  /**
148   * Returns whether a key is set as a lazy getter.  This returns
149   * true only if the getter function was not called already.
150   * @param aKey
151   *        The key to look up.
152   * @returns whether aKey is set as a lazy getter.
153   */
154  isLazyGetter: function Dict_isLazyGetter(aKey) {
155    let descriptor = Object.getOwnPropertyDescriptor(this._state.items,
156                                                     convert(aKey));
157    return (descriptor && descriptor.get != null);
158  },
159
160  /**
161   * Returns whether a key is in the dictionary. If the key is a not a string,
162   * it will be converted to a string before the lookup happens.
163   */
164  has: function Dict_has(aKey) {
165    return (this._state.items.hasOwnProperty(convert(aKey)));
166  },
167
168  /**
169   * Deletes a key from the dictionary. If the key is a not a string, it will be
170   * converted to a string before the delete happens.
171   *
172   * @returns true if the key was found, false if it wasn't.
173   */
174  del: function Dict_del(aKey) {
175    var prop = convert(aKey);
176    if (this._state.items.hasOwnProperty(prop)) {
177      delete this._state.items[prop];
178      this._state.count--;
179      return true;
180    }
181    return false;
182  },
183
184  /**
185   * Returns a shallow copy of this dictionary.
186   */
187  copy: function Dict_copy() {
188    var newItems = {};
189    for (var [key, val] in this.items)
190      newItems[key] = val;
191    return new Dict(newItems);
192  },
193
194  /*
195   * List and iterator functions
196   *
197   * No guarantees whatsoever are made about the order of elements.
198   */
199
200  /**
201   * Returns a list of all the keys in the dictionary in an arbitrary order.
202   */
203  listkeys: function Dict_listkeys() {
204    return [unconvert(k) for (k in this._state.items)];
205  },
206
207  /**
208   * Returns a list of all the values in the dictionary in an arbitrary order.
209   */
210  listvalues: function Dict_listvalues() {
211    var items = this._state.items;
212    return [items[k] for (k in items)];
213  },
214
215  /**
216   * Returns a list of all the items in the dictionary as key-value pairs
217   * in an arbitrary order.
218   */
219  listitems: function Dict_listitems() {
220    var items = this._state.items;
221    return [[unconvert(k), items[k]] for (k in items)];
222  },
223
224  /**
225   * Returns an iterator over all the keys in the dictionary in an arbitrary
226   * order. No guarantees are made about what happens if the dictionary is
227   * mutated during iteration.
228   */
229  get keys() {
230    // If we don't capture this._state.items here then the this-binding will be
231    // incorrect when the generator is executed
232    var items = this._state.items;
233    return (unconvert(k) for (k in items));
234  },
235
236  /**
237   * Returns an iterator over all the values in the dictionary in an arbitrary
238   * order. No guarantees are made about what happens if the dictionary is
239   * mutated during iteration.
240   */
241  get values() {
242    // If we don't capture this._state.items here then the this-binding will be
243    // incorrect when the generator is executed
244    var items = this._state.items;
245    return (items[k] for (k in items));
246  },
247
248  /**
249   * Returns an iterator over all the items in the dictionary as key-value pairs
250   * in an arbitrary order. No guarantees are made about what happens if the
251   * dictionary is mutated during iteration.
252   */
253  get items() {
254    // If we don't capture this._state.items here then the this-binding will be
255    // incorrect when the generator is executed
256    var items = this._state.items;
257    return ([unconvert(k), items[k]] for (k in items));
258  },
259
260  /**
261   * Returns a string representation of this dictionary.
262   */
263  toString: function Dict_toString() {
264    return "{" +
265      [(key + ": " + val) for ([key, val] in this.items)].join(", ") +
266      "}";
267  },
268});