UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

397 lines (350 loc) 14.2 kB
/*! * OpenUI5 * (c) Copyright 2026 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ /* eslint-disable no-loop-func */ sap.ui.define([ "sap/base/util/extend" ], function(extend) { "use strict"; /** * For example: " Sold-to (Party) " -> "Sold to Party" * * @param {string} sString - the string to normalize * @param {string} [sSpaceReplacement=" "] - string to fill the space between words. By default, just limits the * return value to a single space between each word. * @returns {string} the normalized input string * @private * @function * @static */ function normalize(sString, sSpaceReplacement) { sSpaceReplacement = sSpaceReplacement || " "; return sString .replace(/[\-_]/g, " ") // replaces all hyphens or underscores with spaces .trim() .replace(/(?!\s)\W/g, "") // removes all non alphanumeric characters (except for spaces) .replace(/\s+/g, sSpaceReplacement); // replaces any space between words with the input sSpaceReplacement } /** * Provides utility functions for formatting 2D arrays of strings (such as the raw data loaded from a Gherkin * feature file) into a more useful format such as an array of objects or a single object. Also handles normalization * of the raw strings. * * @author Rodrigo Jordao * @author Jonathan Benn * @alias sap.ui.test.gherkin.dataTableUtils * @since 1.40 * @namespace * @static * @public */ var dataTableUtils = { /** * A simple object containing a series of normalization functions that change a string according to a * particular strategy. All strategies do the following normalization as a minimum: * * <ul> * <li>Trim spaces off the string on both sides. For example: <code>" hello "</code> becomes * <code>"hello"</code>.</li> * <li>Assume that hyphens and underscores are analogs for a space. For example: <code>"sold-to party"</code> and * <code>"sold to party"</code> are equivalent, and would both convert to the camelCase * <code>"soldToParty"</code>.</li> * <li>Trim multiple spaces between words. For example: <code>"hello____world"</code> becomes * <code>"hello world"</code>.</li> * <li>Remove any characters that are not alphanumeric or whitespace. For example: <code>"(hello)"</code> becomes * <code>"hello"</code>.</li> * </ul> * * @alias sap.ui.test.gherkin.dataTableUtils.normalization * @namespace * @static * @public */ normalization: { /** * For example: "first name" -> "First Name" * * @param {string} sString - the string to normalize * @returns {string} the normalized input string with all words capitalized * @public * @function * @static */ titleCase : function(sString) { dataTableUtils._testNormalizationInput(sString, "titleCase"); return normalize(sString) .replace(/\w*/g, function(s){return s.charAt(0).toUpperCase() + s.substr(1).toLowerCase();}); }, /** * For example: "first name" -> "FirstName" * * @param {string} sString - the string to normalize * @returns {string} the normalized input string with all words capitalized and all spaces removed * @public * @function * @static */ pascalCase : function(sString) { dataTableUtils._testNormalizationInput(sString, "pascalCase"); return dataTableUtils.normalization.titleCase(sString).split(/\s/).join(""); }, /** * For example: "First Name" -> "firstName" * * @param {string} sString - the string to normalize * @returns {string} the normalized input string with all words after the first capitalized and all spaces * removed * @public * @function * @static */ camelCase : function(sString) { dataTableUtils._testNormalizationInput(sString, "camelCase"); return dataTableUtils.normalization.pascalCase(sString).replace(/^(\w)/, function(s){return s.toLowerCase();}); }, /** * For example: "First Name" -> "first-name" * * @param {string} sString - the string to normalize * @returns {string} the normalized input string changed to lower case and with space between words * replaced by a hyphen ("-") * @public * @function * @static */ hyphenated : function(sString) { dataTableUtils._testNormalizationInput(sString, "hyphenated"); return normalize(sString, "-").toLowerCase(); }, /** * For example: "First Name" -> "First Name" * * @param {string} sString - the string to normalize * @returns {string} the original unchanged input string * @public * @function * @static */ none : function(sString) { dataTableUtils._testNormalizationInput(sString, "none"); return sString; } }, /** * Takes the inputed 2D array "aData" and returns an equivalent array of objects. The data is expected to * have a header row, with each subsequent row being an entity, and each column being a property of that * entity. E.g. * <pre> * [ * ["Their Name", "Their Age"], * ["Alice", "16"], * ["Bob", "22"] * ] * </pre> * * The data's column headers become the returned objects' property names. The property names get normalized * according to the strategy defined by the parameter "vNorm". E.g. using hyphenation strategy this is returned: * <pre> * [ * {their-name: "Alice", their-age: "16"}, * {their-name: "Bob", their-age: "22"} * ] * </pre> * * @param {string[]} aData - the 2D array of strings to be converted, with a header row * @param {(string|function)} [vNorm="none"] - the normalization function to use to normalize property * names. Can also be a String with values "titleCase", "pascalCase", * "camelCase", "hyphenated" or "none". * @returns {object[]} - an array of objects equivalent to the input data, with property names normalized * @throws {Error} if the inputed array aData contains duplicate values in the header row * @public * @function * @static */ toTable : function(aData, vNorm) { this._testArrayInput(aData, "toTable"); var fnNorm = this._getNormalizationFunction(vNorm, "toTable"); // first row are the object's keys (table column headers) var aKeys = aData[0].map(fnNorm); // transform the remaining rows return aData.slice(1).map(function(aRow) { var oGenerated = {}; // for each key for (var i = 0; i < aKeys.length; ++i) { var sCurrentKey = aKeys[i]; // if this is the first time the key is being used if (oGenerated.hasOwnProperty(sCurrentKey) === false) { // then add the new key and its associated value to the generated object oGenerated[sCurrentKey] = aRow[i]; // else if this is a repeat use of the key } else { throw new Error("dataTableUtils.toTable: data table contains duplicate header: | " + sCurrentKey + " |"); } } return oGenerated; }); }, /** * Takes the inputed 2D array "aData" and returns an equivalent object. Each row of data is expected to * be a property-value pair. To create nested objects, add extra columns to the data. E.g. * <pre> * [ * ["Name", "Alice"], * ["Mass", "135 lbs"], * ["Telephone Number", "Home", "123-456-7890"], * ["Telephone Number", "Work", "123-456-0987"] * ] * </pre> * For each data row, the right-most element becomes a property value, and everything else is a property * name. The property names get normalized according to the strategy defined by the parameter "vNorm". * E.g. using camelCase strategy * <pre> * { * name: "Alice", * mass: "135 lbs", * telephoneNumber: { * home: "123-456-7890", * work: "123-456-0987" * } * } * </pre> * @param {string[]} aData - the 2D array of strings to be converted * @param {(string|function)} [vNorm="none"] - the normalization function to use to normalize property * names. Can also be a string with values "titleCase", "pascalCase", * "camelCase", "hyphenated" or "none". * @returns {object} - an object equivalent to the input data, with property names normalized * @throws {Error} if the inputed array aData contains duplicate keys such that a row would be overwritten * @public * @function * @static */ toObject : function(aData, vNorm) { this._testArrayInput(aData, "toObject"); var fnNorm = this._getNormalizationFunction(vNorm, "toObject"); this._detectDuplicateKeys(aData, fnNorm); var oResult = {}; // for each row in the inputed data aData.forEach(function(aRow) { var sKey = fnNorm(aRow[0]); var vValue = aRow.slice(1); // vValue starts as an array and becomes a string or object // if the value is an array with a single string if (vValue.length === 1) { // then transform vValue into a string vValue = vValue[0]; // else if the value array contains multiple strings } else { // then transform vValue into an object (might be a nested object) // this function transforms the array into a nested object, e.g. ["A", "B", "C"] => {A: {B: "C"}} vValue = vValue.reduceRight(function(i,j) {var o = {}; o[fnNorm(j)] = i; return o;}); } // if this is a new key if (!oResult.hasOwnProperty(sKey)) { // then just add the new string/object directly oResult[sKey] = vValue; // else if we are adding an object for a key that already contains an object } else { // then merge the new object with the existing one extend(oResult[sKey], vValue); } }); return oResult; }, /** * Detects errors in aData due to duplicate keys, and throws an Error if an issue was found * * @param {string[]} aData - a 2D array of strings * @param {function} fnNorm - the normalization function to use to normalize keys * @throws {Error} if the inputed array aData contains duplicate keys such that a row would be overwritten * @private */ _detectDuplicateKeys: function(aData, fnNorm) { var oRowSet = {}; // used to help us identify duplicate keys // for each row in the inputed data aData.forEach(function(aRow) { var aKeys = aRow.slice(0, (aRow.length - 1)).map(fnNorm); // for each hierarchical level of the keys // e.g. when aKeys = ['A', 'B', 'C'], then sKeys will have the values 'A-B-C', 'A-B' and 'A', in that order for (var i = aKeys.length; i > 0; --i) { var sKeys = aKeys.slice(0, i).join('-'); // if this set of keys is unique so far if (!oRowSet[sKeys]) { // then take note of it for next time oRowSet[sKeys] = aRow; // else if this set of keys is NOT unique } else { var aOldRow = oRowSet[sKeys]; var aOldKeys = aOldRow.slice(0, (aOldRow.length - 1)).map(fnNorm); // if the new row would overwrite the old one if ( (aOldRow.length !== aRow.length) || (aOldKeys.every(function(str, index) {return aKeys[index] === str;})) ) { var sOutput = "| " + aOldRow.join(" | ") + " |"; throw new Error("dataTableUtils.toObject: data table row is being overwritten: " + sOutput); } } } }); }, /** * Finds the normalization function equivalent to input parameter "vFun" * * @param {(string|function)} [vFun="none"] - a normalization function. Can also be a string with values * "titleCase", "pascalCase", "camelCase", "hyphenated" or "none". * @param {string} sFunc - the name of the calling function, for error reporting * @returns {function} the normalization function equivalent to the inputed value, or <none> if the input was * undefined. * @throws {Error} if the input string is invalid * @private */ _getNormalizationFunction: function(vFun, sFunc) { var sErrorMessage = "dataTableUtils." + sFunc + ": parameter 'vNorm' must be either a Function or a String with the value 'titleCase', 'pascalCase', 'camelCase', 'hyphenated' or 'none'"; if (typeof vFun === "string" || vFun instanceof String) { var fnNormalize = this.normalization[vFun]; if (fnNormalize === undefined) { throw new Error(sErrorMessage); } return fnNormalize; } else if (typeof vFun === "function") { return vFun; } else if (vFun === undefined || vFun === null) { return this.normalization.none; } else { throw new Error(sErrorMessage); } }, /** * Tests a normalization function's input parameter to make sure it's valid * * @param {string} sString - the parameter to test * @param {string} sNormalizationFunction - the normalization function whose input we're testing * @throws {Error} if the input string is invalid * @private */ _testNormalizationInput: function(sString, sNormalizationFunction) { if (typeof sString !== "string" && !(sString instanceof String)) { throw new Error("dataTableUtils.normalization." + sNormalizationFunction + ": parameter 'sString' must be a valid string"); } }, /** * Tests a conversion function's input parameter to make sure it's valid * * @param {array} aArray - the parameter to test * @param {string} sFunc - the conversion function whose input we're testing (e.g. "toObject" or "toTable") * @throws {Error} if the input parameter is invalid * @private */ _testArrayInput: function(aArray, sFunc) { var sErrorMessage = "dataTableUtils." + sFunc + ": parameter 'aData' must be an Array of Array of Strings"; if (!Array.isArray(aArray)) { throw new Error(sErrorMessage); } if (!aArray.every(function(a) { return Array.isArray(a) && (a.every(function(s){return (typeof s === "string" || s instanceof String);})); })) { throw new Error(sErrorMessage); } } }; return dataTableUtils; }, /* bExport= */ true);