/**
* @copyright 2018 Tridium, Inc. All Rights Reserved.
* @author Andy Sutton
*/
/**
* Defines {@link baja.PermissionsMap}.
* @module baja/obj/PermissionsMap
*/
define([ 'bajaScript/sys',
'bajaScript/baja/obj/Simple',
'bajaScript/baja/obj/objUtil' ],
function (baja, Simple, objUtil) {
'use strict';
var subclass = baja.subclass,
callSuper = baja.callSuper,
cacheDecode = objUtil.cacheDecode,
cacheEncode = objUtil.cacheEncode;
/**
* PermissionsMap for a given array of baja.Permissions.
*
* @class
* @alias baja.PermissionsMap
* @extends baja.Simple
*/
var PermissionsMap = function PermissionsMap(permissions, stringEncoding) {
callSuper(PermissionsMap, this, arguments);
this.$permissions = permissions;
this.$stringEncoding = stringEncoding; // this variable is called 'string' in BPermissionsMap.java
this.$size = -1; // cache for size
};
subclass(PermissionsMap, Simple);
var permissionsMapDefault = new PermissionsMap([], ''),
permissionsSuperUser = new PermissionsMap([], 'super');
/**
* The SUPER_USER instance automatically is granted
* all permissions in any category.
*/
PermissionsMap.SUPER_USER = permissionsSuperUser;
/**
* The default instance is the empty permissions map.
*/
PermissionsMap.DEFAULT = permissionsMapDefault;
/**
* Make a PermissionsMap object.
* Make map where category number maps to array index.
* The first item (permissions[0]) is unused. If any
* items in the array are null, then baja.Permissions.none
* is assumed.
*
* @param {Array.<baja.Permissions>} permissions an array of baja.Permissions to decode.
* @returns {baja.PermissionsMap}
*/
PermissionsMap.prototype.make = function (permissions) {
if (!Array.isArray(permissions) || permissions.length <= 1) {
return PermissionsMap.DEFAULT;
}
// Use a copy of the baja.Permissions array to make a new PermissionsMap.
// baja.Permissions objects are Simples and should be immutable so a shallow copy should be fine.
return new PermissionsMap(permissions.slice(), null);
};
/**
* Make a PermissionsMap object.
*
* @param {Array.<baja.Permissions>} permissions an array of baja.Permissions to decode.
* @returns {baja.PermissionsMap}
*/
PermissionsMap.make = function (permissions) {
return PermissionsMap.DEFAULT.make.apply(baja.PermissionsMap.DEFAULT, arguments);
};
/**
* Return if this is the super user permission map which
* is automatically granted all permissions in all categories.
*/
PermissionsMap.prototype.isSuperUser = function () {
return this === PermissionsMap.SUPER_USER;
};
/**
* Get the number of category/permissions mappings.
*/
PermissionsMap.prototype.size = function () {
if (this.$size < 0) {
var p, x = this.$permissions.length - 1;
while (x > 0) {
p = this.$permissions[x];
if (p && p.getMask() !== 0) { break; }
x--;
}
this.$size = x + 1;
}
return this.$size;
};
/**
* Given a category index, return the mapped baja.Permissions
* or return baja.Permissions.none if no mapping configured.
* If this is the super user, then always return baja.Permissions.all.
*
* @param {Number} index
* @returns {baja.Permissions}
* @throws {Error} If index is not a number or less than 1
*/
PermissionsMap.prototype.getPermissions = function (index) {
var p, categoryIndex;
if (index) { categoryIndex = Number(index); }
if (index === 0) { categoryIndex = 0; }
if (!categoryIndex && categoryIndex !== 0) {
throw new Error('invalid Category index: ' + index);
}
// no such thing as category 0 (or below)
if (categoryIndex < 1) { throw new Error('Category index < 1'); }
// super is always all
if (this === PermissionsMap.SUPER_USER) { return baja.Permissions.all; }
// out of range is always none
if (categoryIndex >= this.$permissions.length) { return baja.Permissions.none; }
// otherwise map to zero
p = this.$permissions[categoryIndex];
return p || baja.Permissions.none;
};
/**
* Decode `PermissionsMap` from a `String`.
*
* @method
* @returns {baja.PermissionsMap}
*/
PermissionsMap.prototype.decodeFromString = cacheDecode(function (s) {
var permissions = [],
eq, index, p;
// special string encoding
if (s === 'super') { return PermissionsMap.SUPER_USER; }
if (s === '') { return PermissionsMap.DEFAULT; }
s.split(';').forEach(function (token) {
eq = token.indexOf('=');
index = token.substring(0, eq);
p = baja.Permissions.make(token.substring(eq + 1));
// assign into specified index
permissions[index] = p;
});
return PermissionsMap.make(permissions);
});
/**
* Encode `PermissionsMap` to a `String`.
*
* @method
* @returns {String}
*/
PermissionsMap.prototype.encodeToString = cacheEncode(function () {
if (typeof this.$stringEncoding !== 'string') {
var i, p, s = '';
// encode into 'index=permission;' pairs
for (i = 1; i < this.$permissions.length; i++) {
p = this.$permissions[i];
// don't encode for this index if there's no baja.Permissions, or if it's bajaPermissions.none (ie 0)
if (!p || !p.getMask()) { continue; }
if (s.length > 0) { s += ';'; }
s += i + '=' + p.encodeToString();
}
this.$stringEncoding = s;
}
return this.$stringEncoding;
});
/**
* Create a new baja.PermissionsMap from the bitwise OR of this baja.PermissionsMap
* instance and the specified baja.PermissionsMap.
*
* @param {baja.PermissionsMap} otherPermissionsMap
* @returns {baja.PermissionsMap}
*/
PermissionsMap.prototype.or = function (otherPermissionsMap) {
if (this.isSuperUser() || otherPermissionsMap.isSuperUser()) {
return baja.PermissionsMap.SUPER_USER;
}
if (this === baja.PermissionsMap.DEFAULT && otherPermissionsMap === baja.PermissionsMap.DEFAULT) {
return baja.PermissionsMap.DEFAULT;
}
var pMap1Size = this.size(),
pMap2Size = otherPermissionsMap.size(),
newSize = Math.max(pMap1Size, pMap2Size),
newPermissions = [];
for (var i = 1; i < newSize; i++) {
var p1 = i < pMap1Size ? this.getPermissions(i) : baja.Permissions.none;
var p2 = i < pMap2Size ? otherPermissionsMap.getPermissions(i) : baja.Permissions.none;
newPermissions[i] = p1.or(p2);
}
return baja.PermissionsMap.make(newPermissions);
};
return PermissionsMap;
});