/*! * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to you under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import {Optional} from "./Monad"; import {Es2019ArrayFrom} from "./Es2019Array"; /** * Lang helpers crossported from the apache myfaces project */ export namespace Lang { //should be in lang, but for now here to avoid recursive imports, not sure if typescript still has a problem with those /** * helper function to safely resolve anything * this is not an elvis operator, it resolves * a value without exception in a tree and if * it is not resolvable then an optional of * a default value is restored or Optional.empty * if none is given * * usage * * let var: Optiona = saveResolve(() => a.b.c.d.e, "foobaz") * * * @param resolverProducer a lambda which can produce the value * @param defaultValue an optional default value if the producer fails to produce anything * @returns an Optional of the produced value */ export function saveResolve(resolverProducer: () => T, defaultValue: T | null = null): Optional { try { let result = resolverProducer(); return Optional.fromNullable(result ?? defaultValue); } catch (e) { return Optional.absent; } } /** * lazy resolve... aka the function is called on resolve and a default value also * is a producing function (called only if the original producer does not produce any result) * @param resolverProducer the producer for the resolve * @param defaultValue the default value producer function */ export function saveResolveLazy(resolverProducer: () => T, defaultValue: (() => T) | null = null): Optional { try { let result = resolverProducer(); return Optional.fromNullable(result ?? defaultValue!()); } catch (e) { return Optional.absent; } } /** * String to array function performs a string to array transformation * @param {String} it the string which has to be changed into an array * @param {RegExp} splitter our splitter reglar expression * @return a trimmed array of the splitted string */ export function strToArray(it: string, splitter: string | RegExp = /\./gi): Array { let ret: string[] = []; it.split(splitter).forEach((element => { ret.push(trim(element)); })); return ret; } /** * hyperfast trim * http://blog.stevenlevithan.com/archives/faster-trim-javascript * crossported from dojo */ export function trim(str: string): string { str = str.replace(/^\s\s*/, ''); let ws = /\s/, i = str.length; while (ws.test(str.charAt(--i))) { //do nothing } return str.slice(0, i + 1); } /** * generic object arrays like dom definitions to array conversion method which * transforms any object to something array like * @param obj * @param offset * @param pack * @returns an array converted from the object */ export function objToArray(obj: any, offset: number = 0, pack: Array = []): Array { if ((obj ?? "__undefined__") == "__undefined__") { return pack ?? null; } //since offset is numeric we cannot use the shortcut due to 0 being false //special condition array delivered no offset no pack if ((obj) instanceof Array && !offset && !pack as any) return obj; return Es2019ArrayFrom(pack.concat(Array.prototype.slice.call(obj, offset))); } /** * equalsIgnoreCase, case-insensitive comparison of two strings * * @param source * @param destination */ export function equalsIgnoreCase(source?: string | null, destination?: string | null): boolean { let finalSource = source ?? "___no_value__"; let finalDest = destination ?? "___no_value__"; //in any other case we do a strong string comparison return finalSource.toLowerCase() === finalDest.toLowerCase(); } /** * runtime type assertion * * @param probe the probe to be tested for a type * @param theType the type to be tested for */ export function assertType(probe: any, theType: any): boolean { return isString(theType) ? typeof probe == theType : probe instanceof theType; } /** * Back ported from Dojo * a failsafe string determination method * (since in javascript String != "" typeof alone fails!) * @param it {|Object|} the object to be checked for being a string * @return true in case of being a string false otherwise */ export function isString(it?: any): boolean { // summary: // Return true if it is a String return !!arguments.length && it != null && (typeof it == "string" || it instanceof String); // Boolean } /** * Back-ported, a failsafe determination code for checking whether an object is a function * @param it the object to check for being a function */ export function isFunc(it: any): boolean { return it instanceof Function || typeof it === "function"; } export function objAssign(target: any, ...theArgs: any[]) { // .length of function is 2 if (target == null) { // TypeError if undefined or null throw new TypeError('Cannot convert undefined or null to object'); } let to = Object(target); if((Object).assign as any) { theArgs.forEach(item => (Object).assign(to, item) as any); return to; } theArgs.filter(item => item != null).forEach(item => { let nextSource = item; const stringKeys = Object.keys(nextSource); const symbolKeys = Object.getOwnPropertySymbols(nextSource) .filter(sym => Object.prototype.propertyIsEnumerable.call(nextSource, sym)); [...stringKeys, ...symbolKeys] .forEach(key => to[key] = nextSource[key]); }); return to; } }