airbug/bugcore

View on GitHub
libraries/bugcore/js/src/data/Collection.js

Summary

Maintainability
C
1 day
Test Coverage
/*
 * Copyright (c) 2016 airbug Inc. http://airbug.com
 *
 * bugcore may be freely distributed under the MIT license.
 */


//-------------------------------------------------------------------------------
// Annotations
//-------------------------------------------------------------------------------

//@Export('Collection')

//@Require('ArgumentBug')
//@Require('Class')
//@Require('Exception')
//@Require('HashStore')
//@Require('IArrayable')
//@Require('ICollection')
//@Require('IIterable')
//@Require('IStreamable')
//@Require('Obj')
//@Require('Stream')
//@Require('Suppliers')
//@Require('TypeUtil')


//-------------------------------------------------------------------------------
// Context
//-------------------------------------------------------------------------------

require('bugpack').context("*", function(bugpack) {

    //-------------------------------------------------------------------------------
    // BugPack
    //-------------------------------------------------------------------------------

    var ArgumentBug     = bugpack.require('ArgumentBug');
    var Class           = bugpack.require('Class');
    var Exception       = bugpack.require('Exception');
    var HashStore       = bugpack.require('HashStore');
    var IArrayable      = bugpack.require('IArrayable');
    var ICollection     = bugpack.require('ICollection');
    var IIterable       = bugpack.require('IIterable');
    var IStreamable     = bugpack.require('IStreamable');
    var Obj             = bugpack.require('Obj');
    var Stream          = bugpack.require('Stream');
    var Suppliers       = bugpack.require('Suppliers');
    var TypeUtil        = bugpack.require('TypeUtil');


    //-------------------------------------------------------------------------------
    // Declare Class
    //-------------------------------------------------------------------------------

    /**
     * @class
     * @extends {Obj}
     * @implements {IArrayable}
     * @implements {ICollection<I>}
     * @implements {IIterable<I>}
     * @implements {IStreamable<I>}
     * @template I
     */
    var Collection = Class.extend(Obj, /** @lends {Collection.prototype} */{

        _name: "Collection",


        //-------------------------------------------------------------------------------
        // Constructor
        //-------------------------------------------------------------------------------

        /**
         * @constructs
         */
        _constructor: function() {

            this._super();


            //-------------------------------------------------------------------------------
            // Private Properties
            //-------------------------------------------------------------------------------

            /**
             * @private
             * @type {HashStore}
             */
            this.hashStore = new HashStore();
        },


        //-------------------------------------------------------------------------------
        // Init Methods
        //-------------------------------------------------------------------------------

        /**
         * @param {(IArrayable<I> | Array<I>)=} items
         * @return {Collection<I>}
         */
        init: function(items) {
            var _this = this._super();

            if (_this) {
                if (items) {
                    this.addAll(items);
                }
            }
            return _this;
        },


        //-------------------------------------------------------------------------------
        // Getters and Setters
        //-------------------------------------------------------------------------------

        /**
         * @return {HashStore}
         */
        getHashStore: function() {
            return this.hashStore;
        },


        //-------------------------------------------------------------------------------
        // IArrayable Implementation
        //-------------------------------------------------------------------------------

        /**
         * @override
         * @return (Array<I>)
         */
        toArray: function() {
            return this.hashStore.toArray();
        },


        //-------------------------------------------------------------------------------
        // ICollection Implementation
        //-------------------------------------------------------------------------------

        /**
         * @param {I} item
         * @return {boolean}
         */
        add: function(item) {
            this.hashStore.add(item);
            return true;
        },

        /**
         * @param {(IIterable<I> | IArrayable<I> | Array<I>)} items
         */
        addAll: function(items) {
            if (Class.doesImplement(items, IArrayable) && !Class.doesImplement(items, ICollection)) {
                items = items.toArray();
            }
            if (Class.doesImplement(items, ICollection) || Class.doesImplement(items, IIterable) || TypeUtil.isArray(items)) {
                var _this = this;
                items.forEach(function(item) {
                    _this.add(item);
                });
            } else {
                throw new ArgumentBug(ArgumentBug.ILLEGAL, "items", items, "parameter must implement IArrayable or IIterable or be an Array");
            }
        },

        /**
         *
         */
        clear: function() {
            this.removeAll(this);
        },

        /**
         * @param {*} value
         * @return {boolean}
         */
        contains: function(value) {
            return this.hashStore.contains(value);
        },

        /**
         * Multiple elements are ignored in this function.
         * e.g. Collection[0,1] containsAll Collection[0,1,1,1] is true
         * If you want to check for exact equality, use the equals function.
         * Empty collections are always contained by another collection
         * e.g. Collection[0,1] containsAll Collection[] is true
         * @param {(IIterable<I> | IArrayable<I> | Array<*>)} values
         * @return {boolean}
         */
        containsAll: function(values) {
            var _this = this;
            if (Class.doesImplement(values, IArrayable) || Class.doesImplement(values, IIterable) || TypeUtil.isArray(values)) {
                var valueArray = values;
                if (Class.doesImplement(values, IArrayable)) {
                    valueArray = values.toArray();
                }
                try {
                    valueArray.forEach(function(value) {
                        if (!_this.contains(value)) {
                            throw false;
                        }
                    });
                } catch(error) {
                    return false;
                }
                return true;
            } else {
                throw new ArgumentBug(ArgumentBug.ILLEGAL, "values", values, "parameter must implement ICollection or be an Array");
            }
        },

        /**
         * @param {(IIterable<I> | IArrayable<I> | Array<*>)} values
         * @return {boolean}
         */
        containsEqual: function(values) {
            var collection = undefined;
            if (TypeUtil.isArray(values) || ((Class.doesImplement(values, IArrayable) || Class.doesImplement(values, IIterable)) && !Class.doesImplement(values, ICollection))) {
                collection = new Collection(values);
            } else {
                collection = values;
            }
            if (Class.doesImplement(collection, ICollection)) {
                if (collection.getCount() === this.getCount()) {
                    var collectionValueArray = this.toArray();
                    for (var i1 = 0, size1 = collectionValueArray.length; i1 < size1; i1++) {
                        var collectionValue = collectionValueArray[i1];
                        if (this.countValue(collectionValue) !== collection.countValue(collectionValue)) {
                            return false;
                        }
                    }
                    return true;
                }
                return false;
            } else {
                throw new ArgumentBug(ArgumentBug.ILLEGAL, "values", values, "parameter must implement IArrayable or be an Array");
            }
        },

        /**
         * @param {*} value
         * @return {number}
         */
        countValue: function(value) {
            return this.hashStore.countValue(value);
        },

        /**
         * @return {number}
         */
        getCount: function() {
            return this.hashStore.getCount();
        },

        /**
         * @return {boolean}
         */
        isEmpty: function() {
            return this.hashStore.isEmpty();
        },

        /**
         * @param {*} value
         * @return {boolean}
         */
        remove: function(value) {
            return this.hashStore.remove(value);
        },

        /**
         * @param {(IIterable<I> | IArrayable<I> | Array<*>)} values
         */
        removeAll: function(values) {
            /** @type {ICollection<*> | Array<*>} */
            var collection = null;
            if ((Class.doesImplement(values, IArrayable) && !Class.doesImplement(values, ICollection))) {
                collection = new Collection(values);
            } else {
                collection = /** @type {ICollection<*>} */(values);
            }
            if (Class.doesImplement(collection, ICollection) || Class.doesImplement(values, IIterable) || TypeUtil.isArray(collection)) {
                var _this = this;
                collection.forEach(function(value) {
                    _this.remove(value);
                });
            } else {
                throw new ArgumentBug(ArgumentBug.ILLEGAL, "values", values, "parameter must implement ICollection or be an Array");
            }
        },

        /**
         * @param {(IIterable<I> | IArrayable<I> | Array<*>)} values
         */
        retainAll: function(values) {
            /** @type {ICollection} */
            var collection = null;
            if (TypeUtil.isArray(values) || ((Class.doesImplement(values, IArrayable) || Class.doesImplement(values, IIterable)) && !Class.doesImplement(values, ICollection))) {
                collection = new Collection(values);
            } else {
                collection = /** @type {ICollection} */(values);
            }
            if (Class.doesImplement(collection, ICollection)) {
                var _this = this;
                _this.forEach(function(value) {
                    if (!collection.contains(value)) {
                        _this.remove(value);
                    }
                });
            } else {
                throw new ArgumentBug(ArgumentBug.ILLEGAL, "values", values, "parameter must implement IArrayable or be an Array");
            }
        },


        //-------------------------------------------------------------------------------
        // IIterable Implementation
        //-------------------------------------------------------------------------------

        /**
         * NOTE BRN: If a value is modified in one iteration and then visited at a later time, its value in the loop is
         * its value at that later time. A value that is deleted before it has been visited will not be visited later.
         * Values added to the Collection over which iteration is occurring may either be visited or omitted from iteration.
         *
         * @param {function(I)} func
         */
        forEach: function(func) {
            this.hashStore.forEach(func);
        },

        /**
         * NOTE BRN: If a value is modified in one iteration and then visited at a later time, its value in the loop is
         * its value at that later time. A value that is deleted before it has been visited will not be visited later.
         * Values added to the Collection over which iteration is occurring may either be visited or omitted from iteration.
         *
         * @return {IIterator<I>}
         */
        iterator: function() {
            return this.hashStore.iterator();
        },


        //-------------------------------------------------------------------------------
        // IStreamable Implementation
        //-------------------------------------------------------------------------------

        /**
         * @return {Stream<I>}
         */
        stream: function() {
            return Stream.newStream(Suppliers.iterable(this));
        },


        //-------------------------------------------------------------------------------
        // Obj Methods
        //-------------------------------------------------------------------------------

        /**
         * @param {boolean=} deep
         * @return {Collection<I>}
         */
        clone: function(deep) {
            var cloneCollection = new Collection();
            if (deep) {
                this.forEach(function(item) {
                    cloneCollection.add(Obj.clone(item, true));
                });
            } else {
                cloneCollection.addAll(this);
            }
            return cloneCollection;
        }
    });


    //-------------------------------------------------------------------------------
    // Implement Interfaces
    //-------------------------------------------------------------------------------

    Class.implement(Collection, ICollection);
    Class.implement(Collection, IIterable);
    Class.implement(Collection, IStreamable);


    //-------------------------------------------------------------------------------
    // Exports
    //-------------------------------------------------------------------------------

    bugpack.export('Collection', Collection);
});