classes/headers.js

var HeadersMarshal = require('../marshals/headers')
var HashMap = require('./hashmap')
var utils = require('../utils')

/**
 * An object that maps case insensitive keys to values. A map cannot contain duplicate keys
 * each key can map to at most one value.
 *
 * @class
 * @type {Object}
 * @augments HashMap
 * @author Nijiko Yonskai
 * @param  {Object} collection The map whose mappings are to be placed in this map
 * @return {Headers}
 */
function Headers (collection) {
  /**
   * The mapping table.
   *
   * @private
   * @member
   * @type {Object}
   */
  this.map = {}

  /**
   * The marshal mechanism for converting String to Headers.
   *
   * @private
   * @member
   * @type {Function}
   * @see HeadersMarshal#marshal
   */
  this.marshal = HeadersMarshal.marshal

  /**
   * The marshal mechanism for converting Headers to String.
   *
   * @private
   * @member
   * @type {Function}
   * @see HeadersMarshal#unmarshal
   */
  this.unmarshal = HeadersMarshal.unmarshal

  if (collection) {
    this.putAll(collection)
  }
}

Headers.prototype = Object.create(HashMap.prototype)
Headers.prototype.constructor = Headers

/**
 * Returns original key if this map contains a mapping for the specified key with a case insensitive check,
 * otherwise returns false.
 *
 * @method
 * @override
 * @param  {String} key Specified key to evaluate existance of.
 * @return {String|Boolean}
 */
Headers.prototype.containsKey = function containsKey (key) {
  var i = 0
  var caseSensitiveMap
  var caseSensitiveMapLength

  key = key.toLowerCase()
  caseSensitiveMap = Object.keys(this.map)
  caseSensitiveMapLength = caseSensitiveMap.length

  for (; i < caseSensitiveMapLength; i++) {
    if (caseSensitiveMap[i].toLowerCase() === key) {
      return caseSensitiveMap[i]
    }
  }

  return false
}

/**
 * Returns original value if this map contains a mapping for the specified value with a case insensitive check,
 * otherwise returns false.
 *
 * @method
 * @override
 * @param  {String} value Specified value to evaluate existance of.
 * @return {String|Boolean}
 */
Headers.prototype.containsValue = function containsValue (value) {
  var key

  value = value.toLowerCase()

  for (key in this.map) {
    if (this.map.hasOwnProperty(key) && this.map[key].toLowerCase() === value) {
      return this.map[key]
    }
  }

  return false
}

/**
 * Retrieves value of the specified key.
 *
 * @method
 * @override
 * @param {Object} key
 * @return {Object}
 */
Headers.prototype.get = function get (key) {
  var exists = this.containsKey(key)
  return exists ? this.map[exists] : undefined
}

/**
 * Copies all of the mappings from the specified map to this map. These mappings will replace any
 * mappings that this map had.
 *
 * @method
 * @override
 * @param  {Object|Header|HashMap} collection
 * @return {void}
 * @see HeadersMarshal#marshal
 */
Headers.prototype.putAll = function putAll (collection) {
  var key

  // Marshal self
  if (collection instanceof Headers || collection instanceof HashMap) {
    collection = collection.map
  }

  // Marshal collection from string representation
  if (utils.is(collection).a(String)) {
    collection = this.marshal(collection)
  }

  for (key in collection) {
    if (collection.hasOwnProperty(key)) {
      this.put(key, collection[key])
    }
  }
}

/**
 * Returns a string representation of this map.
 *
 * @method
 * @override
 * @return {String}
 * @see HeadersMarshal#unmarshal
 */
Headers.prototype.toString = function toString () {
  return this.unmarshal(this.map)
}

// Export
module.exports = Headers