|  | // Protocol Buffers - Google's data interchange format | 
|  | // Copyright 2008 Google Inc.  All rights reserved. | 
|  | // https://developers.google.com/protocol-buffers/ | 
|  | // | 
|  | // Redistribution and use in source and binary forms, with or without | 
|  | // modification, are permitted provided that the following conditions are | 
|  | // met: | 
|  | // | 
|  | //     * Redistributions of source code must retain the above copyright | 
|  | // notice, this list of conditions and the following disclaimer. | 
|  | //     * Redistributions in binary form must reproduce the above | 
|  | // copyright notice, this list of conditions and the following disclaimer | 
|  | // in the documentation and/or other materials provided with the | 
|  | // distribution. | 
|  | //     * Neither the name of Google Inc. nor the names of its | 
|  | // contributors may be used to endorse or promote products derived from | 
|  | // this software without specific prior written permission. | 
|  | // | 
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  |  | 
|  | goog.provide('jspb.Map'); | 
|  |  | 
|  | goog.require('goog.asserts'); | 
|  |  | 
|  | goog.forwardDeclare('jspb.BinaryReader'); | 
|  | goog.forwardDeclare('jspb.BinaryWriter'); | 
|  |  | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Constructs a new Map. A Map is a container that is used to implement map | 
|  | * fields on message objects. It closely follows the ES6 Map API; however, | 
|  | * it is distinct because we do not want to depend on external polyfills or | 
|  | * on ES6 itself. | 
|  | * | 
|  | * This constructor should only be called from generated message code. It is not | 
|  | * intended for general use by library consumers. | 
|  | * | 
|  | * @template K, V | 
|  | * | 
|  | * @param {!Array<!Array<?>>} arr | 
|  | * | 
|  | * @param {?function(new:V, ?=)=} opt_valueCtor | 
|  | *    The constructor for type V, if type V is a message type. | 
|  | * | 
|  | * @constructor | 
|  | * @struct | 
|  | */ | 
|  | jspb.Map = function(arr, opt_valueCtor) { | 
|  | /** @const @private */ | 
|  | this.arr_ = arr; | 
|  | /** @const @private */ | 
|  | this.valueCtor_ = opt_valueCtor; | 
|  |  | 
|  | /** @type {!Object<string, !jspb.Map.Entry_<K,V>>} @private */ | 
|  | this.map_ = {}; | 
|  |  | 
|  | /** | 
|  | * Is `this.arr_ updated with respect to `this.map_`? | 
|  | * @type {boolean} | 
|  | */ | 
|  | this.arrClean = true; | 
|  |  | 
|  | if (this.arr_.length > 0) { | 
|  | this.loadFromArray_(); | 
|  | } | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Load initial content from underlying array. | 
|  | * @private | 
|  | */ | 
|  | jspb.Map.prototype.loadFromArray_ = function() { | 
|  | for (var i = 0; i < this.arr_.length; i++) { | 
|  | var record = this.arr_[i]; | 
|  | var key = record[0]; | 
|  | var value = record[1]; | 
|  | this.map_[key.toString()] = new jspb.Map.Entry_(key, value); | 
|  | } | 
|  | this.arrClean = true; | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Synchronize content to underlying array, if needed, and return it. | 
|  | * @return {!Array<!Array<!Object>>} | 
|  | */ | 
|  | jspb.Map.prototype.toArray = function() { | 
|  | if (this.arrClean) { | 
|  | if (this.valueCtor_) { | 
|  | // We need to recursively sync maps in submessages to their arrays. | 
|  | var m = this.map_; | 
|  | for (var p in m) { | 
|  | if (Object.prototype.hasOwnProperty.call(m, p)) { | 
|  | var valueWrapper = /** @type {?jspb.Message} */ (m[p].valueWrapper); | 
|  | if (valueWrapper) { | 
|  | valueWrapper.toArray(); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } else { | 
|  | // Delete all elements. | 
|  | this.arr_.length = 0; | 
|  | var strKeys = this.stringKeys_(); | 
|  | // Output keys in deterministic (sorted) order. | 
|  | strKeys.sort(); | 
|  | for (var i = 0; i < strKeys.length; i++) { | 
|  | var entry = this.map_[strKeys[i]]; | 
|  | var valueWrapper = /** @type {?jspb.Message} */ (entry.valueWrapper); | 
|  | if (valueWrapper) { | 
|  | valueWrapper.toArray(); | 
|  | } | 
|  | this.arr_.push([entry.key, entry.value]); | 
|  | } | 
|  | this.arrClean = true; | 
|  | } | 
|  | return this.arr_; | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Returns the map formatted as an array of key-value pairs, suitable for the | 
|  | * toObject() form of a message. | 
|  | * | 
|  | * @param {boolean=} includeInstance Whether to include the JSPB instance for | 
|  | *    transitional soy proto support: http://goto/soy-param-migration | 
|  | * @param {!function((boolean|undefined),V):!Object=} valueToObject | 
|  | *    The static toObject() method, if V is a message type. | 
|  | * @return {!Array<!Array<!Object>>} | 
|  | */ | 
|  | jspb.Map.prototype.toObject = function(includeInstance, valueToObject) { | 
|  | var rawArray = this.toArray(); | 
|  | var entries = []; | 
|  | for (var i = 0; i < rawArray.length; i++) { | 
|  | var entry = this.map_[rawArray[i][0].toString()]; | 
|  | this.wrapEntry_(entry); | 
|  | var valueWrapper = /** @type {V|undefined} */ (entry.valueWrapper); | 
|  | if (valueWrapper) { | 
|  | goog.asserts.assert(valueToObject); | 
|  | entries.push([entry.key, valueToObject(includeInstance, valueWrapper)]); | 
|  | } else { | 
|  | entries.push([entry.key, entry.value]); | 
|  | } | 
|  | } | 
|  | return entries; | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Returns a Map from the given array of key-value pairs when the values are of | 
|  | * message type. The values in the array must match the format returned by their | 
|  | * message type's toObject() method. | 
|  | * | 
|  | * @template K, V | 
|  | * @param {!Array<!Array<!Object>>} entries | 
|  | * @param {!function(new:V,?=)} valueCtor | 
|  | *    The constructor for type V. | 
|  | * @param {!function(!Object):V} valueFromObject | 
|  | *    The fromObject function for type V. | 
|  | * @return {!jspb.Map<K, V>} | 
|  | */ | 
|  | jspb.Map.fromObject = function(entries, valueCtor, valueFromObject) { | 
|  | var result = new jspb.Map([], valueCtor); | 
|  | for (var i = 0; i < entries.length; i++) { | 
|  | var key = entries[i][0]; | 
|  | var value = valueFromObject(entries[i][1]); | 
|  | result.set(key, value); | 
|  | } | 
|  | return result; | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Helper: an IteratorIterable over an array. | 
|  | * @template T | 
|  | * @param {!Array<T>} arr the array | 
|  | * @implements {IteratorIterable<T>} | 
|  | * @constructor @struct | 
|  | * @private | 
|  | */ | 
|  | jspb.Map.ArrayIteratorIterable_ = function(arr) { | 
|  | /** @type {number} @private */ | 
|  | this.idx_ = 0; | 
|  |  | 
|  | /** @const @private */ | 
|  | this.arr_ = arr; | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** @override @final */ | 
|  | jspb.Map.ArrayIteratorIterable_.prototype.next = function() { | 
|  | if (this.idx_ < this.arr_.length) { | 
|  | return {done: false, value: this.arr_[this.idx_++]}; | 
|  | } else { | 
|  | return {done: true, value: undefined}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | if (typeof(Symbol) != 'undefined') { | 
|  | /** @override */ | 
|  | jspb.Map.ArrayIteratorIterable_.prototype[Symbol.iterator] = function() { | 
|  | return this; | 
|  | }; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Returns the map's length (number of key/value pairs). | 
|  | * @return {number} | 
|  | */ | 
|  | jspb.Map.prototype.getLength = function() { | 
|  | return this.stringKeys_().length; | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Clears the map. | 
|  | */ | 
|  | jspb.Map.prototype.clear = function() { | 
|  | this.map_ = {}; | 
|  | this.arrClean = false; | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Deletes a particular key from the map. | 
|  | * N.B.: differs in name from ES6 Map's `delete` because IE8 does not support | 
|  | * reserved words as property names. | 
|  | * @this {jspb.Map} | 
|  | * @param {K} key | 
|  | * @return {boolean} Whether any entry with this key was deleted. | 
|  | */ | 
|  | jspb.Map.prototype.del = function(key) { | 
|  | var keyValue = key.toString(); | 
|  | var hadKey = this.map_.hasOwnProperty(keyValue); | 
|  | delete this.map_[keyValue]; | 
|  | this.arrClean = false; | 
|  | return hadKey; | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Returns an array of [key, value] pairs in the map. | 
|  | * | 
|  | * This is redundant compared to the plain entries() method, but we provide this | 
|  | * to help out Angular 1.x users.  Still evaluating whether this is the best | 
|  | * option. | 
|  | * | 
|  | * @return {!Array<!Array<K|V>>} | 
|  | */ | 
|  | jspb.Map.prototype.getEntryList = function() { | 
|  | var entries = []; | 
|  | var strKeys = this.stringKeys_(); | 
|  | strKeys.sort(); | 
|  | for (var i = 0; i < strKeys.length; i++) { | 
|  | var entry = this.map_[strKeys[i]]; | 
|  | entries.push([entry.key, entry.value]); | 
|  | } | 
|  | return entries; | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Returns an iterator-iterable over [key, value] pairs in the map. | 
|  | * Closure compiler sadly doesn't support tuples, ie. Iterator<[K,V]>. | 
|  | * @return {!IteratorIterable<!Array<K|V>>} The iterator-iterable. | 
|  | */ | 
|  | jspb.Map.prototype.entries = function() { | 
|  | var entries = []; | 
|  | var strKeys = this.stringKeys_(); | 
|  | strKeys.sort(); | 
|  | for (var i = 0; i < strKeys.length; i++) { | 
|  | var entry = this.map_[strKeys[i]]; | 
|  | entries.push([entry.key, this.wrapEntry_(entry)]); | 
|  | } | 
|  | return new jspb.Map.ArrayIteratorIterable_(entries); | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Returns an iterator-iterable over keys in the map. | 
|  | * @return {!IteratorIterable<K>} The iterator-iterable. | 
|  | */ | 
|  | jspb.Map.prototype.keys = function() { | 
|  | var keys = []; | 
|  | var strKeys = this.stringKeys_(); | 
|  | strKeys.sort(); | 
|  | for (var i = 0; i < strKeys.length; i++) { | 
|  | var entry = this.map_[strKeys[i]]; | 
|  | keys.push(entry.key); | 
|  | } | 
|  | return new jspb.Map.ArrayIteratorIterable_(keys); | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Returns an iterator-iterable over values in the map. | 
|  | * @return {!IteratorIterable<V>} The iterator-iterable. | 
|  | */ | 
|  | jspb.Map.prototype.values = function() { | 
|  | var values = []; | 
|  | var strKeys = this.stringKeys_(); | 
|  | strKeys.sort(); | 
|  | for (var i = 0; i < strKeys.length; i++) { | 
|  | var entry = this.map_[strKeys[i]]; | 
|  | values.push(this.wrapEntry_(entry)); | 
|  | } | 
|  | return new jspb.Map.ArrayIteratorIterable_(values); | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Iterates over entries in the map, calling a function on each. | 
|  | * @template T | 
|  | * @param {function(this:T, V, K, ?jspb.Map<K, V>)} cb | 
|  | * @param {T=} opt_thisArg | 
|  | */ | 
|  | jspb.Map.prototype.forEach = function(cb, opt_thisArg) { | 
|  | var strKeys = this.stringKeys_(); | 
|  | strKeys.sort(); | 
|  | for (var i = 0; i < strKeys.length; i++) { | 
|  | var entry = this.map_[strKeys[i]]; | 
|  | cb.call(opt_thisArg, this.wrapEntry_(entry), entry.key, this); | 
|  | } | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Sets a key in the map to the given value. | 
|  | * @param {K} key The key | 
|  | * @param {V} value The value | 
|  | * @return {!jspb.Map<K,V>} | 
|  | */ | 
|  | jspb.Map.prototype.set = function(key, value) { | 
|  | var entry = new jspb.Map.Entry_(key); | 
|  | if (this.valueCtor_) { | 
|  | entry.valueWrapper = value; | 
|  | // .toArray() on a message returns a reference to the underlying array | 
|  | // rather than a copy. | 
|  | entry.value = value.toArray(); | 
|  | } else { | 
|  | entry.value = value; | 
|  | } | 
|  | this.map_[key.toString()] = entry; | 
|  | this.arrClean = false; | 
|  | return this; | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Helper: lazily construct a wrapper around an entry, if needed, and return the | 
|  | * user-visible type. | 
|  | * @param {!jspb.Map.Entry_<K,V>} entry | 
|  | * @return {V} | 
|  | * @private | 
|  | */ | 
|  | jspb.Map.prototype.wrapEntry_ = function(entry) { | 
|  | if (this.valueCtor_) { | 
|  | if (!entry.valueWrapper) { | 
|  | entry.valueWrapper = new this.valueCtor_(entry.value); | 
|  | } | 
|  | return /** @type {V} */ (entry.valueWrapper); | 
|  | } else { | 
|  | return entry.value; | 
|  | } | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Gets the value corresponding to a key in the map. | 
|  | * @param {K} key | 
|  | * @return {V|undefined} The value, or `undefined` if key not present | 
|  | */ | 
|  | jspb.Map.prototype.get = function(key) { | 
|  | var keyValue = key.toString(); | 
|  | var entry = this.map_[keyValue]; | 
|  | if (entry) { | 
|  | return this.wrapEntry_(entry); | 
|  | } else { | 
|  | return undefined; | 
|  | } | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Determines whether the given key is present in the map. | 
|  | * @param {K} key | 
|  | * @return {boolean} `true` if the key is present | 
|  | */ | 
|  | jspb.Map.prototype.has = function(key) { | 
|  | var keyValue = key.toString(); | 
|  | return (keyValue in this.map_); | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Write this Map field in wire format to a BinaryWriter, using the given field | 
|  | * number. | 
|  | * @param {number} fieldNumber | 
|  | * @param {!jspb.BinaryWriter} writer | 
|  | * @param {!function(this:jspb.BinaryWriter,number,K)} keyWriterFn | 
|  | *     The method on BinaryWriter that writes type K to the stream. | 
|  | * @param {!function(this:jspb.BinaryWriter,number,V,?=)| | 
|  | *          function(this:jspb.BinaryWriter,number,V,?)} valueWriterFn | 
|  | *     The method on BinaryWriter that writes type V to the stream.  May be | 
|  | *     writeMessage, in which case the second callback arg form is used. | 
|  | * @param {function(V,!jspb.BinaryWriter)=} opt_valueWriterCallback | 
|  | *    The BinaryWriter serialization callback for type V, if V is a message | 
|  | *    type. | 
|  | */ | 
|  | jspb.Map.prototype.serializeBinary = function( | 
|  | fieldNumber, writer, keyWriterFn, valueWriterFn, opt_valueWriterCallback) { | 
|  | var strKeys = this.stringKeys_(); | 
|  | strKeys.sort(); | 
|  | for (var i = 0; i < strKeys.length; i++) { | 
|  | var entry = this.map_[strKeys[i]]; | 
|  | writer.beginSubMessage(fieldNumber); | 
|  | keyWriterFn.call(writer, 1, entry.key); | 
|  | if (this.valueCtor_) { | 
|  | valueWriterFn.call(writer, 2, this.wrapEntry_(entry), | 
|  | opt_valueWriterCallback); | 
|  | } else { | 
|  | /** @type {function(this:jspb.BinaryWriter,number,?)} */ (valueWriterFn) | 
|  | .call(writer, 2, entry.value); | 
|  | } | 
|  | writer.endSubMessage(); | 
|  | } | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Read one key/value message from the given BinaryReader. Compatible as the | 
|  | * `reader` callback parameter to jspb.BinaryReader.readMessage, to be called | 
|  | * when a key/value pair submessage is encountered. If the Key is undefined, | 
|  | * we should default it to 0. | 
|  | * @template K, V | 
|  | * @param {!jspb.Map} map | 
|  | * @param {!jspb.BinaryReader} reader | 
|  | * @param {!function(this:jspb.BinaryReader):K} keyReaderFn | 
|  | *     The method on BinaryReader that reads type K from the stream. | 
|  | * | 
|  | * @param {!function(this:jspb.BinaryReader):V| | 
|  | *          function(this:jspb.BinaryReader,V, | 
|  | *                  function(V,!jspb.BinaryReader))} valueReaderFn | 
|  | *    The method on BinaryReader that reads type V from the stream. May be | 
|  | *    readMessage, in which case the second callback arg form is used. | 
|  | * | 
|  | * @param {?function(V,!jspb.BinaryReader)=} opt_valueReaderCallback | 
|  | *    The BinaryReader parsing callback for type V, if V is a message type | 
|  | * | 
|  | * @param {K=} opt_defaultKey | 
|  | *    The default value for the type of map keys. Accepting map | 
|  | *    entries with unset keys is required for maps to be backwards compatible | 
|  | *    with the repeated message representation described here: goo.gl/zuoLAC | 
|  | * | 
|  | */ | 
|  | jspb.Map.deserializeBinary = function(map, reader, keyReaderFn, valueReaderFn, | 
|  | opt_valueReaderCallback, opt_defaultKey) { | 
|  | var key = opt_defaultKey; | 
|  | var value = undefined; | 
|  |  | 
|  | while (reader.nextField()) { | 
|  | if (reader.isEndGroup()) { | 
|  | break; | 
|  | } | 
|  | var field = reader.getFieldNumber(); | 
|  |  | 
|  | if (field == 1) { | 
|  | // Key. | 
|  | key = keyReaderFn.call(reader); | 
|  | } else if (field == 2) { | 
|  | // Value. | 
|  | if (map.valueCtor_) { | 
|  | goog.asserts.assert(opt_valueReaderCallback); | 
|  | value = new map.valueCtor_(); | 
|  | valueReaderFn.call(reader, value, opt_valueReaderCallback); | 
|  | } else { | 
|  | value = | 
|  | (/** @type {function(this:jspb.BinaryReader):?} */ (valueReaderFn)) | 
|  | .call(reader); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | goog.asserts.assert(key != undefined); | 
|  | goog.asserts.assert(value != undefined); | 
|  | map.set(key, value); | 
|  | }; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Helper: compute the list of all stringified keys in the underlying Object | 
|  | * map. | 
|  | * @return {!Array<string>} | 
|  | * @private | 
|  | */ | 
|  | jspb.Map.prototype.stringKeys_ = function() { | 
|  | var m = this.map_; | 
|  | var ret = []; | 
|  | for (var p in m) { | 
|  | if (Object.prototype.hasOwnProperty.call(m, p)) { | 
|  | ret.push(p); | 
|  | } | 
|  | } | 
|  | return ret; | 
|  | }; | 
|  |  | 
|  |  | 
|  |  | 
|  | /** | 
|  | * @param {K} key The entry's key. | 
|  | * @param {V=} opt_value The entry's value wrapper. | 
|  | * @constructor | 
|  | * @struct | 
|  | * @template K, V | 
|  | * @private | 
|  | */ | 
|  | jspb.Map.Entry_ = function(key, opt_value) { | 
|  | /** @const {K} */ | 
|  | this.key = key; | 
|  |  | 
|  | // The JSPB-serializable value.  For primitive types this will be of type V. | 
|  | // For message types it will be an array. | 
|  | /** @type {V} */ | 
|  | this.value = opt_value; | 
|  |  | 
|  | // Only used for submessage values. | 
|  | /** @type {V} */ | 
|  | this.valueWrapper = undefined; | 
|  | }; |