UNPKG

@cute-dw/core

Version:

This TypeScript library is the main part of a more powerfull package designed for the fast WEB software development. The cornerstone of the library is the **DataStore** class, which might be useful when you need a full control of the data, but do not need

1,305 lines (1,282 loc) 608 kB
import * as i0 from '@angular/core'; import { NgModule, Injectable, InjectionToken, EventEmitter, Component, Input, Output } from '@angular/core'; import { Subject, from, throwError, debounceTime, filter, BehaviorSubject, EMPTY, concat, Subscription } from 'rxjs'; import { retry, catchError, retryWhen, scan, tap, delay, takeUntil } from 'rxjs/operators'; import * as i1 from '@angular/common/http'; import { HttpParams } from '@angular/common/http'; import { __awaiter } from 'tslib'; import { parseExpression } from '@babel/parser'; class CuteCoreModule { } CuteCoreModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: CuteCoreModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); CuteCoreModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.5", ngImport: i0, type: CuteCoreModule }); CuteCoreModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: CuteCoreModule }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: CuteCoreModule, decorators: [{ type: NgModule, args: [{ declarations: [], imports: [], exports: [], providers: [] }] }] }); //import {Objects} from "../Objects"; /** * Superclass of exceptions that can be thrown during the normal operation flow */ class RunTimeException extends Error { constructor(message, cause) { super(message); this._cause = cause; //this.name = Objects.getClassName(this); } get cause() { return this._cause; } } class IllegalArgumentException extends RunTimeException { } /** * This class provides a skeletal implementation of the Collection interface, to minimize the effort required to implement this interface. */ class AbstractCollection { constructor() { this.contentChanged$ = new Subject(); } /** * Returns an observable object of the collection changes */ get contentChanged() { return this.contentChanged$.asObservable(); } ; /** * Returns an iterator over the elements contained in this collection */ get iterator() { return this[Symbol.iterator](); } /** * Returns the number of elements in this collection. */ get length() { return this.size; } /** * Returns the number of elements in this collection */ get size() { return this.toArray().length; } /** * Ensures that this collection contains the specified element. * @param value Element whose presence in this collection is to be ensured * @returns `this` Object reference * @throws `IllegalArgumentException` if the argument's value is _undefined_ * @description This method was defined for JavaScript's `Set` interface compatibility. */ add(value) { if (value === undefined) { throw new IllegalArgumentException(`Method add(): Illegal argument value '${value}'`); } this.append(value); return this; } /** * Appends all the elements in the specified collection to this collection * @param collection Collection containing elements to be added to this collection * @returns _true_ if this collection changed as a result of the call */ appendAll(collection) { let count = 0; if (collection) { for (const elem of collection) { if (this.append(elem)) { count++; } } } return count > 0; } /** * Returns _true_ if this collection contains all the elements in the specified collection * @param coll Collection to be checked for containment in this collection * @returns _true_ if this collection contains all the elements in the specified collection */ containsAll(coll) { if (coll && coll.length > 0) { for (const elem of coll) { if (!this.contains(elem)) { return false; } } return true; } return false; } /** * Compares the specified object with this collection for equality * @param value Object to be compared for equality with this collection * @returns _true_ if the specified `value` is equal to this collection */ equals(value) { return (value && value === this); } /** * Returns the first element in the provided array that satisfies the provided testing function. * If no values satisfy the testing function, _undefined_ is returned. * @param test The testing function * @returns The first element in the collection that satisfies the provided testing function. Otherwise, _undefined_ is returned. */ find(test) { let resultElem = undefined; for (const elem of this) { if (test(elem)) { resultElem = elem; break; } } return resultElem; } /** * Performs the specified action for each element in the collection * @param action {Consumer} The action to be performed for each element * @throws `IllegalArgumentException`, if the specified `action` is null */ forEach(action) { if (!action) { throw new IllegalArgumentException("action is required parameter"); } for (let elem of this) { action(elem); } } /** * Returns _true_ if this collection contains no elements */ isEmpty() { return this.size == 0; } /** * Removes all of this collection's elements that are also contained in the specified collection * @param collection Collection containing elements to be removed from this collection * @returns _true_ if this collection changed as a result of the call */ removeAll(collection) { let nCount = 0; if (collection) { for (const elem of collection) { if (this.remove(elem)) { nCount++; } } } return nCount > 0; } /** * Retains only the elements in this collection that are contained in the specified collection * @param collection Collection containing elements to be retained in this collection * @returns _true_ if this collection changed as a result of the call */ retainAll(collection) { let count = 0; if (collection) { if (Array.isArray(collection)) { for (const elem of this) { if (collection.indexOf(elem) == 0 && this.remove(elem)) { count++; } } } else { for (const elem of this) { if (!collection.contains(elem) && this.remove(elem)) { count++; } } } } return count > 0; } /** * Removes all the elements of this collection that satisfy the given predicate * @param p A predicate which returns true for elements to be removed * @returns _true_ if any elements were removed, _false_ otherwise */ removeIf(p) { let nCount = 0; for (const elem of this) { if (p(elem) && this.remove(elem)) { nCount++; } } return nCount > 0; } /** * Returns a sequential `Observable` object with this collection as its source * @since 0.5.0 */ stream() { return from(this); } /** * Returns a JSON representation of this collection */ toJSON() { return this.toArray(); } /** * Returns a string representation of this collection */ toString() { return this.toArray().toString(); } } /** * This class provides a skeletal implementation of the `List` interface to minimize the effort required to implement this interface backed by a "random access" data store (such as an _array_). */ class AbstractList extends AbstractCollection { constructor() { super(...arguments); this._modCount = 0; } /** Modification counter. Must be overridden in the subclasses that implement `subList` method */ get modCount() { return this._modCount; } /** * Tests whether all elements in the list pass the test implemented by the provided function * @param callbackFn The function is called * @returns {boolean} _true_ if the `callbackFn` function returns a _truthy_ value for every list element. Otherwise, _false_. */ every(callbackFn) { if (this.size > 0) { let i = -1; for (const elem of this) { if (!callbackFn(elem, ++i, this)) { return false; } } return true; } return false; } /** * Creates an array as a shallow copy of a portion of the current list, filtered down to just the elements from the current list that pass the test implemented by the provided function * @param predicate A predicate function, to test each element of the list. Return a value that coerces to _true_ to keep the element, or to _false_ otherwise. * @returns An array of a portion of the given list, filtered down to just the elements from the given list that pass the test implemented by the provided function. If no elements pass the test, an empty array will be returned. */ filter(predicate) { let arr = []; if (this.size > 0) { for (const elem of this) { if (predicate(elem)) { arr.push(elem); } } } return arr; } /** * Gets the parent list of the current list `view` object */ getParent() { return null; } /** * Gets the range of the `from` and `to` indexes related to the `view`'s parent list (if any) * @returns */ getParentRange() { return [0, 0]; } /** * Tests whether all elements in the list pass the test implemented by the provided function * @param callbackFn A function to test for each element * @returns {boolean} _true_ if the `callbackFn` function returns a _truthy_ value for at least one element in the list. Otherwise, _false_. */ some(callbackFn) { let i = -1; for (const elem of this) { if (callbackFn(elem, ++i, this)) { return true; } } return false; } /** * Sorts this list according to the order induced by the specified `Compare` function * @param compare The `Compare` function used to compare list elements. A _undefined_ value indicates that the elements' natural ordering should be used */ sort(compare) { const arr = this.toArray(); const nLen = arr.length; arr.sort(compare); for (let i = 0; i < nLen; i++) { this.set(i, arr[i]); } } } /** * This class provides a skeletal implementation of the mutable `Map` interface, to minimize the effort required to implement this interface. */ class AbstractMap { /** Removes the mapping for a key from this map if it is present */ delete(key) { return (this.remove(key) !== undefined); } /** +Returns _true_ if this map contains no key-value mappings */ isEmpty() { return this.size == 0; } ; } /** * This class provides skeletal implementations of some `Queue` operations. */ class AbstractQueue extends AbstractCollection { } /** * This class provides a skeletal implementation of the mutable `Set` interface to minimize the effort required to implement this interface */ class AbstractSet extends AbstractCollection { } class UnsupportedOperationException extends RunTimeException { } class IndexOutOfBoundsException extends RunTimeException { } class ConcurrentModificationException extends RunTimeException { } var _a$a; /** * This class is denoted to work with a sub set of items in the parent collection */ class ListView extends AbstractList { constructor(parent, fromIndex, toIndex) { super(); this._head = null; this._tail = null; this._ownerFromIndex = 0; this._ownerToIndex = 0; this._size = 0; this[_a$a] = "ListView"; this._parentFromIndex = Math.max(fromIndex, 0); this._parentToIndex = Math.min(toIndex, parent.size); this._ownerFromIndex = this._parentFromIndex; this._ownerToIndex = this._parentToIndex; this._parent = parent; this._owner = parent; let range; let p = parent; while (p) { this._owner = p; range = p.getParentRange(); this._ownerFromIndex += range[0]; p = p.getParent(); } this._ownerToIndex = this._ownerFromIndex + (toIndex - fromIndex); this._savedModCount = this._owner.modCount; if (this._parentFromIndex >= 0 && this._parentToIndex > 0 && this._parentFromIndex <= this._parentToIndex) { this._head = parent.getNode(this._parentFromIndex); this._tail = parent.getNode(this._parentToIndex - 1); if (this._head && this._tail) { this._size = 1; let node = this._head; while (node != this._tail) { this._size++; node = node.next; } } } } /** * @override */ get size() { return this._size; } /** * @private */ _checkModCount() { if (this._savedModCount != this._owner.modCount) { throw new ConcurrentModificationException(); } } *[Symbol.iterator]() { let nextNode; let isTail; let node = this._head; while (node) { nextNode = node.next; isTail = (node == this._tail); yield node.value; if (isTail) break; else node = nextNode; } } clone() { return new ListView(this._parent, this._parentFromIndex, this._parentToIndex); } /** * @override */ getNode(index) { return this._owner.getNode(this._ownerFromIndex + index); } /** * @override */ getParentRange() { return [this._parentFromIndex, this._parentToIndex]; } /** * @override */ getParent() { return this._parent; } /** * @override * @throws ConcurrentModificationException */ clear() { this._checkModCount(); if (this._owner.removeRange(this._ownerFromIndex, this._ownerToIndex)) { this._ownerToIndex = this._ownerFromIndex; this._head = this._tail = null; this._size = 0; this._savedModCount = this._owner.modCount; } } /** * @override * @throws ConcurrentModificationException */ get(index) { this._checkModCount(); return this._owner.get(this._ownerFromIndex + index); } /** * @override * @throws ConcurrentModificationException */ indexOf(value, fromIndex) { this._checkModCount(); return this._owner.indexOf(value, this._ownerFromIndex + (fromIndex !== null && fromIndex !== void 0 ? fromIndex : 0)); } /** * @override * @throws ConcurrentModificationException */ insert(index, value) { this._checkModCount(); if (this._owner.insert(this._ownerFromIndex + index, value)) { if (index == 0) { this._head = this._owner.getNode(this._ownerFromIndex); if (!this._tail) { this._tail = this._head; } } else if ((this._ownerFromIndex + index) > this._ownerToIndex) { this._tail = this._owner.getNode(this._ownerToIndex + 1); if (!this._head) { this._head = this._tail; } } this._size++; this._ownerToIndex++; this._savedModCount = this._owner.modCount; return true; } return false; } /** * @override * @throws ConcurrentModificationException */ lastIndexOf(value, fromIndex) { this._checkModCount(); return this._owner.lastIndexOf(value, this._ownerFromIndex + (fromIndex !== null && fromIndex !== void 0 ? fromIndex : 0)); } /** * @override */ removeRange(fromIndex, toIndex) { this._checkModCount(); return this._owner.removeRange(this._ownerFromIndex + fromIndex, this._ownerToIndex + (toIndex !== null && toIndex !== void 0 ? toIndex : this.size)); } /** * @override * @throws ConcurrentModificationException */ set(index, value) { this._checkModCount(); return this._owner.set(this._ownerFromIndex + index, value); } /** * @override * @throws ConcurrentModificationException */ subList(fromIndex, toIndex) { this._checkModCount(); return new ListView(this, fromIndex, toIndex); } /** * @override * @throws ConcurrentModificationException */ append(value) { this._checkModCount(); if (this._owner.insert(this._ownerToIndex, value)) { this._tail = this._owner.getNode(this._ownerToIndex); if (!this._head) this._head = this._tail; this._size++; return true; } return false; } /** * @override */ contains(value) { let node = this._head; while (node) { if (node.value === value) { return true; } if (node == this._tail) break; else node = node.next; } return false; } /** * @override */ toArray() { const vals = []; let currentNode = this._head; while (currentNode) { vals.push(currentNode.value); if (currentNode == this._tail) break; currentNode = currentNode.next; } return vals; } /** * @override * @throws ConcurrentModificationException */ remove(value) { const index = this.indexOf(value); if (index >= 0) { return (this.removeAt(index) !== undefined); } return false; } /** * @override * @throws ConcurrentModificationException */ removeAt(index) { this._checkModCount(); const node = this._owner.getNode(this._ownerFromIndex + index); let item = this._owner.removeAt(this._ownerFromIndex + index); if (!(item === undefined)) { if (this._head == this._tail && node == this._head) { this._head = this._tail = null; } else if (this._head && node == this._head) { this._head = this._head.next; } else if (this._tail && node == this._tail) { this._tail = this._tail.prev; } this._size--; this._ownerToIndex--; this._savedModCount = this._owner.modCount; return item; } return undefined; } } _a$a = Symbol.toStringTag; class NullPointerException extends RunTimeException { } const LIKE_RE = /([\\.\\\\\\+\\*\\?\\[\\^\\]\\$\\(\\)\\{\\}\\=\\!\\<\\>\\|\\:\\-])/g; const eLBR = encodeURI('{'); const eRBR = encodeURI('}'); /** * This class consists exclusively of the static methods that operate on or return string values */ class Strings { /** * Checks for string emptiness. String is empty if it doesn't contain any characters or equals to null/undefined. * @param str String value to check * @returns true/false */ static isEmpty(str) { return (Strings.trimAll(str)) ? false : true; } /** * Returns a new string if the specified string is empty, i.e. has only blank characters, _null_ or _undefined_ * @param str String value to check * @param def String value to return if `str` is empty * @returns `str` or `def` string if `str` is empty */ static ifEmpty(str, def) { return Strings.isEmpty(str) ? def : str; } /* https://stackoverflow.com/questions/1314045/emulating-sql-like-in-javascript */ /** * SQL Like testing function * * @param str Source string for testing * @param search Search pattern that may has wildcards of sql **LIKE** operator. * The percent sign (%) represents zero, one, or multiple characters * The underscore sign (_) represents one, single character * @returns _true_ if check matches, _false_ if not */ static like(str, search) { if (typeof str !== 'string') { return false; } if (typeof search !== 'string') { return false; } // Remove special chars search = search.replace(LIKE_RE, "\\$1"); // Replace % and _ with equivalent regex search = search.replace(/%/g, '.*').replace(/_/g, '.'); // Check matches? return RegExp('^' + search + '$', 'gi').test(str); } /** * Obtains a specified number of characters from the beginning of a string. * @param str The string you want to search * @param len A long specifying the number of characters you want to return * @returns {string} Returns the leftmost `len` characters in string if it succeeds and the empty string ("") if an error occurs. * If any argument's value is null, *left* returns null. If `len` is greater than or equal to the length of the string, function returns the entire string. It does not add spaces to make the return value's length equal to `len`. * @see {@link right} */ static left(str, len) { if (typeof (str) == "string" && typeof (len) == "number") { return str.substring(0, len); } return null; } /** * Obtains a specified number of characters from the end of a string. * @param str The string from which you want characters returned * @param len A long whose value is the number of characters you want returned from the right end of string * @returns Returns the rightmost `len` characters in string if it succeeds and the empty string ("") if an error occurs. If any argument's value is null, function returns null. If `len` is greater than or equal to the length of the string, *right* returns the entire string. It does not add spaces to make the return value's length equal to `len`. * @see {@link left} */ static right(str, len) { if (typeof (str) == "string" && typeof (len) == "number") { return str.substring(str.length - len); } return null; } /** * Removes leading and trailing specified characters from a string. * @param str The string you want returned with leading and trailing `chars` deleted * @param chars Characters to delete from the start and the end of a string. Default is all types of spaces. * @returns Returns a copy of string with all leading and trailing `chars` deleted if it succeeds. * If string is null, _trimAll_ returns null. * @see {@link trimLeft} * @see {@link trimRight} */ static trimAll(str, chars) { return Strings.trimRight(Strings.trimLeft(str, chars), chars); } /** * Removes trailing specified characters from a string. * @param str The string you want returned with trailing `chars` deleted * @param chars Characters to delete from the end of a string. Default is all types of spaces. * @returns Returns a copy of string with all trailing `chars` deleted if it succeeds. * If string is null, _trimRight_ returns null. * @see {@link trimLeft} * @see {@link trimAll} */ static trimRight(str, chars) { if (!(typeof (str) === "string")) return null; if (!chars) return str.trimEnd(); let start = 0, end = str.length; while (end > start && chars.indexOf(str[end - 1]) >= 0) --end; return (end < str.length) ? str.substring(start, end) : str; } /** * Removes leading specified characters from a string. * @param str The string you want returned with leading `chars` deleted * @param chars Characters to delete from the start of a string. Default is all types of spaces. * @returns Returns a copy of string with all leading `chars` deleted if it succeeds. * If `str` is null, _trimLeft_ returns null. * @see {@link trimAll} * @see {@link trimRight} */ static trimLeft(str, chars) { if (!(typeof (str) === "string")) return null; if (!chars) return str.trimStart(); let start = 0, end = str.length; while (start < end && chars.indexOf(str[start]) >= 0) ++start; return (start > 0) ? str.substring(start, end) : str; } /** * Bites out the _first_ element of the splitted string `str` on the base of the `separator` value. If the `separator` is undefined, returns a whole string * @param str Source string * @param separator String-delimitor * @returns Tuple of two elements: Token string and the Remaining part of the `str` after the bitting token */ static getToken(str, separator) { if (!(typeof str === "string" && typeof separator === "string")) return [undefined, undefined]; const sep = str.indexOf(separator); let token = ""; let rest = ""; if (sep >= 0) { token = str.substring(0, sep); rest = str.substring(sep + separator.length); } else { token = str; } return [token, rest]; } /** * Bites out the _last_ element of the splitted string `str` on the base of the `separator` value. If the `separator` is undefined, returns a whole string * @param str Source string * @param separator String-delimitor * @returns Tuple of two elements: Token string and the Remaining part of the `str` after the bitting token */ static getLastToken(str, separator) { if (!(typeof str === "string" && typeof separator === "string")) return [undefined, undefined]; const sep = str.lastIndexOf(separator); let token = ""; let rest = ""; if (sep >= 0) { token = str.substring(sep + separator.length); rest = str.substring(0, sep); } else { token = str; } return [token, rest]; } /** * Bites out _all_ elements of the splitted string `str` on the base of the `separator` value(s). * @param str Source string * @param separator String or array of the delimiter characters. Default are whitespace characters. * @returns Array of string tokens * @since 0.5.0 */ static getTokens(str, separator = " \t\r\n") { let output = []; if (!(typeof str === "string") || str === "") return output; if (!separator) return [str]; let issep = false; for (const char of str) { if (separator.indexOf(char) >= 0) { if (!issep || separator.length == 1) { output.push(""); } issep = true; } else { issep = false; if (output.length == 0) { output.push(""); } output[output.length - 1] += char; } } return output; } /** * Gets the value portion of a keyword=value pair from a string * @param source The string to be searched * @param keyword The keyword to be searched for * @param separator The separator character used in the source string * @returns The value found for the `keyword`. If no matching keyword is found, _undefined_ is returned. * If any argument's value is _null_, the function returns _null_. * @since 0.5.0 * @example * let valB = Strings.getKeyValue("a=123; b=foo; c=true", "b", ";"); // foo * let valM = Strings.getKeyValue("a=123; b=foo; c=true", "missing", ";"); // undefined */ static getKeyValue(source, keyword, separator) { if (!(typeof (source) == "string" && typeof (keyword) == "string" && typeof (separator) == "string")) return null; let done = false; let iKeyWord, iSeparator, iEqual, iKeyLen, iSepLen; let sValue = undefined, sSaved, sLeftPart; keyword = keyword.toLowerCase(); iKeyLen = keyword.length; iSepLen = separator.length; while (!done) { iKeyWord = source.toLowerCase().indexOf(keyword); if (iKeyWord >= 0) { sSaved = source; source = Strings.trimLeft(Strings.right(source, source.length - (iKeyWord + iKeyLen))); // See if this is an exact match. Either the match will be at the start of the string or // after a separator character. So check for both cases if (separator.endsWith(" ")) { iEqual = iKeyWord - iSepLen; if (iEqual > 0) { if (source.substring(iEqual, iEqual + iSepLen) != separator) { // not the separator string so continue looking continue; } } } else { sLeftPart = Strings.trimRight(Strings.left(sSaved, iKeyWord - 1)); if (sLeftPart) { if (Strings.right(sLeftPart, iSepLen) != separator) { continue; } } } if (source === null || source === void 0 ? void 0 : source.startsWith('=')) { iSeparator = source.indexOf(separator, 1); if (iSeparator >= 0) { sValue = source.substring(1, iSeparator); } else { sValue = source.substring(1); } sValue = Strings.trimAll(sValue) || ""; done = true; } } else { done = true; } } return sValue; } /** * Returns the number that `search` occurs in the string `source` * @param source The source string to be searched * @param search The search string * @returns Number of occurs */ static occurs(source, search) { if (!(typeof (source) == "string" && typeof (search) == "string")) return null; return source.split(search).length - 1; } /** * Reverses the order or characters in a string. * @param str A string whose characters you want to reorder so that the last character is first and the first character is last * @returns A string with the characters of `str` in reversed order. Returns the empty string if it fails. */ static reverse(str) { if (str == null) return null; return str.split("").reverse().join(""); } /** * Replaces all matches of `search` string with `newstr` value in the source string `str` * @param str The source string * @param search Search string to replace * @param newstr Replacing string value * @param ignoreCase Set _true_, if you want to ignore character case. Default is _false_ * @returns New string or _null_ if any argument has a nullish value. */ static replaceAll(str, search, newstr, ignoreCase = false) { if (str == null || search == null || newstr == null) return null; if (search === newstr) return str; const re = new RegExp(search, "g" + (ignoreCase ? "i" : "")); return str.replace(re, newstr); } /** * Removes single and double quotes from the left and right sides of the string * @param str Source string * @returns Unquoted string */ static unquote(str) { if (str) { let cl, cr; cl = Strings.left(str, 1); cr = Strings.right(str, 1); if ((cl == cr) && (cl == "'" || cl == '"')) { return str.substring(1, str.length - 1); } cl = Strings.left(str, 2); cr = Strings.right(str, 2); if ((cl == cr) && (cl == "\\'" || cl == '\\"')) { return str.substring(2, str.length - 2); } } return str; } /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals */ /** * A function that parses a tagged template literal and returns a closure function * @param strings An array of string values of the template literal. For any template, its length is equal to the number of substitutions (occurrences of ${…}) plus one, and is therefore always non-empty. * @param keys The remaining arguments are related to the expressions (substitutions). Each argument may have an _integer_ or _string_ type. The former is an index of the element in the `values` array. The latest is the key of the object's property that assigned to the last element of the `values` array in the closure. * @returns A closure function that should be called with `values` for each expression in `keys`. The last argument can be a JavaScript object whose key names can be used as values in `keys`. * @example * let t1closure = Strings.parseTemplate`${0}${1}${0}!`; * t1closure('Y', 'A'); // "YAY!" * let t2closure = Strings.parseTemplate`${0} ${'foo'}!`; * t2closure('Hello', {foo: 'World'}); // "Hello World!" * (Strings.parseTemplate`${0} ${'user'}!`)('Hello', {user: 'Mike'}); // "Hello Mike!" */ static parseTemplate(strings, ...keys) { return (...values) => { let value; let result = [strings[0]]; let nLen = Array.isArray(values) ? values.length : 0; if (nLen > 0) { let dict = values[nLen - 1] || {}; keys.forEach((key, i) => { value = undefined; if (Number.isInteger(key)) { if (key >= 0 && key < nLen) { value = values[key]; } } else if (typeof key == "string" && typeof dict == "object") { value = dict[key]; } result.push(value, strings[i + 1]); }); } else { for (let i = 1; i < nLen; i++) { result.push(strings[i]); } } return result.join(''); }; } /** C#-like `Format` method that replaces the format item in a specified `template` with the text equivalent of the value of the `args` array argument. The `template` parameter consists of zero or more runs of text intermixed with zero or more indexed placeholders, called _format items_, that correspond to an object in the parameter list of this method. The formatting process replaces each format item with the text representation of the value of the corresponding object. The syntax of a format item is `{index[,alignment][:formatString]}`, which specifies a mandatory index, the optional length and alignment of the formatted text, and an optional string of format specifier characters that govern how the value of the corresponding object is formatted. The components of a format item are: _index_ A zero-based integer that indicates which element in a list of objects to format. If the object specified by index is null reference, then the format item is replaced by the empty string (""). _alignment_ An optional integer indicating the minimum width of the region to contain the formatted value. If the length of the formatted value is less than alignment, then the region is padded with spaces. If alignment is negative, the formatted value is left justified in the region; if alignment is positive, the formatted value is right justified. If _alignment_ is not specified, the length of the region is the length of the formatted value. The comma is required if alignment is specified. _formatString_ An optional string of format specifiers. If formatString is not specified and the corresponding argument implements the IFormattable interface, then null reference is used. Therefore, all implementations of IFormattable..::.ToString are required to allow nullNothingnullptra null reference (Nothing in Visual Basic) as a format string, and return default formatting of the object representation as a String object. The colon is required if formatString is specified. The leading and trailing brace characters, '{' and '}', are required. To specify a single literal brace character in `template`, specify two leading or trailing brace characters; that is, "{{" or "}}". @param template Template string @param args Argument list @returns Formatted string or _null_ if the `template` is _null_ @example let s1 = Strings.format("{0} {1}!", "Hello", "World"); // Hello World! let s2 = Strings.format("[{0, 10}]", 1234.56); // [ 1234.56] let s3 = Strings.format("[{0,-10}]", 1234.56); // [1234.56 ] */ static format(template, ...args) { let result = "", argNo, nArgs, alignment, argValue; let token, formatString, indexToken, alignToken, sValue; let lbrPos = 0, rbrPos = 0, colPos = 0, commaPos = 0; let processed = false, lbrEsc = false, rbrEsc = false; if (template == null) return null; nArgs = args.length; //if (nArgs == 0) return template; lbrPos = template.indexOf("{"); if (lbrPos >= 0) { lbrEsc = template.indexOf("{{") >= 0; if (lbrEsc) template = Strings.replaceAll(template, "{{", eLBR); rbrEsc = template.indexOf("}}") >= 0; if (rbrEsc) template = Strings.replaceAll(template, "}}", eRBR); while (lbrPos >= 0) { result += template.substring(0, lbrPos); rbrPos = template.indexOf("}", lbrPos + 1); if (rbrPos < 0) { result += template.substring(lbrPos); template = ""; break; } processed = false; token = template.substring(lbrPos + 1, rbrPos).trim(); if (token != "") { colPos = token.indexOf(":"); if (colPos >= 0) { indexToken = token.substring(0, colPos); formatString = token.substring(colPos + 1).trim(); } else { indexToken = token; formatString = ""; } commaPos = indexToken.indexOf(","); if (commaPos < 0) { alignToken = ""; } else { alignToken = indexToken.substring(commaPos + 1).trim(); indexToken = indexToken.substring(0, commaPos); } argNo = Number.parseInt(indexToken); if (Number.isInteger(argNo)) { argValue = null; if (argNo >= 0 && argNo < nArgs) { argValue = args[argNo]; } sValue = (argValue == null ? "" : String(argValue)); // TODO! // Formatting with formatString alignment = Number.parseInt(alignToken); if (alignment > 0) { result += sValue.trimStart().padStart(alignment); } else if (alignment < 0) { result += sValue.trimEnd().padEnd(-alignment); } else { result += sValue; } processed = true; } } if (!processed) { result += template.substring(lbrPos, rbrPos + 1); } template = template.substring(rbrPos + 1); lbrPos = template.indexOf("{"); } result += template; if (lbrEsc) result = Strings.replaceAll(result, eLBR, "{"); // single "{" if (rbrEsc) result = Strings.replaceAll(result, eRBR, "}"); // single "}" } else { result = template; } return result; } /** * Compare the version string `ver1` against the version string `ver2`. A version * string looks like: **1.2.3.100** or possibly truncated: **1.2.3** or **1.2**. * @param ver1 First version string * @param ver2 Second version string * @returns A positive number if ver1>ver2, _0_ if ver1==ver2 and a negative number if ver1<ver2. * @since 0.5.0 */ static compareVersions(ver1, ver2) { let rc = 0; let thisField, thatField; let thisVer, thatVer; if (ver1 == null || ver2 == null) { return null; } thisVer = ver1.trim(); thatVer = ver2.trim(); if (thisVer !== thatVer) { while (thisVer.length > 0 || thatVer.length > 0) { // Read the first field from the string and // remove the first field from the string [thisField, thisVer] = Strings.getToken(thisVer, "."); [thatField, thatVer] = Strings.getToken(thatVer, "."); thisField = Number.parseInt(thisField); thatField = Number.parseInt(thatField); if (thisField > thatField) { rc = 1; break; } else if (thisField < thatField) { rc = -1; break; } } } return rc; } } //import { ClassCastException } from "./exception/ClassCastException"; const ARRAY_TAG = '[object Array]'; const BOOL_TAG = '[object Boolean]'; const DATE_TAG = '[object Date]'; const FUNC_TAG = '[object Function]'; const NULL_TAG = '[object Null]'; const NUMBER_TAG = '[object Number]'; const OBJECT_TAG = '[object Object]'; const REGEXP_TAG = '[object RegExp]'; const STRING_TAG = '[object String]'; const MAP_TAG = '[object Map]'; const SET_TAG = '[object Set]'; const UNDEFINED_TAG = '[object Undefined]'; const RE_CLASS_NAME = /^\[object\s(.*)\]$/; const FN_TOSTRING = Object.prototype.toString; /** * This class consists of the static utility methods for operating on objects, or checking certain conditions before operation */ class Objects { /** * Returns a string presentation of any JavaScript object * @static * @param value JavaScript object/value of any type * @returns String in the following format: [object \<type\>] */ static toString(value) { return FN_TOSTRING.call(value); } /** * Returns a first not _nullable_ value from the specified `values` * @param values An array or list of values to check * @returns A first not _nullable_ value in the `values` array if any, or _null_ if else. */ static coalesce(...values) { if (values && values.length) { for (let i = 0; i < values.length; i++) { if (!(values[i] == null)) { return values[i]; } } } return null; } /** * * @param v1 * @param v2 * @returns */ static ifNull(v1, v2) { if (v1 === null) return v2; return v1; } static ifUndefined(v1, v2) { return (v1 === undefined) ? v2 : v1; } static ifEmpty(v1, v2) { if (Objects.isEmpty(v1)) { return v2; } return v1; } static ifNotArray(v1, v2) { if (Objects.isArray(v1)) { return v1; } return v2; } static nullIf(v1, v2) { if (v1 === v2) return null; return v1; } static isArray(value) { return Objects.toString(value) == ARRAY_TAG; } static isBoolean(value) { return Objects.toString(value) == BOOL_TAG; } static isCloneable(value) { return "clone" in value && (typeof value.clone === 'function'); } static isDate(value) { return Objects.toString(value) == DATE_TAG || (value instanceof Date); } static isIterable(value) { return Symbol.iterator in value; } static isMap(value) { return value instanceof Map; } static isNull(value) { return (value === null); } static isNumber(value) { return typeof value === 'number' && isFinite(value); } static isSet(value) { return value instanceof Set; } static isString(value) { return Objects.toString(value) === STRING_TAG; } static isUndefined(value) { return (value === undefined); } /** * Check if specified value is empty (falsy) * @static * @param {any} value Any value to check * @returns {boolean} true/false */ static isEmpty(value) { if (value) { const type = Objects.toString(value); switch (type) { case STRING_TAG: return Strings.isEmpty(value); case OBJECT_TAG: return Object.keys(value).length == 0; case ARRAY_TAG: return value.length == 0; case MAP_TAG: return value.size == 0; case SET_TAG: return value.size == 0; } return false; } return true; } /** * Return whether the provided value is a function. * * @static * @param {*} value The value to test. * @returns {boolean} Whether the provided value is a function. * @since 1.0.0 * @example * const a = function () { console.log('foo bar'); }; * const b = { foo: "bar" }; * console.log(Objects.isFunction(a)); // true * console.log(Objects.isFunction(b)); // false */ static isFunction(value) { return Objects.toString(value) == FUNC_TAG; } static isNumberObject(value) { return (Objects.toString(value) === NUMBER_TAG); } /** * Return whether the provided value is an object. * * @static * @param {*} value The value to test. * @returns {boolean} Whether the provided value is an object. * @since 1.0.0 * * @example * const a = { foo: "bar" }; * const b = 'foo bar'; * console.log(Objects.isObject(a)); // true * console.log(Objects.isObject(b)); // false */ static isObject(value) { return (value instanceof Object) && !Array.isArray(value); } static isPlainObject(value) { return !!value && typeof value === 'object' && value.constructor === Object; } static isPrimitive(arg) { return (arg !== Object(arg)); } /** * Objects casting with runtime checkings * @param obj Object to cast * @param AnyClass Target class value or type * @returns Object of type `AnyClass` if succeeds, or _undefined_ if the `obj` is not the `AnyClass`' instance * @example