diff --git a/externs/olx.js b/externs/olx.js index fbd98e7ae2..01445f7d27 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -8,6 +8,21 @@ var olx; /* typedefs for object literals provided by applications */ +/** + * @typedef {{unique: (boolean|undefined)}} + */ +olx.CollectionOptions; + + +/** + * Disallow the same item from being added to the collection twice. Default is + * false. + * @type {boolean|undefined} + * @api + */ +olx.CollectionOptions.prototype.unique; + + /** * @typedef {{html: string, * tileRanges: (Object.>|undefined)}} diff --git a/src/ol/collection.js b/src/ol/collection.js index c1b3fd8d28..c21bc2f867 100644 --- a/src/ol/collection.js +++ b/src/ol/collection.js @@ -23,19 +23,34 @@ goog.require('ol.events.Event'); * @extends {ol.Object} * @fires ol.Collection.Event * @param {!Array.=} opt_array Array. + * @param {olx.CollectionOptions=} opt_options Collection options. * @template T * @api */ -ol.Collection = function(opt_array) { +ol.Collection = function(opt_array, opt_options) { ol.Object.call(this); + var options = opt_options || {}; + + /** + * @private + * @type {boolean} + */ + this.unique_ = !!options.unique; + /** * @private * @type {!Array.} */ this.array_ = opt_array ? opt_array : []; + if (this.unique_) { + for (var i = 0, ii = this.array_.length; i < ii; ++i) { + this.assertUnique_(this.array_[i], i); + } + } + this.updateLength_(); }; @@ -125,6 +140,9 @@ ol.Collection.prototype.getLength = function() { * @api */ ol.Collection.prototype.insertAt = function(index, elem) { + if (this.unique_) { + this.assertUnique_(elem); + } this.array_.splice(index, 0, elem); this.updateLength_(); this.dispatchEvent( @@ -150,6 +168,9 @@ ol.Collection.prototype.pop = function() { * @api */ ol.Collection.prototype.push = function(elem) { + if (this.unique_) { + this.assertUnique_(elem); + } var n = this.getLength(); this.insertAt(n, elem); return this.getLength(); @@ -200,6 +221,9 @@ ol.Collection.prototype.removeAt = function(index) { ol.Collection.prototype.setAt = function(index, elem) { var n = this.getLength(); if (index < n) { + if (this.unique_) { + this.assertUnique_(elem, index); + } var prev = this.array_[index]; this.array_[index] = elem; this.dispatchEvent( @@ -224,6 +248,20 @@ ol.Collection.prototype.updateLength_ = function() { }; +/** + * @private + * @param {T} elem Element. + * @param {number=} opt_except Optional index to ignore. + */ +ol.Collection.prototype.assertUnique_ = function(elem, opt_except) { + for (var i = 0, ii = this.array_.length; i < ii; ++i) { + if (this.array_[i] === elem && i !== opt_except) { + throw new Error('Duplicate item added to a unique collection'); + } + } +}; + + /** * @enum {string} * @private diff --git a/test/spec/ol/collection.test.js b/test/spec/ol/collection.test.js index 00f7dc9a13..893a08330a 100644 --- a/test/spec/ol/collection.test.js +++ b/test/spec/ol/collection.test.js @@ -298,4 +298,67 @@ describe('ol.collection', function() { }); }); + describe('unique collection', function() { + it('allows unique items in the constructor', function() { + new ol.Collection([{}, {}, {}], {unique: true}); + }); + + it('throws if duplicate items are passed to the contructor', function() { + var item = {}; + var call = function() { + new ol.Collection([item, item], {unique: true}); + }; + expect(call).to.throwException(); + }); + + it('allows unique items to be added via push', function() { + var unique = new ol.Collection(undefined, {unique: true}); + unique.push({}); + unique.push({}); + }); + + it('throws if duplicate items are added via push', function() { + var unique = new ol.Collection(undefined, {unique: true}); + var item = {}; + unique.push(item); + var call = function() { + unique.push(item); + }; + expect(call).to.throwException(); + }); + + it('allows unique items to be added via insertAt', function() { + var unique = new ol.Collection(undefined, {unique: true}); + unique.insertAt(0, {}); + unique.insertAt(0, {}); + }); + + it('throws if duplicate items are added via insertAt', function() { + var unique = new ol.Collection(undefined, {unique: true}); + var item = {}; + unique.insertAt(0, item); + var call = function() { + unique.insertAt(0, item); + }; + expect(call).to.throwException(); + }); + + it('allows unique items to be added via setAt', function() { + var unique = new ol.Collection(undefined, {unique: true}); + unique.setAt(0, {}); + unique.setAt(1, {}); + }); + + it('throws if duplicate items are added via setAt', function() { + var unique = new ol.Collection(undefined, {unique: true}); + var item = {}; + unique.setAt(0, item); + var call = function() { + unique.setAt(1, item); + }; + expect(call).to.throwException(); + }); + + }); + });