baja/obj/EnumSet.js

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

/**
 * Defines {@link baja.EnumSet}.
 * @module baja/obj/EnumSet
 */
define(["bajaScript/sys", 
        "bajaScript/baja/obj/EnumRange",
        "bajaScript/baja/obj/Simple",
        "bajaScript/baja/obj/objUtil"], 
        function (baja, EnumRange, Simple, objUtil) {
  
  'use strict';
  
  var subclass = baja.subclass,
      callSuper = baja.callSuper,
      objectify = baja.objectify,
      strictArg = baja.strictArg,
      cacheDecode = objUtil.cacheDecode,
      cacheEncode = objUtil.cacheEncode;
  
  /**
   * Represents a `baja:EnumSet` in BajaScript.
   * 
   * An `EnumSet` contains an `EnumRange` and an array of ordinals.
   * 
   * When creating a `Simple`, always use the `make()` method instead of 
   * creating a new Object.
   *
   * @class
   * @alias baja.EnumSet
   * @extends baja.Simple
   */
  var EnumSet = function EnumSet(ordinals, range) {
    callSuper(EnumSet, this, arguments);  
    this.$ordinals = strictArg(ordinals);
    this.$range = strictArg(range, Object);
  };
  
  subclass(EnumSet, Simple);
  
  /**
   * Make an `EnumSet`. An `EnumSet` can be created using either an array of
   * ordinals (in which case the range will be set to 
   * {@link baja.EnumRange.DEFAULT}), or, to specify a range as well, an 
   * object literal with `ordinals` and `range` properties.
   *
   * @param {Object|Array<Number>} obj the object literal that holds the 
   * method's arguments (or an array of ordinals).
   * @param {Array.<Number>} [obj.ordinals] an array of ordinals.
   * @param {baja.EnumRange} [obj.range] the EnumRange to assign the EnumSet.
   * @returns {baja.EnumSet} the EnumSet.
   * 
   * @example
   *   var defaultRange = baja.EnumSet.make([0, 1, 2]);
   *   var customRange = baja.EnumSet.make({
   *     ordinals: [0, 2, 4],
   *     range: baja.EnumRange.make({
   *       ordinals: [0, 1, 2, 3, 4],
   *       tags: ['a', 'b', 'c', 'd', 'e']
   *     })
   *   });
   */
  EnumSet.make = function (obj) {    
    obj = objectify(obj, "ordinals");
    
    var ordinals = obj.ordinals,
        range = obj.range;
    
    if (ordinals === undefined) {
      ordinals = [];
    }
    
    if (range === undefined) {
      range = EnumRange.DEFAULT;
    }
   
    strictArg(ordinals, Array);
    strictArg(range, EnumRange);
    
    // optimization
    if (ordinals.length === 0 && range === EnumRange.DEFAULT) {
      return EnumSet.NULL;
    }
    
    return new EnumSet(ordinals, range);
  };
  
  /**
   * Make an `EnumSet`. Same as static method {@link baja.EnumSet.make}.
   *
   * @see baja.EnumSet.make
   */
  EnumSet.prototype.make = function (obj) {    
    return EnumSet.make.apply(EnumSet, arguments);
  };

  /**
   * Decode an `EnumSet` from a `String`.
   *
   * @method
   * @param {String} str
   * @returns {baja.EnumSet}
   */
  EnumSet.prototype.decodeFromString = cacheDecode(function decodeFromString(str) {
    // parse range if specified
    var ordinals = [],
        ordinal,
        range,
        at = str.indexOf('@'),
        split,
        count,
        i;
    
    if (at >= 0) {
      range = EnumRange.DEFAULT.decodeFromString(str.substring(at + 1));
      str = str.substring(0, at);
    }                       

    if (str.length) {
      split = str.split(',');
      count = split.length;
      for (i = 0; i < count; i++) {
        ordinal = parseInt(split[i], 10);
        if (isNaN(ordinal)) {
          throw new Error("Invalid ordinal: " + split[i]);
        }
        ordinals.push(ordinal);
      }
    }
    
    return this.make({
      ordinals: ordinals,
      range: range
    });
  });
  
  /**
   * Encode an `EnumSet` to a `String`.
   *
   * @method
   * @returns {String}
   */
  EnumSet.prototype.encodeToString = cacheEncode(function encodeToString() {  
    var ordinals = this.$ordinals,
        range = this.$range,
        s = ordinals.join(',');
    
    if (range && (range !== EnumRange.DEFAULT)) {
      s += '@' + range.encodeToString();
    }
    return s;
  });
  
  /**
   * Return the data type symbol (E).
   *
   * @returns {String} data type symbol
   */
  EnumSet.prototype.getDataTypeSymbol = function () {
    return "E";
  };
  
  /**
   * Return all of the ordinals for the `EnumSet`.
   *
   * @returns {Array} an array of numbers that represents the ordinals for this EnumSet.
   */
  EnumSet.prototype.getOrdinals = function () {
    return this.$ordinals;
  };
  
  /**
   * Return the range.
   *
   * @returns {baja.EnumRange}
   */
  EnumSet.prototype.getRange = function () {
    return this.$range;
  };
  
  /**
   * Default `EnumSet` instance.
   * @type {baja.EnumSet}
   */   
  EnumSet.DEFAULT = new EnumSet([], EnumRange.DEFAULT);
  
  /**
   * NULL `EnumSet` instance.
   * @type {baja.EnumSet}
   */
  EnumSet.NULL = EnumSet.DEFAULT;
  
  return EnumSet;
});