baja/obj/NameList.js

/**
 * @copyright 2015 Tridium, Inc. All Rights Reserved.
 * @author Gareth Johnson
 */

/**
 * Defines {@link baja.NameList}.
 * @module baja/obj/NameList
 */
define([ 'bajaScript/sys',
        'bajaScript/baja/obj/Simple',
        'bajaScript/baja/obj/objUtil',
        'bajaScript/baja/ord/SlotPath',
        'bajaScript/baja/sys/bajaUtils' ], function (
         baja,
         Simple,
         objUtil,
         SlotPath,
         bajaUtils) {

  'use strict';

  var subclass = baja.subclass,
      cacheDecode = objUtil.cacheDecode,
      cacheEncode = objUtil.cacheEncode,
      iterate = bajaUtils.iterate;

  /**
   * A `NameList` simply contains a set of name strings. Names must be valid
   * `SlotPath` names.
   *
   * Note that the actual behavior of this class is more of a set than a list,
   * but we conform with the class name in `javax.baja.util`. Heigh ho.
   *
   * @class
   * @alias baja.NameList
   * @extends baja.Simple
   */
  var NameList = function NameList() {
    Simple.apply(this, arguments);
  };
  subclass(NameList, Simple);

  /**
   * Create a new `baja:NameList` instance from a name or set of names.
   *
   * @param {String|Array.<String>} names a name, or array of names. These must
   * all be valid SlotPath names.
   * @returns {baja.NameList}
   * @throws {Error} if names that are valid SlotPaths are not given
   */
  NameList.make = function (names) {
    if (typeof names === 'string') { names = [ names ]; }
    if (!Array.isArray(names)) { throw new Error('names required'); }

    if (!names.length) { return NameList.DEFAULT; }

    names.forEach(function (str, i) {
      names[i] = names[i].trim();
    });

    var list = new NameList();
    baja.iterate(names, SlotPath.verifyValidName);
    list.$names = names;
    return list;
  };

  /**
   * Create a new `NameList` which is the union of the two name lists with no
   * duplicates.
   *
   * @param {baja.NameList} a
   * @param {baja.NameList} b
   * @returns {baja.NameList}
   */
  NameList.union = function (a, b) {
    var map = {}, result = [];
    iterate(a.getNames().concat(b.getNames()), function (n) { map[n] = n; });
    iterate(map, function (name) { result.push(name); });
    return NameList.make(result);
  };

  /**
   * Create a new `NameList` which is the intersection of the two name lists.
   *
   * @param {baja.NameList} a
   * @param {baja.NameList} b
   * @returns {baja.NameList}
   */
  NameList.intersection = function (a, b) {
    var map = {}, result = [];
    iterate(a.getNames(), function (n) { map[n] = true; });
    iterate(b.getNames(), function (n) { if (map[n]) { result.push(n); } });
    return NameList.make(result);
  };

  /**
   * Create a new `NameList` which is the difference of the two name lists.
   *
   * @param {baja.NameList} a
   * @param {baja.NameList} b
   * @returns {baja.NameList}
   */
  NameList.difference = function (a, b) {
    var map = {}, result = [];
    iterate(b.getNames(), function (n) { map[n] = true; });
    iterate(a.getNames(), function (n) { if (!map[n]) { result.push(n); } });
    return NameList.make(result);
  };

  /**
   * Get the names contained by this `NameList`.
   *
   * @returns {Array.<String>}
   */
  NameList.prototype.getNames = function () {
    return this.$names.slice();
  };

  /**
   * Return the difference which is this `NameList` minus the names in the other
   * `NameList`.
   * @param {baja.NameList} o
   * @returns {baja.NameList}
   */
  NameList.prototype.difference = function (o) {
    return NameList.difference(this, o);
  };

  /**
   * Return the intersection of the names in this `NameList` and the other
   * `NameList`.
   * @param {baja.NameList} o
   * @returns {baja.NameList}
   */
  NameList.prototype.intersection = function (o) {
    return NameList.intersection(this, o);
  };

  /**
   * Return the union of the names in this `NameList` plus the names in the
   * other `NameList`, with no duplicates.
   * @param {baja.NameList} o
   * @returns {baja.NameList}
   */
  NameList.prototype.union = function (o) {
    return NameList.union(this, o);
  };


  /**
   * @see .make
   * @returns {baja.NameList}
   */
  NameList.prototype.make = function () {
    return NameList.make.apply(NameList, arguments);
  };

  /**
   * Decode a `NameList` from a string.
   *
   * @function
   * @param {String} str
   * @returns {baja.NameList}
   */
  NameList.prototype.decodeFromString = cacheDecode(function (str) {
    if (!str) {
      return NameList.DEFAULT;
    }
    return NameList.make(str.replace(/;$/, '').split(';'));
  });

  /**
   * Encode this `NameList` to a string.
   *
   * @function
   * @returns {String}
   */
  NameList.prototype.encodeToString = cacheEncode(function () {
    return this.getNames().join(';');
  });

  /**
   * Two `NameList`s are considered equal if they contain all the same strings,
   * not necessarily in the same order.
   *
   * @param {baja.NameList} o
   * @returns {boolean}
   */
  NameList.prototype.equals = function (o) {
    if (!baja.hasType(o, 'baja:NameList')) { return false; }

    return this.getNames().sort().join(';') === o.getNames().sort().join(';');
  };

  NameList.DEFAULT = NameList.NULL = new NameList();
  NameList.DEFAULT.$names = [];

  return NameList;
});