Source: store/memory.js

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */
'use strict';

/** @module store/memory */


var utils = require('./../utils.js');

// Imports.
var throwErrorCallback = utils.throwErrorCallback;
var delay = utils.delay;

/** Constructor for store objects that use a sorted array as the underlying mechanism.
 * @class MemoryStore
 * @constructor
 * @param {String} name - Store name.
 */
function MemoryStore(name) {

    var holes = [];
    var items = [];
    var keys = {};

    this.name = name;

    var getErrorCallback = function (error) {
        return error || this.defaultError;
    };

    /** Validates that the specified key is not undefined, not null, and not an array
     * @param key - Key value.
     * @param {Function} error - Error callback.
     * @returns {Boolean} True if the key is valid. False if the key is invalid and the error callback has been queued for execution.
     */
    function validateKeyInput(key, error) {

        var messageString;

        if (key instanceof Array) {
            messageString = "Array of keys not supported";
        }

        if (key === undefined || key === null) {
            messageString = "Invalid key";
        }

        if (messageString) {
            delay(error, { message: messageString });
            return false;
        }
        return true;
    }

    /** This method errors out if the store already contains the specified key.
     * @summary Adds a new value identified by a key to the store.
     * @method module:store/memory~MemoryStore#add
     * @param {String} key - Key string.
     * @param value - Value that is going to be added to the store.
     * @param {Function} success - Callback for a successful add operation.
     * @param {Function} error - Callback for handling errors. If not specified then store.defaultError is invoked.
     */
    this.add = function (key, value, success, error) {
        error = getErrorCallback(error);

        if (validateKeyInput(key, error)) {
            if (!keys.hasOwnProperty(key)) {
                this.addOrUpdate(key, value, success, error);
            } else {
                error({ message: "key already exists", key: key });
            }
        }
    };

    /** This method will overwrite the key's current value if it already exists in the store; otherwise it simply adds the new key and value.
     * @summary Adds or updates a value identified by a key to the store.
     * @method module:store/memory~MemoryStore#addOrUpdate
     * @param {String} key - Key string.
     * @param value - Value that is going to be added or updated to the store.
     * @param {Function} success - Callback for a successful add or update operation.
     * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked.
    */
    this.addOrUpdate = function (key, value, success, error) {
        
        error = getErrorCallback(error);

        if (validateKeyInput(key, error)) {
            var index = keys[key];
            if (index === undefined) {
                if (holes.length > 0) {
                    index = holes.splice(0, 1);
                } else {
                    index = items.length;
                }
            }
            items[index] = value;
            keys[key] = index;
            delay(success, key, value);
        }
    };

    /** Removes all the data associated with this store object.
     * @method module:store/memory~MemoryStore#clear
     * @param {Function} success - Callback for a successful clear operation.
     */
    this.clear = function (success) {
        items = [];
        keys = {};
        holes = [];
        delay(success);
    };

    /** Checks whether a key exists in the store.
     * @method module:store/memory~MemoryStore#contains
     * @param {String} key - Key string.
     * @param {Function} success - Callback indicating whether the store contains the key or not.
     */
    this.contains = function (key, success) {
        var contained = keys.hasOwnProperty(key);
        delay(success, contained);
    };

    /** Gets all the keys that exist in the store.
     * @method module:store/memory~MemoryStore#getAllKeys
     * @param {Function} success - Callback for a successful get operation.
     */
    this.getAllKeys = function (success) {

        var results = [];
        for (var name in keys) {
            results.push(name);
        }
        delay(success, results);
    };

    /** Reads the value associated to a key in the store.
     * @method module:store/memory~MemoryStore#read
     * @param {String} key - Key string.
     * @param {Function} success - Callback for a successful reads operation.
     * @param {Function} error - Callback for handling errors. If not specified then store.defaultError is invoked.
     */
    this.read = function (key, success, error) {
        error = getErrorCallback(error);

        if (validateKeyInput(key, error)) {
            var index = keys[key];
            delay(success, key, items[index]);
        }
    };

    /** Removes a key and its value from the store.
     * @method module:store/memory~MemoryStore#remove
     * @param {String} key - Key string.
     * @param {Function} success - Callback for a successful remove operation.
     * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked.
     */
    this.remove = function (key, success, error) {
        error = getErrorCallback(error);

        if (validateKeyInput(key, error)) {
            var index = keys[key];
            if (index !== undefined) {
                if (index === items.length - 1) {
                    items.pop();
                } else {
                    items[index] = undefined;
                    holes.push(index);
                }
                delete keys[key];

                // The last item was removed, no need to keep track of any holes in the array.
                if (items.length === 0) {
                    holes = [];
                }
            }

            delay(success);
        }
    };

    /** Updates the value associated to a key in the store.
     * @method module:store/memory~MemoryStore#update
     * @param {String} key - Key string.
     * @param value - New value.
     * @param {Function} success - Callback for a successful update operation.
     * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked.
     * This method errors out if the specified key is not found in the store.
     */
    this.update = function (key, value, success, error) {
        error = getErrorCallback(error);
        if (validateKeyInput(key, error)) {
            if (keys.hasOwnProperty(key)) {
                this.addOrUpdate(key, value, success, error);
            } else {
                error({ message: "key not found", key: key });
            }
        }
    };
}

/** Creates a store object that uses memory storage as its underlying mechanism.
 * @method MemoryStore.create
 * @param {String} name - Store name.
 * @returns {Object} Store object.
 */
MemoryStore.create = function (name) {
    return new MemoryStore(name);
};

/** Checks whether the underlying mechanism for this kind of store objects is supported by the browser.
 * @method MemoryStore.isSupported
 * @returns {Boolean} True if the mechanism is supported by the browser; otherwise false.
 */
MemoryStore.isSupported = function () {
    return true;
};

/** This function does nothing in MemoryStore as it does not have a connection model.
*/
MemoryStore.prototype.close = function () {
};

MemoryStore.prototype.defaultError = throwErrorCallback;

/** Identifies the underlying mechanism used by the store.
*/
MemoryStore.prototype.mechanism = "memory";


/** MemoryStore (see {@link MemoryStore}) */
module.exports = MemoryStore;