/**
* @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",
"Promise" ],
function (baja, EnumRange, Simple, objUtil, Promise) {
'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
* @param {Object} [params]
* @param {Boolean} [params.unsafe=false] if set to true, this will allow
* decodeFromString to continue. If not, decodeFromString will throw an error. This flag is for
* internal bajaScript use only. All external implementations should use decodeAsync instead.
* @returns {baja.EnumSet}
*/
EnumSet.prototype.decodeFromString = cacheDecode(function decodeFromString(str, { unsafe = false } = {}) {
if (!unsafe) { throw new Error('EnumSet#decodeAsync should be called instead to ensure all types are loaded for the decode'); }
// parse range if specified
var ordinals = [],
range,
at = str.indexOf('@');
if (at >= 0) {
range = EnumRange.DEFAULT.decodeFromString(str.substring(at + 1), baja.Simple.$unsafeDecode);
str = str.substring(0, at);
}
ordinals = parseOrdinals(str);
return this.make({
ordinals: ordinals,
range: range
});
});
/**
* @param {string} str
* @returns {Promise.<baja.EnumSet>}
*/
EnumSet.prototype.decodeAsync = function (str) {
const [ o, r ] = str.split('@');
const ordinals = parseOrdinals(o);
const defaultRange = EnumRange.DEFAULT;
return Promise.resolve(r ? defaultRange.decodeAsync(r) : defaultRange)
.then((range) => this.make({ ordinals, 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;
function parseOrdinals(str) {
if (!str.length) { return []; }
const ordinals = [];
const split = str.split(',');
const count = split.length;
for (let i = 0; i < count; i++) {
const ordinal = parseInt(split[i], 10);
if (isNaN(ordinal)) {
throw new Error("Invalid ordinal: " + split[i]);
}
ordinals.push(ordinal);
}
return ordinals;
}
return EnumSet;
});