airbug/bugcore

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

Summary

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


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

//@Export('MultiMap')

//@Require('Class')
//@Require('Collection')
//@Require('IMap')
//@Require('IMultiMap')
//@Require('Map')


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

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

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

    var Class       = bugpack.require('Class');
    var Collection  = bugpack.require('Collection');
    var IMap        = bugpack.require('IMap');
    var IMultiMap   = bugpack.require('IMultiMap');
    var Map         = bugpack.require('Map');


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

    /**
     * Map info
     * 1) Supports null values but not undefined values. Undefined values are used to indicate something doesn't exist.
     * 2) Any value can be used as a key including null but not undefined.
     *
     * @class
     * @extends {Map.<K, V>}
     * @implements {IMultiMap.<K, V>}
     * @template K, V
     */
    var MultiMap = Class.extend(Map, /** @lends {MultiMap.prototype} */{

        _name: "MultiMap",


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

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


        //-------------------------------------------------------------------------------
        // Object Implementation
        //-------------------------------------------------------------------------------

        /**
         * @param {boolean=} deep
         * @return {MultiMap.<K, V>}
         */
        clone: function(deep) {
            var cloneMultiMap = new MultiMap();
            cloneMultiMap.putAll(this);
            return cloneMultiMap;
        },


        //-------------------------------------------------------------------------------
        // Public methods
        //-------------------------------------------------------------------------------

        /**
         * @param {*} value
         * @return {boolean}
         */
        containsValue: function(value) {
            var iterator = this.iterator();
            while (iterator.hasNext()) {
                var valueCollection = iterator.nextValue();
                if (valueCollection.contains(value)) {
                    return true;
                }
            }
            return false;
        },

        /**
         * @param {function(ICollection.<V>, K)} func
         */
        forEachCollection: function(func) {
            this.getHashTable().forEach(func);
        },

        /**
         * @param {function(V, K)} func
         */
        forEachValue: function(func) {
            this.getHashTable().forEach(function(valueCollection) {
                valueCollection.forEach(func);
            });
        },

        /**
         * @override
         * @param {*} key
         * @return {ICollection.<V>}
         */
        get: function(key) {
            return this.getHashTable().get(key);
        },

        /**
         * @param {K} key
         * @param {V} value
         * @return {V}
         */
        put: function(key, value) {
            var valueCollection = this.getHashTable().get(key);
            if (!valueCollection) {
                valueCollection = new Collection();
                this.getHashTable().put(key, valueCollection);
            }
            valueCollection.add(value);
            return value;
        },

        /**
         * @param {IMap.<K, V>} map
         */
        putAll: function(map) {
            var _this = this;
            if (Class.doesImplement(map, IMap)) {
                map.toKeyArray().forEach(function(key) {
                    var value = map.get(key);
                    _this.put(key, value);
                });
            } else if (Class.doesImplement(map, IMultiMap)) {
                map.toKeyArray().forEach(function(key) {
                    var valueCollection = map.get(key);
                    valueCollection.forEach(function(value) {
                        _this.put(key, value);
                    });
                });
            }
        },

        /**
         * Removes all values under the key
         * @param {*} key
         * @return {ICollection.<V>}
         */
        remove: function(key) {
            return this.getHashTable().remove(key);
        },

        /**
         * Removes a specific value associated with the key
         * @param {*} key
         * @param {*} value
         * @return {boolean}
         */
        removeKeyValuePair: function(key, value) {
            var result = false;
            var valueCollection = this.getHashTable().get(key);
            if (valueCollection) {
                result = valueCollection.remove(value);
                if (result && valueCollection.isEmpty()) {
                    this.getHashTable().remove(valueCollection);
                }
            }
            return result;
        },

        /**
         * @return {ICollection.<K>}
         */
        toKeyCollection: function() {
            var keyCollection = new Collection();
            this.forIn(function(key) {
                keyCollection.add(key);
            });
            return keyCollection;
        },

        /**
         * @return {Array.<V>}
         */
        toValueArray: function() {
            var valueArray = [];
            this.forEach(function(valueCollection) {
                valueArray = valueArray.concat(valueCollection.toValueArray());
            });
            return valueArray;
        },

        /**
         * @return {ICollection.<V>}
         */
        toValueCollection: function() {
            var valueCollection = new Collection();
            this.getHashTable().forEach(function(valueSet) {
                valueCollection.addAll(valueSet);
            });
            return valueCollection;
        }
    });


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

    Class.implement(MultiMap, IMultiMap);


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

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