UNPKG

bugcore

Version:

bugcore is a JavaScript library that provides a foundational architecture for object oriented JS

400 lines (352 loc) 13.7 kB
/* * Copyright (c) 2016 airbug Inc. http://airbug.com * * bugcore may be freely distributed under the MIT license. */ //------------------------------------------------------------------------------- // Annotations //------------------------------------------------------------------------------- //@Export('ArgUtil') //@Require('ArgumentBug') //@Require('Bug') //@Require('Class') //@Require('Obj') //@Require('ObjectUtil') //@Require('TypeUtil') //------------------------------------------------------------------------------- // Context //------------------------------------------------------------------------------- require('bugpack').context("*", function(bugpack) { //------------------------------------------------------------------------------- // BugPack //------------------------------------------------------------------------------- var ArgumentBug = bugpack.require('ArgumentBug'); var Bug = bugpack.require('Bug'); var Class = bugpack.require('Class'); var Obj = bugpack.require('Obj'); var ObjectUtil = bugpack.require('ObjectUtil'); var TypeUtil = bugpack.require('TypeUtil'); //------------------------------------------------------------------------------- // Declare Class //------------------------------------------------------------------------------- /** * @class * @extends {Obj} */ var ArgUtil = Class.extend(Obj, { _name: "ArgUtil" }); //------------------------------------------------------------------------------- // Public Static Methods //------------------------------------------------------------------------------- /** * @static * @param {*} rawArgs * @param {Array.<{ * name: string, * type: string=, * optional: boolean=, * default: *= * }>=} descriptions * @return {Object} */ ArgUtil.process = function(rawArgs, descriptions) { var args = ArgUtil.processArgs(rawArgs); var argsObject = {}; var descriptionResults = ArgUtil.processDescriptions(args, descriptions); var descriptionArgMap = descriptionResults.descriptionArgMap; var unusedDescriptions = descriptionResults.unusedDescriptions; ArgUtil.processDescriptionArgMap(args, argsObject, descriptionArgMap); ArgUtil.processUnusedDescriptions(args, argsObject, unusedDescriptions); return argsObject; }; /** * @private * @param {*} rawArgs * @return {Array.<*>} */ ArgUtil.processArgs = function(rawArgs) { var args = ArgUtil.toArray(rawArgs); while (TypeUtil.isUndefined(args[args.length - 1]) && args.length > 0) { args.pop(); } return args; }; /** * @static * @param {Array.<*>} args * @param {Array.<{ * name: string, * type: string=, * optional: boolean=, * default: *= * }>} descriptions * @return {{ * usedDescriptions: Array.<{ * name: string, * type: string=, * optional: boolean=, * default: *= * }>, * unusedDescriptions: Array.<{ * name: string, * type: string=, * optional: boolean=, * default: *= * }> * }} */ ArgUtil.processDescriptions = function(args, descriptions) { ArgUtil.validateDescriptions(descriptions, args.length); var argsCopy = ([]).concat(args); var descriptionsCopy = ([]).concat(descriptions); var descriptionArgMap = {}; var unusedDescriptions = []; var notFoundDescriptions = []; while (descriptionsCopy.length > 0) { var description = descriptionsCopy.shift(); if (descriptionsCopy.length > argsCopy.length - 1) { if (description.optional) { unusedDescriptions.push(description); } else { if (argsCopy.length > 0) { descriptionArgMap[description.name] = { arg: argsCopy.shift(), description: description }; } else { notFoundDescriptions.push(description); } } } else { if (argsCopy.length > 0) { descriptionArgMap[description.name] = { arg: argsCopy.shift(), description: description }; } else { notFoundDescriptions.push(description); } } } if (notFoundDescriptions.length > 0) { var throwable = null; if (notFoundDescriptions.length === 1) { throwable = new ArgumentBug(ArgumentBug.ILLEGAL, notFoundDescriptions[0].name, undefined, "Argument missing"); } else { var missingThrowables = []; notFoundDescriptions.forEach(function(notFoundDescription) { missingThrowables.push(new ArgumentBug(ArgumentBug.ILLEGAL, notFoundDescription.name, undefined, "argument missing")); }); throwable = new Bug("MultipleIllegalArguments", {}, "Multiple arguments missing", missingThrowables); } throw throwable; } /*if (args.length === descriptions.length) { usedDescriptions = usedDescriptions.concat(descriptions); } else { var neededDescriptions = []; for (var i = descriptions.length - 1; i >= 0; i--) { var description = descriptions[i]; //This arg is not present if (i >= args.length) { if (!description.optional) { neededDescriptions.push(description); } else { unusedDescriptions.unshift(description); } } else { var arg = args[i]; if (neededDescriptions.length > 0) { var found = false; while (neededDescriptions.length > 0 && !found) { var neededDescription = neededDescriptions.shift(); if (ArgUtil.checkTypeMatch(arg, neededDescription)) { found = true; usedDescriptions.unshift(neededDescription); if (!description.optional) { neededDescriptions.push(description); } else { unusedDescriptions.unshift(description); } } } } else { usedDescriptions.unshift(description); } } } }*/ return { descriptionArgMap: descriptionArgMap, unusedDescriptions: unusedDescriptions }; }; /** * @static * @param {*} rawArgs * @return {Array.<*>} */ ArgUtil.toArray = function(rawArgs) { return Array.prototype.slice.call(rawArgs, 0); }; //------------------------------------------------------------------------------- // Private Static Methods //------------------------------------------------------------------------------- /** * @static * @private * @param {*} arg * @param {{ * name: string, * type: string=, * optional: boolean=, * default: *= * }} description * @return {boolean} */ ArgUtil.checkTypeMatch = function(arg, description) { var validType = true; if (description.type) { var type = TypeUtil.toType(arg); if (TypeUtil.isArray(description.type)) { validType = (description.type.indexOf(type) >= 0); } else { validType = (type === description.type); } } return validType; }; /** * @static * @private * @param {Array.<*>} args * @param {Object} argsObject * @param {Array.<{ * name: string, * type: string=, * optional: boolean=, * default: *= * }>} unusedDescriptions */ ArgUtil.processUnusedDescriptions = function(args, argsObject, unusedDescriptions) { for (var i = unusedDescriptions.length - 1; i >= 0; i--) { var unusedDescription = unusedDescriptions[i]; if (unusedDescription.default) { argsObject[unusedDescription.name] = unusedDescription.default; } else { argsObject[unusedDescription.name] = undefined; } } }; /** * @static * @private * @param {Array.<*>} args * @param {Object} argsObject * @param {Array.<{ * name: string, * type: string=, * optional: boolean=, * default: *= * }>} descriptionArgMap */ ArgUtil.processDescriptionArgMap = function(args, argsObject, descriptionArgMap) { ObjectUtil.forIn(descriptionArgMap, function(name, value) { ArgUtil.setArgOnArgsObject(value.arg, argsObject, value.description); }); /* var neededDescriptions = []; for (var i = usedDescriptions.length - 1; i >= 0; i--) { var arg = args[i]; if (TypeUtil.isUndefined(arg)) { } } for (var i = usedDescriptions.length - 1; i >= 0; i--) { var arg = args[i]; var usedDescription = usedDescriptions[i]; if (neededDescriptions.length > 0) { } if (ArgUtil.checkTypeMatch(arg, usedDescription)) { ArgUtil.setArgOnArgsObject(arg, argsObject, usedDescription); } else { if (!usedDescription.optional) { neededDescriptions.push(usedDescription); } else { } } if (neededDescriptions.length > 0) { var found = false; while (neededDescriptions.length > 0 && !found) { var neededDescription = neededDescriptions.shift(); if (ArgUtil.checkTypeMatch(arg, neededDescription)) { found = true; usedDescriptions.unshift(neededDescription); if (!description.optional) { neededDescriptions.push(description); } else { unusedDescriptions.unshift(description); } } else { notFoundDescriptions.push(neededDescription); } } } else { usedDescriptions.unshift(description); } }*/ }; /** * @static * @private * @param {*} arg * @param {Object} argsObject * @param {{ * name: string, * type: string=, * optional: boolean=, * default: *= * }} description */ ArgUtil.setArgOnArgsObject = function(arg, argsObject, description) { var validType = ArgUtil.checkTypeMatch(arg, description); if (!validType) { throw new ArgumentBug(ArgumentBug.ILLEGAL, description.name, arg, "Argument type does not match. Must be of type '" + description.type + "'"); } argsObject[description.name] = arg; }; /** * @static * @private * @param {Array.<{ * name: string, * type: string=, * optional: boolean=, * default: * * }>=} descriptions * @param {number} expectedNumber */ ArgUtil.validateDescriptions = function(descriptions, expectedNumber) { if (!TypeUtil.isArray(descriptions)) { throw new ArgumentBug(ArgumentBug.ILLEGAL, "descriptions", descriptions, "parameter must be an Array"); } //TODO BRN: Is this something that we want to support? Should we have to declare all args? if (expectedNumber > descriptions.length) { throw new ArgumentBug(ArgumentBug.ILLEGAL, "descriptions", descriptions, "Too few descriptions. Number of descriptions must be equal to or greater than the number of arguments"); } for (var i = descriptions.length - 1; i >= 0; i--) { var description = descriptions[i]; if (!TypeUtil.isObject(description)) { throw new ArgumentBug(ArgumentBug.ILLEGAL, "descriptions", descriptions, "descriptions Array must only contain description objects"); } if (!TypeUtil.isString(description.name)) { throw new ArgumentBug(ArgumentBug.ILLEGAL, "descriptions", descriptions, "description objects must have a name"); } } }; //------------------------------------------------------------------------------- // Exports //------------------------------------------------------------------------------- bugpack.export('ArgUtil', ArgUtil); });