Adding mapbox-gl branch
This commit is contained in:
@@ -0,0 +1,348 @@
|
||||
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview A map data structure that offers a convenient API to
|
||||
* manipulate a key, value map. The key must be a string.
|
||||
*
|
||||
* This implementation also ensure that you can use keys that would
|
||||
* not be usable using a normal object literal {}. Some examples
|
||||
* include __proto__ (all newer browsers), toString/hasOwnProperty (IE
|
||||
* <= 8).
|
||||
* @author chrishenry@google.com (Chris Henry)
|
||||
*/
|
||||
|
||||
goog.provide('goog.labs.structs.Map');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.labs.object');
|
||||
goog.require('goog.object');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new map.
|
||||
* @constructor
|
||||
* @struct
|
||||
* @final
|
||||
*/
|
||||
goog.labs.structs.Map = function() {
|
||||
// clear() initializes the map to the empty state.
|
||||
this.clear();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @type {function(this: Object, string): boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.labs.structs.Map.objectPropertyIsEnumerable_ =
|
||||
Object.prototype.propertyIsEnumerable;
|
||||
|
||||
|
||||
/**
|
||||
* @type {function(this: Object, string): boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.labs.structs.Map.objectHasOwnProperty_ =
|
||||
Object.prototype.hasOwnProperty;
|
||||
|
||||
|
||||
/**
|
||||
* Primary backing store of this map.
|
||||
* @type {!Object}
|
||||
* @private
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.map_;
|
||||
|
||||
|
||||
/**
|
||||
* Secondary backing store for keys. The index corresponds to the
|
||||
* index for secondaryStoreValues_.
|
||||
* @type {!Array<string>}
|
||||
* @private
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.secondaryStoreKeys_;
|
||||
|
||||
|
||||
/**
|
||||
* Secondary backing store for keys. The index corresponds to the
|
||||
* index for secondaryStoreValues_.
|
||||
* @type {!Array<*>}
|
||||
* @private
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.secondaryStoreValues_;
|
||||
|
||||
|
||||
/**
|
||||
* @private {number}
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.count_;
|
||||
|
||||
|
||||
/**
|
||||
* Adds the (key, value) pair, overriding previous entry with the same
|
||||
* key, if any.
|
||||
* @param {string} key The key.
|
||||
* @param {*} value The value.
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.set = function(key, value) {
|
||||
this.assertKeyIsString_(key);
|
||||
|
||||
var newKey = !this.hasKeyInPrimaryStore_(key);
|
||||
this.map_[key] = value;
|
||||
|
||||
// __proto__ is not settable on object.
|
||||
if (key == '__proto__' ||
|
||||
// Shadows for built-in properties are not enumerable in IE <= 8 .
|
||||
(!goog.labs.structs.Map.BrowserFeature.OBJECT_CREATE_SUPPORTED &&
|
||||
!goog.labs.structs.Map.objectPropertyIsEnumerable_.call(
|
||||
this.map_, key))) {
|
||||
delete this.map_[key];
|
||||
var index = goog.array.indexOf(this.secondaryStoreKeys_, key);
|
||||
if ((newKey = index < 0)) {
|
||||
index = this.secondaryStoreKeys_.length;
|
||||
}
|
||||
|
||||
this.secondaryStoreKeys_[index] = key;
|
||||
this.secondaryStoreValues_[index] = value;
|
||||
}
|
||||
|
||||
if (newKey) this.count_++;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the value for the given key.
|
||||
* @param {string} key The key whose value we want to retrieve.
|
||||
* @param {*=} opt_default The default value to return if the key does
|
||||
* not exist in the map, default to undefined.
|
||||
* @return {*} The value corresponding to the given key, or opt_default
|
||||
* if the key does not exist in this map.
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.get = function(key, opt_default) {
|
||||
this.assertKeyIsString_(key);
|
||||
|
||||
if (this.hasKeyInPrimaryStore_(key)) {
|
||||
return this.map_[key];
|
||||
}
|
||||
|
||||
var index = goog.array.indexOf(this.secondaryStoreKeys_, key);
|
||||
return index >= 0 ? this.secondaryStoreValues_[index] : opt_default;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes the map entry with the given key.
|
||||
* @param {string} key The key to remove.
|
||||
* @return {boolean} True if the entry is removed.
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.remove = function(key) {
|
||||
this.assertKeyIsString_(key);
|
||||
|
||||
if (this.hasKeyInPrimaryStore_(key)) {
|
||||
this.count_--;
|
||||
delete this.map_[key];
|
||||
return true;
|
||||
} else {
|
||||
var index = goog.array.indexOf(this.secondaryStoreKeys_, key);
|
||||
if (index >= 0) {
|
||||
this.count_--;
|
||||
goog.array.removeAt(this.secondaryStoreKeys_, index);
|
||||
goog.array.removeAt(this.secondaryStoreValues_, index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds the content of the map to this map. If a new entry uses a key
|
||||
* that already exists in this map, the existing key is replaced.
|
||||
* @param {!goog.labs.structs.Map} map The map to add.
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.addAll = function(map) {
|
||||
goog.array.forEach(map.getKeys(), function(key) {
|
||||
this.set(key, map.get(key));
|
||||
}, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} True if the map is empty.
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.isEmpty = function() {
|
||||
return !this.count_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} The number of the entries in this map.
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.getCount = function() {
|
||||
return this.count_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} key The key to check.
|
||||
* @return {boolean} True if the map contains the given key.
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.containsKey = function(key) {
|
||||
this.assertKeyIsString_(key);
|
||||
return this.hasKeyInPrimaryStore_(key) ||
|
||||
goog.array.contains(this.secondaryStoreKeys_, key);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Whether the map contains the given value. The comparison is done
|
||||
* using !== comparator. Also returns true if the passed value is NaN
|
||||
* and a NaN value exists in the map.
|
||||
* @param {*} value Value to check.
|
||||
* @return {boolean} True if the map contains the given value.
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.containsValue = function(value) {
|
||||
var found = goog.object.some(this.map_, function(v, k) {
|
||||
return this.hasKeyInPrimaryStore_(k) &&
|
||||
goog.labs.object.is(v, value);
|
||||
}, this);
|
||||
return found || goog.array.contains(this.secondaryStoreValues_, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {!Array<string>} An array of all the keys contained in this map.
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.getKeys = function() {
|
||||
var keys;
|
||||
if (goog.labs.structs.Map.BrowserFeature.OBJECT_KEYS_SUPPORTED) {
|
||||
keys = goog.array.clone(Object.keys(this.map_));
|
||||
} else {
|
||||
keys = [];
|
||||
for (var key in this.map_) {
|
||||
if (goog.labs.structs.Map.objectHasOwnProperty_.call(this.map_, key)) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
goog.array.extend(keys, this.secondaryStoreKeys_);
|
||||
return keys;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {!Array<*>} An array of all the values contained in this map.
|
||||
* There may be duplicates.
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.getValues = function() {
|
||||
var values = [];
|
||||
var keys = this.getKeys();
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
values.push(this.get(keys[i]));
|
||||
}
|
||||
return values;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {!Array<Array<?>>} An array of entries. Each entry is of the
|
||||
* form [key, value]. Do not rely on consistent ordering of entries.
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.getEntries = function() {
|
||||
var entries = [];
|
||||
var keys = this.getKeys();
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var key = keys[i];
|
||||
entries.push([key, this.get(key)]);
|
||||
}
|
||||
return entries;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the map to the initial state.
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.clear = function() {
|
||||
this.map_ = goog.labs.structs.Map.BrowserFeature.OBJECT_CREATE_SUPPORTED ?
|
||||
Object.create(null) : {};
|
||||
this.secondaryStoreKeys_ = [];
|
||||
this.secondaryStoreValues_ = [];
|
||||
this.count_ = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clones this map.
|
||||
* @return {!goog.labs.structs.Map} The clone of this map.
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.clone = function() {
|
||||
var map = new goog.labs.structs.Map();
|
||||
map.addAll(this);
|
||||
return map;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} key The key to check.
|
||||
* @return {boolean} True if the given key has been added successfully
|
||||
* to the primary store.
|
||||
* @private
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.hasKeyInPrimaryStore_ = function(key) {
|
||||
// New browsers that support Object.create do not allow setting of
|
||||
// __proto__. In other browsers, hasOwnProperty will return true for
|
||||
// __proto__ for object created with literal {}, so we need to
|
||||
// special case it.
|
||||
if (key == '__proto__') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (goog.labs.structs.Map.BrowserFeature.OBJECT_CREATE_SUPPORTED) {
|
||||
return key in this.map_;
|
||||
}
|
||||
|
||||
return goog.labs.structs.Map.objectHasOwnProperty_.call(this.map_, key);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that the given key is a string.
|
||||
* @param {string} key The key to check.
|
||||
* @private
|
||||
*/
|
||||
goog.labs.structs.Map.prototype.assertKeyIsString_ = function(key) {
|
||||
goog.asserts.assert(goog.isString(key), 'key must be a string.');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Browser feature enum necessary for map.
|
||||
* @enum {boolean}
|
||||
*/
|
||||
goog.labs.structs.Map.BrowserFeature = {
|
||||
// TODO(chrishenry): Replace with goog.userAgent detection.
|
||||
/**
|
||||
* Whether Object.create method is supported.
|
||||
*/
|
||||
OBJECT_CREATE_SUPPORTED: !!Object.create,
|
||||
|
||||
/**
|
||||
* Whether Object.keys method is supported.
|
||||
*/
|
||||
OBJECT_KEYS_SUPPORTED: !!Object.keys
|
||||
};
|
||||
@@ -0,0 +1,204 @@
|
||||
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Performance test for goog.structs.Map and
|
||||
* goog.labs.structs.Map. To run this test fairly, you would have to
|
||||
* compile this via JsCompiler (with --export_test_functions), and
|
||||
* pull the compiled JS into an empty HTML file.
|
||||
* @author chrishenry@google.com (Chris Henry)
|
||||
*/
|
||||
|
||||
goog.provide('goog.labs.structs.MapPerf');
|
||||
goog.setTestOnly('goog.labs.structs.MapPerf');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.labs.structs.Map');
|
||||
goog.require('goog.structs.Map');
|
||||
goog.require('goog.testing.PerformanceTable');
|
||||
goog.require('goog.testing.jsunit');
|
||||
|
||||
goog.scope(function() {
|
||||
var MapPerf = goog.labs.structs.MapPerf;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {goog.labs.structs.Map|goog.structs.Map}
|
||||
*/
|
||||
MapPerf.MapType;
|
||||
|
||||
|
||||
/**
|
||||
* @type {goog.testing.PerformanceTable}
|
||||
*/
|
||||
MapPerf.perfTable;
|
||||
|
||||
|
||||
/**
|
||||
* A key list. This maps loop index to key name to be used during
|
||||
* benchmark. This ensure that we do not need to pay the cost of
|
||||
* string concatenation/GC whenever we derive a key from loop index.
|
||||
*
|
||||
* This is filled once in setUpPage and then remain unchanged for the
|
||||
* rest of the test case.
|
||||
*
|
||||
* @type {!Array<string>}
|
||||
*/
|
||||
MapPerf.keyList = [];
|
||||
|
||||
|
||||
/**
|
||||
* Maxium number of keys in keyList (and, by extension, the map under
|
||||
* test).
|
||||
* @type {number}
|
||||
*/
|
||||
MapPerf.MAX_NUM_KEY = 10000;
|
||||
|
||||
|
||||
/**
|
||||
* Fills the given map with generated key-value pair.
|
||||
* @param {MapPerf.MapType} map The map to fill.
|
||||
* @param {number} numKeys The number of key-value pair to fill.
|
||||
*/
|
||||
MapPerf.fillMap = function(map, numKeys) {
|
||||
goog.asserts.assert(numKeys <= MapPerf.MAX_NUM_KEY);
|
||||
|
||||
for (var i = 0; i < numKeys; ++i) {
|
||||
map.set(MapPerf.keyList[i], i);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Primes the given map with deletion of keys.
|
||||
* @param {MapPerf.MapType} map The map to prime.
|
||||
* @return {MapPerf.MapType} The primed map (for chaining).
|
||||
*/
|
||||
MapPerf.primeMapWithDeletion = function(map) {
|
||||
for (var i = 0; i < 1000; ++i) {
|
||||
map.set(MapPerf.keyList[i], i);
|
||||
}
|
||||
for (var i = 0; i < 1000; ++i) {
|
||||
map.remove(MapPerf.keyList[i]);
|
||||
}
|
||||
return map;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Runs performance test for Map#get with the given map.
|
||||
* @param {MapPerf.MapType} map The map to stress.
|
||||
* @param {string} message Message to be put in performance table.
|
||||
*/
|
||||
MapPerf.runPerformanceTestForMapGet = function(map, message) {
|
||||
MapPerf.fillMap(map, 10000);
|
||||
|
||||
MapPerf.perfTable.run(
|
||||
function() {
|
||||
// Creates local alias for map and keyList.
|
||||
var localMap = map;
|
||||
var localKeyList = MapPerf.keyList;
|
||||
|
||||
for (var i = 0; i < 500; ++i) {
|
||||
var sum = 0;
|
||||
for (var j = 0; j < 10000; ++j) {
|
||||
sum += localMap.get(localKeyList[j]);
|
||||
}
|
||||
}
|
||||
},
|
||||
message);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Runs performance test for Map#set with the given map.
|
||||
* @param {MapPerf.MapType} map The map to stress.
|
||||
* @param {string} message Message to be put in performance table.
|
||||
*/
|
||||
MapPerf.runPerformanceTestForMapSet = function(map, message) {
|
||||
MapPerf.perfTable.run(
|
||||
function() {
|
||||
// Creates local alias for map and keyList.
|
||||
var localMap = map;
|
||||
var localKeyList = MapPerf.keyList;
|
||||
|
||||
for (var i = 0; i < 500; ++i) {
|
||||
for (var j = 0; j < 10000; ++j) {
|
||||
localMap.set(localKeyList[i], i);
|
||||
}
|
||||
}
|
||||
},
|
||||
message);
|
||||
};
|
||||
|
||||
|
||||
goog.global['setUpPage'] = function() {
|
||||
var content = goog.dom.createDom('div');
|
||||
goog.dom.insertChildAt(document.body, content, 0);
|
||||
var ua = navigator.userAgent;
|
||||
content.innerHTML =
|
||||
'<h1>Closure Performance Tests - Map</h1>' +
|
||||
'<p><strong>User-agent: </strong><span id="ua">' + ua + '</span></p>' +
|
||||
'<div id="perf-table"></div>' +
|
||||
'<hr>';
|
||||
|
||||
MapPerf.perfTable = new goog.testing.PerformanceTable(
|
||||
goog.dom.getElement('perf-table'));
|
||||
|
||||
// Fills keyList.
|
||||
for (var i = 0; i < MapPerf.MAX_NUM_KEY; ++i) {
|
||||
MapPerf.keyList.push('k' + i);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
goog.global['testGetFromLabsMap'] = function() {
|
||||
MapPerf.runPerformanceTestForMapGet(
|
||||
new goog.labs.structs.Map(), '#get: no previous deletion (Labs)');
|
||||
};
|
||||
|
||||
|
||||
goog.global['testGetFromOriginalMap'] = function() {
|
||||
MapPerf.runPerformanceTestForMapGet(
|
||||
new goog.structs.Map(), '#get: no previous deletion (Original)');
|
||||
};
|
||||
|
||||
|
||||
goog.global['testGetWithPreviousDeletionFromLabsMap'] = function() {
|
||||
MapPerf.runPerformanceTestForMapGet(
|
||||
MapPerf.primeMapWithDeletion(new goog.labs.structs.Map()),
|
||||
'#get: with previous deletion (Labs)');
|
||||
};
|
||||
|
||||
|
||||
goog.global['testGetWithPreviousDeletionFromOriginalMap'] = function() {
|
||||
MapPerf.runPerformanceTestForMapGet(
|
||||
MapPerf.primeMapWithDeletion(new goog.structs.Map()),
|
||||
'#get: with previous deletion (Original)');
|
||||
};
|
||||
|
||||
|
||||
goog.global['testSetFromLabsMap'] = function() {
|
||||
MapPerf.runPerformanceTestForMapSet(
|
||||
new goog.labs.structs.Map(), '#set: no previous deletion (Labs)');
|
||||
};
|
||||
|
||||
|
||||
goog.global['testSetFromOriginalMap'] = function() {
|
||||
MapPerf.runPerformanceTestForMapSet(
|
||||
new goog.structs.Map(), '#set: no previous deletion (Original)');
|
||||
};
|
||||
|
||||
}); // goog.scope
|
||||
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright 2012 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
Use of this source code is governed by the Apache License, Version 2.0.
|
||||
See the COPYING file for details.
|
||||
-->
|
||||
<!--
|
||||
Author: chrishenry@google.com (Chris Henry)
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>
|
||||
Closure Unit Tests - goog.labs.structs.Map
|
||||
</title>
|
||||
<script src="../../base.js">
|
||||
</script>
|
||||
<script>
|
||||
goog.require('goog.labs.structs.MapTest');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,432 @@
|
||||
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('goog.labs.structs.MapTest');
|
||||
goog.setTestOnly('goog.labs.structs.MapTest');
|
||||
|
||||
goog.require('goog.labs.structs.Map');
|
||||
goog.require('goog.testing.PropertyReplacer');
|
||||
goog.require('goog.testing.jsunit');
|
||||
|
||||
var map;
|
||||
var stubs;
|
||||
|
||||
function setUpPage() {
|
||||
stubs = new goog.testing.PropertyReplacer();
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
map = new goog.labs.structs.Map();
|
||||
}
|
||||
|
||||
|
||||
function testSet() {
|
||||
var key = 'test';
|
||||
var value = 'value';
|
||||
map.set(key, value);
|
||||
assertEquals(value, map.get(key));
|
||||
}
|
||||
|
||||
|
||||
function testSetsWithSameKey() {
|
||||
var key = 'test';
|
||||
var value = 'value';
|
||||
var value2 = 'value2';
|
||||
map.set(key, value);
|
||||
map.set(key, value2);
|
||||
assertEquals(value2, map.get(key));
|
||||
}
|
||||
|
||||
|
||||
function testSetWithUndefinedValue() {
|
||||
var key = 'test';
|
||||
map.set(key, undefined);
|
||||
assertUndefined(map.get(key));
|
||||
}
|
||||
|
||||
|
||||
function testSetWithUnderUnderProtoUnderUnder() {
|
||||
var key = '__proto__';
|
||||
var value = 'value';
|
||||
var value2 = 'value2';
|
||||
|
||||
map.set(key, value);
|
||||
assertEquals(value, map.get(key));
|
||||
|
||||
map.set(key, value2);
|
||||
assertEquals(value2, map.get(key));
|
||||
}
|
||||
|
||||
|
||||
function testSetWithBuiltInPropertyShadows() {
|
||||
var key = 'toString';
|
||||
var value = 'value';
|
||||
var key2 = 'hasOwnProperty';
|
||||
var value2 = 'value2';
|
||||
|
||||
map.set(key, value);
|
||||
map.set(key2, value2);
|
||||
assertEquals(value, map.get(key));
|
||||
assertEquals(value2, map.get(key2));
|
||||
|
||||
map.set(key, value2);
|
||||
map.set(key2, value);
|
||||
assertEquals(value2, map.get(key));
|
||||
assertEquals(value, map.get(key2));
|
||||
}
|
||||
|
||||
|
||||
function testGetBeforeSetOfUnderUnderProtoUnderUnder() {
|
||||
assertUndefined(map.get('__proto__'));
|
||||
}
|
||||
|
||||
|
||||
function testContainsKey() {
|
||||
assertFalse(map.containsKey('key'));
|
||||
assertFalse(map.containsKey('__proto__'));
|
||||
assertFalse(map.containsKey('toString'));
|
||||
assertFalse(map.containsKey('hasOwnProperty'));
|
||||
assertFalse(map.containsKey('key2'));
|
||||
assertFalse(map.containsKey('key3'));
|
||||
assertFalse(map.containsKey('key4'));
|
||||
|
||||
map.set('key', 'v');
|
||||
map.set('__proto__', 'v');
|
||||
map.set('toString', 'v');
|
||||
map.set('hasOwnProperty', 'v');
|
||||
map.set('key2', undefined);
|
||||
map.set('key3', null);
|
||||
map.set('key4', '');
|
||||
|
||||
assertTrue(map.containsKey('key'));
|
||||
assertTrue(map.containsKey('__proto__'));
|
||||
assertTrue(map.containsKey('toString'));
|
||||
assertTrue(map.containsKey('hasOwnProperty'));
|
||||
assertTrue(map.containsKey('key2'));
|
||||
assertTrue(map.containsKey('key3'));
|
||||
assertTrue(map.containsKey('key4'));
|
||||
}
|
||||
|
||||
|
||||
function testContainsValueWithShadowKeys() {
|
||||
assertFalse(map.containsValue('v2'));
|
||||
assertFalse(map.containsValue('v3'));
|
||||
assertFalse(map.containsValue('v4'));
|
||||
|
||||
map.set('__proto__', 'v2');
|
||||
map.set('toString', 'v3');
|
||||
map.set('hasOwnProperty', 'v4');
|
||||
|
||||
assertTrue(map.containsValue('v2'));
|
||||
assertTrue(map.containsValue('v3'));
|
||||
assertTrue(map.containsValue('v4'));
|
||||
|
||||
assertFalse(map.containsValue(Object.prototype.toString));
|
||||
assertFalse(map.containsValue(Object.prototype.hasOwnProperty));
|
||||
}
|
||||
|
||||
|
||||
function testContainsValueWithNullAndUndefined() {
|
||||
assertFalse(map.containsValue(undefined));
|
||||
assertFalse(map.containsValue(null));
|
||||
|
||||
map.set('key2', undefined);
|
||||
map.set('key3', null);
|
||||
|
||||
assertTrue(map.containsValue(undefined));
|
||||
assertTrue(map.containsValue(null));
|
||||
}
|
||||
|
||||
|
||||
function testContainsValueWithNumber() {
|
||||
assertFalse(map.containsValue(-1));
|
||||
assertFalse(map.containsValue(0));
|
||||
assertFalse(map.containsValue(1));
|
||||
map.set('key', -1);
|
||||
map.set('key2', 0);
|
||||
map.set('key3', 1);
|
||||
assertTrue(map.containsValue(-1));
|
||||
assertTrue(map.containsValue(0));
|
||||
assertTrue(map.containsValue(1));
|
||||
}
|
||||
|
||||
|
||||
function testContainsValueWithNaN() {
|
||||
assertFalse(map.containsValue(NaN));
|
||||
map.set('key', NaN);
|
||||
assertTrue(map.containsValue(NaN));
|
||||
}
|
||||
|
||||
|
||||
function testContainsValueWithNegativeZero() {
|
||||
assertFalse(map.containsValue(-0));
|
||||
map.set('key', -0);
|
||||
assertTrue(map.containsValue(-0));
|
||||
assertFalse(map.containsValue(0));
|
||||
|
||||
map.set('key', 0);
|
||||
assertFalse(map.containsValue(-0));
|
||||
assertTrue(map.containsValue(0));
|
||||
}
|
||||
|
||||
|
||||
function testContainsValueWithStrings() {
|
||||
assertFalse(map.containsValue(''));
|
||||
assertFalse(map.containsValue('v'));
|
||||
map.set('key', '');
|
||||
map.set('key2', 'v');
|
||||
assertTrue(map.containsValue(''));
|
||||
assertTrue(map.containsValue('v'));
|
||||
}
|
||||
|
||||
function testRemove() {
|
||||
map.set('key', 'v');
|
||||
map.set('__proto__', 'v2');
|
||||
map.set('toString', 'v3');
|
||||
map.set('hasOwnProperty', 'v4');
|
||||
map.set('key2', undefined);
|
||||
map.set('key3', null);
|
||||
map.set('key4', '');
|
||||
|
||||
assertFalse(map.remove('key do not exist'));
|
||||
|
||||
assertTrue(map.remove('key'));
|
||||
assertFalse(map.containsKey('key'));
|
||||
assertFalse(map.remove('key'));
|
||||
|
||||
assertTrue(map.remove('__proto__'));
|
||||
assertFalse(map.containsKey('__proto__'));
|
||||
assertFalse(map.remove('__proto__'));
|
||||
|
||||
assertTrue(map.remove('toString'));
|
||||
assertFalse(map.containsKey('toString'));
|
||||
assertFalse(map.remove('toString'));
|
||||
|
||||
assertTrue(map.remove('hasOwnProperty'));
|
||||
assertFalse(map.containsKey('hasOwnProperty'));
|
||||
assertFalse(map.remove('hasOwnProperty'));
|
||||
|
||||
assertTrue(map.remove('key2'));
|
||||
assertFalse(map.containsKey('key2'));
|
||||
assertFalse(map.remove('key2'));
|
||||
|
||||
assertTrue(map.remove('key3'));
|
||||
assertFalse(map.containsKey('key3'));
|
||||
assertFalse(map.remove('key3'));
|
||||
|
||||
assertTrue('', map.remove('key4'));
|
||||
assertFalse(map.containsKey('key4'));
|
||||
assertFalse(map.remove('key4'));
|
||||
}
|
||||
|
||||
|
||||
function testGetCountAndIsEmpty() {
|
||||
assertEquals(0, map.getCount());
|
||||
assertTrue(map.isEmpty());
|
||||
|
||||
map.set('key', 'v');
|
||||
assertEquals(1, map.getCount());
|
||||
map.set('__proto__', 'v2');
|
||||
assertEquals(2, map.getCount());
|
||||
map.set('toString', 'v3');
|
||||
assertEquals(3, map.getCount());
|
||||
map.set('hasOwnProperty', 'v4');
|
||||
assertEquals(4, map.getCount());
|
||||
|
||||
map.set('key', 'a');
|
||||
assertEquals(4, map.getCount());
|
||||
map.set('__proto__', 'a2');
|
||||
assertEquals(4, map.getCount());
|
||||
map.set('toString', 'a3');
|
||||
assertEquals(4, map.getCount());
|
||||
map.set('hasOwnProperty', 'a4');
|
||||
assertEquals(4, map.getCount());
|
||||
|
||||
map.remove('key');
|
||||
assertEquals(3, map.getCount());
|
||||
map.remove('__proto__');
|
||||
assertEquals(2, map.getCount());
|
||||
map.remove('toString');
|
||||
assertEquals(1, map.getCount());
|
||||
map.remove('hasOwnProperty');
|
||||
assertEquals(0, map.getCount());
|
||||
}
|
||||
|
||||
|
||||
function testClear() {
|
||||
map.set('key', 'v');
|
||||
map.set('__proto__', 'v');
|
||||
map.set('toString', 'v');
|
||||
map.set('hasOwnProperty', 'v');
|
||||
map.set('key2', undefined);
|
||||
map.set('key3', null);
|
||||
map.set('key4', '');
|
||||
|
||||
map.clear();
|
||||
|
||||
assertFalse(map.containsKey('key'));
|
||||
assertFalse(map.containsKey('__proto__'));
|
||||
assertFalse(map.containsKey('toString'));
|
||||
assertFalse(map.containsKey('hasOwnProperty'));
|
||||
assertFalse(map.containsKey('key2'));
|
||||
assertFalse(map.containsKey('key3'));
|
||||
assertFalse(map.containsKey('key4'));
|
||||
}
|
||||
|
||||
|
||||
function testGetEntries() {
|
||||
map.set('key', 'v');
|
||||
map.set('__proto__', 'v');
|
||||
map.set('toString', 'v');
|
||||
map.set('hasOwnProperty', 'v');
|
||||
map.set('key2', undefined);
|
||||
map.set('key3', null);
|
||||
map.set('key4', '');
|
||||
|
||||
var entries = map.getEntries();
|
||||
assertEquals(7, entries.length);
|
||||
assertContainsEntry(['key', 'v'], entries);
|
||||
assertContainsEntry(['__proto__', 'v'], entries);
|
||||
assertContainsEntry(['toString', 'v'], entries);
|
||||
assertContainsEntry(['hasOwnProperty', 'v'], entries);
|
||||
assertContainsEntry(['key2', undefined], entries);
|
||||
assertContainsEntry(['key3', null], entries);
|
||||
assertContainsEntry(['key4', ''], entries);
|
||||
}
|
||||
|
||||
|
||||
function testGetKeys() {
|
||||
map.set('key', 'v');
|
||||
map.set('__proto__', 'v');
|
||||
map.set('toString', 'v');
|
||||
map.set('hasOwnProperty', 'v');
|
||||
map.set('key2', undefined);
|
||||
map.set('key3', null);
|
||||
map.set('k4', '');
|
||||
|
||||
var values = map.getKeys();
|
||||
assertSameElements(
|
||||
['key', '__proto__', 'toString', 'hasOwnProperty', 'key2', 'key3', 'k4'],
|
||||
values);
|
||||
}
|
||||
|
||||
|
||||
function testGetValues() {
|
||||
map.set('key', 'v');
|
||||
map.set('__proto__', 'v');
|
||||
map.set('toString', 'v');
|
||||
map.set('hasOwnProperty', 'v');
|
||||
map.set('key2', undefined);
|
||||
map.set('key3', null);
|
||||
map.set('key4', '');
|
||||
|
||||
var values = map.getValues();
|
||||
assertSameElements(['v', 'v', 'v', 'v', undefined, null, ''], values);
|
||||
}
|
||||
|
||||
|
||||
function testAddAllToEmptyMap() {
|
||||
map.set('key', 'v');
|
||||
map.set('key2', 'v2');
|
||||
map.set('key3', 'v3');
|
||||
map.set('key4', 'v4');
|
||||
|
||||
var map2 = new goog.labs.structs.Map();
|
||||
map2.addAll(map);
|
||||
|
||||
assertEquals(4, map2.getCount());
|
||||
assertEquals('v', map2.get('key'));
|
||||
assertEquals('v2', map2.get('key2'));
|
||||
assertEquals('v3', map2.get('key3'));
|
||||
assertEquals('v4', map2.get('key4'));
|
||||
}
|
||||
|
||||
|
||||
function testAddAllToNonEmptyMap() {
|
||||
map.set('key', 'v');
|
||||
map.set('key2', 'v2');
|
||||
map.set('key3', 'v3');
|
||||
map.set('key4', 'v4');
|
||||
|
||||
var map2 = new goog.labs.structs.Map();
|
||||
map2.set('key0', 'o');
|
||||
map2.set('key', 'o');
|
||||
map2.set('key2', 'o2');
|
||||
map2.set('key3', 'o3');
|
||||
map2.addAll(map);
|
||||
|
||||
assertEquals(5, map2.getCount());
|
||||
assertEquals('o', map2.get('key0'));
|
||||
assertEquals('v', map2.get('key'));
|
||||
assertEquals('v2', map2.get('key2'));
|
||||
assertEquals('v3', map2.get('key3'));
|
||||
assertEquals('v4', map2.get('key4'));
|
||||
}
|
||||
|
||||
|
||||
function testClone() {
|
||||
map.set('key', 'v');
|
||||
map.set('key2', 'v2');
|
||||
map.set('key3', 'v3');
|
||||
map.set('key4', 'v4');
|
||||
|
||||
var map2 = map.clone();
|
||||
|
||||
assertEquals(4, map2.getCount());
|
||||
assertEquals('v', map2.get('key'));
|
||||
assertEquals('v2', map2.get('key2'));
|
||||
assertEquals('v3', map2.get('key3'));
|
||||
assertEquals('v4', map2.get('key4'));
|
||||
}
|
||||
|
||||
|
||||
function testMapWithModifiedObjectPrototype() {
|
||||
stubs.set(Object.prototype, 'toString', function() {});
|
||||
stubs.set(Object.prototype, 'foo', function() {});
|
||||
stubs.set(Object.prototype, 'field', 100);
|
||||
stubs.set(Object.prototype, 'fooKey', function() {});
|
||||
|
||||
map = new goog.labs.structs.Map();
|
||||
map.set('key', 'v');
|
||||
map.set('key2', 'v2');
|
||||
map.set('fooKey', 'v3');
|
||||
|
||||
assertTrue(map.containsKey('key'));
|
||||
assertTrue(map.containsKey('key2'));
|
||||
assertTrue(map.containsKey('fooKey'));
|
||||
assertFalse(map.containsKey('toString'));
|
||||
assertFalse(map.containsKey('foo'));
|
||||
assertFalse(map.containsKey('field'));
|
||||
|
||||
assertTrue(map.containsValue('v'));
|
||||
assertTrue(map.containsValue('v2'));
|
||||
assertTrue(map.containsValue('v3'));
|
||||
assertFalse(map.containsValue(100));
|
||||
|
||||
var entries = map.getEntries();
|
||||
assertEquals(3, entries.length);
|
||||
assertContainsEntry(['key', 'v'], entries);
|
||||
assertContainsEntry(['key2', 'v2'], entries);
|
||||
assertContainsEntry(['fooKey', 'v3'], entries);
|
||||
}
|
||||
|
||||
|
||||
function assertContainsEntry(entry, entryList) {
|
||||
for (var i = 0; i < entryList.length; ++i) {
|
||||
if (entry[0] == entryList[i][0] && entry[1] === entryList[i][1]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
fail('Did not find entry: ' + entry + ' in: ' + entryList);
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview A collection similar to
|
||||
* {@code goog.labs.structs.Map}, but also allows associating multiple
|
||||
* values with a single key.
|
||||
*
|
||||
* This implementation ensures that you can use any string keys.
|
||||
*
|
||||
* @author chrishenry@google.com (Chris Henry)
|
||||
*/
|
||||
|
||||
goog.provide('goog.labs.structs.Multimap');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.labs.object');
|
||||
goog.require('goog.labs.structs.Map');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new multimap.
|
||||
* @constructor
|
||||
* @struct
|
||||
* @final
|
||||
*/
|
||||
goog.labs.structs.Multimap = function() {
|
||||
this.clear();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The backing map.
|
||||
* @type {!goog.labs.structs.Map}
|
||||
* @private
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.map_;
|
||||
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.count_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Clears the multimap.
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.clear = function() {
|
||||
this.count_ = 0;
|
||||
this.map_ = new goog.labs.structs.Map();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clones this multimap.
|
||||
* @return {!goog.labs.structs.Multimap} A multimap that contains all
|
||||
* the mapping this multimap has.
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.clone = function() {
|
||||
var map = new goog.labs.structs.Multimap();
|
||||
map.addAllFromMultimap(this);
|
||||
return map;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds the given (key, value) pair to the map. The (key, value) pair
|
||||
* is guaranteed to be added.
|
||||
* @param {string} key The key to add.
|
||||
* @param {*} value The value to add.
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.add = function(key, value) {
|
||||
var values = this.map_.get(key);
|
||||
if (!values) {
|
||||
this.map_.set(key, (values = []));
|
||||
}
|
||||
|
||||
values.push(value);
|
||||
this.count_++;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Stores a collection of values to the given key. Does not replace
|
||||
* existing (key, value) pairs.
|
||||
* @param {string} key The key to add.
|
||||
* @param {!Array<*>} values The values to add.
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.addAllValues = function(key, values) {
|
||||
goog.array.forEach(values, function(v) {
|
||||
this.add(key, v);
|
||||
}, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds the contents of the given map/multimap to this multimap.
|
||||
* @param {!(goog.labs.structs.Map|goog.labs.structs.Multimap)} map The
|
||||
* map to add.
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.addAllFromMultimap = function(map) {
|
||||
goog.array.forEach(map.getEntries(), function(entry) {
|
||||
this.add(entry[0], entry[1]);
|
||||
}, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Replaces all the values for the given key with the given values.
|
||||
* @param {string} key The key whose values are to be replaced.
|
||||
* @param {!Array<*>} values The new values. If empty, this is
|
||||
* equivalent to {@code removaAll(key)}.
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.replaceValues = function(key, values) {
|
||||
this.removeAll(key);
|
||||
this.addAllValues(key, values);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the values correspond to the given key.
|
||||
* @param {string} key The key to retrieve.
|
||||
* @return {!Array<*>} An array of values corresponding to the given
|
||||
* key. May be empty. Note that the ordering of values are not
|
||||
* guaranteed to be consistent.
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.get = function(key) {
|
||||
var values = /** @type {Array<*>} */ (this.map_.get(key));
|
||||
return values ? goog.array.clone(values) : [];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes a single occurrence of (key, value) pair.
|
||||
* @param {string} key The key to remove.
|
||||
* @param {*} value The value to remove.
|
||||
* @return {boolean} Whether any matching (key, value) pair is removed.
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.remove = function(key, value) {
|
||||
var values = /** @type {Array<*>} */ (this.map_.get(key));
|
||||
if (!values) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var removed = goog.array.removeIf(values, function(v) {
|
||||
return goog.labs.object.is(value, v);
|
||||
});
|
||||
|
||||
if (removed) {
|
||||
this.count_--;
|
||||
if (values.length == 0) {
|
||||
this.map_.remove(key);
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes all values corresponding to the given key.
|
||||
* @param {string} key The key whose values are to be removed.
|
||||
* @return {boolean} Whether any value is removed.
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.removeAll = function(key) {
|
||||
// We have to first retrieve the values from the backing map because
|
||||
// we need to keep track of count (and correctly calculates the
|
||||
// return value). values may be undefined.
|
||||
var values = this.map_.get(key);
|
||||
if (this.map_.remove(key)) {
|
||||
this.count_ -= values.length;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the multimap is empty.
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.isEmpty = function() {
|
||||
return !this.count_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} The count of (key, value) pairs in the map.
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.getCount = function() {
|
||||
return this.count_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} key The key to check.
|
||||
* @param {*} value The value to check.
|
||||
* @return {boolean} Whether the (key, value) pair exists in the multimap.
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.containsEntry = function(key, value) {
|
||||
var values = /** @type {Array<*>} */ (this.map_.get(key));
|
||||
if (!values) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var index = goog.array.findIndex(values, function(v) {
|
||||
return goog.labs.object.is(v, value);
|
||||
});
|
||||
return index >= 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} key The key to check.
|
||||
* @return {boolean} Whether the multimap contains at least one (key,
|
||||
* value) pair with the given key.
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.containsKey = function(key) {
|
||||
return this.map_.containsKey(key);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {*} value The value to check.
|
||||
* @return {boolean} Whether the multimap contains at least one (key,
|
||||
* value) pair with the given value.
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.containsValue = function(value) {
|
||||
return goog.array.some(this.map_.getValues(),
|
||||
function(values) {
|
||||
return goog.array.some(/** @type {Array<?>} */ (values), function(v) {
|
||||
return goog.labs.object.is(v, value);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {!Array<string>} An array of unique keys.
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.getKeys = function() {
|
||||
return this.map_.getKeys();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {!Array<*>} An array of values. There may be duplicates.
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.getValues = function() {
|
||||
return goog.array.flatten(this.map_.getValues());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {!Array<!Array<?>>} An array of entries. Each entry is of the
|
||||
* form [key, value].
|
||||
*/
|
||||
goog.labs.structs.Multimap.prototype.getEntries = function() {
|
||||
var keys = this.getKeys();
|
||||
var entries = [];
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var key = keys[i];
|
||||
var values = this.get(key);
|
||||
for (var j = 0; j < values.length; j++) {
|
||||
entries.push([key, values[j]]);
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright 2012 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
Use of this source code is governed by the Apache License, Version 2.0.
|
||||
See the COPYING file for details.
|
||||
-->
|
||||
<!--
|
||||
Author: chrishenry@google.com (Chris Henry)
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>
|
||||
Closure Unit Tests - goog.labs.structs.Multimap
|
||||
</title>
|
||||
<script src="../../base.js">
|
||||
</script>
|
||||
<script>
|
||||
goog.require('goog.labs.structs.MultimapTest');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,328 @@
|
||||
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('goog.labs.structs.MultimapTest');
|
||||
goog.setTestOnly('goog.labs.structs.MultimapTest');
|
||||
|
||||
goog.require('goog.labs.structs.Map');
|
||||
goog.require('goog.labs.structs.Multimap');
|
||||
goog.require('goog.testing.jsunit');
|
||||
|
||||
var map;
|
||||
|
||||
|
||||
function setUp() {
|
||||
map = new goog.labs.structs.Multimap();
|
||||
}
|
||||
|
||||
|
||||
function testGetCountWithEmptyMultimap() {
|
||||
assertEquals(0, map.getCount());
|
||||
assertTrue(map.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
function testClone() {
|
||||
map.add('k', 'v');
|
||||
map.addAllValues('k2', ['v', 'v1', 'v2']);
|
||||
|
||||
var map2 = map.clone();
|
||||
|
||||
assertSameElements(['v'], map.get('k'));
|
||||
assertSameElements(['v', 'v1', 'v2'], map.get('k2'));
|
||||
}
|
||||
|
||||
|
||||
function testAdd() {
|
||||
map.add('key', 'v');
|
||||
assertEquals(1, map.getCount());
|
||||
map.add('key', 'v2');
|
||||
assertEquals(2, map.getCount());
|
||||
map.add('key', 'v3');
|
||||
assertEquals(3, map.getCount());
|
||||
|
||||
var values = map.get('key');
|
||||
assertEquals(3, values.length);
|
||||
assertContains('v', values);
|
||||
assertContains('v2', values);
|
||||
assertContains('v3', values);
|
||||
}
|
||||
|
||||
|
||||
function testAddValues() {
|
||||
map.addAllValues('key', ['v', 'v2', 'v3']);
|
||||
assertSameElements(['v', 'v2', 'v3'], map.get('key'));
|
||||
|
||||
map.add('key2', 'a');
|
||||
map.addAllValues('key2', ['v', 'v2', 'v3']);
|
||||
assertSameElements(['a', 'v', 'v2', 'v3'], map.get('key2'));
|
||||
}
|
||||
|
||||
|
||||
function testAddAllWithMultimap() {
|
||||
map.add('k', 'v');
|
||||
map.addAllValues('k2', ['v', 'v1', 'v2']);
|
||||
|
||||
var map2 = new goog.labs.structs.Multimap();
|
||||
map2.add('k2', 'v');
|
||||
map2.addAllValues('k3', ['a', 'a1', 'a2']);
|
||||
|
||||
map.addAllFromMultimap(map2);
|
||||
assertSameElements(['v'], map.get('k'));
|
||||
assertSameElements(['v', 'v1', 'v2', 'v'], map.get('k2'));
|
||||
assertSameElements(['a', 'a1', 'a2'], map.get('k3'));
|
||||
}
|
||||
|
||||
|
||||
function testAddAllWithMap() {
|
||||
map.add('k', 'v');
|
||||
map.addAllValues('k2', ['v', 'v1', 'v2']);
|
||||
|
||||
var map2 = new goog.labs.structs.Map();
|
||||
map2.set('k2', 'v');
|
||||
map2.set('k3', 'a');
|
||||
|
||||
map.addAllFromMultimap(map2);
|
||||
assertSameElements(['v'], map.get('k'));
|
||||
assertSameElements(['v', 'v1', 'v2', 'v'], map.get('k2'));
|
||||
assertSameElements(['a'], map.get('k3'));
|
||||
}
|
||||
|
||||
|
||||
function testReplaceValues() {
|
||||
map.add('key', 'v');
|
||||
map.add('key', 'v2');
|
||||
|
||||
map.replaceValues('key', [0, 1, 2]);
|
||||
assertSameElements([0, 1, 2], map.get('key'));
|
||||
assertEquals(3, map.getCount());
|
||||
|
||||
map.replaceValues('key', ['v']);
|
||||
assertSameElements(['v'], map.get('key'));
|
||||
assertEquals(1, map.getCount());
|
||||
|
||||
map.replaceValues('key', []);
|
||||
assertSameElements([], map.get('key'));
|
||||
assertEquals(0, map.getCount());
|
||||
}
|
||||
|
||||
|
||||
function testRemove() {
|
||||
map.add('key', 'v');
|
||||
map.add('key', 'v2');
|
||||
map.add('key', 'v3');
|
||||
|
||||
assertTrue(map.remove('key', 'v'));
|
||||
var values = map.get('key');
|
||||
assertEquals(2, map.getCount());
|
||||
assertEquals(2, values.length);
|
||||
assertContains('v2', values);
|
||||
assertContains('v3', values);
|
||||
assertFalse(map.remove('key', 'v'));
|
||||
|
||||
assertTrue(map.remove('key', 'v2'));
|
||||
values = map.get('key');
|
||||
assertEquals(1, map.getCount());
|
||||
assertEquals(1, values.length);
|
||||
assertContains('v3', values);
|
||||
assertFalse(map.remove('key', 'v2'));
|
||||
|
||||
assertTrue(map.remove('key', 'v3'));
|
||||
map.remove('key', 'v3');
|
||||
assertTrue(map.isEmpty());
|
||||
assertEquals(0, map.get('key').length);
|
||||
assertFalse(map.remove('key', 'v2'));
|
||||
}
|
||||
|
||||
|
||||
function testRemoveWithNaN() {
|
||||
map.add('key', NaN);
|
||||
map.add('key', NaN);
|
||||
|
||||
assertTrue(map.remove('key', NaN));
|
||||
var values = map.get('key');
|
||||
assertEquals(1, values.length);
|
||||
assertTrue(isNaN(values[0]));
|
||||
|
||||
assertTrue(map.remove('key', NaN));
|
||||
assertEquals(0, map.get('key').length);
|
||||
assertFalse(map.remove('key', NaN));
|
||||
}
|
||||
|
||||
|
||||
function testRemoveWithNegativeZero() {
|
||||
map.add('key', 0);
|
||||
map.add('key', -0);
|
||||
|
||||
assertTrue(map.remove('key', -0));
|
||||
var values = map.get('key');
|
||||
assertEquals(1, values.length);
|
||||
assertTrue(1 / values[0] === 1 / 0);
|
||||
assertFalse(map.remove('key', -0));
|
||||
|
||||
map.add('key', -0);
|
||||
|
||||
assertTrue(map.remove('key', 0));
|
||||
var values = map.get('key');
|
||||
assertEquals(1, values.length);
|
||||
assertTrue(1 / values[0] === 1 / -0);
|
||||
assertFalse(map.remove('key', 0));
|
||||
|
||||
assertTrue(map.remove('key', -0));
|
||||
assertEquals(0, map.get('key').length);
|
||||
}
|
||||
|
||||
|
||||
function testRemoveAll() {
|
||||
map.add('key', 'v');
|
||||
map.add('key', 'v2');
|
||||
map.add('key', 'v3');
|
||||
map.add('key', 'v4');
|
||||
map.add('key2', 'v');
|
||||
|
||||
assertTrue(map.removeAll('key'));
|
||||
assertSameElements([], map.get('key'));
|
||||
assertSameElements(['v'], map.get('key2'));
|
||||
assertFalse(map.removeAll('key'));
|
||||
assertEquals(1, map.getCount());
|
||||
|
||||
assertTrue(map.removeAll('key2'));
|
||||
assertSameElements([], map.get('key2'));
|
||||
assertFalse(map.removeAll('key2'));
|
||||
assertTrue(map.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
function testAddWithDuplicateValue() {
|
||||
map.add('key', 'v');
|
||||
map.add('key', 'v');
|
||||
map.add('key', 'v');
|
||||
assertArrayEquals(['v', 'v', 'v'], map.get('key'));
|
||||
}
|
||||
|
||||
|
||||
function testContainsEntry() {
|
||||
assertFalse(map.containsEntry('k', 'v'));
|
||||
assertFalse(map.containsEntry('k', 'v2'));
|
||||
assertFalse(map.containsEntry('k2', 'v'));
|
||||
|
||||
map.add('k', 'v');
|
||||
assertTrue(map.containsEntry('k', 'v'));
|
||||
assertFalse(map.containsEntry('k', 'v2'));
|
||||
assertFalse(map.containsEntry('k2', 'v'));
|
||||
|
||||
map.add('k', 'v2');
|
||||
assertTrue(map.containsEntry('k', 'v'));
|
||||
assertTrue(map.containsEntry('k', 'v2'));
|
||||
assertFalse(map.containsEntry('k2', 'v'));
|
||||
|
||||
map.add('k2', 'v');
|
||||
assertTrue(map.containsEntry('k', 'v'));
|
||||
assertTrue(map.containsEntry('k', 'v2'));
|
||||
assertTrue(map.containsEntry('k2', 'v'));
|
||||
}
|
||||
|
||||
|
||||
function testContainsKey() {
|
||||
assertFalse(map.containsKey('k'));
|
||||
assertFalse(map.containsKey('k2'));
|
||||
|
||||
map.add('k', 'v');
|
||||
assertTrue(map.containsKey('k'));
|
||||
map.add('k2', 'v');
|
||||
assertTrue(map.containsKey('k2'));
|
||||
|
||||
map.remove('k', 'v');
|
||||
assertFalse(map.containsKey('k'));
|
||||
map.remove('k2', 'v');
|
||||
assertFalse(map.containsKey('k2'));
|
||||
}
|
||||
|
||||
|
||||
function testContainsValue() {
|
||||
assertFalse(map.containsValue('v'));
|
||||
assertFalse(map.containsValue('v2'));
|
||||
|
||||
map.add('key', 'v');
|
||||
assertTrue(map.containsValue('v'));
|
||||
map.add('key', 'v2');
|
||||
assertTrue(map.containsValue('v2'));
|
||||
}
|
||||
|
||||
|
||||
function testGetEntries() {
|
||||
map.add('key', 'v');
|
||||
map.add('key', 'v2');
|
||||
map.add('key2', 'v3');
|
||||
|
||||
var entries = map.getEntries();
|
||||
assertEquals(3, entries.length);
|
||||
assertContainsEntry(['key', 'v'], entries);
|
||||
assertContainsEntry(['key', 'v2'], entries);
|
||||
assertContainsEntry(['key2', 'v3'], entries);
|
||||
}
|
||||
|
||||
|
||||
function testGetKeys() {
|
||||
map.add('key', 'v');
|
||||
map.add('key', 'v2');
|
||||
map.add('key2', 'v3');
|
||||
map.add('key3', 'v4');
|
||||
map.removeAll('key3');
|
||||
|
||||
assertSameElements(['key', 'key2'], map.getKeys());
|
||||
}
|
||||
|
||||
|
||||
function testGetKeys() {
|
||||
map.add('key', 'v');
|
||||
map.add('key', 'v2');
|
||||
map.add('key2', 'v2');
|
||||
map.add('key3', 'v4');
|
||||
map.removeAll('key3');
|
||||
|
||||
assertSameElements(['v', 'v2', 'v2'], map.getValues());
|
||||
}
|
||||
|
||||
|
||||
function testGetReturnsDefensiveCopyOfUnderlyingData() {
|
||||
map.add('key', 'v');
|
||||
map.add('key', 'v2');
|
||||
map.add('key', 'v3');
|
||||
|
||||
var values = map.get('key');
|
||||
values.push('v4');
|
||||
assertFalse(map.containsEntry('key', 'v4'));
|
||||
}
|
||||
|
||||
|
||||
function testClear() {
|
||||
map.add('key', 'v');
|
||||
map.add('key', 'v2');
|
||||
map.add('key2', 'v3');
|
||||
|
||||
map.clear();
|
||||
assertTrue(map.isEmpty());
|
||||
assertSameElements([], map.getEntries());
|
||||
}
|
||||
|
||||
|
||||
function assertContainsEntry(entry, entryList) {
|
||||
for (var i = 0; i < entryList.length; ++i) {
|
||||
if (entry[0] == entryList[i][0] && entry[1] === entryList[i][1]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
fail('Did not find entry: ' + entry + ' in: ' + entryList);
|
||||
}
|
||||
Reference in New Issue
Block a user